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

24
#include <config.h>
25 26 27 28 29 30

#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
E
Eric Blake 已提交
31
#include <sys/wait.h>
32

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

44 45
VIR_LOG_INIT("util.iptables");

46
#define VIR_FROM_THIS VIR_FROM_NONE
47

48 49 50 51 52
enum {
    ADD = 0,
    REMOVE
};

53

54 55 56
static void
iptablesInput(virFirewallPtr fw,
              virFirewallLayer layer,
57 58 59 60 61 62 63 64 65 66
              const char *iface,
              int port,
              int action,
              int tcp)
{
    char portstr[32];

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

67 68 69 70 71 72 73 74
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       action == ADD ? "--insert" : "--delete", "INPUT",
                       "--in-interface", iface,
                       "--protocol", tcp ? "tcp" : "udp",
                       "--destination-port", portstr,
                       "--jump", "ACCEPT",
                       NULL);
75 76
}

77 78 79
static void
iptablesOutput(virFirewallPtr fw,
               virFirewallLayer layer,
80 81 82 83 84 85 86 87 88 89
               const char *iface,
               int port,
               int action,
               int tcp)
{
    char portstr[32];

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

90 91 92 93 94 95 96 97
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       action == ADD ? "--insert" : "--delete", "OUTPUT",
                       "--out-interface", iface,
                       "--protocol", tcp ? "tcp" : "udp",
                       "--destination-port", portstr,
                       "--jump", "ACCEPT",
                       NULL);
98 99
}

100 101 102 103 104 105 106 107 108
/**
 * 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
 */
109 110 111
void
iptablesAddTcpInput(virFirewallPtr fw,
                    virFirewallLayer layer,
112 113 114
                    const char *iface,
                    int port)
{
115
    iptablesInput(fw, layer, iface, port, ADD, 1);
116 117
}

118 119 120 121 122 123
/**
 * iptablesRemoveTcpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the TCP port to remove
 *
R
Richard W.M. Jones 已提交
124
 * Removes an input from the IP table, hence forbidding access to the given
125 126
 * @port on the given @iface interface for TCP packets
 */
127 128 129
void
iptablesRemoveTcpInput(virFirewallPtr fw,
                       virFirewallLayer layer,
130 131 132
                       const char *iface,
                       int port)
{
133
    iptablesInput(fw, layer, iface, port, REMOVE, 1);
134 135
}

136 137 138 139 140 141 142 143 144
/**
 * 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
 */
145 146 147
void
iptablesAddUdpInput(virFirewallPtr fw,
                    virFirewallLayer layer,
148 149 150
                    const char *iface,
                    int port)
{
151
    iptablesInput(fw, layer, iface, port, ADD, 0);
152 153
}

154 155 156 157 158 159
/**
 * iptablesRemoveUdpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to remove
 *
R
Richard W.M. Jones 已提交
160
 * Removes an input from the IP table, hence forbidding access to the given
161 162
 * @port on the given @iface interface for UDP packets
 */
163 164 165
void
iptablesRemoveUdpInput(virFirewallPtr fw,
                       virFirewallLayer layer,
166 167 168
                       const char *iface,
                       int port)
{
169
    return iptablesInput(fw, layer, iface, port, REMOVE, 0);
170 171
}

172 173 174 175 176 177 178 179 180
/**
 * 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
 */
181 182 183
void
iptablesAddUdpOutput(virFirewallPtr fw,
                     virFirewallLayer layer,
184 185 186
                     const char *iface,
                     int port)
{
187
    iptablesOutput(fw, layer, iface, port, ADD, 0);
188 189 190 191 192 193 194 195 196 197 198
}

/**
 * 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
 */
199 200 201
void
iptablesRemoveUdpOutput(virFirewallPtr fw,
                        virFirewallLayer layer,
202 203 204
                        const char *iface,
                        int port)
{
205
    iptablesOutput(fw, layer, iface, port, REMOVE, 0);
206 207
}

208

209
static char *iptablesFormatNetwork(virSocketAddr *netaddr,
210
                                   unsigned int prefix)
211 212
{
    virSocketAddr network;
213
    VIR_AUTOFREE(char *) netstr = NULL;
214 215
    char *ret;

216 217
    if (!(VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET) ||
          VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET6))) {
218 219
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Only IPv4 or IPv6 addresses can be used with iptables"));
220 221 222
        return NULL;
    }

223
    if (virSocketAddrMaskByPrefix(netaddr, prefix, &network) < 0) {
224 225
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failure to mask address"));
226 227
        return NULL;
    }
228

229
    netstr = virSocketAddrFormat(&network);
230 231 232 233

    if (!netstr)
        return NULL;

234
    ignore_value(virAsprintf(&ret, "%s/%d", netstr, prefix));
235 236 237 238 239

    return ret;
}


240 241 242
/* Allow all traffic coming from the bridge, with a valid network address
 * to proceed to WAN
 */
243
static int
244 245
iptablesForwardAllowOut(virFirewallPtr fw,
                        virSocketAddr *netaddr,
246
                        unsigned int prefix,
247 248 249
                        const char *iface,
                        const char *physdev,
                        int action)
250
{
251
    VIR_AUTOFREE(char *) networkstr = NULL;
252 253
    virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ?
        VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
254

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

258
    if (physdev && physdev[0])
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
        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);
275

276
    return 0;
277 278
}

279 280 281 282 283 284
/**
 * iptablesAddForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
285
 *
286 287 288 289 290 291
 * 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
 */
292
int
293 294
iptablesAddForwardAllowOut(virFirewallPtr fw,
                           virSocketAddr *netaddr,
295
                           unsigned int prefix,
296 297
                           const char *iface,
                           const char *physdev)
298
{
299
    return iptablesForwardAllowOut(fw, netaddr, prefix, iface, physdev, ADD);
300 301
}

302 303 304 305 306 307
/**
 * iptablesRemoveForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
308
 *
309 310 311 312 313 314
 * 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
 */
315
int
316 317
iptablesRemoveForwardAllowOut(virFirewallPtr fw,
                              virSocketAddr *netaddr,
318
                              unsigned int prefix,
319 320
                              const char *iface,
                              const char *physdev)
321
{
322
    return iptablesForwardAllowOut(fw, netaddr, prefix, iface, physdev, REMOVE);
323 324
}

325 326 327 328

/* Allow all traffic destined to the bridge, with a valid network address
 * and associated with an existing connection
 */
329
static int
330 331
iptablesForwardAllowRelatedIn(virFirewallPtr fw,
                              virSocketAddr *netaddr,
332
                              unsigned int prefix,
333 334 335
                              const char *iface,
                              const char *physdev,
                              int action)
336
{
337 338
    virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ?
        VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
339
    VIR_AUTOFREE(char *) networkstr = NULL;
340

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

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
    if (physdev && physdev[0])
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
                           action == ADD ? "--insert" : "--delete", "FORWARD",
                           "--destination", networkstr,
                           "--in-interface", physdev,
                           "--out-interface", iface,
                           "--match", "conntrack",
                           "--ctstate", "ESTABLISHED,RELATED",
                           "--jump", "ACCEPT",
                           NULL);
    else
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
                           action == ADD ? "--insert" : "--delete", "FORWARD",
                           "--destination", networkstr,
                           "--out-interface", iface,
                           "--match", "conntrack",
                           "--ctstate", "ESTABLISHED,RELATED",
                           "--jump", "ACCEPT",
                           NULL);

    return 0;
367 368
}

369 370 371 372 373 374 375 376 377 378 379 380 381 382
/**
 * 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
383 384
iptablesAddForwardAllowRelatedIn(virFirewallPtr fw,
                                 virSocketAddr *netaddr,
385
                                 unsigned int prefix,
386 387
                                 const char *iface,
                                 const char *physdev)
388
{
389
    return iptablesForwardAllowRelatedIn(fw, netaddr, prefix, iface, physdev, ADD);
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
}

/**
 * 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
406 407
iptablesRemoveForwardAllowRelatedIn(virFirewallPtr fw,
                                    virSocketAddr *netaddr,
408
                                    unsigned int prefix,
409 410
                                    const char *iface,
                                    const char *physdev)
411
{
412
    return iptablesForwardAllowRelatedIn(fw, netaddr, prefix, iface, physdev, REMOVE);
413 414 415 416 417
}

/* Allow all traffic destined to the bridge, with a valid network address
 */
static int
418 419
iptablesForwardAllowIn(virFirewallPtr fw,
                       virSocketAddr *netaddr,
420
                       unsigned int prefix,
421 422 423 424
                       const char *iface,
                       const char *physdev,
                       int action)
{
425 426
    virFirewallLayer layer = VIR_SOCKET_ADDR_FAMILY(netaddr) == AF_INET ?
        VIR_FIREWALL_LAYER_IPV4 : VIR_FIREWALL_LAYER_IPV6;
427
    VIR_AUTOFREE(char *) networkstr = NULL;
428

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

432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
    if (physdev && physdev[0])
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
                           action == ADD ? "--insert" : "--delete", "FORWARD",
                           "--destination", networkstr,
                           "--in-interface", physdev,
                           "--out-interface", iface,
                           "--jump", "ACCEPT",
                           NULL);
    else
        virFirewallAddRule(fw, layer,
                           "--table", "filter",
                           action == ADD ? "--insert" : "--delete", "FORWARD",
                           "--destination", networkstr,
                           "--out-interface", iface,
                           "--jump", "ACCEPT",
                           NULL);
    return 0;
450 451
}

452 453 454 455 456 457
/**
 * 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
458
 *
459 460 461 462 463 464
 * 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
 */
465
int
466 467
iptablesAddForwardAllowIn(virFirewallPtr fw,
                          virSocketAddr *netaddr,
468
                          unsigned int prefix,
469 470 471
                          const char *iface,
                          const char *physdev)
{
472
    return iptablesForwardAllowIn(fw, netaddr, prefix, iface, physdev, ADD);
473 474
}

475 476 477 478 479 480
/**
 * 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
481
 *
482 483 484 485 486 487
 * 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
 */
488
int
489 490
iptablesRemoveForwardAllowIn(virFirewallPtr fw,
                             virSocketAddr *netaddr,
491
                             unsigned int prefix,
492 493 494
                             const char *iface,
                             const char *physdev)
{
495
    return iptablesForwardAllowIn(fw, netaddr, prefix, iface, physdev, REMOVE);
496 497
}

498 499 500 501 502 503 504 505 506 507 508
/**
 * 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
 */
509 510 511
void
iptablesAddForwardAllowCross(virFirewallPtr fw,
                             virFirewallLayer layer,
512 513
                             const char *iface)
{
514 515 516 517 518 519 520
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       "--insert", "FORWARD",
                       "--in-interface", iface,
                       "--out-interface", iface,
                       "--jump", "ACCEPT",
                       NULL);
521 522
}

523 524 525 526 527 528 529 530 531 532 533
/**
 * 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
 */
534 535 536
void
iptablesRemoveForwardAllowCross(virFirewallPtr fw,
                                virFirewallLayer layer,
537 538
                                const char *iface)
{
539 540 541 542 543 544 545
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       "--delete", "FORWARD",
                       "--in-interface", iface,
                       "--out-interface", iface,
                       "--jump", "ACCEPT",
                       NULL);
546 547
}

548 549 550 551 552 553 554 555 556 557
/**
 * 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
 */
558 559 560
void
iptablesAddForwardRejectOut(virFirewallPtr fw,
                            virFirewallLayer layer,
561 562
                            const char *iface)
{
563 564 565 566 567 568
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       "--insert", "FORWARD",
                       "--in-interface", iface,
                       "--jump", "REJECT",
                       NULL);
569 570
}

571 572 573 574 575 576 577 578 579 580
/**
 * 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
 */
581 582 583
void
iptablesRemoveForwardRejectOut(virFirewallPtr fw,
                               virFirewallLayer layer,
584 585
                               const char *iface)
{
586 587 588 589 590 591
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       "--delete", "FORWARD",
                       "--in-interface", iface,
                       "--jump", "REJECT",
                       NULL);
592 593 594
}


595 596 597 598 599 600 601 602 603 604
/**
 * 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
 */
605 606 607
void
iptablesAddForwardRejectIn(virFirewallPtr fw,
                           virFirewallLayer layer,
608
                           const char *iface)
609
{
610 611 612 613 614 615
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       "--insert", "FORWARD",
                       "--out-interface", iface,
                       "--jump", "REJECT",
                       NULL);
616 617
}

618 619 620 621 622 623 624 625 626 627
/**
 * 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
 */
628 629 630
void
iptablesRemoveForwardRejectIn(virFirewallPtr fw,
                              virFirewallLayer layer,
631
                              const char *iface)
632
{
633 634 635 636 637 638
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       "--delete", "FORWARD",
                       "--out-interface", iface,
                       "--jump", "REJECT",
                       NULL);
639 640
}

641 642 643 644

/* Masquerade all traffic coming from the network associated
 * with the bridge
 */
645
static int
646 647
iptablesForwardMasquerade(virFirewallPtr fw,
                          virSocketAddr *netaddr,
648
                          unsigned int prefix,
649
                          const char *physdev,
650 651
                          virSocketAddrRangePtr addr,
                          virPortRangePtr port,
652 653
                          const char *protocol,
                          int action)
654
{
655 656 657 658 659
    VIR_AUTOFREE(char *) networkstr = NULL;
    VIR_AUTOFREE(char *) addrStartStr = NULL;
    VIR_AUTOFREE(char *) addrEndStr = NULL;
    VIR_AUTOFREE(char *) portRangeStr = NULL;
    VIR_AUTOFREE(char *) natRangeStr = NULL;
660
    virFirewallRulePtr rule;
661

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

665
    if (!VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET)) {
666
        /* Higher level code *should* guaranteee it's impossible to get here. */
667 668 669
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Attempted to NAT '%s'. NAT is only supported for IPv4."),
                       networkstr);
670
        return -1;
671 672
    }

673 674
    if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->start, AF_INET)) {
        if (!(addrStartStr = virSocketAddrFormat(&addr->start)))
675
            return -1;
676 677
        if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->end, AF_INET)) {
            if (!(addrEndStr = virSocketAddrFormat(&addr->end)))
678
                return -1;
679
        }
680 681
    }

682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
    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);
    }
698 699

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

702
    if (protocol && protocol[0]) {
703 704 705
        if (port->start == 0 && port->end == 0) {
            port->start = 1024;
            port->end = 65535;
706 707
        }

708 709
        if (port->start < port->end && port->end < 65536) {
            if (virAsprintf(&portRangeStr, ":%u-%u",
710
                            port->start, port->end) < 0)
711
                return -1;
712 713 714
        } else {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Invalid port range '%u-%u'."),
715
                           port->start, port->end);
716 717 718
        }
    }

719 720 721
    /* Use --jump SNAT if public addr is specified */
    if (addrStartStr && addrStartStr[0]) {
        int r = 0;
722

723 724
        if (addrEndStr && addrEndStr[0]) {
            r = virAsprintf(&natRangeStr, "%s-%s%s", addrStartStr, addrEndStr,
725
                            portRangeStr ? portRangeStr : "");
726
        } else {
727 728
            r = virAsprintf(&natRangeStr, "%s%s", addrStartStr,
                            portRangeStr ? portRangeStr : "");
729 730
        }

731
        if (r < 0)
732
            return -1;
733

734 735
        virFirewallRuleAddArgList(fw, rule,
                                  "--jump", "SNAT",
736
                                  "--to-source", natRangeStr, NULL);
737 738 739
    } else {
        virFirewallRuleAddArgList(fw, rule,
                                  "--jump", "MASQUERADE", NULL);
740

741 742 743 744
        if (portRangeStr && portRangeStr[0])
            virFirewallRuleAddArgList(fw, rule,
                                      "--to-ports", &portRangeStr[1], NULL);
    }
745

746
    return 0;
747 748
}

749 750 751 752 753
/**
 * iptablesAddForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
754
 * @protocol: the network protocol or NULL
755
 *
756 757 758 759 760 761
 * 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
 */
762
int
763 764
iptablesAddForwardMasquerade(virFirewallPtr fw,
                             virSocketAddr *netaddr,
765
                             unsigned int prefix,
766
                             const char *physdev,
767 768
                             virSocketAddrRangePtr addr,
                             virPortRangePtr port,
769
                             const char *protocol)
770
{
771
    return iptablesForwardMasquerade(fw, netaddr, prefix, physdev, addr, port,
772
                                     protocol, ADD);
773 774
}

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


802 803 804 805
/* Don't masquerade traffic coming from the network associated with the bridge
 * if said traffic targets @destaddr.
 */
static int
806 807
iptablesForwardDontMasquerade(virFirewallPtr fw,
                              virSocketAddr *netaddr,
808 809 810 811 812
                              unsigned int prefix,
                              const char *physdev,
                              const char *destaddr,
                              int action)
{
813
    VIR_AUTOFREE(char *) networkstr = NULL;
814 815 816 817 818 819 820 821 822

    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);
823
        return -1;
824 825 826
    }

    if (physdev && physdev[0])
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
        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);

844
    return 0;
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
}

/**
 * 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
862 863
iptablesAddDontMasquerade(virFirewallPtr fw,
                          virSocketAddr *netaddr,
864 865 866 867
                          unsigned int prefix,
                          const char *physdev,
                          const char *destaddr)
{
868
    return iptablesForwardDontMasquerade(fw, netaddr, prefix, physdev, destaddr,
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
                                         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
887 888
iptablesRemoveDontMasquerade(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
                                         REMOVE);
}


898 899 900
static void
iptablesOutputFixUdpChecksum(virFirewallPtr fw,
                             const char *iface,
901 902 903 904 905 906 907 908
                             int port,
                             int action)
{
    char portstr[32];

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

909 910 911 912 913 914 915 916
    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);
917 918 919 920 921 922 923 924
}

/**
 * iptablesAddOutputFixUdpChecksum:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to match
 *
E
Eric Blake 已提交
925
 * Add a rule to the mangle table's POSTROUTING chain that fixes up the
926 927 928 929
 * checksum of packets with the given destination @port.
 * the given @iface interface for TCP packets.
 *
 */
930 931 932
void
iptablesAddOutputFixUdpChecksum(virFirewallPtr fw,
                                const char *iface,
933 934
                                int port)
{
935
    iptablesOutputFixUdpChecksum(fw, iface, port, ADD);
936 937 938 939 940 941 942 943 944 945 946
}

/**
 * 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.
 */
947 948 949
void
iptablesRemoveOutputFixUdpChecksum(virFirewallPtr fw,
                                   const char *iface,
950 951
                                   int port)
{
952
    iptablesOutputFixUdpChecksum(fw, iface, port, REMOVE);
953
}