viriptables.c 35.0 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>
E
Eric Blake 已提交
28
#include <sys/wait.h>
29

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

42 43
VIR_LOG_INIT("util.iptables");

44
#define VIR_FROM_THIS VIR_FROM_NONE
45

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

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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
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
iptablesSetupPrivateChains(void)
{
    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[] = {
        { VIR_FIREWALL_LAYER_IPV4, "filter",
          filter_chains, ARRAY_CARDINALITY(filter_chains), &changed },
        { VIR_FIREWALL_LAYER_IPV4, "nat",
          natmangle_chains, ARRAY_CARDINALITY(natmangle_chains), &changed },
        { VIR_FIREWALL_LAYER_IPV4, "mangle",
          natmangle_chains, ARRAY_CARDINALITY(natmangle_chains), &changed },
        { VIR_FIREWALL_LAYER_IPV6, "filter",
          filter_chains, ARRAY_CARDINALITY(filter_chains), &changed },
        { VIR_FIREWALL_LAYER_IPV6, "nat",
          natmangle_chains, ARRAY_CARDINALITY(natmangle_chains), &changed },
        { VIR_FIREWALL_LAYER_IPV6, "mangle",
          natmangle_chains, ARRAY_CARDINALITY(natmangle_chains), &changed },
    };
    size_t i;

    fw = virFirewallNew();

    virFirewallStartTransaction(fw, 0);

    for (i = 0; i < ARRAY_CARDINALITY(data); i++)
        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;
}


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

    snprintf(portstr, sizeof(portstr), "%d", port);
    portstr[sizeof(portstr) - 1] = '\0';

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

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

    snprintf(portstr, sizeof(portstr), "%d", port);
    portstr[sizeof(portstr) - 1] = '\0';

218 219 220 221 222 223 224 225
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       action == ADD ? "--insert" : "--delete", "OUTPUT",
                       "--out-interface", iface,
                       "--protocol", tcp ? "tcp" : "udp",
                       "--destination-port", portstr,
                       "--jump", "ACCEPT",
                       NULL);
226 227
}

228 229 230 231 232 233 234 235 236
/**
 * 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
 */
237 238 239
void
iptablesAddTcpInput(virFirewallPtr fw,
                    virFirewallLayer layer,
240 241 242
                    const char *iface,
                    int port)
{
243
    iptablesInput(fw, layer, iface, port, ADD, 1);
244 245
}

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

264 265 266 267 268 269 270 271 272
/**
 * 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
 */
273 274 275
void
iptablesAddUdpInput(virFirewallPtr fw,
                    virFirewallLayer layer,
276 277 278
                    const char *iface,
                    int port)
{
279
    iptablesInput(fw, layer, iface, port, ADD, 0);
280 281
}

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

300 301 302 303 304 305 306 307 308
/**
 * 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
 */
309 310 311
void
iptablesAddUdpOutput(virFirewallPtr fw,
                     virFirewallLayer layer,
312 313 314
                     const char *iface,
                     int port)
{
315
    iptablesOutput(fw, layer, iface, port, ADD, 0);
316 317 318 319 320 321 322 323 324 325 326
}

/**
 * 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
 */
327 328 329
void
iptablesRemoveUdpOutput(virFirewallPtr fw,
                        virFirewallLayer layer,
330 331 332
                        const char *iface,
                        int port)
{
333
    iptablesOutput(fw, layer, iface, port, REMOVE, 0);
334 335
}

336

337
static char *iptablesFormatNetwork(virSocketAddr *netaddr,
338
                                   unsigned int prefix)
339 340
{
    virSocketAddr network;
341
    VIR_AUTOFREE(char *) netstr = NULL;
342 343
    char *ret;

344 345
    if (!(VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET) ||
          VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET6))) {
346 347
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Only IPv4 or IPv6 addresses can be used with iptables"));
348 349 350
        return NULL;
    }

351
    if (virSocketAddrMaskByPrefix(netaddr, prefix, &network) < 0) {
352 353
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failure to mask address"));
354 355
        return NULL;
    }
356

357
    netstr = virSocketAddrFormat(&network);
358 359 360 361

    if (!netstr)
        return NULL;

362
    ignore_value(virAsprintf(&ret, "%s/%d", netstr, prefix));
363 364 365 366 367

    return ret;
}


368 369 370
/* Allow all traffic coming from the bridge, with a valid network address
 * to proceed to WAN
 */
371
static int
372 373
iptablesForwardAllowOut(virFirewallPtr fw,
                        virSocketAddr *netaddr,
374
                        unsigned int prefix,
375 376 377
                        const char *iface,
                        const char *physdev,
                        int action)
378
{
379
    VIR_AUTOFREE(char *) networkstr = NULL;
380 381
    virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ?
        VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
382

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

386
    if (physdev && physdev[0])
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
                           action == ADD ? "--insert" : "--delete", "FORWARD",
                           "--source", networkstr,
                           "--in-interface", iface,
                           "--out-interface", physdev,
                           "--jump", "ACCEPT",
                           NULL);
    else
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
                           action == ADD ? "--insert" : "--delete", "FORWARD",
                           "--source", networkstr,
                           "--in-interface", iface,
                           "--jump", "ACCEPT",
                           NULL);
403

404
    return 0;
405 406
}

407 408 409 410 411 412
/**
 * iptablesAddForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
413
 *
414 415 416 417 418 419
 * 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
 */
420
int
421 422
iptablesAddForwardAllowOut(virFirewallPtr fw,
                           virSocketAddr *netaddr,
423
                           unsigned int prefix,
424 425
                           const char *iface,
                           const char *physdev)
426
{
427
    return iptablesForwardAllowOut(fw, netaddr, prefix, iface, physdev, ADD);
428 429
}

430 431 432 433 434 435
/**
 * iptablesRemoveForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
436
 *
437 438 439 440 441 442
 * 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
 */
443
int
444 445
iptablesRemoveForwardAllowOut(virFirewallPtr fw,
                              virSocketAddr *netaddr,
446
                              unsigned int prefix,
447 448
                              const char *iface,
                              const char *physdev)
449
{
450
    return iptablesForwardAllowOut(fw, netaddr, prefix, iface, physdev, REMOVE);
451 452
}

453 454 455 456

/* Allow all traffic destined to the bridge, with a valid network address
 * and associated with an existing connection
 */
457
static int
458 459
iptablesForwardAllowRelatedIn(virFirewallPtr fw,
                              virSocketAddr *netaddr,
460
                              unsigned int prefix,
461 462 463
                              const char *iface,
                              const char *physdev,
                              int action)
464
{
465 466
    virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ?
        VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
467
    VIR_AUTOFREE(char *) networkstr = NULL;
468

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

472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
    if (physdev && physdev[0])
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
                           action == ADD ? "--insert" : "--delete", "FORWARD",
                           "--destination", networkstr,
                           "--in-interface", physdev,
                           "--out-interface", iface,
                           "--match", "conntrack",
                           "--ctstate", "ESTABLISHED,RELATED",
                           "--jump", "ACCEPT",
                           NULL);
    else
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
                           action == ADD ? "--insert" : "--delete", "FORWARD",
                           "--destination", networkstr,
                           "--out-interface", iface,
                           "--match", "conntrack",
                           "--ctstate", "ESTABLISHED,RELATED",
                           "--jump", "ACCEPT",
                           NULL);

    return 0;
495 496
}

497 498 499 500 501 502 503 504 505 506 507 508 509 510
/**
 * 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
511 512
iptablesAddForwardAllowRelatedIn(virFirewallPtr fw,
                                 virSocketAddr *netaddr,
513
                                 unsigned int prefix,
514 515
                                 const char *iface,
                                 const char *physdev)
516
{
517
    return iptablesForwardAllowRelatedIn(fw, netaddr, prefix, iface, physdev, ADD);
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
}

/**
 * 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
534 535
iptablesRemoveForwardAllowRelatedIn(virFirewallPtr fw,
                                    virSocketAddr *netaddr,
536
                                    unsigned int prefix,
537 538
                                    const char *iface,
                                    const char *physdev)
539
{
540
    return iptablesForwardAllowRelatedIn(fw, netaddr, prefix, iface, physdev, REMOVE);
541 542 543 544 545
}

/* Allow all traffic destined to the bridge, with a valid network address
 */
static int
546 547
iptablesForwardAllowIn(virFirewallPtr fw,
                       virSocketAddr *netaddr,
548
                       unsigned int prefix,
549 550 551 552
                       const char *iface,
                       const char *physdev,
                       int action)
{
553 554
    virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ?
        VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
555
    VIR_AUTOFREE(char *) networkstr = NULL;
556

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

560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
    if (physdev && physdev[0])
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
                           action == ADD ? "--insert" : "--delete", "FORWARD",
                           "--destination", networkstr,
                           "--in-interface", physdev,
                           "--out-interface", iface,
                           "--jump", "ACCEPT",
                           NULL);
    else
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
                           action == ADD ? "--insert" : "--delete", "FORWARD",
                           "--destination", networkstr,
                           "--out-interface", iface,
                           "--jump", "ACCEPT",
                           NULL);
    return 0;
578 579
}

580 581 582 583 584 585
/**
 * 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
586
 *
587 588 589 590 591 592
 * 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
 */
593
int
594 595
iptablesAddForwardAllowIn(virFirewallPtr fw,
                          virSocketAddr *netaddr,
596
                          unsigned int prefix,
597 598 599
                          const char *iface,
                          const char *physdev)
{
600
    return iptablesForwardAllowIn(fw, netaddr, prefix, iface, physdev, ADD);
601 602
}

603 604 605 606 607 608
/**
 * 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
609
 *
610 611 612 613 614 615
 * 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
 */
616
int
617 618
iptablesRemoveForwardAllowIn(virFirewallPtr fw,
                             virSocketAddr *netaddr,
619
                             unsigned int prefix,
620 621 622
                             const char *iface,
                             const char *physdev)
{
623
    return iptablesForwardAllowIn(fw, netaddr, prefix, iface, physdev, REMOVE);
624 625
}

626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
static void
iptablesForwardAllowCross(virFirewallPtr fw,
                          virFirewallLayer layer,
                          const char *iface,
                          int action)
{
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       action == ADD ? "--insert" : "--delete", "FORWARD",
                       "--in-interface", iface,
                       "--out-interface", iface,
                       "--jump", "ACCEPT",
                       NULL);
}

641 642 643 644 645 646 647 648 649 650 651
/**
 * 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
 */
652 653 654
void
iptablesAddForwardAllowCross(virFirewallPtr fw,
                             virFirewallLayer layer,
655 656
                             const char *iface)
{
657
    iptablesForwardAllowCross(fw, layer, iface, ADD);
658 659
}

660 661 662 663 664 665 666 667 668 669 670
/**
 * 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
 */
671 672 673
void
iptablesRemoveForwardAllowCross(virFirewallPtr fw,
                                virFirewallLayer layer,
674
                                const char *iface)
675 676 677 678 679 680 681 682 683
{
    iptablesForwardAllowCross(fw, layer, iface, REMOVE);
}

static void
iptablesForwardRejectOut(virFirewallPtr fw,
                         virFirewallLayer layer,
                         const char *iface,
                         int action)
684
{
685 686
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
687
                       action == ADD ? "--insert" : "delete", "FORWARD",
688
                       "--in-interface", iface,
689
                       "--jump", "REJECT",
690
                       NULL);
691 692
}

693 694 695 696 697 698 699 700 701 702
/**
 * 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
 */
703 704 705
void
iptablesAddForwardRejectOut(virFirewallPtr fw,
                            virFirewallLayer layer,
706 707
                            const char *iface)
{
708
    iptablesForwardRejectOut(fw, layer, iface, ADD);
709 710
}

711 712 713 714 715 716 717 718 719 720
/**
 * 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
 */
721 722 723
void
iptablesRemoveForwardRejectOut(virFirewallPtr fw,
                               virFirewallLayer layer,
724
                               const char *iface)
725 726 727 728 729 730 731 732 733 734
{
    iptablesForwardRejectOut(fw, layer, iface, REMOVE);
}


static void
iptablesForwardRejectIn(virFirewallPtr fw,
                        virFirewallLayer layer,
                        const char *iface,
                        int action)
735
{
736 737
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
738 739
                       action == ADD ? "--insert" : "--delete", "FORWARD",
                       "--out-interface", iface,
740 741
                       "--jump", "REJECT",
                       NULL);
742 743
}

744 745 746 747 748 749 750 751 752 753
/**
 * 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
 */
754 755 756
void
iptablesAddForwardRejectIn(virFirewallPtr fw,
                           virFirewallLayer layer,
757
                           const char *iface)
758
{
759
    iptablesForwardRejectIn(fw, layer, iface, ADD);
760 761
}

762 763 764 765 766 767 768 769 770 771
/**
 * 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
 */
772 773 774
void
iptablesRemoveForwardRejectIn(virFirewallPtr fw,
                              virFirewallLayer layer,
775
                              const char *iface)
776
{
777
    iptablesForwardRejectIn(fw, layer, iface, REMOVE);
778 779
}

780 781 782 783

/* Masquerade all traffic coming from the network associated
 * with the bridge
 */
784
static int
785 786
iptablesForwardMasquerade(virFirewallPtr fw,
                          virSocketAddr *netaddr,
787
                          unsigned int prefix,
788
                          const char *physdev,
789 790
                          virSocketAddrRangePtr addr,
                          virPortRangePtr port,
791 792
                          const char *protocol,
                          int action)
793
{
794 795 796 797 798
    VIR_AUTOFREE(char *) networkstr = NULL;
    VIR_AUTOFREE(char *) addrStartStr = NULL;
    VIR_AUTOFREE(char *) addrEndStr = NULL;
    VIR_AUTOFREE(char *) portRangeStr = NULL;
    VIR_AUTOFREE(char *) natRangeStr = NULL;
799
    virFirewallRulePtr rule;
800

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

804
    if (!VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET)) {
805
        /* Higher level code *should* guaranteee it's impossible to get here. */
806 807 808
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Attempted to NAT '%s'. NAT is only supported for IPv4."),
                       networkstr);
809
        return -1;
810 811
    }

812 813
    if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->start, AF_INET)) {
        if (!(addrStartStr = virSocketAddrFormat(&addr->start)))
814
            return -1;
815 816
        if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->end, AF_INET)) {
            if (!(addrEndStr = virSocketAddrFormat(&addr->end)))
817
                return -1;
818
        }
819 820
    }

821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
    if (protocol && protocol[0]) {
        rule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
                                  "--table", "nat",
                                  action == ADD ? "--insert" : "--delete", "POSTROUTING",
                                  "--source", networkstr,
                                  "-p", protocol,
                                  "!", "--destination", networkstr,
                                  NULL);
    } else {
        rule = virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
                                  "--table", "nat",
                                  action == ADD ? "--insert" : "--delete", "POSTROUTING",
                                  "--source", networkstr,
                                  "!", "--destination", networkstr,
                                  NULL);
    }
837 838

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

841
    if (protocol && protocol[0]) {
842 843 844
        if (port->start == 0 && port->end == 0) {
            port->start = 1024;
            port->end = 65535;
845 846
        }

847 848
        if (port->start < port->end && port->end < 65536) {
            if (virAsprintf(&portRangeStr, ":%u-%u",
849
                            port->start, port->end) < 0)
850
                return -1;
851 852 853
        } else {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Invalid port range '%u-%u'."),
854
                           port->start, port->end);
855 856 857
        }
    }

858 859 860
    /* Use --jump SNAT if public addr is specified */
    if (addrStartStr && addrStartStr[0]) {
        int r = 0;
861

862 863
        if (addrEndStr && addrEndStr[0]) {
            r = virAsprintf(&natRangeStr, "%s-%s%s", addrStartStr, addrEndStr,
864
                            portRangeStr ? portRangeStr : "");
865
        } else {
866 867
            r = virAsprintf(&natRangeStr, "%s%s", addrStartStr,
                            portRangeStr ? portRangeStr : "");
868 869
        }

870
        if (r < 0)
871
            return -1;
872

873 874
        virFirewallRuleAddArgList(fw, rule,
                                  "--jump", "SNAT",
875
                                  "--to-source", natRangeStr, NULL);
876 877 878
    } else {
        virFirewallRuleAddArgList(fw, rule,
                                  "--jump", "MASQUERADE", NULL);
879

880 881 882 883
        if (portRangeStr && portRangeStr[0])
            virFirewallRuleAddArgList(fw, rule,
                                      "--to-ports", &portRangeStr[1], NULL);
    }
884

885
    return 0;
886 887
}

888 889 890 891 892
/**
 * iptablesAddForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
893
 * @protocol: the network protocol or NULL
894
 *
895 896 897 898 899 900
 * 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
 */
901
int
902 903
iptablesAddForwardMasquerade(virFirewallPtr fw,
                             virSocketAddr *netaddr,
904
                             unsigned int prefix,
905
                             const char *physdev,
906 907
                             virSocketAddrRangePtr addr,
                             virPortRangePtr port,
908
                             const char *protocol)
909
{
910
    return iptablesForwardMasquerade(fw, netaddr, prefix, physdev, addr, port,
911
                                     protocol, ADD);
912 913
}

914 915 916 917 918
/**
 * iptablesRemoveForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
919
 * @protocol: the network protocol or NULL
920
 *
921 922 923 924 925 926
 * 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
 */
927
int
928 929
iptablesRemoveForwardMasquerade(virFirewallPtr fw,
                                virSocketAddr *netaddr,
930
                                unsigned int prefix,
931
                                const char *physdev,
932 933
                                virSocketAddrRangePtr addr,
                                virPortRangePtr port,
934
                                const char *protocol)
935
{
936
    return iptablesForwardMasquerade(fw, netaddr, prefix, physdev, addr, port,
937
                                     protocol, REMOVE);
938
}
939 940


941 942 943 944
/* Don't masquerade traffic coming from the network associated with the bridge
 * if said traffic targets @destaddr.
 */
static int
945 946
iptablesForwardDontMasquerade(virFirewallPtr fw,
                              virSocketAddr *netaddr,
947 948 949 950 951
                              unsigned int prefix,
                              const char *physdev,
                              const char *destaddr,
                              int action)
{
952
    VIR_AUTOFREE(char *) networkstr = NULL;
953 954 955 956 957 958 959 960 961

    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);
962
        return -1;
963 964 965
    }

    if (physdev && physdev[0])
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982
        virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
                           "--table", "nat",
                           action == ADD ? "--insert" : "--delete", "POSTROUTING",
                           "--out-interface", physdev,
                           "--source", networkstr,
                           "--destination", destaddr,
                           "--jump", "RETURN",
                           NULL);
    else
        virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
                           "--table", "nat",
                           action == ADD ? "--insert" : "--delete", "POSTROUTING",
                           "--source", networkstr,
                           "--destination", destaddr,
                           "--jump", "RETURN",
                           NULL);

983
    return 0;
984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
}

/**
 * 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
1001 1002
iptablesAddDontMasquerade(virFirewallPtr fw,
                          virSocketAddr *netaddr,
1003 1004 1005 1006
                          unsigned int prefix,
                          const char *physdev,
                          const char *destaddr)
{
1007
    return iptablesForwardDontMasquerade(fw, netaddr, prefix, physdev, destaddr,
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
                                         ADD);
}

/**
 * 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
1026 1027
iptablesRemoveDontMasquerade(virFirewallPtr fw,
                             virSocketAddr *netaddr,
1028 1029 1030 1031
                             unsigned int prefix,
                             const char *physdev,
                             const char *destaddr)
{
1032
    return iptablesForwardDontMasquerade(fw, netaddr, prefix, physdev, destaddr,
1033 1034 1035 1036
                                         REMOVE);
}


1037 1038 1039
static void
iptablesOutputFixUdpChecksum(virFirewallPtr fw,
                             const char *iface,
1040 1041 1042 1043 1044 1045 1046 1047
                             int port,
                             int action)
{
    char portstr[32];

    snprintf(portstr, sizeof(portstr), "%d", port);
    portstr[sizeof(portstr) - 1] = '\0';

1048 1049 1050 1051 1052 1053 1054 1055
    virFirewallAddRule(fw, VIR_FIREWALL_LAYER_IPV4,
                       "--table", "mangle",
                       action == ADD ? "--insert" : "--delete", "POSTROUTING",
                       "--out-interface", iface,
                       "--protocol", "udp",
                       "--destination-port", portstr,
                       "--jump", "CHECKSUM", "--checksum-fill",
                       NULL);
1056 1057 1058 1059 1060 1061 1062 1063
}

/**
 * iptablesAddOutputFixUdpChecksum:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to match
 *
E
Eric Blake 已提交
1064
 * Add a rule to the mangle table's POSTROUTING chain that fixes up the
1065 1066 1067 1068
 * checksum of packets with the given destination @port.
 * the given @iface interface for TCP packets.
 *
 */
1069 1070 1071
void
iptablesAddOutputFixUdpChecksum(virFirewallPtr fw,
                                const char *iface,
1072 1073
                                int port)
{
1074
    iptablesOutputFixUdpChecksum(fw, iface, port, ADD);
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
}

/**
 * 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.
 */
1086 1087 1088
void
iptablesRemoveOutputFixUdpChecksum(virFirewallPtr fw,
                                   const char *iface,
1089 1090
                                   int port)
{
1091
    iptablesOutputFixUdpChecksum(fw, iface, port, REMOVE);
1092
}