viriptables.c 31.3 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 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
VIR_LOG_INIT("util.iptables");

55
#define VIR_FROM_THIS VIR_FROM_NONE
56

57 58 59 60 61
enum {
    ADD = 0,
    REMOVE
};

62

63 64 65
static void
iptablesInput(virFirewallPtr fw,
              virFirewallLayer layer,
66 67 68 69 70 71 72 73 74 75
              const char *iface,
              int port,
              int action,
              int tcp)
{
    char portstr[32];

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

76 77 78 79 80 81 82 83
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       action == ADD ? "--insert" : "--delete", "INPUT",
                       "--in-interface", iface,
                       "--protocol", tcp ? "tcp" : "udp",
                       "--destination-port", portstr,
                       "--jump", "ACCEPT",
                       NULL);
84 85
}

86 87 88
static void
iptablesOutput(virFirewallPtr fw,
               virFirewallLayer layer,
89 90 91 92 93 94 95 96 97 98
               const char *iface,
               int port,
               int action,
               int tcp)
{
    char portstr[32];

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

99 100 101 102 103 104 105 106
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       action == ADD ? "--insert" : "--delete", "OUTPUT",
                       "--out-interface", iface,
                       "--protocol", tcp ? "tcp" : "udp",
                       "--destination-port", portstr,
                       "--jump", "ACCEPT",
                       NULL);
107 108
}

109 110 111 112 113 114 115 116 117
/**
 * 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
 */
118 119 120
void
iptablesAddTcpInput(virFirewallPtr fw,
                    virFirewallLayer layer,
121 122 123
                    const char *iface,
                    int port)
{
124
    iptablesInput(fw, layer, iface, port, ADD, 1);
125 126
}

127 128 129 130 131 132
/**
 * iptablesRemoveTcpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the TCP port to remove
 *
R
Richard W.M. Jones 已提交
133
 * Removes an input from the IP table, hence forbidding access to the given
134 135
 * @port on the given @iface interface for TCP packets
 */
136 137 138
void
iptablesRemoveTcpInput(virFirewallPtr fw,
                       virFirewallLayer layer,
139 140 141
                       const char *iface,
                       int port)
{
142
    iptablesInput(fw, layer, iface, port, REMOVE, 1);
143 144
}

145 146 147 148 149 150 151 152 153
/**
 * 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
 */
154 155 156
void
iptablesAddUdpInput(virFirewallPtr fw,
                    virFirewallLayer layer,
157 158 159
                    const char *iface,
                    int port)
{
160
    iptablesInput(fw, layer, iface, port, ADD, 0);
161 162
}

163 164 165 166 167 168
/**
 * iptablesRemoveUdpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to remove
 *
R
Richard W.M. Jones 已提交
169
 * Removes an input from the IP table, hence forbidding access to the given
170 171
 * @port on the given @iface interface for UDP packets
 */
172 173 174
void
iptablesRemoveUdpInput(virFirewallPtr fw,
                       virFirewallLayer layer,
175 176 177
                       const char *iface,
                       int port)
{
178
    return iptablesInput(fw, layer, iface, port, REMOVE, 0);
179 180
}

181 182 183 184 185 186 187 188 189
/**
 * 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
 */
190 191 192
void
iptablesAddUdpOutput(virFirewallPtr fw,
                     virFirewallLayer layer,
193 194 195
                     const char *iface,
                     int port)
{
196
    iptablesOutput(fw, layer, iface, port, ADD, 0);
197 198 199 200 201 202 203 204 205 206 207
}

/**
 * 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
 */
208 209 210
void
iptablesRemoveUdpOutput(virFirewallPtr fw,
                        virFirewallLayer layer,
211 212 213
                        const char *iface,
                        int port)
{
214
    iptablesOutput(fw, layer, iface, port, REMOVE, 0);
215 216
}

217

218
static char *iptablesFormatNetwork(virSocketAddr *netaddr,
219
                                   unsigned int prefix)
220 221 222 223 224
{
    virSocketAddr network;
    char *netstr;
    char *ret;

225 226
    if (!(VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET) ||
          VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET6))) {
227 228
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Only IPv4 or IPv6 addresses can be used with iptables"));
229 230 231
        return NULL;
    }

232
    if (virSocketAddrMaskByPrefix(netaddr, prefix, &network) < 0) {
233 234
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failure to mask address"));
235 236
        return NULL;
    }
237

238
    netstr = virSocketAddrFormat(&network);
239 240 241 242

    if (!netstr)
        return NULL;

243
    ignore_value(virAsprintf(&ret, "%s/%d", netstr, prefix));
244 245 246 247 248 249

    VIR_FREE(netstr);
    return ret;
}


250 251 252
/* Allow all traffic coming from the bridge, with a valid network address
 * to proceed to WAN
 */
253
static int
254 255
iptablesForwardAllowOut(virFirewallPtr fw,
                        virSocketAddr *netaddr,
256
                        unsigned int prefix,
257 258 259
                        const char *iface,
                        const char *physdev,
                        int action)
260
{
261
    char *networkstr;
262 263
    virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ?
        VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
264

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

268
    if (physdev && physdev[0])
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
        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);
285

286
    VIR_FREE(networkstr);
287
    return 0;
288 289
}

290 291 292 293 294 295
/**
 * iptablesAddForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
296
 *
297 298 299 300 301 302
 * 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
 */
303
int
304 305
iptablesAddForwardAllowOut(virFirewallPtr fw,
                           virSocketAddr *netaddr,
306
                           unsigned int prefix,
307 308
                           const char *iface,
                           const char *physdev)
309
{
310
    return iptablesForwardAllowOut(fw, netaddr, prefix, iface, physdev, ADD);
311 312
}

313 314 315 316 317 318
/**
 * iptablesRemoveForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
319
 *
320 321 322 323 324 325
 * 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
 */
326
int
327 328
iptablesRemoveForwardAllowOut(virFirewallPtr fw,
                              virSocketAddr *netaddr,
329
                              unsigned int prefix,
330 331
                              const char *iface,
                              const char *physdev)
332
{
333
    return iptablesForwardAllowOut(fw, netaddr, prefix, iface, physdev, REMOVE);
334 335
}

336 337 338 339

/* Allow all traffic destined to the bridge, with a valid network address
 * and associated with an existing connection
 */
340
static int
341 342
iptablesForwardAllowRelatedIn(virFirewallPtr fw,
                              virSocketAddr *netaddr,
343
                              unsigned int prefix,
344 345 346
                              const char *iface,
                              const char *physdev,
                              int action)
347
{
348 349
    virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ?
        VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
350 351
    char *networkstr;

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

355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
    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);

377
    VIR_FREE(networkstr);
378
    return 0;
379 380
}

381 382 383 384 385 386 387 388 389 390 391 392 393 394
/**
 * 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
395 396
iptablesAddForwardAllowRelatedIn(virFirewallPtr fw,
                                 virSocketAddr *netaddr,
397
                                 unsigned int prefix,
398 399
                                 const char *iface,
                                 const char *physdev)
400
{
401
    return iptablesForwardAllowRelatedIn(fw, netaddr, prefix, iface, physdev, ADD);
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
}

/**
 * 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
418 419
iptablesRemoveForwardAllowRelatedIn(virFirewallPtr fw,
                                    virSocketAddr *netaddr,
420
                                    unsigned int prefix,
421 422
                                    const char *iface,
                                    const char *physdev)
423
{
424
    return iptablesForwardAllowRelatedIn(fw, netaddr, prefix, iface, physdev, REMOVE);
425 426 427 428 429
}

/* Allow all traffic destined to the bridge, with a valid network address
 */
static int
430 431
iptablesForwardAllowIn(virFirewallPtr fw,
                       virSocketAddr *netaddr,
432
                       unsigned int prefix,
433 434 435 436
                       const char *iface,
                       const char *physdev,
                       int action)
{
437 438
    virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ?
        VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
439 440
    char *networkstr;

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

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
    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);
461
    VIR_FREE(networkstr);
462
    return 0;
463 464
}

465 466 467 468 469 470
/**
 * 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
471
 *
472 473 474 475 476 477
 * 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
 */
478
int
479 480
iptablesAddForwardAllowIn(virFirewallPtr fw,
                          virSocketAddr *netaddr,
481
                          unsigned int prefix,
482 483 484
                          const char *iface,
                          const char *physdev)
{
485
    return iptablesForwardAllowIn(fw, netaddr, prefix, iface, physdev, ADD);
486 487
}

488 489 490 491 492 493
/**
 * 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
494
 *
495 496 497 498 499 500
 * 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
 */
501
int
502 503
iptablesRemoveForwardAllowIn(virFirewallPtr fw,
                             virSocketAddr *netaddr,
504
                             unsigned int prefix,
505 506 507
                             const char *iface,
                             const char *physdev)
{
508
    return iptablesForwardAllowIn(fw, netaddr, prefix, iface, physdev, REMOVE);
509 510
}

511 512 513 514 515 516 517 518 519 520 521
/**
 * 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
 */
522 523 524
void
iptablesAddForwardAllowCross(virFirewallPtr fw,
                             virFirewallLayer layer,
525 526
                             const char *iface)
{
527 528 529 530 531 532 533
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       "--insert", "FORWARD",
                       "--in-interface", iface,
                       "--out-interface", iface,
                       "--jump", "ACCEPT",
                       NULL);
534 535
}

536 537 538 539 540 541 542 543 544 545 546
/**
 * 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
 */
547 548 549
void
iptablesRemoveForwardAllowCross(virFirewallPtr fw,
                                virFirewallLayer layer,
550 551
                                const char *iface)
{
552 553 554 555 556 557 558
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       "--delete", "FORWARD",
                       "--in-interface", iface,
                       "--out-interface", iface,
                       "--jump", "ACCEPT",
                       NULL);
559 560
}

561 562 563 564 565 566 567 568 569 570
/**
 * 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
 */
571 572 573
void
iptablesAddForwardRejectOut(virFirewallPtr fw,
                            virFirewallLayer layer,
574 575
                            const char *iface)
{
576 577 578 579 580 581
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       "--insert", "FORWARD",
                       "--in-interface", iface,
                       "--jump", "REJECT",
                       NULL);
582 583
}

584 585 586 587 588 589 590 591 592 593
/**
 * 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
 */
594 595 596
void
iptablesRemoveForwardRejectOut(virFirewallPtr fw,
                               virFirewallLayer layer,
597 598
                               const char *iface)
{
599 600 601 602 603 604
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       "--delete", "FORWARD",
                       "--in-interface", iface,
                       "--jump", "REJECT",
                       NULL);
605 606 607
}


608 609 610 611 612 613 614 615 616 617
/**
 * 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
 */
618 619 620
void
iptablesAddForwardRejectIn(virFirewallPtr fw,
                           virFirewallLayer layer,
621
                           const char *iface)
622
{
623 624 625 626 627 628
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       "--insert", "FORWARD",
                       "--out-interface", iface,
                       "--jump", "REJECT",
                       NULL);
629 630
}

631 632 633 634 635 636 637 638 639 640
/**
 * 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
 */
641 642 643
void
iptablesRemoveForwardRejectIn(virFirewallPtr fw,
                              virFirewallLayer layer,
644
                              const char *iface)
645
{
646 647 648 649 650 651
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       "--delete", "FORWARD",
                       "--out-interface", iface,
                       "--jump", "REJECT",
                       NULL);
652 653
}

654 655 656 657

/* Masquerade all traffic coming from the network associated
 * with the bridge
 */
658
static int
659 660
iptablesForwardMasquerade(virFirewallPtr fw,
                          virSocketAddr *netaddr,
661
                          unsigned int prefix,
662
                          const char *physdev,
663 664
                          virSocketAddrRangePtr addr,
                          virPortRangePtr port,
665 666
                          const char *protocol,
                          int action)
667
{
668 669 670 671
    int ret = -1;
    char *networkstr = NULL;
    char *addrStartStr = NULL;
    char *addrEndStr = NULL;
672
    char *portRangeStr = NULL;
673
    char *natRangeStr = NULL;
674
    virFirewallRulePtr rule;
675

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

679
    if (!VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET)) {
680
        /* Higher level code *should* guaranteee it's impossible to get here. */
681 682 683
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Attempted to NAT '%s'. NAT is only supported for IPv4."),
                       networkstr);
684 685 686
        goto cleanup;
    }

687 688
    if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->start, AF_INET)) {
        if (!(addrStartStr = virSocketAddrFormat(&addr->start)))
689
            goto cleanup;
690 691
        if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->end, AF_INET)) {
            if (!(addrEndStr = virSocketAddrFormat(&addr->end)))
692 693
                goto cleanup;
        }
694 695
    }

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
    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);
    }
712 713

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

716
    if (protocol && protocol[0]) {
717 718 719
        if (port->start == 0 && port->end == 0) {
            port->start = 1024;
            port->end = 65535;
720 721
        }

722 723
        if (port->start < port->end && port->end < 65536) {
            if (virAsprintf(&portRangeStr, ":%u-%u",
724
                            port->start, port->end) < 0)
725 726 727 728
                goto cleanup;
        } else {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Invalid port range '%u-%u'."),
729
                           port->start, port->end);
730 731 732
        }
    }

733 734 735
    /* Use --jump SNAT if public addr is specified */
    if (addrStartStr && addrStartStr[0]) {
        int r = 0;
736

737 738
        if (addrEndStr && addrEndStr[0]) {
            r = virAsprintf(&natRangeStr, "%s-%s%s", addrStartStr, addrEndStr,
739
                            portRangeStr ? portRangeStr : "");
740
        } else {
741 742
            r = virAsprintf(&natRangeStr, "%s%s", addrStartStr,
                            portRangeStr ? portRangeStr : "");
743 744
        }

745
        if (r < 0)
746 747
            goto cleanup;

748 749
        virFirewallRuleAddArgList(fw, rule,
                                  "--jump", "SNAT",
750
                                  "--to-source", natRangeStr, NULL);
751 752 753
    } else {
        virFirewallRuleAddArgList(fw, rule,
                                  "--jump", "MASQUERADE", NULL);
754

755 756 757 758
        if (portRangeStr && portRangeStr[0])
            virFirewallRuleAddArgList(fw, rule,
                                      "--to-ports", &portRangeStr[1], NULL);
    }
759

760
    ret = 0;
761
 cleanup:
762
    VIR_FREE(networkstr);
763 764
    VIR_FREE(addrStartStr);
    VIR_FREE(addrEndStr);
765
    VIR_FREE(portRangeStr);
766
    VIR_FREE(natRangeStr);
767
    return ret;
768 769
}

770 771 772 773 774
/**
 * iptablesAddForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
775
 * @protocol: the network protocol or NULL
776
 *
777 778 779 780 781 782
 * 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
 */
783
int
784 785
iptablesAddForwardMasquerade(virFirewallPtr fw,
                             virSocketAddr *netaddr,
786
                             unsigned int prefix,
787
                             const char *physdev,
788 789
                             virSocketAddrRangePtr addr,
                             virPortRangePtr port,
790
                             const char *protocol)
791
{
792
    return iptablesForwardMasquerade(fw, netaddr, prefix, physdev, addr, port,
793
                                     protocol, ADD);
794 795
}

796 797 798 799 800
/**
 * iptablesRemoveForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
801
 * @protocol: the network protocol or NULL
802
 *
803 804 805 806 807 808
 * 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
 */
809
int
810 811
iptablesRemoveForwardMasquerade(virFirewallPtr fw,
                                virSocketAddr *netaddr,
812
                                unsigned int prefix,
813
                                const char *physdev,
814 815
                                virSocketAddrRangePtr addr,
                                virPortRangePtr port,
816
                                const char *protocol)
817
{
818
    return iptablesForwardMasquerade(fw, netaddr, prefix, physdev, addr, port,
819
                                     protocol, REMOVE);
820
}
821 822


823 824 825 826
/* Don't masquerade traffic coming from the network associated with the bridge
 * if said traffic targets @destaddr.
 */
static int
827 828
iptablesForwardDontMasquerade(virFirewallPtr fw,
                              virSocketAddr *netaddr,
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
                              unsigned int prefix,
                              const char *physdev,
                              const char *destaddr,
                              int action)
{
    int ret = -1;
    char *networkstr = NULL;

    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);
        goto cleanup;
    }

    if (physdev && physdev[0])
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
        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);

    ret = 0;
867
 cleanup:
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
    VIR_FREE(networkstr);
    return ret;
}

/**
 * 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
887 888
iptablesAddDontMasquerade(virFirewallPtr fw,
                          virSocketAddr *netaddr,
889 890 891 892
                          unsigned int prefix,
                          const char *physdev,
                          const char *destaddr)
{
893
    return iptablesForwardDontMasquerade(fw, netaddr, prefix, physdev, destaddr,
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
                                         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
912 913
iptablesRemoveDontMasquerade(virFirewallPtr fw,
                             virSocketAddr *netaddr,
914 915 916 917
                             unsigned int prefix,
                             const char *physdev,
                             const char *destaddr)
{
918
    return iptablesForwardDontMasquerade(fw, netaddr, prefix, physdev, destaddr,
919 920 921 922
                                         REMOVE);
}


923 924 925
static void
iptablesOutputFixUdpChecksum(virFirewallPtr fw,
                             const char *iface,
926 927 928 929 930 931 932 933
                             int port,
                             int action)
{
    char portstr[32];

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

934 935 936 937 938 939 940 941
    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);
942 943 944 945 946 947 948 949
}

/**
 * iptablesAddOutputFixUdpChecksum:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to match
 *
E
Eric Blake 已提交
950
 * Add a rule to the mangle table's POSTROUTING chain that fixes up the
951 952 953 954
 * checksum of packets with the given destination @port.
 * the given @iface interface for TCP packets.
 *
 */
955 956 957
void
iptablesAddOutputFixUdpChecksum(virFirewallPtr fw,
                                const char *iface,
958 959
                                int port)
{
960
    iptablesOutputFixUdpChecksum(fw, iface, port, ADD);
961 962 963 964 965 966 967 968 969 970 971
}

/**
 * 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.
 */
972 973 974
void
iptablesRemoveOutputFixUdpChecksum(virFirewallPtr fw,
                                   const char *iface,
975 976
                                   int port)
{
977
    iptablesOutputFixUdpChecksum(fw, iface, port, REMOVE);
978
}