viriptables.c 30.4 KB
Newer Older
1
/*
2 3
 * viriptables.c: helper APIs for managing iptables
 *
4
 * Copyright (C) 2007-2013 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 "virfile.h"
48
#include "virlog.h"
49
#include "virthread.h"
50 51
#include "virstring.h"
#include "virutil.h"
52 53 54 55 56 57 58 59 60

#if HAVE_FIREWALLD
static char *firewall_cmd_path = NULL;

static int
virIpTablesOnceInit(void)
{
    firewall_cmd_path = virFindFileInPath("firewall-cmd");
    if (!firewall_cmd_path) {
61
        VIR_INFO("firewall-cmd not found on system. "
62 63 64 65 66 67 68
                 "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) {
69
            VIR_INFO("firewall-cmd found but disabled for iptables");
70 71 72
            VIR_FREE(firewall_cmd_path);
            firewall_cmd_path = NULL;
        } else {
73
            VIR_INFO("using firewalld for iptables commands");
74 75 76 77 78 79 80 81 82
        }
        virCommandFree(cmd);
    }
    return 0;
}

VIR_ONCE_GLOBAL_INIT(virIpTables)

#endif
83

84
#define VIR_FROM_THIS VIR_FROM_NONE
85

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
enum {
    ADD = 0,
    REMOVE
};

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

struct _iptablesContext
{
    iptRules *input_filter;
    iptRules *forward_filter;
    iptRules *nat_postrouting;
102
    iptRules *mangle_postrouting;
103 104 105 106 107
};

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

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

119
    if (VIR_ALLOC(rules) < 0)
120 121
        return NULL;

122
    if (VIR_STRDUP(rules->table, table) < 0)
123 124
        goto error;

125
    if (VIR_STRDUP(rules->chain, chain) < 0)
126 127 128 129 130 131 132 133 134
        goto error;

    return rules;

 error:
    iptRulesFree(rules);
    return NULL;
}

135 136
static virCommandPtr
iptablesCommandNew(iptRules *rules, int family, int action)
137
{
138 139 140 141 142 143 144 145 146 147 148 149
    virCommandPtr cmd = NULL;
#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)
150
                        ? IP6TABLES_PATH : IPTABLES_PATH);
151
    }
152

153 154
    virCommandAddArgList(cmd, "--table", rules->table,
                         action == ADD ? "--insert" : "--delete",
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
                         rules->chain, NULL);
    return cmd;
}

static int
iptablesCommandRunAndFree(virCommandPtr cmd)
{
    int ret;
    ret = virCommandRun(cmd, NULL);
    virCommandFree(cmd);
    return ret;
}

static int ATTRIBUTE_SENTINEL
iptablesAddRemoveRule(iptRules *rules, int family, int action,
                      const char *arg, ...)
{
    va_list args;
    virCommandPtr cmd = NULL;
    const char *s;

    cmd = iptablesCommandNew(rules, family, action);
    virCommandAddArg(cmd, arg);
178 179

    va_start(args, arg);
180 181
    while ((s = va_arg(args, const char *)))
        virCommandAddArg(cmd, s);
182 183
    va_end(args);

184
    return iptablesCommandRunAndFree(cmd);
185 186
}

187 188 189 190 191 192 193
/**
 * iptablesContextNew:
 *
 * Create a new IPtable context
 *
 * Returns a pointer to the new structure or NULL in case of error
 */
194 195 196 197 198
iptablesContext *
iptablesContextNew(void)
{
    iptablesContext *ctx;

199
    if (VIR_ALLOC(ctx) < 0)
200 201
        return NULL;

202
    if (!(ctx->input_filter = iptRulesNew("filter", "INPUT")))
203 204
        goto error;

205
    if (!(ctx->forward_filter = iptRulesNew("filter", "FORWARD")))
206 207
        goto error;

208
    if (!(ctx->nat_postrouting = iptRulesNew("nat", "POSTROUTING")))
209 210
        goto error;

211 212 213
    if (!(ctx->mangle_postrouting = iptRulesNew("mangle", "POSTROUTING")))
        goto error;

214 215 216 217 218 219 220
    return ctx;

 error:
    iptablesContextFree(ctx);
    return NULL;
}

221 222 223 224
/**
 * iptablesContextFree:
 * @ctx: pointer to the IP table context
 *
R
Richard W.M. Jones 已提交
225
 * Free the resources associated with an IP table context
226
 */
227 228 229
void
iptablesContextFree(iptablesContext *ctx)
{
230 231 232 233 234 235
    if (ctx->input_filter)
        iptRulesFree(ctx->input_filter);
    if (ctx->forward_filter)
        iptRulesFree(ctx->forward_filter);
    if (ctx->nat_postrouting)
        iptRulesFree(ctx->nat_postrouting);
236 237
    if (ctx->mangle_postrouting)
        iptRulesFree(ctx->mangle_postrouting);
238
    VIR_FREE(ctx);
239 240 241 242
}

static int
iptablesInput(iptablesContext *ctx,
243
              int family,
244 245 246 247 248 249 250 251 252 253
              const char *iface,
              int port,
              int action,
              int tcp)
{
    char portstr[32];

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

254
    return iptablesAddRemoveRule(ctx->input_filter,
255
                                 family,
256 257 258 259 260 261
                                 action,
                                 "--in-interface", iface,
                                 "--protocol", tcp ? "tcp" : "udp",
                                 "--destination-port", portstr,
                                 "--jump", "ACCEPT",
                                 NULL);
262 263
}

264 265 266 267 268 269 270 271 272 273 274 275
/**
 * 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
 */

276 277
int
iptablesAddTcpInput(iptablesContext *ctx,
278
                    int family,
279 280 281
                    const char *iface,
                    int port)
{
282
    return iptablesInput(ctx, family, iface, port, ADD, 1);
283 284
}

285 286 287 288 289 290
/**
 * iptablesRemoveTcpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the TCP port to remove
 *
R
Richard W.M. Jones 已提交
291
 * Removes an input from the IP table, hence forbidding access to the given
292 293 294 295
 * @port on the given @iface interface for TCP packets
 *
 * Returns 0 in case of success or an error code in case of error
 */
296 297
int
iptablesRemoveTcpInput(iptablesContext *ctx,
298
                       int family,
299 300 301
                       const char *iface,
                       int port)
{
302
    return iptablesInput(ctx, family, iface, port, REMOVE, 1);
303 304
}

305 306 307 308 309 310 311 312 313 314 315 316
/**
 * 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
 */

317 318
int
iptablesAddUdpInput(iptablesContext *ctx,
319
                    int family,
320 321 322
                    const char *iface,
                    int port)
{
323
    return iptablesInput(ctx, family, iface, port, ADD, 0);
324 325
}

326 327 328 329 330 331
/**
 * iptablesRemoveUdpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to remove
 *
R
Richard W.M. Jones 已提交
332
 * Removes an input from the IP table, hence forbidding access to the given
333 334 335 336
 * @port on the given @iface interface for UDP packets
 *
 * Returns 0 in case of success or an error code in case of error
 */
337 338
int
iptablesRemoveUdpInput(iptablesContext *ctx,
339
                       int family,
340 341 342
                       const char *iface,
                       int port)
{
343
    return iptablesInput(ctx, family, iface, port, REMOVE, 0);
344 345 346
}


347
static char *iptablesFormatNetwork(virSocketAddr *netaddr,
348
                                   unsigned int prefix)
349 350 351 352 353
{
    virSocketAddr network;
    char *netstr;
    char *ret;

354 355
    if (!(VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET) ||
          VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET6))) {
356 357
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Only IPv4 or IPv6 addresses can be used with iptables"));
358 359 360
        return NULL;
    }

361
    if (virSocketAddrMaskByPrefix(netaddr, prefix, &network) < 0) {
362 363
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failure to mask address"));
364 365
        return NULL;
    }
366

367
    netstr = virSocketAddrFormat(&network);
368 369 370 371 372 373 374 375 376 377 378 379

    if (!netstr)
        return NULL;

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

    VIR_FREE(netstr);
    return ret;
}


380 381 382
/* Allow all traffic coming from the bridge, with a valid network address
 * to proceed to WAN
 */
383
static int
384
iptablesForwardAllowOut(iptablesContext *ctx,
385
                        virSocketAddr *netaddr,
386
                        unsigned int prefix,
387 388 389
                        const char *iface,
                        const char *physdev,
                        int action)
390
{
391 392
    int ret;
    char *networkstr;
393
    virCommandPtr cmd = NULL;
394

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

398 399 400 401 402 403 404 405 406 407 408 409 410
    cmd = iptablesCommandNew(ctx->forward_filter,
                             VIR_SOCKET_ADDR_FAMILY(netaddr),
                             action);
    virCommandAddArgList(cmd,
                         "--source", networkstr,
                         "--in-interface", iface, NULL);

    if (physdev && physdev[0])
        virCommandAddArgList(cmd, "--out-interface", physdev, NULL);

    virCommandAddArgList(cmd, "--jump", "ACCEPT", NULL);

    ret = iptablesCommandRunAndFree(cmd);
411 412
    VIR_FREE(networkstr);
    return ret;
413 414
}

415 416 417 418 419 420
/**
 * iptablesAddForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
421
 *
422 423 424 425 426 427
 * 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
 */
428
int
429
iptablesAddForwardAllowOut(iptablesContext *ctx,
430
                           virSocketAddr *netaddr,
431
                           unsigned int prefix,
432 433
                           const char *iface,
                           const char *physdev)
434
{
435
    return iptablesForwardAllowOut(ctx, netaddr, prefix, iface, physdev, ADD);
436 437
}

438 439 440 441 442 443
/**
 * iptablesRemoveForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
444
 *
445 446 447 448 449 450
 * 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
 */
451
int
452
iptablesRemoveForwardAllowOut(iptablesContext *ctx,
453
                              virSocketAddr *netaddr,
454
                              unsigned int prefix,
455 456
                              const char *iface,
                              const char *physdev)
457
{
458
    return iptablesForwardAllowOut(ctx, netaddr, prefix, iface, physdev, REMOVE);
459 460
}

461 462 463 464

/* Allow all traffic destined to the bridge, with a valid network address
 * and associated with an existing connection
 */
465
static int
466
iptablesForwardAllowRelatedIn(iptablesContext *ctx,
467
                              virSocketAddr *netaddr,
468
                              unsigned int prefix,
469 470 471
                              const char *iface,
                              const char *physdev,
                              int action)
472
{
473 474 475
    int ret;
    char *networkstr;

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

479
    if (physdev && physdev[0]) {
480
        ret = iptablesAddRemoveRule(ctx->forward_filter,
481
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
482 483 484 485
                                    action,
                                    "--destination", networkstr,
                                    "--in-interface", physdev,
                                    "--out-interface", iface,
S
Stefan Seyfried 已提交
486 487
                                    "--match", "conntrack",
                                    "--ctstate", "ESTABLISHED,RELATED",
488 489
                                    "--jump", "ACCEPT",
                                    NULL);
490
    } else {
491
        ret = iptablesAddRemoveRule(ctx->forward_filter,
492
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
493 494 495
                                    action,
                                    "--destination", networkstr,
                                    "--out-interface", iface,
S
Stefan Seyfried 已提交
496 497
                                    "--match", "conntrack",
                                    "--ctstate", "ESTABLISHED,RELATED",
498 499
                                    "--jump", "ACCEPT",
                                    NULL);
500
    }
501 502
    VIR_FREE(networkstr);
    return ret;
503 504
}

505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
/**
 * 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,
520
                                 virSocketAddr *netaddr,
521
                                 unsigned int prefix,
522 523
                                 const char *iface,
                                 const char *physdev)
524
{
525
    return iptablesForwardAllowRelatedIn(ctx, netaddr, prefix, iface, physdev, ADD);
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
}

/**
 * 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,
543
                                    virSocketAddr *netaddr,
544
                                    unsigned int prefix,
545 546
                                    const char *iface,
                                    const char *physdev)
547
{
548
    return iptablesForwardAllowRelatedIn(ctx, netaddr, prefix, iface, physdev, REMOVE);
549 550 551 552 553 554
}

/* Allow all traffic destined to the bridge, with a valid network address
 */
static int
iptablesForwardAllowIn(iptablesContext *ctx,
555
                       virSocketAddr *netaddr,
556
                       unsigned int prefix,
557 558 559 560
                       const char *iface,
                       const char *physdev,
                       int action)
{
561 562 563
    int ret;
    char *networkstr;

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

567
    if (physdev && physdev[0]) {
568
        ret = iptablesAddRemoveRule(ctx->forward_filter,
569
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
570 571 572 573 574 575
                                    action,
                                    "--destination", networkstr,
                                    "--in-interface", physdev,
                                    "--out-interface", iface,
                                    "--jump", "ACCEPT",
                                    NULL);
576
    } else {
577
        ret = iptablesAddRemoveRule(ctx->forward_filter,
578
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
579 580 581 582 583
                                    action,
                                    "--destination", networkstr,
                                    "--out-interface", iface,
                                    "--jump", "ACCEPT",
                                    NULL);
584
    }
585 586
    VIR_FREE(networkstr);
    return ret;
587 588
}

589 590 591 592 593 594
/**
 * 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
595
 *
596 597 598 599 600 601
 * 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
 */
602
int
603
iptablesAddForwardAllowIn(iptablesContext *ctx,
604
                          virSocketAddr *netaddr,
605
                          unsigned int prefix,
606 607 608
                          const char *iface,
                          const char *physdev)
{
609
    return iptablesForwardAllowIn(ctx, netaddr, prefix, iface, physdev, ADD);
610 611
}

612 613 614 615 616 617
/**
 * 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
618
 *
619 620 621 622 623 624
 * 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
 */
625 626
int
iptablesRemoveForwardAllowIn(iptablesContext *ctx,
627
                             virSocketAddr *netaddr,
628
                             unsigned int prefix,
629 630 631
                             const char *iface,
                             const char *physdev)
{
632
    return iptablesForwardAllowIn(ctx, netaddr, prefix, iface, physdev, REMOVE);
633 634 635 636 637 638 639 640
}


/* Allow all traffic between guests on the same bridge,
 * with a valid network address
 */
static int
iptablesForwardAllowCross(iptablesContext *ctx,
641
                          int family,
642 643 644 645
                          const char *iface,
                          int action)
{
    return iptablesAddRemoveRule(ctx->forward_filter,
646
                                 family,
647 648 649 650 651 652 653
                                 action,
                                 "--in-interface", iface,
                                 "--out-interface", iface,
                                 "--jump", "ACCEPT",
                                 NULL);
}

654 655 656 657 658 659 660 661 662 663 664
/**
 * 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
 */
665 666
int
iptablesAddForwardAllowCross(iptablesContext *ctx,
667 668 669 670
                             int family,
                             const char *iface)
{
    return iptablesForwardAllowCross(ctx, family, iface, ADD);
671 672
}

673 674 675 676 677 678 679 680 681 682 683
/**
 * 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
 */
684 685
int
iptablesRemoveForwardAllowCross(iptablesContext *ctx,
686 687 688 689
                                int family,
                                const char *iface)
{
    return iptablesForwardAllowCross(ctx, family, iface, REMOVE);
690 691 692 693 694 695 696 697
}


/* Drop all traffic trying to forward from the bridge.
 * ie the bridge is the in interface
 */
static int
iptablesForwardRejectOut(iptablesContext *ctx,
698
                         int family,
699 700 701 702
                         const char *iface,
                         int action)
{
    return iptablesAddRemoveRule(ctx->forward_filter,
703 704 705 706 707
                                 family,
                                 action,
                                 "--in-interface", iface,
                                 "--jump", "REJECT",
                                 NULL);
708 709
}

710 711 712 713 714 715 716 717 718 719
/**
 * 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
 */
720 721
int
iptablesAddForwardRejectOut(iptablesContext *ctx,
722
                            int family,
723 724
                            const char *iface)
{
725
    return iptablesForwardRejectOut(ctx, family, iface, ADD);
726 727
}

728 729 730 731 732 733 734 735 736 737
/**
 * 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
 */
738 739
int
iptablesRemoveForwardRejectOut(iptablesContext *ctx,
740
                               int family,
741 742
                               const char *iface)
{
743
    return iptablesForwardRejectOut(ctx, family, iface, REMOVE);
744 745 746 747 748 749 750 751 752 753
}




/* Drop all traffic trying to forward to the bridge.
 * ie the bridge is the out interface
 */
static int
iptablesForwardRejectIn(iptablesContext *ctx,
754
                        int family,
755
                        const char *iface,
756 757 758
                        int action)
{
    return iptablesAddRemoveRule(ctx->forward_filter,
759
                                 family,
760 761 762 763 764 765
                                 action,
                                 "--out-interface", iface,
                                 "--jump", "REJECT",
                                 NULL);
}

766 767 768 769 770 771 772 773 774 775
/**
 * 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
 */
776 777
int
iptablesAddForwardRejectIn(iptablesContext *ctx,
778
                           int family,
779
                           const char *iface)
780
{
781
    return iptablesForwardRejectIn(ctx, family, iface, ADD);
782 783
}

784 785 786 787 788 789 790 791 792 793
/**
 * 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
 */
794
int
795
iptablesRemoveForwardRejectIn(iptablesContext *ctx,
796
                              int family,
797
                              const char *iface)
798
{
799
    return iptablesForwardRejectIn(ctx, family, iface, REMOVE);
800 801
}

802 803 804 805

/* Masquerade all traffic coming from the network associated
 * with the bridge
 */
806
static int
807
iptablesForwardMasquerade(iptablesContext *ctx,
808
                          virSocketAddr *netaddr,
809
                          unsigned int prefix,
810
                          const char *physdev,
811 812
                          virSocketAddrRangePtr addr,
                          virPortRangePtr port,
813 814
                          const char *protocol,
                          int action)
815
{
816 817 818 819
    int ret = -1;
    char *networkstr = NULL;
    char *addrStartStr = NULL;
    char *addrEndStr = NULL;
820
    char *portRangeStr = NULL;
821
    char *natRangeStr = NULL;
822
    virCommandPtr cmd = NULL;
823

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

827
    if (!VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET)) {
828
        /* Higher level code *should* guaranteee it's impossible to get here. */
829 830 831
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Attempted to NAT '%s'. NAT is only supported for IPv4."),
                       networkstr);
832 833 834
        goto cleanup;
    }

835 836
    if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->start, AF_INET)) {
        if (!(addrStartStr = virSocketAddrFormat(&addr->start)))
837
            goto cleanup;
838 839
        if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->end, AF_INET)) {
            if (!(addrEndStr = virSocketAddrFormat(&addr->end)))
840 841
                goto cleanup;
        }
842 843
    }

844 845 846 847 848 849 850 851 852 853 854
    cmd = iptablesCommandNew(ctx->nat_postrouting, AF_INET, action);
    virCommandAddArgList(cmd, "--source", networkstr, NULL);

    if (protocol && protocol[0])
        virCommandAddArgList(cmd, "-p", protocol, NULL);

    virCommandAddArgList(cmd, "!", "--destination", networkstr, NULL);

    if (physdev && physdev[0])
        virCommandAddArgList(cmd, "--out-interface", physdev, NULL);

855
    if (protocol && protocol[0]) {
856 857 858
        if (port->start == 0 && port->end == 0) {
            port->start = 1024;
            port->end = 65535;
859 860
        }

861 862 863
        if (port->start < port->end && port->end < 65536) {
            if (virAsprintf(&portRangeStr, ":%u-%u",
                            port->start, port->end) < 0) {
864 865 866 867 868 869
                virReportOOMError();
                goto cleanup;
            }
        } else {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Invalid port range '%u-%u'."),
870
                           port->start, port->end);
871 872 873
        }
    }

874 875 876
    /* Use --jump SNAT if public addr is specified */
    if (addrStartStr && addrStartStr[0]) {
        int r = 0;
877

878 879
        if (addrEndStr && addrEndStr[0]) {
            r = virAsprintf(&natRangeStr, "%s-%s%s", addrStartStr, addrEndStr,
880
                            portRangeStr ? portRangeStr : "");
881
        } else {
882 883
            r = virAsprintf(&natRangeStr, "%s%s", addrStartStr,
                            portRangeStr ? portRangeStr : "");
884 885 886 887 888 889 890 891 892 893 894 895
        }

        if (r < 0) {
            virReportOOMError();
            goto cleanup;
        }

        virCommandAddArgList(cmd, "--jump", "SNAT",
                                  "--to-source", natRangeStr, NULL);
     } else {
         virCommandAddArgList(cmd, "--jump", "MASQUERADE", NULL);

896 897
         if (portRangeStr && portRangeStr[0])
             virCommandAddArgList(cmd, "--to-ports", &portRangeStr[1], NULL);
898 899 900 901 902
     }

    ret = virCommandRun(cmd, NULL);
cleanup:
    virCommandFree(cmd);
903
    VIR_FREE(networkstr);
904 905
    VIR_FREE(addrStartStr);
    VIR_FREE(addrEndStr);
906
    VIR_FREE(portRangeStr);
907
    VIR_FREE(natRangeStr);
908
    return ret;
909 910
}

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

937 938 939 940 941
/**
 * iptablesRemoveForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
942
 * @protocol: the network protocol or NULL
943
 *
944 945 946 947 948 949
 * 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
 */
950
int
951
iptablesRemoveForwardMasquerade(iptablesContext *ctx,
952
                                virSocketAddr *netaddr,
953
                                unsigned int prefix,
954
                                const char *physdev,
955 956
                                virSocketAddrRangePtr addr,
                                virPortRangePtr port,
957
                                const char *protocol)
958
{
959
    return iptablesForwardMasquerade(ctx, netaddr, prefix, physdev, addr, port,
960
                                     protocol, REMOVE);
961
}
962 963 964 965 966 967 968 969 970 971 972 973 974 975


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,
976
                                 AF_INET,
977 978 979 980 981 982 983 984 985 986 987 988 989 990
                                 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 已提交
991
 * Add a rule to the mangle table's POSTROUTING chain that fixes up the
992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
 * 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);
}