viriptables.c 29.6 KB
Newer Older
1
/*
2 3
 * viriptables.c: helper APIs for managing iptables
 *
4
 * Copyright (C) 2007-2012 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 22 23
 *
 * Authors:
 *     Mark McLoughlin <markmc@redhat.com>
 */

24
#include <config.h>
25 26 27 28 29 30 31 32 33 34 35

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
E
Eric Blake 已提交
36
#include <sys/wait.h>
37 38

#ifdef HAVE_PATHS_H
39
# include <paths.h>
40
#endif
41

42
#include "internal.h"
43
#include "viriptables.h"
44
#include "vircommand.h"
45
#include "viralloc.h"
46
#include "virerror.h"
47
#include "virlog.h"
48
#include "virthread.h"
49 50 51 52 53 54 55 56 57

#if HAVE_FIREWALLD
static char *firewall_cmd_path = NULL;

static int
virIpTablesOnceInit(void)
{
    firewall_cmd_path = virFindFileInPath("firewall-cmd");
    if (!firewall_cmd_path) {
58
        VIR_INFO("firewall-cmd not found on system. "
59 60 61 62 63 64 65
                 "firewalld support disabled for iptables.");
    } else {
        virCommandPtr cmd = virCommandNew(firewall_cmd_path);
        int status;

        virCommandAddArgList(cmd, "--state", NULL);
        if (virCommandRun(cmd, &status) < 0 || status != 0) {
66
            VIR_INFO("firewall-cmd found but disabled for iptables");
67 68 69
            VIR_FREE(firewall_cmd_path);
            firewall_cmd_path = NULL;
        } else {
70
            VIR_INFO("using firewalld for iptables commands");
71 72 73 74 75 76 77 78 79
        }
        virCommandFree(cmd);
    }
    return 0;
}

VIR_ONCE_GLOBAL_INIT(virIpTables)

#endif
80

81
#define VIR_FROM_THIS VIR_FROM_NONE
82

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
enum {
    ADD = 0,
    REMOVE
};

typedef struct
{
    char  *table;
    char  *chain;
} iptRules;

struct _iptablesContext
{
    iptRules *input_filter;
    iptRules *forward_filter;
    iptRules *nat_postrouting;
99
    iptRules *mangle_postrouting;
100 101 102 103 104
};

static void
iptRulesFree(iptRules *rules)
{
105 106
    VIR_FREE(rules->table);
    VIR_FREE(rules->chain);
107
    VIR_FREE(rules);
108 109 110 111 112 113 114 115
}

static iptRules *
iptRulesNew(const char *table,
            const char *chain)
{
    iptRules *rules;

116
    if (VIR_ALLOC(rules) < 0)
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
        return NULL;

    if (!(rules->table = strdup(table)))
        goto error;

    if (!(rules->chain = strdup(chain)))
        goto error;

    return rules;

 error:
    iptRulesFree(rules);
    return NULL;
}

132
static int ATTRIBUTE_SENTINEL
133 134
iptablesAddRemoveRule(iptRules *rules, int family, int action,
                      const char *arg, ...)
135 136
{
    va_list args;
137
    int ret;
138
    virCommandPtr cmd = NULL;
139 140
    const char *s;

141 142 143 144 145 146 147 148 149 150 151
#if HAVE_FIREWALLD
    virIpTablesInitialize();
    if (firewall_cmd_path) {
        cmd = virCommandNew(firewall_cmd_path);
        virCommandAddArgList(cmd, "--direct", "--passthrough",
                             (family == AF_INET6) ? "ipv6" : "ipv4", NULL);
    }
#endif

    if (cmd == NULL) {
        cmd = virCommandNew((family == AF_INET6)
152
                        ? IP6TABLES_PATH : IPTABLES_PATH);
153
    }
154

155 156 157
    virCommandAddArgList(cmd, "--table", rules->table,
                         action == ADD ? "--insert" : "--delete",
                         rules->chain, arg, NULL);
158 159

    va_start(args, arg);
160 161
    while ((s = va_arg(args, const char *)))
        virCommandAddArg(cmd, s);
162 163
    va_end(args);

164 165 166
    ret = virCommandRun(cmd, NULL);
    virCommandFree(cmd);
    return ret;
167 168
}

169 170 171 172 173 174 175
/**
 * iptablesContextNew:
 *
 * Create a new IPtable context
 *
 * Returns a pointer to the new structure or NULL in case of error
 */
176 177 178 179 180
iptablesContext *
iptablesContextNew(void)
{
    iptablesContext *ctx;

181
    if (VIR_ALLOC(ctx) < 0)
182 183
        return NULL;

184
    if (!(ctx->input_filter = iptRulesNew("filter", "INPUT")))
185 186
        goto error;

187
    if (!(ctx->forward_filter = iptRulesNew("filter", "FORWARD")))
188 189
        goto error;

190
    if (!(ctx->nat_postrouting = iptRulesNew("nat", "POSTROUTING")))
191 192
        goto error;

193 194 195
    if (!(ctx->mangle_postrouting = iptRulesNew("mangle", "POSTROUTING")))
        goto error;

196 197 198 199 200 201 202
    return ctx;

 error:
    iptablesContextFree(ctx);
    return NULL;
}

203 204 205 206
/**
 * iptablesContextFree:
 * @ctx: pointer to the IP table context
 *
R
Richard W.M. Jones 已提交
207
 * Free the resources associated with an IP table context
208
 */
209 210 211
void
iptablesContextFree(iptablesContext *ctx)
{
212 213 214 215 216 217
    if (ctx->input_filter)
        iptRulesFree(ctx->input_filter);
    if (ctx->forward_filter)
        iptRulesFree(ctx->forward_filter);
    if (ctx->nat_postrouting)
        iptRulesFree(ctx->nat_postrouting);
218 219
    if (ctx->mangle_postrouting)
        iptRulesFree(ctx->mangle_postrouting);
220
    VIR_FREE(ctx);
221 222 223 224
}

static int
iptablesInput(iptablesContext *ctx,
225
              int family,
226 227 228 229 230 231 232 233 234 235
              const char *iface,
              int port,
              int action,
              int tcp)
{
    char portstr[32];

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

236
    return iptablesAddRemoveRule(ctx->input_filter,
237
                                 family,
238 239 240 241 242 243
                                 action,
                                 "--in-interface", iface,
                                 "--protocol", tcp ? "tcp" : "udp",
                                 "--destination-port", portstr,
                                 "--jump", "ACCEPT",
                                 NULL);
244 245
}

246 247 248 249 250 251 252 253 254 255 256 257
/**
 * 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
 *
 * Returns 0 in case of success or an error code in case of error
 */

258 259
int
iptablesAddTcpInput(iptablesContext *ctx,
260
                    int family,
261 262 263
                    const char *iface,
                    int port)
{
264
    return iptablesInput(ctx, family, iface, port, ADD, 1);
265 266
}

267 268 269 270 271 272
/**
 * iptablesRemoveTcpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the TCP port to remove
 *
R
Richard W.M. Jones 已提交
273
 * Removes an input from the IP table, hence forbidding access to the given
274 275 276 277
 * @port on the given @iface interface for TCP packets
 *
 * Returns 0 in case of success or an error code in case of error
 */
278 279
int
iptablesRemoveTcpInput(iptablesContext *ctx,
280
                       int family,
281 282 283
                       const char *iface,
                       int port)
{
284
    return iptablesInput(ctx, family, iface, port, REMOVE, 1);
285 286
}

287 288 289 290 291 292 293 294 295 296 297 298
/**
 * 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
 *
 * Returns 0 in case of success or an error code in case of error
 */

299 300
int
iptablesAddUdpInput(iptablesContext *ctx,
301
                    int family,
302 303 304
                    const char *iface,
                    int port)
{
305
    return iptablesInput(ctx, family, iface, port, ADD, 0);
306 307
}

308 309 310 311 312 313
/**
 * iptablesRemoveUdpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to remove
 *
R
Richard W.M. Jones 已提交
314
 * Removes an input from the IP table, hence forbidding access to the given
315 316 317 318
 * @port on the given @iface interface for UDP packets
 *
 * Returns 0 in case of success or an error code in case of error
 */
319 320
int
iptablesRemoveUdpInput(iptablesContext *ctx,
321
                       int family,
322 323 324
                       const char *iface,
                       int port)
{
325
    return iptablesInput(ctx, family, iface, port, REMOVE, 0);
326 327 328
}


329
static char *iptablesFormatNetwork(virSocketAddr *netaddr,
330
                                   unsigned int prefix)
331 332 333 334 335
{
    virSocketAddr network;
    char *netstr;
    char *ret;

336 337
    if (!(VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET) ||
          VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET6))) {
338 339
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Only IPv4 or IPv6 addresses can be used with iptables"));
340 341 342
        return NULL;
    }

343
    if (virSocketAddrMaskByPrefix(netaddr, prefix, &network) < 0) {
344 345
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failure to mask address"));
346 347
        return NULL;
    }
348

349
    netstr = virSocketAddrFormat(&network);
350 351 352 353 354 355 356 357 358 359 360 361

    if (!netstr)
        return NULL;

    if (virAsprintf(&ret, "%s/%d", netstr, prefix) < 0)
        virReportOOMError();

    VIR_FREE(netstr);
    return ret;
}


362 363 364
/* Allow all traffic coming from the bridge, with a valid network address
 * to proceed to WAN
 */
365
static int
366
iptablesForwardAllowOut(iptablesContext *ctx,
367
                        virSocketAddr *netaddr,
368
                        unsigned int prefix,
369 370 371
                        const char *iface,
                        const char *physdev,
                        int action)
372
{
373 374 375
    int ret;
    char *networkstr;

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

379
    if (physdev && physdev[0]) {
380
        ret = iptablesAddRemoveRule(ctx->forward_filter,
381
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
382 383 384 385 386 387
                                    action,
                                    "--source", networkstr,
                                    "--in-interface", iface,
                                    "--out-interface", physdev,
                                    "--jump", "ACCEPT",
                                    NULL);
388
    } else {
389
        ret = iptablesAddRemoveRule(ctx->forward_filter,
390
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
391 392 393 394 395
                                    action,
                                    "--source", networkstr,
                                    "--in-interface", iface,
                                    "--jump", "ACCEPT",
                                    NULL);
396
    }
397 398
    VIR_FREE(networkstr);
    return ret;
399 400
}

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

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

447 448 449 450

/* Allow all traffic destined to the bridge, with a valid network address
 * and associated with an existing connection
 */
451
static int
452
iptablesForwardAllowRelatedIn(iptablesContext *ctx,
453
                              virSocketAddr *netaddr,
454
                              unsigned int prefix,
455 456 457
                              const char *iface,
                              const char *physdev,
                              int action)
458
{
459 460 461
    int ret;
    char *networkstr;

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

465
    if (physdev && physdev[0]) {
466
        ret = iptablesAddRemoveRule(ctx->forward_filter,
467
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
468 469 470 471 472 473 474 475
                                    action,
                                    "--destination", networkstr,
                                    "--in-interface", physdev,
                                    "--out-interface", iface,
                                    "--match", "state",
                                    "--state", "ESTABLISHED,RELATED",
                                    "--jump", "ACCEPT",
                                    NULL);
476
    } else {
477
        ret = iptablesAddRemoveRule(ctx->forward_filter,
478
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
479 480 481 482 483 484 485
                                    action,
                                    "--destination", networkstr,
                                    "--out-interface", iface,
                                    "--match", "state",
                                    "--state", "ESTABLISHED,RELATED",
                                    "--jump", "ACCEPT",
                                    NULL);
486
    }
487 488
    VIR_FREE(networkstr);
    return ret;
489 490
}

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

/**
 * 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
iptablesRemoveForwardAllowRelatedIn(iptablesContext *ctx,
529
                                    virSocketAddr *netaddr,
530
                                    unsigned int prefix,
531 532
                                    const char *iface,
                                    const char *physdev)
533
{
534
    return iptablesForwardAllowRelatedIn(ctx, netaddr, prefix, iface, physdev, REMOVE);
535 536 537 538 539 540
}

/* Allow all traffic destined to the bridge, with a valid network address
 */
static int
iptablesForwardAllowIn(iptablesContext *ctx,
541
                       virSocketAddr *netaddr,
542
                       unsigned int prefix,
543 544 545 546
                       const char *iface,
                       const char *physdev,
                       int action)
{
547 548 549
    int ret;
    char *networkstr;

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

553
    if (physdev && physdev[0]) {
554
        ret = iptablesAddRemoveRule(ctx->forward_filter,
555
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
556 557 558 559 560 561
                                    action,
                                    "--destination", networkstr,
                                    "--in-interface", physdev,
                                    "--out-interface", iface,
                                    "--jump", "ACCEPT",
                                    NULL);
562
    } else {
563
        ret = iptablesAddRemoveRule(ctx->forward_filter,
564
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
565 566 567 568 569
                                    action,
                                    "--destination", networkstr,
                                    "--out-interface", iface,
                                    "--jump", "ACCEPT",
                                    NULL);
570
    }
571 572
    VIR_FREE(networkstr);
    return ret;
573 574
}

575 576 577 578 579 580
/**
 * 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
581
 *
582 583 584 585 586 587
 * 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
 */
588
int
589
iptablesAddForwardAllowIn(iptablesContext *ctx,
590
                          virSocketAddr *netaddr,
591
                          unsigned int prefix,
592 593 594
                          const char *iface,
                          const char *physdev)
{
595
    return iptablesForwardAllowIn(ctx, netaddr, prefix, iface, physdev, ADD);
596 597
}

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


/* Allow all traffic between guests on the same bridge,
 * with a valid network address
 */
static int
iptablesForwardAllowCross(iptablesContext *ctx,
627
                          int family,
628 629 630 631
                          const char *iface,
                          int action)
{
    return iptablesAddRemoveRule(ctx->forward_filter,
632
                                 family,
633 634 635 636 637 638 639
                                 action,
                                 "--in-interface", iface,
                                 "--out-interface", iface,
                                 "--jump", "ACCEPT",
                                 NULL);
}

640 641 642 643 644 645 646 647 648 649 650
/**
 * 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
 */
651 652
int
iptablesAddForwardAllowCross(iptablesContext *ctx,
653 654 655 656
                             int family,
                             const char *iface)
{
    return iptablesForwardAllowCross(ctx, family, iface, ADD);
657 658
}

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


/* Drop all traffic trying to forward from the bridge.
 * ie the bridge is the in interface
 */
static int
iptablesForwardRejectOut(iptablesContext *ctx,
684
                         int family,
685 686 687 688
                         const char *iface,
                         int action)
{
    return iptablesAddRemoveRule(ctx->forward_filter,
689 690 691 692 693
                                 family,
                                 action,
                                 "--in-interface", iface,
                                 "--jump", "REJECT",
                                 NULL);
694 695
}

696 697 698 699 700 701 702 703 704 705
/**
 * 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
 */
706 707
int
iptablesAddForwardRejectOut(iptablesContext *ctx,
708
                            int family,
709 710
                            const char *iface)
{
711
    return iptablesForwardRejectOut(ctx, family, iface, ADD);
712 713
}

714 715 716 717 718 719 720 721 722 723
/**
 * 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
 */
724 725
int
iptablesRemoveForwardRejectOut(iptablesContext *ctx,
726
                               int family,
727 728
                               const char *iface)
{
729
    return iptablesForwardRejectOut(ctx, family, iface, REMOVE);
730 731 732 733 734 735 736 737 738 739
}




/* Drop all traffic trying to forward to the bridge.
 * ie the bridge is the out interface
 */
static int
iptablesForwardRejectIn(iptablesContext *ctx,
740
                        int family,
741
                        const char *iface,
742 743 744
                        int action)
{
    return iptablesAddRemoveRule(ctx->forward_filter,
745
                                 family,
746 747 748 749 750 751
                                 action,
                                 "--out-interface", iface,
                                 "--jump", "REJECT",
                                 NULL);
}

752 753 754 755 756 757 758 759 760 761
/**
 * 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
 */
762 763
int
iptablesAddForwardRejectIn(iptablesContext *ctx,
764
                           int family,
765
                           const char *iface)
766
{
767
    return iptablesForwardRejectIn(ctx, family, iface, ADD);
768 769
}

770 771 772 773 774 775 776 777 778 779
/**
 * 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
 */
780
int
781
iptablesRemoveForwardRejectIn(iptablesContext *ctx,
782
                              int family,
783
                              const char *iface)
784
{
785
    return iptablesForwardRejectIn(ctx, family, iface, REMOVE);
786 787
}

788 789 790 791

/* Masquerade all traffic coming from the network associated
 * with the bridge
 */
792
static int
793
iptablesForwardMasquerade(iptablesContext *ctx,
794
                          virSocketAddr *netaddr,
795
                          unsigned int prefix,
796 797 798
                          const char *physdev,
                          const char *protocol,
                          int action)
799
{
800 801 802
    int ret;
    char *networkstr;

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

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

815 816
    if (protocol && protocol[0]) {
        if (physdev && physdev[0]) {
817
            ret = iptablesAddRemoveRule(ctx->nat_postrouting,
818
                                        AF_INET,
819 820 821 822 823 824 825 826
                                        action,
                                        "--source", networkstr,
                                        "-p", protocol,
                                        "!", "--destination", networkstr,
                                        "--out-interface", physdev,
                                        "--jump", "MASQUERADE",
                                        "--to-ports", "1024-65535",
                                        NULL);
827
        } else {
828
            ret = iptablesAddRemoveRule(ctx->nat_postrouting,
829
                                        AF_INET,
830 831 832 833 834 835 836
                                        action,
                                        "--source", networkstr,
                                        "-p", protocol,
                                        "!", "--destination", networkstr,
                                        "--jump", "MASQUERADE",
                                        "--to-ports", "1024-65535",
                                        NULL);
837
        }
838
    } else {
839
        if (physdev && physdev[0]) {
840
            ret = iptablesAddRemoveRule(ctx->nat_postrouting,
841
                                        AF_INET,
842 843 844 845 846 847
                                        action,
                                        "--source", networkstr,
                                        "!", "--destination", networkstr,
                                        "--out-interface", physdev,
                                        "--jump", "MASQUERADE",
                                        NULL);
848
        } else {
849
            ret = iptablesAddRemoveRule(ctx->nat_postrouting,
850
                                        AF_INET,
851 852 853 854 855
                                        action,
                                        "--source", networkstr,
                                        "!", "--destination", networkstr,
                                        "--jump", "MASQUERADE",
                                        NULL);
856
        }
857
    }
858 859
    VIR_FREE(networkstr);
    return ret;
860 861
}

862 863 864 865 866
/**
 * iptablesAddForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
867
 * @protocol: the network protocol or NULL
868
 *
869 870 871 872 873 874
 * 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
 */
875
int
876
iptablesAddForwardMasquerade(iptablesContext *ctx,
877
                             virSocketAddr *netaddr,
878
                             unsigned int prefix,
879 880
                             const char *physdev,
                             const char *protocol)
881
{
882
    return iptablesForwardMasquerade(ctx, netaddr, prefix, physdev, protocol, ADD);
883 884
}

885 886 887 888 889
/**
 * iptablesRemoveForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
890
 * @protocol: the network protocol or NULL
891
 *
892 893 894 895 896 897
 * 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
 */
898
int
899
iptablesRemoveForwardMasquerade(iptablesContext *ctx,
900
                                virSocketAddr *netaddr,
901
                                unsigned int prefix,
902 903
                                const char *physdev,
                                const char *protocol)
904
{
905
    return iptablesForwardMasquerade(ctx, netaddr, prefix, physdev, protocol, REMOVE);
906
}
907 908 909 910 911 912 913 914 915 916 917 918 919 920


static int
iptablesOutputFixUdpChecksum(iptablesContext *ctx,
                             const char *iface,
                             int port,
                             int action)
{
    char portstr[32];

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

    return iptablesAddRemoveRule(ctx->mangle_postrouting,
921
                                 AF_INET,
922 923 924 925 926 927 928 929 930 931 932 933 934 935
                                 action,
                                 "--out-interface", iface,
                                 "--protocol", "udp",
                                 "--destination-port", portstr,
                                 "--jump", "CHECKSUM", "--checksum-fill",
                                 NULL);
}

/**
 * iptablesAddOutputFixUdpChecksum:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to match
 *
E
Eric Blake 已提交
936
 * Add a rule to the mangle table's POSTROUTING chain that fixes up the
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972
 * checksum of packets with the given destination @port.
 * the given @iface interface for TCP packets.
 *
 * Returns 0 in case of success or an error code in case of error.
 * (NB: if the system's iptables does not support checksum mangling,
 * this will return an error, which should be ignored.)
 */

int
iptablesAddOutputFixUdpChecksum(iptablesContext *ctx,
                                const char *iface,
                                int port)
{
    return iptablesOutputFixUdpChecksum(ctx, iface, port, ADD);
}

/**
 * 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.
 *
 * Returns 0 in case of success or an error code in case of error
 * (again, if iptables doesn't support checksum fixup, this will
 * return an error, which should be ignored)
 */
int
iptablesRemoveOutputFixUdpChecksum(iptablesContext *ctx,
                                   const char *iface,
                                   int port)
{
    return iptablesOutputFixUdpChecksum(ctx, iface, port, REMOVE);
}