viriptables.c 31.2 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 509 510 511 512
static void
iptablesForwardAllowCross(virFirewallPtr fw,
                          virFirewallLayer layer,
                          const char *iface,
                          int action)
{
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
                       action == ADD ? "--insert" : "--delete", "FORWARD",
                       "--in-interface", iface,
                       "--out-interface", iface,
                       "--jump", "ACCEPT",
                       NULL);
}

513 514 515 516 517 518 519 520 521 522 523
/**
 * 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
 */
524 525 526
void
iptablesAddForwardAllowCross(virFirewallPtr fw,
                             virFirewallLayer layer,
527 528
                             const char *iface)
{
529
    iptablesForwardAllowCross(fw, layer, iface, ADD);
530 531
}

532 533 534 535 536 537 538 539 540 541 542
/**
 * 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
 */
543 544 545
void
iptablesRemoveForwardAllowCross(virFirewallPtr fw,
                                virFirewallLayer layer,
546
                                const char *iface)
547 548 549 550 551 552 553 554 555
{
    iptablesForwardAllowCross(fw, layer, iface, REMOVE);
}

static void
iptablesForwardRejectOut(virFirewallPtr fw,
                         virFirewallLayer layer,
                         const char *iface,
                         int action)
556
{
557 558
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
559
                       action == ADD ? "--insert" : "delete", "FORWARD",
560
                       "--in-interface", iface,
561
                       "--jump", "REJECT",
562
                       NULL);
563 564
}

565 566 567 568 569 570 571 572 573 574
/**
 * 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
 */
575 576 577
void
iptablesAddForwardRejectOut(virFirewallPtr fw,
                            virFirewallLayer layer,
578 579
                            const char *iface)
{
580
    iptablesForwardRejectOut(fw, layer, iface, ADD);
581 582
}

583 584 585 586 587 588 589 590 591 592
/**
 * 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
 */
593 594 595
void
iptablesRemoveForwardRejectOut(virFirewallPtr fw,
                               virFirewallLayer layer,
596
                               const char *iface)
597 598 599 600 601 602 603 604 605 606
{
    iptablesForwardRejectOut(fw, layer, iface, REMOVE);
}


static void
iptablesForwardRejectIn(virFirewallPtr fw,
                        virFirewallLayer layer,
                        const char *iface,
                        int action)
607
{
608 609
    virFirewallAddRule(fw, layer,
                       "--table", "filter",
610 611
                       action == ADD ? "--insert" : "--delete", "FORWARD",
                       "--out-interface", iface,
612 613
                       "--jump", "REJECT",
                       NULL);
614 615
}

616 617 618 619 620 621 622 623 624 625
/**
 * 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
 */
626 627 628
void
iptablesAddForwardRejectIn(virFirewallPtr fw,
                           virFirewallLayer layer,
629
                           const char *iface)
630
{
631
    iptablesForwardRejectIn(fw, layer, iface, ADD);
632 633
}

634 635 636 637 638 639 640 641 642 643
/**
 * 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
 */
644 645 646
void
iptablesRemoveForwardRejectIn(virFirewallPtr fw,
                              virFirewallLayer layer,
647
                              const char *iface)
648
{
649
    iptablesForwardRejectIn(fw, layer, iface, REMOVE);
650 651
}

652 653 654 655

/* Masquerade all traffic coming from the network associated
 * with the bridge
 */
656
static int
657 658
iptablesForwardMasquerade(virFirewallPtr fw,
                          virSocketAddr *netaddr,
659
                          unsigned int prefix,
660
                          const char *physdev,
661 662
                          virSocketAddrRangePtr addr,
                          virPortRangePtr port,
663 664
                          const char *protocol,
                          int action)
665
{
666 667 668 669 670
    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;
671
    virFirewallRulePtr rule;
672

673
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
674 675
        return -1;

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

684 685
    if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->start, AF_INET)) {
        if (!(addrStartStr = virSocketAddrFormat(&addr->start)))
686
            return -1;
687 688
        if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->end, AF_INET)) {
            if (!(addrEndStr = virSocketAddrFormat(&addr->end)))
689
                return -1;
690
        }
691 692
    }

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

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

713
    if (protocol && protocol[0]) {
714 715 716
        if (port->start == 0 && port->end == 0) {
            port->start = 1024;
            port->end = 65535;
717 718
        }

719 720
        if (port->start < port->end && port->end < 65536) {
            if (virAsprintf(&portRangeStr, ":%u-%u",
721
                            port->start, port->end) < 0)
722
                return -1;
723 724 725
        } else {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Invalid port range '%u-%u'."),
726
                           port->start, port->end);
727 728 729
        }
    }

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

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

742
        if (r < 0)
743
            return -1;
744

745 746
        virFirewallRuleAddArgList(fw, rule,
                                  "--jump", "SNAT",
747
                                  "--to-source", natRangeStr, NULL);
748 749 750
    } else {
        virFirewallRuleAddArgList(fw, rule,
                                  "--jump", "MASQUERADE", NULL);
751

752 753 754 755
        if (portRangeStr && portRangeStr[0])
            virFirewallRuleAddArgList(fw, rule,
                                      "--to-ports", &portRangeStr[1], NULL);
    }
756

757
    return 0;
758 759
}

760 761 762 763 764
/**
 * iptablesAddForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
765
 * @protocol: the network protocol or NULL
766
 *
767 768 769 770 771 772
 * 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
 */
773
int
774 775
iptablesAddForwardMasquerade(virFirewallPtr fw,
                             virSocketAddr *netaddr,
776
                             unsigned int prefix,
777
                             const char *physdev,
778 779
                             virSocketAddrRangePtr addr,
                             virPortRangePtr port,
780
                             const char *protocol)
781
{
782
    return iptablesForwardMasquerade(fw, netaddr, prefix, physdev, addr, port,
783
                                     protocol, ADD);
784 785
}

786 787 788 789 790
/**
 * iptablesRemoveForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
791
 * @protocol: the network protocol or NULL
792
 *
793 794 795 796 797 798
 * 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
 */
799
int
800 801
iptablesRemoveForwardMasquerade(virFirewallPtr fw,
                                virSocketAddr *netaddr,
802
                                unsigned int prefix,
803
                                const char *physdev,
804 805
                                virSocketAddrRangePtr addr,
                                virPortRangePtr port,
806
                                const char *protocol)
807
{
808
    return iptablesForwardMasquerade(fw, netaddr, prefix, physdev, addr, port,
809
                                     protocol, REMOVE);
810
}
811 812


813 814 815 816
/* Don't masquerade traffic coming from the network associated with the bridge
 * if said traffic targets @destaddr.
 */
static int
817 818
iptablesForwardDontMasquerade(virFirewallPtr fw,
                              virSocketAddr *netaddr,
819 820 821 822 823
                              unsigned int prefix,
                              const char *physdev,
                              const char *destaddr,
                              int action)
{
824
    VIR_AUTOFREE(char *) networkstr = NULL;
825 826 827 828 829 830 831 832 833

    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);
834
        return -1;
835 836 837
    }

    if (physdev && physdev[0])
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
        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);

855
    return 0;
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
}

/**
 * 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
873 874
iptablesAddDontMasquerade(virFirewallPtr fw,
                          virSocketAddr *netaddr,
875 876 877 878
                          unsigned int prefix,
                          const char *physdev,
                          const char *destaddr)
{
879
    return iptablesForwardDontMasquerade(fw, netaddr, prefix, physdev, destaddr,
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
                                         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
898 899
iptablesRemoveDontMasquerade(virFirewallPtr fw,
                             virSocketAddr *netaddr,
900 901 902 903
                             unsigned int prefix,
                             const char *physdev,
                             const char *destaddr)
{
904
    return iptablesForwardDontMasquerade(fw, netaddr, prefix, physdev, destaddr,
905 906 907 908
                                         REMOVE);
}


909 910 911
static void
iptablesOutputFixUdpChecksum(virFirewallPtr fw,
                             const char *iface,
912 913 914 915 916 917 918 919
                             int port,
                             int action)
{
    char portstr[32];

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

920 921 922 923 924 925 926 927
    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);
928 929 930 931 932 933 934 935
}

/**
 * iptablesAddOutputFixUdpChecksum:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to match
 *
E
Eric Blake 已提交
936
 * Add a rule to the mangle table's POSTROUTING chain that fixes up the
937 938 939 940
 * checksum of packets with the given destination @port.
 * the given @iface interface for TCP packets.
 *
 */
941 942 943
void
iptablesAddOutputFixUdpChecksum(virFirewallPtr fw,
                                const char *iface,
944 945
                                int port)
{
946
    iptablesOutputFixUdpChecksum(fw, iface, port, ADD);
947 948 949 950 951 952 953 954 955 956 957
}

/**
 * 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.
 */
958 959 960
void
iptablesRemoveOutputFixUdpChecksum(virFirewallPtr fw,
                                   const char *iface,
961 962
                                   int port)
{
963
    iptablesOutputFixUdpChecksum(fw, iface, port, REMOVE);
964
}