viriptables.c 36.9 KB
Newer Older
1
/*
2 3
 * viriptables.c: helper APIs for managing iptables
 *
4
 * Copyright (C) 2007-2014 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library.  If not, see
O
Osier Yang 已提交
18
 * <http://www.gnu.org/licenses/>.
19 20
 */

21
#include <config.h>
22 23 24 25 26 27

#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
28

29
#include "internal.h"
30
#include "viriptables.h"
31
#include "vircommand.h"
32
#include "viralloc.h"
33
#include "virerror.h"
34
#include "virfile.h"
35
#include "virlog.h"
36
#include "virthread.h"
37 38
#include "virstring.h"
#include "virutil.h"
39
#include "virhash.h"
40

41 42
VIR_LOG_INIT("util.iptables");

43
#define VIR_FROM_THIS VIR_FROM_NONE
44

45 46 47 48 49
enum {
    ADD = 0,
    REMOVE
};

50
static bool deletePrivate = true;
51

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
typedef struct {
    const char *parent;
    const char *child;
} iptablesGlobalChain;

typedef struct {
    virFirewallLayer layer;
    const char *table;
    iptablesGlobalChain *chains;
    size_t nchains;
    bool *changed;
} iptablesGlobalChainData;


static int
iptablesPrivateChainCreate(virFirewallPtr fw,
                           virFirewallLayer layer,
                           const char *const *lines,
                           void *opaque)
{
    iptablesGlobalChainData *data = opaque;
    virHashTablePtr chains = NULL;
    virHashTablePtr links = NULL;
    const char *const *tmp;
    int ret = -1;
    size_t i;

    if (!(chains = virHashCreate(50, NULL)))
        goto cleanup;
    if (!(links = virHashCreate(50, NULL)))
        goto cleanup;

    tmp = lines;
    while (tmp && *tmp) {
        if (STRPREFIX(*tmp, "-N ")) { /* eg "-N LIBVIRT_INP" */
            if (virHashUpdateEntry(chains, *tmp + 3, (void *)0x1) < 0)
                goto cleanup;
        } else if (STRPREFIX(*tmp, "-A ")) { /* eg "-A INPUT -j LIBVIRT_INP" */
            char *sep = strchr(*tmp + 3, ' ');
            if (sep) {
                *sep = '\0';
                if (STRPREFIX(sep + 1, "-j ")) {
                    if (virHashUpdateEntry(links, sep + 4,
                                           (char *)*tmp + 3) < 0)
                        goto cleanup;
                }
            }
        }
        tmp++;
    }

    for (i = 0; i < data->nchains; i++) {
        const char *from;
        if (!virHashLookup(chains, data->chains[i].child)) {
            virFirewallAddRule(fw, layer,
                               "--table", data->table,
                               "--new-chain", data->chains[i].child, NULL);
            *data->changed = true;
        }

        from = virHashLookup(links, data->chains[i].child);
        if (!from || STRNEQ(from, data->chains[i].parent))
            virFirewallAddRule(fw, layer,
                               "--table", data->table,
                               "--insert", data->chains[i].parent,
                               "--jump", data->chains[i].child, NULL);
    }

    ret = 0;
 cleanup:
    virHashFree(chains);
    virHashFree(links);
    return ret;
}


int
129
iptablesSetupPrivateChains(virFirewallLayer layer)
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
{
    virFirewallPtr fw = NULL;
    int ret = -1;
    iptablesGlobalChain filter_chains[] = {
        {"INPUT", "LIBVIRT_INP"},
        {"OUTPUT", "LIBVIRT_OUT"},
        {"FORWARD", "LIBVIRT_FWO"},
        {"FORWARD", "LIBVIRT_FWI"},
        {"FORWARD", "LIBVIRT_FWX"},
    };
    iptablesGlobalChain natmangle_chains[] = {
        {"POSTROUTING",  "LIBVIRT_PRT"},
    };
    bool changed = false;
    iptablesGlobalChainData data[] = {
145
        { layer, "filter",
146
          filter_chains, G_N_ELEMENTS(filter_chains), &changed },
147
        { layer, "nat",
148
          natmangle_chains, G_N_ELEMENTS(natmangle_chains), &changed },
149
        { layer, "mangle",
150
          natmangle_chains, G_N_ELEMENTS(natmangle_chains), &changed },
151 152 153 154 155 156 157
    };
    size_t i;

    fw = virFirewallNew();

    virFirewallStartTransaction(fw, 0);

158
    for (i = 0; i < G_N_ELEMENTS(data); i++)
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
        virFirewallAddRuleFull(fw, data[i].layer,
                               false, iptablesPrivateChainCreate,
                               &(data[i]), "--table", data[i].table,
                               "--list-rules", NULL);

    if (virFirewallApply(fw) < 0)
        goto cleanup;

    ret = changed ? 1 : 0;

 cleanup:

    virFirewallFree(fw);
    return ret;
}


176 177 178 179 180 181 182
void
iptablesSetDeletePrivate(bool pvt)
{
    deletePrivate = pvt;
}


183 184 185
static void
iptablesInput(virFirewallPtr fw,
              virFirewallLayer layer,
186
              bool pvt,
187 188 189 190 191 192 193
              const char *iface,
              int port,
              int action,
              int tcp)
{
    char portstr[32];

194
    g_snprintf(portstr, sizeof(portstr), "%d", port);
195 196
    portstr[sizeof(portstr) - 1] = '\0';

197 198
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
199 200
                       action == ADD ? "--insert" : "--delete",
                       pvt ? "LIBVIRT_INP" : "INPUT",
201 202 203 204 205
                       "--in-interface", iface,
                       "--protocol", tcp ? "tcp" : "udp",
                       "--destination-port", portstr,
                       "--jump", "ACCEPT",
                       NULL);
206 207
}

208 209 210
static void
iptablesOutput(virFirewallPtr fw,
               virFirewallLayer layer,
211
               bool pvt,
212 213 214 215 216 217 218
               const char *iface,
               int port,
               int action,
               int tcp)
{
    char portstr[32];

219
    g_snprintf(portstr, sizeof(portstr), "%d", port);
220 221
    portstr[sizeof(portstr) - 1] = '\0';

222 223
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
224 225
                       action == ADD ? "--insert" : "--delete",
                       pvt ? "LIBVIRT_OUT" : "OUTPUT",
226 227 228 229 230
                       "--out-interface", iface,
                       "--protocol", tcp ? "tcp" : "udp",
                       "--destination-port", portstr,
                       "--jump", "ACCEPT",
                       NULL);
231 232
}

233 234 235 236 237 238 239 240 241
/**
 * iptablesAddTcpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the TCP port to add
 *
 * Add an input to the IP table allowing access to the given @port on
 * the given @iface interface for TCP packets
 */
242 243 244
void
iptablesAddTcpInput(virFirewallPtr fw,
                    virFirewallLayer layer,
245 246 247
                    const char *iface,
                    int port)
{
248
    iptablesInput(fw, layer, true, iface, port, ADD, 1);
249 250
}

251 252 253 254 255 256
/**
 * iptablesRemoveTcpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the TCP port to remove
 *
R
Richard W.M. Jones 已提交
257
 * Removes an input from the IP table, hence forbidding access to the given
258 259
 * @port on the given @iface interface for TCP packets
 */
260 261 262
void
iptablesRemoveTcpInput(virFirewallPtr fw,
                       virFirewallLayer layer,
263 264 265
                       const char *iface,
                       int port)
{
266
    iptablesInput(fw, layer, deletePrivate, iface, port, REMOVE, 1);
267 268
}

269 270 271 272 273 274 275 276 277
/**
 * iptablesAddUdpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to add
 *
 * Add an input to the IP table allowing access to the given @port on
 * the given @iface interface for UDP packets
 */
278 279 280
void
iptablesAddUdpInput(virFirewallPtr fw,
                    virFirewallLayer layer,
281 282 283
                    const char *iface,
                    int port)
{
284
    iptablesInput(fw, layer, true, iface, port, ADD, 0);
285 286
}

287 288 289 290 291 292
/**
 * iptablesRemoveUdpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to remove
 *
R
Richard W.M. Jones 已提交
293
 * Removes an input from the IP table, hence forbidding access to the given
294 295
 * @port on the given @iface interface for UDP packets
 */
296 297 298
void
iptablesRemoveUdpInput(virFirewallPtr fw,
                       virFirewallLayer layer,
299 300 301
                       const char *iface,
                       int port)
{
302
    iptablesInput(fw, layer, deletePrivate, iface, port, REMOVE, 0);
303 304
}

305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
/**
 * iptablesAddTcpOutput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the TCP port to add
 *
 * Add an output to the IP table allowing access to the given @port from
 * the given @iface interface for TCP packets
 */
void
iptablesAddTcpOutput(virFirewallPtr fw,
                     virFirewallLayer layer,
                     const char *iface,
                     int port)
{
    iptablesOutput(fw, layer, true, iface, port, ADD, 1);
}

/**
 * iptablesRemoveTcpOutput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to remove
 *
 * Removes an output from the IP table, hence forbidding access to the given
 * @port from the given @iface interface for TCP packets
 */
void
iptablesRemoveTcpOutput(virFirewallPtr fw,
                        virFirewallLayer layer,
                        const char *iface,
                        int port)
{
    iptablesOutput(fw, layer, deletePrivate, iface, port, REMOVE, 1);
}

341 342 343 344 345 346 347 348 349
/**
 * iptablesAddUdpOutput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to add
 *
 * Add an output to the IP table allowing access to the given @port from
 * the given @iface interface for UDP packets
 */
350 351 352
void
iptablesAddUdpOutput(virFirewallPtr fw,
                     virFirewallLayer layer,
353 354 355
                     const char *iface,
                     int port)
{
356
    iptablesOutput(fw, layer, true, iface, port, ADD, 0);
357 358 359 360 361 362 363 364 365 366 367
}

/**
 * iptablesRemoveUdpOutput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to remove
 *
 * Removes an output from the IP table, hence forbidding access to the given
 * @port from the given @iface interface for UDP packets
 */
368 369 370
void
iptablesRemoveUdpOutput(virFirewallPtr fw,
                        virFirewallLayer layer,
371 372 373
                        const char *iface,
                        int port)
{
374
    iptablesOutput(fw, layer, deletePrivate, iface, port, REMOVE, 0);
375 376
}

377

378
static char *iptablesFormatNetwork(virSocketAddr *netaddr,
379
                                   unsigned int prefix)
380 381
{
    virSocketAddr network;
382
    g_autofree char *netstr = NULL;
383 384
    char *ret;

385 386
    if (!(VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET) ||
          VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET6))) {
387 388
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Only IPv4 or IPv6 addresses can be used with iptables"));
389 390 391
        return NULL;
    }

392
    if (virSocketAddrMaskByPrefix(netaddr, prefix, &network) < 0) {
393 394
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failure to mask address"));
395 396
        return NULL;
    }
397

398
    netstr = virSocketAddrFormat(&network);
399 400 401 402

    if (!netstr)
        return NULL;

403
    ret = g_strdup_printf("%s/%d", netstr, prefix);
404 405 406 407 408

    return ret;
}


409 410 411
/* Allow all traffic coming from the bridge, with a valid network address
 * to proceed to WAN
 */
412
static int
413
iptablesForwardAllowOut(virFirewallPtr fw,
414
                        bool pvt,
415
                        virSocketAddr *netaddr,
416
                        unsigned int prefix,
417 418 419
                        const char *iface,
                        const char *physdev,
                        int action)
420
{
421
    g_autofree char *networkstr = NULL;
422 423
    virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ?
        VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
424

425
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
426 427
        return -1;

428
    if (physdev && physdev[0])
429 430
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
431 432
                           action == ADD ? "--insert" : "--delete",
                           pvt ? "LIBVIRT_FWO" : "FORWARD",
433 434 435 436 437 438 439 440
                           "--source", networkstr,
                           "--in-interface", iface,
                           "--out-interface", physdev,
                           "--jump", "ACCEPT",
                           NULL);
    else
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
441 442
                           action == ADD ? "--insert" : "--delete",
                           pvt ? "LIBVIRT_FWO" : "FORWARD",
443 444 445 446
                           "--source", networkstr,
                           "--in-interface", iface,
                           "--jump", "ACCEPT",
                           NULL);
447

448
    return 0;
449 450
}

451 452 453 454 455 456
/**
 * iptablesAddForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
457
 *
458 459 460 461 462 463
 * Add a rule to the IP table context to allow the traffic for the
 * network @network via interface @iface to be forwarded to
 * @physdev device. This allow the outbound traffic on a bridge.
 *
 * Returns 0 in case of success or an error code otherwise
 */
464
int
465 466
iptablesAddForwardAllowOut(virFirewallPtr fw,
                           virSocketAddr *netaddr,
467
                           unsigned int prefix,
468 469
                           const char *iface,
                           const char *physdev)
470
{
471
    return iptablesForwardAllowOut(fw, true, netaddr, prefix, iface, physdev, ADD);
472 473
}

474 475 476 477 478 479
/**
 * iptablesRemoveForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
480
 *
481 482 483 484 485 486
 * Remove a rule from the IP table context hence forbidding forwarding
 * of the traffic for the network @network via interface @iface
 * to the @physdev device output. This stops the outbound traffic on a bridge.
 *
 * Returns 0 in case of success or an error code otherwise
 */
487
int
488 489
iptablesRemoveForwardAllowOut(virFirewallPtr fw,
                              virSocketAddr *netaddr,
490
                              unsigned int prefix,
491 492
                              const char *iface,
                              const char *physdev)
493
{
494
    return iptablesForwardAllowOut(fw, deletePrivate, netaddr, prefix, iface, physdev, REMOVE);
495 496
}

497 498 499 500

/* Allow all traffic destined to the bridge, with a valid network address
 * and associated with an existing connection
 */
501
static int
502
iptablesForwardAllowRelatedIn(virFirewallPtr fw,
503
                              bool pvt,
504
                              virSocketAddr *netaddr,
505
                              unsigned int prefix,
506 507 508
                              const char *iface,
                              const char *physdev,
                              int action)
509
{
510 511
    virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ?
        VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
512
    g_autofree char *networkstr = NULL;
513

514
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
515 516
        return -1;

517 518 519
    if (physdev && physdev[0])
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
520 521
                           action == ADD ? "--insert" : "--delete",
                           pvt ? "LIBVIRT_FWI" : "FORWARD",
522 523 524 525 526 527 528 529 530 531
                           "--destination", networkstr,
                           "--in-interface", physdev,
                           "--out-interface", iface,
                           "--match", "conntrack",
                           "--ctstate", "ESTABLISHED,RELATED",
                           "--jump", "ACCEPT",
                           NULL);
    else
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
532 533
                           action == ADD ? "--insert" : "--delete",
                           pvt ? "LIBVIRT_FWI" : "FORWARD",
534 535 536 537 538 539 540 541
                           "--destination", networkstr,
                           "--out-interface", iface,
                           "--match", "conntrack",
                           "--ctstate", "ESTABLISHED,RELATED",
                           "--jump", "ACCEPT",
                           NULL);

    return 0;
542 543
}

544 545 546 547 548 549 550 551 552 553 554 555 556 557
/**
 * iptablesAddForwardAllowRelatedIn:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the output interface name
 * @physdev: the physical input device or NULL
 *
 * Add rules to the IP table context to allow the traffic for the
 * network @network on @physdev device to be forwarded to
 * interface @iface, if it is part of an existing connection.
 *
 * Returns 0 in case of success or an error code otherwise
 */
int
558 559
iptablesAddForwardAllowRelatedIn(virFirewallPtr fw,
                                 virSocketAddr *netaddr,
560
                                 unsigned int prefix,
561 562
                                 const char *iface,
                                 const char *physdev)
563
{
564
    return iptablesForwardAllowRelatedIn(fw, true, netaddr, prefix, iface, physdev, ADD);
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
}

/**
 * iptablesRemoveForwardAllowRelatedIn:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the output interface name
 * @physdev: the physical input device or NULL
 *
 * Remove rules from the IP table context hence forbidding the traffic for
 * network @network on @physdev device to be forwarded to
 * interface @iface, if it is part of an existing connection.
 *
 * Returns 0 in case of success or an error code otherwise
 */
int
581 582
iptablesRemoveForwardAllowRelatedIn(virFirewallPtr fw,
                                    virSocketAddr *netaddr,
583
                                    unsigned int prefix,
584 585
                                    const char *iface,
                                    const char *physdev)
586
{
587
    return iptablesForwardAllowRelatedIn(fw, deletePrivate, netaddr, prefix, iface, physdev, REMOVE);
588 589 590 591 592
}

/* Allow all traffic destined to the bridge, with a valid network address
 */
static int
593
iptablesForwardAllowIn(virFirewallPtr fw,
594
                       bool pvt,
595
                       virSocketAddr *netaddr,
596
                       unsigned int prefix,
597 598 599 600
                       const char *iface,
                       const char *physdev,
                       int action)
{
601 602
    virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ?
        VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
603
    g_autofree char *networkstr = NULL;
604

605
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
606 607
        return -1;

608 609 610
    if (physdev && physdev[0])
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
611 612
                           action == ADD ? "--insert" : "--delete",
                           pvt ? "LIBVIRT_FWI" : "FORWARD",
613 614 615 616 617 618 619 620
                           "--destination", networkstr,
                           "--in-interface", physdev,
                           "--out-interface", iface,
                           "--jump", "ACCEPT",
                           NULL);
    else
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
621 622
                           action == ADD ? "--insert" : "--delete",
                           pvt ? "LIBVIRT_FWI" : "FORWARD",
623 624 625 626 627
                           "--destination", networkstr,
                           "--out-interface", iface,
                           "--jump", "ACCEPT",
                           NULL);
    return 0;
628 629
}

630 631 632 633 634 635
/**
 * iptablesAddForwardAllowIn:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the output interface name
 * @physdev: the physical input device or NULL
636
 *
637 638 639 640 641 642
 * Add rules to the IP table context to allow the traffic for the
 * network @network on @physdev device to be forwarded to
 * interface @iface. This allow the inbound traffic on a bridge.
 *
 * Returns 0 in case of success or an error code otherwise
 */
643
int
644 645
iptablesAddForwardAllowIn(virFirewallPtr fw,
                          virSocketAddr *netaddr,
646
                          unsigned int prefix,
647 648 649
                          const char *iface,
                          const char *physdev)
{
650
    return iptablesForwardAllowIn(fw, true, netaddr, prefix, iface, physdev, ADD);
651 652
}

653 654 655 656 657 658
/**
 * iptablesRemoveForwardAllowIn:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the output interface name
 * @physdev: the physical input device or NULL
659
 *
660 661 662 663 664 665
 * Remove rules from the IP table context hence forbidding the traffic for
 * network @network on @physdev device to be forwarded to
 * interface @iface. This stops the inbound traffic on a bridge.
 *
 * Returns 0 in case of success or an error code otherwise
 */
666
int
667 668
iptablesRemoveForwardAllowIn(virFirewallPtr fw,
                             virSocketAddr *netaddr,
669
                             unsigned int prefix,
670 671 672
                             const char *iface,
                             const char *physdev)
{
673
    return iptablesForwardAllowIn(fw, deletePrivate, netaddr, prefix, iface, physdev, REMOVE);
674 675
}

676 677 678
static void
iptablesForwardAllowCross(virFirewallPtr fw,
                          virFirewallLayer layer,
679
                          bool pvt,
680 681 682 683 684
                          const char *iface,
                          int action)
{
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
685 686
                       action == ADD ? "--insert" : "--delete",
                       pvt ? "LIBVIRT_FWX" : "FORWARD",
687 688 689 690 691 692
                       "--in-interface", iface,
                       "--out-interface", iface,
                       "--jump", "ACCEPT",
                       NULL);
}

693 694 695 696 697 698 699 700 701 702 703
/**
 * iptablesAddForwardAllowCross:
 * @ctx: pointer to the IP table context
 * @iface: the input/output interface name
 *
 * Add rules to the IP table context to allow traffic to cross that
 * interface. It allows all traffic between guests on the same bridge
 * represented by that interface.
 *
 * Returns 0 in case of success or an error code otherwise
 */
704 705 706
void
iptablesAddForwardAllowCross(virFirewallPtr fw,
                             virFirewallLayer layer,
707 708
                             const char *iface)
{
709
    iptablesForwardAllowCross(fw, layer, true, iface, ADD);
710 711
}

712 713 714 715 716 717 718 719 720 721 722
/**
 * iptablesRemoveForwardAllowCross:
 * @ctx: pointer to the IP table context
 * @iface: the input/output interface name
 *
 * Remove rules to the IP table context to block traffic to cross that
 * interface. It forbids traffic between guests on the same bridge
 * represented by that interface.
 *
 * Returns 0 in case of success or an error code otherwise
 */
723 724 725
void
iptablesRemoveForwardAllowCross(virFirewallPtr fw,
                                virFirewallLayer layer,
726
                                const char *iface)
727
{
728
    iptablesForwardAllowCross(fw, layer, deletePrivate, iface, REMOVE);
729 730 731 732 733
}

static void
iptablesForwardRejectOut(virFirewallPtr fw,
                         virFirewallLayer layer,
734
                         bool pvt,
735 736
                         const char *iface,
                         int action)
737
{
738 739
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
740 741
                       action == ADD ? "--insert" : "--delete",
                       pvt ? "LIBVIRT_FWO" : "FORWARD",
742
                       "--in-interface", iface,
743
                       "--jump", "REJECT",
744
                       NULL);
745 746
}

747 748 749 750 751 752 753 754 755 756
/**
 * iptablesAddForwardRejectOut:
 * @ctx: pointer to the IP table context
 * @iface: the output interface name
 *
 * Add rules to the IP table context to forbid all traffic to that
 * interface. It forbids forwarding from the bridge to that interface.
 *
 * Returns 0 in case of success or an error code otherwise
 */
757 758 759
void
iptablesAddForwardRejectOut(virFirewallPtr fw,
                            virFirewallLayer layer,
760 761
                            const char *iface)
{
762
    iptablesForwardRejectOut(fw, layer, true, iface, ADD);
763 764
}

765 766 767 768 769 770 771 772 773 774
/**
 * iptablesRemoveForwardRejectOut:
 * @ctx: pointer to the IP table context
 * @iface: the output interface name
 *
 * Remove rules from the IP table context forbidding all traffic to that
 * interface. It reallow forwarding from the bridge to that interface.
 *
 * Returns 0 in case of success or an error code otherwise
 */
775 776 777
void
iptablesRemoveForwardRejectOut(virFirewallPtr fw,
                               virFirewallLayer layer,
778
                               const char *iface)
779
{
780
    iptablesForwardRejectOut(fw, layer, deletePrivate, iface, REMOVE);
781 782 783 784 785 786
}


static void
iptablesForwardRejectIn(virFirewallPtr fw,
                        virFirewallLayer layer,
787
                        bool pvt,
788 789
                        const char *iface,
                        int action)
790
{
791 792
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
793 794
                       action == ADD ? "--insert" : "--delete",
                       pvt ? "LIBVIRT_FWI" : "FORWARD",
795
                       "--out-interface", iface,
796 797
                       "--jump", "REJECT",
                       NULL);
798 799
}

800 801 802 803 804 805 806 807 808 809
/**
 * iptablesAddForwardRejectIn:
 * @ctx: pointer to the IP table context
 * @iface: the input interface name
 *
 * Add rules to the IP table context to forbid all traffic from that
 * interface. It forbids forwarding from that interface to the bridge.
 *
 * Returns 0 in case of success or an error code otherwise
 */
810 811 812
void
iptablesAddForwardRejectIn(virFirewallPtr fw,
                           virFirewallLayer layer,
813
                           const char *iface)
814
{
815
    iptablesForwardRejectIn(fw, layer, true, iface, ADD);
816 817
}

818 819 820 821 822 823 824 825 826 827
/**
 * iptablesRemoveForwardRejectIn:
 * @ctx: pointer to the IP table context
 * @iface: the input interface name
 *
 * Remove rules from the IP table context forbidding all traffic from that
 * interface. It allows forwarding from that interface to the bridge.
 *
 * Returns 0 in case of success or an error code otherwise
 */
828 829 830
void
iptablesRemoveForwardRejectIn(virFirewallPtr fw,
                              virFirewallLayer layer,
831
                              const char *iface)
832
{
833
    iptablesForwardRejectIn(fw, layer, deletePrivate, iface, REMOVE);
834 835
}

836 837 838 839

/* Masquerade all traffic coming from the network associated
 * with the bridge
 */
840
static int
841
iptablesForwardMasquerade(virFirewallPtr fw,
842
                          bool pvt,
843
                          virSocketAddr *netaddr,
844
                          unsigned int prefix,
845
                          const char *physdev,
846 847
                          virSocketAddrRangePtr addr,
                          virPortRangePtr port,
848 849
                          const char *protocol,
                          int action)
850
{
851 852 853 854 855
    g_autofree char *networkstr = NULL;
    g_autofree char *addrStartStr = NULL;
    g_autofree char *addrEndStr = NULL;
    g_autofree char *portRangeStr = NULL;
    g_autofree char *natRangeStr = NULL;
856
    virFirewallRulePtr rule;
857

858
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
859 860
        return -1;

861
    if (!VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET)) {
862
        /* Higher level code *should* guaranteee it's impossible to get here. */
863 864 865
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Attempted to NAT '%s'. NAT is only supported for IPv4."),
                       networkstr);
866
        return -1;
867 868
    }

869 870
    if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->start, AF_INET)) {
        if (!(addrStartStr = virSocketAddrFormat(&addr->start)))
871
            return -1;
872 873
        if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->end, AF_INET)) {
            if (!(addrEndStr = virSocketAddrFormat(&addr->end)))
874
                return -1;
875
        }
876 877
    }

878 879 880
    if (protocol && protocol[0]) {
        rule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
                                  "--table", "nat",
881 882
                                  action == ADD ? "--insert" : "--delete",
                                  pvt ? "LIBVIRT_PRT" : "POSTROUTING",
883 884 885 886 887 888 889
                                  "--source", networkstr,
                                  "-p", protocol,
                                  "!", "--destination", networkstr,
                                  NULL);
    } else {
        rule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
                                  "--table", "nat",
890 891
                                  action == ADD ? "--insert" : "--delete",
                                  pvt ? "LIBVIRT_PRT" : "POSTROUTING",
892 893 894 895
                                  "--source", networkstr,
                                  "!", "--destination", networkstr,
                                  NULL);
    }
896 897

    if (physdev && physdev[0])
898
        virFirewallRuleAddArgList(fw, rule, "--out-interface", physdev, NULL);
899

900
    if (protocol && protocol[0]) {
901 902 903
        if (port->start == 0 && port->end == 0) {
            port->start = 1024;
            port->end = 65535;
904 905
        }

906
        if (port->start < port->end && port->end < 65536) {
907
            portRangeStr = g_strdup_printf(":%u-%u", port->start, port->end);
908 909 910
        } else {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Invalid port range '%u-%u'."),
911
                           port->start, port->end);
912
            return -1;
913 914 915
        }
    }

916 917 918
    /* Use --jump SNAT if public addr is specified */
    if (addrStartStr && addrStartStr[0]) {
        if (addrEndStr && addrEndStr[0]) {
919 920
            natRangeStr = g_strdup_printf("%s-%s%s", addrStartStr, addrEndStr,
                                          portRangeStr ? portRangeStr : "");
921
        } else {
922 923
            natRangeStr = g_strdup_printf("%s%s", addrStartStr,
                                          portRangeStr ? portRangeStr : "");
924 925
        }

926 927
        virFirewallRuleAddArgList(fw, rule,
                                  "--jump", "SNAT",
928
                                  "--to-source", natRangeStr, NULL);
929 930 931
    } else {
        virFirewallRuleAddArgList(fw, rule,
                                  "--jump", "MASQUERADE", NULL);
932

933 934 935 936
        if (portRangeStr && portRangeStr[0])
            virFirewallRuleAddArgList(fw, rule,
                                      "--to-ports", &portRangeStr[1], NULL);
    }
937

938
    return 0;
939 940
}

941 942 943 944 945
/**
 * iptablesAddForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
946
 * @protocol: the network protocol or NULL
947
 *
948 949 950 951 952 953
 * Add rules to the IP table context to allow masquerading
 * network @network on @physdev. This allow the bridge to
 * masquerade for that network (on @physdev).
 *
 * Returns 0 in case of success or an error code otherwise
 */
954
int
955 956
iptablesAddForwardMasquerade(virFirewallPtr fw,
                             virSocketAddr *netaddr,
957
                             unsigned int prefix,
958
                             const char *physdev,
959 960
                             virSocketAddrRangePtr addr,
                             virPortRangePtr port,
961
                             const char *protocol)
962
{
963 964
    return iptablesForwardMasquerade(fw, true, netaddr, prefix,
                                     physdev, addr, port, protocol, ADD);
965 966
}

967 968 969 970 971
/**
 * iptablesRemoveForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
972
 * @protocol: the network protocol or NULL
973
 *
974 975 976 977 978 979
 * Remove rules from the IP table context to stop masquerading
 * network @network on @physdev. This stops the bridge from
 * masquerading for that network (on @physdev).
 *
 * Returns 0 in case of success or an error code otherwise
 */
980
int
981 982
iptablesRemoveForwardMasquerade(virFirewallPtr fw,
                                virSocketAddr *netaddr,
983
                                unsigned int prefix,
984
                                const char *physdev,
985 986
                                virSocketAddrRangePtr addr,
                                virPortRangePtr port,
987
                                const char *protocol)
988
{
989 990
    return iptablesForwardMasquerade(fw, deletePrivate, netaddr, prefix,
                                     physdev, addr, port, protocol, REMOVE);
991
}
992 993


994 995 996 997
/* Don't masquerade traffic coming from the network associated with the bridge
 * if said traffic targets @destaddr.
 */
static int
998
iptablesForwardDontMasquerade(virFirewallPtr fw,
999
                              bool pvt,
1000
                              virSocketAddr *netaddr,
1001 1002 1003 1004 1005
                              unsigned int prefix,
                              const char *physdev,
                              const char *destaddr,
                              int action)
{
1006
    g_autofree char *networkstr = NULL;
1007 1008 1009 1010 1011 1012 1013 1014 1015

    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
        return -1;

    if (!VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET)) {
        /* Higher level code *should* guaranteee it's impossible to get here. */
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Attempted to NAT '%s'. NAT is only supported for IPv4."),
                       networkstr);
1016
        return -1;
1017 1018 1019
    }

    if (physdev && physdev[0])
1020 1021
        virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
                           "--table", "nat",
1022 1023
                           action == ADD ? "--insert" : "--delete",
                           pvt ? "LIBVIRT_PRT" : "POSTROUTING",
1024 1025 1026 1027 1028 1029 1030 1031
                           "--out-interface", physdev,
                           "--source", networkstr,
                           "--destination", destaddr,
                           "--jump", "RETURN",
                           NULL);
    else
        virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
                           "--table", "nat",
1032 1033
                           action == ADD ? "--insert" : "--delete",
                           pvt ? "LIBVIRT_PRT" : "POSTROUTING",
1034 1035 1036 1037 1038
                           "--source", networkstr,
                           "--destination", destaddr,
                           "--jump", "RETURN",
                           NULL);

1039
    return 0;
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056
}

/**
 * iptablesAddDontMasquerade:
 * @netaddr: the source network name
 * @prefix: prefix (# of 1 bits) of netmask to apply to @netaddr
 * @physdev: the physical output device or NULL
 * @destaddr: the destination network not to masquerade for
 *
 * Add rules to the IP table context to avoid masquerading from
 * @netaddr/@prefix to @destaddr on @physdev. @destaddr must be in a format
 * directly consumable by iptables, it must not depend on user input or
 * configuration.
 *
 * Returns 0 in case of success or an error code otherwise.
 */
int
1057 1058
iptablesAddDontMasquerade(virFirewallPtr fw,
                          virSocketAddr *netaddr,
1059 1060 1061 1062
                          unsigned int prefix,
                          const char *physdev,
                          const char *destaddr)
{
1063 1064
    return iptablesForwardDontMasquerade(fw, true, netaddr, prefix,
                                         physdev, destaddr, ADD);
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
}

/**
 * iptablesRemoveDontMasquerade:
 * @netaddr: the source network name
 * @prefix: prefix (# of 1 bits) of netmask to apply to @netaddr
 * @physdev: the physical output device or NULL
 * @destaddr: the destination network not to masquerade for
 *
 * Remove rules from the IP table context that prevent masquerading from
 * @netaddr/@prefix to @destaddr on @physdev. @destaddr must be in a format
 * directly consumable by iptables, it must not depend on user input or
 * configuration.
 *
 * Returns 0 in case of success or an error code otherwise.
 */
int
1082 1083
iptablesRemoveDontMasquerade(virFirewallPtr fw,
                             virSocketAddr *netaddr,
1084 1085 1086 1087
                             unsigned int prefix,
                             const char *physdev,
                             const char *destaddr)
{
1088 1089
    return iptablesForwardDontMasquerade(fw, deletePrivate, netaddr, prefix,
                                         physdev, destaddr, REMOVE);
1090 1091 1092
}


1093 1094
static void
iptablesOutputFixUdpChecksum(virFirewallPtr fw,
1095
                             bool pvt,
1096
                             const char *iface,
1097 1098 1099 1100 1101
                             int port,
                             int action)
{
    char portstr[32];

1102
    g_snprintf(portstr, sizeof(portstr), "%d", port);
1103 1104
    portstr[sizeof(portstr) - 1] = '\0';

1105 1106
    virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
                       "--table", "mangle",
1107 1108
                       action == ADD ? "--insert" : "--delete",
                       pvt ? "LIBVIRT_PRT" : "POSTROUTING",
1109 1110 1111 1112 1113
                       "--out-interface", iface,
                       "--protocol", "udp",
                       "--destination-port", portstr,
                       "--jump", "CHECKSUM", "--checksum-fill",
                       NULL);
1114 1115 1116 1117 1118 1119 1120 1121
}

/**
 * iptablesAddOutputFixUdpChecksum:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to match
 *
E
Eric Blake 已提交
1122
 * Add a rule to the mangle table's POSTROUTING chain that fixes up the
1123 1124 1125 1126
 * checksum of packets with the given destination @port.
 * the given @iface interface for TCP packets.
 *
 */
1127 1128 1129
void
iptablesAddOutputFixUdpChecksum(virFirewallPtr fw,
                                const char *iface,
1130 1131
                                int port)
{
1132
    iptablesOutputFixUdpChecksum(fw, true, iface, port, ADD);
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143
}

/**
 * iptablesRemoveOutputFixUdpChecksum:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port of the rule to remove
 *
 * Removes the checksum fixup rule that was previous added with
 * iptablesAddOutputFixUdpChecksum.
 */
1144 1145 1146
void
iptablesRemoveOutputFixUdpChecksum(virFirewallPtr fw,
                                   const char *iface,
1147 1148
                                   int port)
{
1149
    iptablesOutputFixUdpChecksum(fw, deletePrivate, iface, port, REMOVE);
1150
}