viriptables.c 32.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 56
bool iptables_supports_xlock = false;

57 58
#if HAVE_FIREWALLD
static char *firewall_cmd_path = NULL;
59
#endif
60 61 62 63

static int
virIpTablesOnceInit(void)
{
64
    virCommandPtr cmd;
65
    int status;
66 67

#if HAVE_FIREWALLD
68 69
    firewall_cmd_path = virFindFileInPath("firewall-cmd");
    if (!firewall_cmd_path) {
70
        VIR_INFO("firewall-cmd not found on system. "
71 72
                 "firewalld support disabled for iptables.");
    } else {
73
        cmd = virCommandNew(firewall_cmd_path);
74 75

        virCommandAddArgList(cmd, "--state", NULL);
76 77
        /* don't log non-zero status */
        if (virCommandRun(cmd, &status) < 0 || status != 0) {
78
            VIR_INFO("firewall-cmd found but disabled for iptables");
79 80 81
            VIR_FREE(firewall_cmd_path);
            firewall_cmd_path = NULL;
        } else {
82
            VIR_INFO("using firewalld for iptables commands");
83 84 85
        }
        virCommandFree(cmd);
    }
86 87 88 89 90 91 92 93

    if (firewall_cmd_path)
        return 0;

#endif

    cmd = virCommandNew(IPTABLES_PATH);
    virCommandAddArgList(cmd, "-w", "-L", "-n", NULL);
94 95
    /* don't log non-zero status */
    if (virCommandRun(cmd, &status) < 0 || status != 0) {
96 97 98 99 100 101
        VIR_INFO("xtables locking not supported by your iptables");
    } else {
        VIR_INFO("using xtables locking for iptables");
        iptables_supports_xlock = true;
    }
    virCommandFree(cmd);
102 103 104 105 106
    return 0;
}

VIR_ONCE_GLOBAL_INIT(virIpTables)

107
#define VIR_FROM_THIS VIR_FROM_NONE
108

109 110 111 112 113
enum {
    ADD = 0,
    REMOVE
};

114
static virCommandPtr
R
Roman Bogorodskiy 已提交
115
iptablesCommandNew(const char *table, const char *chain, int family, int action)
116
{
117 118
    virCommandPtr cmd = NULL;
    virIpTablesInitialize();
119
#if HAVE_FIREWALLD
120 121 122 123 124 125 126 127 128
    if (firewall_cmd_path) {
        cmd = virCommandNew(firewall_cmd_path);
        virCommandAddArgList(cmd, "--direct", "--passthrough",
                             (family == AF_INET6) ? "ipv6" : "ipv4", NULL);
    }
#endif

    if (cmd == NULL) {
        cmd = virCommandNew((family == AF_INET6)
129
                        ? IP6TABLES_PATH : IPTABLES_PATH);
130 131 132

        if (iptables_supports_xlock)
            virCommandAddArgList(cmd, "-w", NULL);
133
    }
134

R
Roman Bogorodskiy 已提交
135
    virCommandAddArgList(cmd, "--table", table,
136
                         action == ADD ? "--insert" : "--delete",
R
Roman Bogorodskiy 已提交
137
                         chain, NULL);
138 139 140 141 142 143 144 145 146 147 148 149 150
    return cmd;
}

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

static int ATTRIBUTE_SENTINEL
R
Roman Bogorodskiy 已提交
151
iptablesAddRemoveRule(const char *table, const char *chain, int family, int action,
152 153 154 155 156 157
                      const char *arg, ...)
{
    va_list args;
    virCommandPtr cmd = NULL;
    const char *s;

R
Roman Bogorodskiy 已提交
158
    cmd = iptablesCommandNew(table, chain, family, action);
159
    virCommandAddArg(cmd, arg);
160 161

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

166
    return iptablesCommandRunAndFree(cmd);
167 168 169
}

static int
R
Roman Bogorodskiy 已提交
170
iptablesInput(int family,
171 172 173 174 175 176 177 178 179 180
              const char *iface,
              int port,
              int action,
              int tcp)
{
    char portstr[32];

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

R
Roman Bogorodskiy 已提交
181
    return iptablesAddRemoveRule("filter", "INPUT",
182
                                 family,
183 184 185 186 187 188
                                 action,
                                 "--in-interface", iface,
                                 "--protocol", tcp ? "tcp" : "udp",
                                 "--destination-port", portstr,
                                 "--jump", "ACCEPT",
                                 NULL);
189 190
}

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
static int
iptablesOutput(int family,
               const char *iface,
               int port,
               int action,
               int tcp)
{
    char portstr[32];

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

    return iptablesAddRemoveRule("filter", "OUTPUT",
                                 family,
                                 action,
                                 "--out-interface", iface,
                                 "--protocol", tcp ? "tcp" : "udp",
                                 "--destination-port", portstr,
                                 "--jump", "ACCEPT",
                                 NULL);
}

213 214 215 216 217 218 219 220 221 222 223 224
/**
 * iptablesAddTcpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the TCP port to add
 *
 * Add an input to the IP table allowing access to the given @port on
 * the given @iface interface for TCP packets
 *
 * Returns 0 in case of success or an error code in case of error
 */

225
int
R
Roman Bogorodskiy 已提交
226
iptablesAddTcpInput(int family,
227 228 229
                    const char *iface,
                    int port)
{
R
Roman Bogorodskiy 已提交
230
    return iptablesInput(family, iface, port, ADD, 1);
231 232
}

233 234 235 236 237 238
/**
 * iptablesRemoveTcpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the TCP port to remove
 *
R
Richard W.M. Jones 已提交
239
 * Removes an input from the IP table, hence forbidding access to the given
240 241 242 243
 * @port on the given @iface interface for TCP packets
 *
 * Returns 0 in case of success or an error code in case of error
 */
244
int
R
Roman Bogorodskiy 已提交
245
iptablesRemoveTcpInput(int family,
246 247 248
                       const char *iface,
                       int port)
{
R
Roman Bogorodskiy 已提交
249
    return iptablesInput(family, iface, port, REMOVE, 1);
250 251
}

252 253 254 255 256 257 258 259 260 261 262 263
/**
 * iptablesAddUdpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to add
 *
 * Add an input to the IP table allowing access to the given @port on
 * the given @iface interface for UDP packets
 *
 * Returns 0 in case of success or an error code in case of error
 */

264
int
R
Roman Bogorodskiy 已提交
265
iptablesAddUdpInput(int family,
266 267 268
                    const char *iface,
                    int port)
{
R
Roman Bogorodskiy 已提交
269
    return iptablesInput(family, iface, port, ADD, 0);
270 271
}

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

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
/**
 * 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
 *
 * Returns 0 in case of success or an error code in case of error
 */

int
iptablesAddUdpOutput(int family,
                     const char *iface,
                     int port)
{
    return iptablesOutput(family, iface, port, ADD, 0);
}

/**
 * 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
 *
 * Returns 0 in case of success or an error code in case of error
 */
int
iptablesRemoveUdpOutput(int family,
                        const char *iface,
                        int port)
{
    return iptablesOutput(family, iface, port, REMOVE, 0);
}

330

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

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

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

351
    netstr = virSocketAddrFormat(&network);
352 353 354 355

    if (!netstr)
        return NULL;

356
    ignore_value(virAsprintf(&ret, "%s/%d", netstr, prefix));
357 358 359 360 361 362

    VIR_FREE(netstr);
    return ret;
}


363 364 365
/* Allow all traffic coming from the bridge, with a valid network address
 * to proceed to WAN
 */
366
static int
R
Roman Bogorodskiy 已提交
367
iptablesForwardAllowOut(virSocketAddr *netaddr,
368
                        unsigned int prefix,
369 370 371
                        const char *iface,
                        const char *physdev,
                        int action)
372
{
373 374
    int ret;
    char *networkstr;
375
    virCommandPtr cmd = NULL;
376

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

R
Roman Bogorodskiy 已提交
380
    cmd = iptablesCommandNew("filter", "FORWARD",
381 382 383 384 385 386 387 388 389 390 391 392
                             VIR_SOCKET_ADDR_FAMILY(netaddr),
                             action);
    virCommandAddArgList(cmd,
                         "--source", networkstr,
                         "--in-interface", iface, NULL);

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

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

    ret = iptablesCommandRunAndFree(cmd);
393 394
    VIR_FREE(networkstr);
    return ret;
395 396
}

397 398 399 400 401 402
/**
 * iptablesAddForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
403
 *
404 405 406 407 408 409
 * 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
 */
410
int
R
Roman Bogorodskiy 已提交
411
iptablesAddForwardAllowOut(virSocketAddr *netaddr,
412
                           unsigned int prefix,
413 414
                           const char *iface,
                           const char *physdev)
415
{
R
Roman Bogorodskiy 已提交
416
    return iptablesForwardAllowOut(netaddr, prefix, iface, physdev, ADD);
417 418
}

419 420 421 422 423 424
/**
 * iptablesRemoveForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
425
 *
426 427 428 429 430 431
 * 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
 */
432
int
R
Roman Bogorodskiy 已提交
433
iptablesRemoveForwardAllowOut(virSocketAddr *netaddr,
434
                              unsigned int prefix,
435 436
                              const char *iface,
                              const char *physdev)
437
{
R
Roman Bogorodskiy 已提交
438
    return iptablesForwardAllowOut(netaddr, prefix, iface, physdev, REMOVE);
439 440
}

441 442 443 444

/* Allow all traffic destined to the bridge, with a valid network address
 * and associated with an existing connection
 */
445
static int
R
Roman Bogorodskiy 已提交
446
iptablesForwardAllowRelatedIn(virSocketAddr *netaddr,
447
                              unsigned int prefix,
448 449 450
                              const char *iface,
                              const char *physdev,
                              int action)
451
{
452 453 454
    int ret;
    char *networkstr;

455
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
456 457
        return -1;

458
    if (physdev && physdev[0]) {
R
Roman Bogorodskiy 已提交
459
        ret = iptablesAddRemoveRule("filter", "FORWARD",
460
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
461 462 463 464
                                    action,
                                    "--destination", networkstr,
                                    "--in-interface", physdev,
                                    "--out-interface", iface,
S
Stefan Seyfried 已提交
465 466
                                    "--match", "conntrack",
                                    "--ctstate", "ESTABLISHED,RELATED",
467 468
                                    "--jump", "ACCEPT",
                                    NULL);
469
    } else {
R
Roman Bogorodskiy 已提交
470
        ret = iptablesAddRemoveRule("filter", "FORWARD",
471
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
472 473 474
                                    action,
                                    "--destination", networkstr,
                                    "--out-interface", iface,
S
Stefan Seyfried 已提交
475 476
                                    "--match", "conntrack",
                                    "--ctstate", "ESTABLISHED,RELATED",
477 478
                                    "--jump", "ACCEPT",
                                    NULL);
479
    }
480 481
    VIR_FREE(networkstr);
    return ret;
482 483
}

484 485 486 487 488 489 490 491 492 493 494 495 496 497
/**
 * 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
R
Roman Bogorodskiy 已提交
498
iptablesAddForwardAllowRelatedIn(virSocketAddr *netaddr,
499
                                 unsigned int prefix,
500 501
                                 const char *iface,
                                 const char *physdev)
502
{
R
Roman Bogorodskiy 已提交
503
    return iptablesForwardAllowRelatedIn(netaddr, prefix, iface, physdev, ADD);
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
}

/**
 * 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
R
Roman Bogorodskiy 已提交
520
iptablesRemoveForwardAllowRelatedIn(virSocketAddr *netaddr,
521
                                    unsigned int prefix,
522 523
                                    const char *iface,
                                    const char *physdev)
524
{
R
Roman Bogorodskiy 已提交
525
    return iptablesForwardAllowRelatedIn(netaddr, prefix, iface, physdev, REMOVE);
526 527 528 529 530
}

/* Allow all traffic destined to the bridge, with a valid network address
 */
static int
R
Roman Bogorodskiy 已提交
531
iptablesForwardAllowIn(virSocketAddr *netaddr,
532
                       unsigned int prefix,
533 534 535 536
                       const char *iface,
                       const char *physdev,
                       int action)
{
537 538 539
    int ret;
    char *networkstr;

540
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
541 542
        return -1;

543
    if (physdev && physdev[0]) {
R
Roman Bogorodskiy 已提交
544
        ret = iptablesAddRemoveRule("filter", "FORWARD",
545
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
546 547 548 549 550 551
                                    action,
                                    "--destination", networkstr,
                                    "--in-interface", physdev,
                                    "--out-interface", iface,
                                    "--jump", "ACCEPT",
                                    NULL);
552
    } else {
R
Roman Bogorodskiy 已提交
553
        ret = iptablesAddRemoveRule("filter", "FORWARD",
554
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
555 556 557 558 559
                                    action,
                                    "--destination", networkstr,
                                    "--out-interface", iface,
                                    "--jump", "ACCEPT",
                                    NULL);
560
    }
561 562
    VIR_FREE(networkstr);
    return ret;
563 564
}

565 566 567 568 569 570
/**
 * 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
571
 *
572 573 574 575 576 577
 * 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
 */
578
int
R
Roman Bogorodskiy 已提交
579
iptablesAddForwardAllowIn(virSocketAddr *netaddr,
580
                          unsigned int prefix,
581 582 583
                          const char *iface,
                          const char *physdev)
{
R
Roman Bogorodskiy 已提交
584
    return iptablesForwardAllowIn(netaddr, prefix, iface, physdev, ADD);
585 586
}

587 588 589 590 591 592
/**
 * 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
593
 *
594 595 596 597 598 599
 * 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
 */
600
int
R
Roman Bogorodskiy 已提交
601
iptablesRemoveForwardAllowIn(virSocketAddr *netaddr,
602
                             unsigned int prefix,
603 604 605
                             const char *iface,
                             const char *physdev)
{
R
Roman Bogorodskiy 已提交
606
    return iptablesForwardAllowIn(netaddr, prefix, iface, physdev, REMOVE);
607 608 609 610 611 612 613
}


/* Allow all traffic between guests on the same bridge,
 * with a valid network address
 */
static int
R
Roman Bogorodskiy 已提交
614
iptablesForwardAllowCross(int family,
615 616 617
                          const char *iface,
                          int action)
{
R
Roman Bogorodskiy 已提交
618
    return iptablesAddRemoveRule("filter", "FORWARD",
619
                                 family,
620 621 622 623 624 625 626
                                 action,
                                 "--in-interface", iface,
                                 "--out-interface", iface,
                                 "--jump", "ACCEPT",
                                 NULL);
}

627 628 629 630 631 632 633 634 635 636 637
/**
 * 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
 */
638
int
R
Roman Bogorodskiy 已提交
639
iptablesAddForwardAllowCross(int family,
640 641
                             const char *iface)
{
R
Roman Bogorodskiy 已提交
642
    return iptablesForwardAllowCross(family, iface, ADD);
643 644
}

645 646 647 648 649 650 651 652 653 654 655
/**
 * 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
 */
656
int
R
Roman Bogorodskiy 已提交
657
iptablesRemoveForwardAllowCross(int family,
658 659
                                const char *iface)
{
R
Roman Bogorodskiy 已提交
660
    return iptablesForwardAllowCross(family, iface, REMOVE);
661 662 663 664 665 666 667
}


/* Drop all traffic trying to forward from the bridge.
 * ie the bridge is the in interface
 */
static int
R
Roman Bogorodskiy 已提交
668
iptablesForwardRejectOut(int family,
669 670 671
                         const char *iface,
                         int action)
{
R
Roman Bogorodskiy 已提交
672
    return iptablesAddRemoveRule("filter", "FORWARD",
673 674 675 676 677
                                 family,
                                 action,
                                 "--in-interface", iface,
                                 "--jump", "REJECT",
                                 NULL);
678 679
}

680 681 682 683 684 685 686 687 688 689
/**
 * 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
 */
690
int
R
Roman Bogorodskiy 已提交
691
iptablesAddForwardRejectOut(int family,
692 693
                            const char *iface)
{
R
Roman Bogorodskiy 已提交
694
    return iptablesForwardRejectOut(family, iface, ADD);
695 696
}

697 698 699 700 701 702 703 704 705 706
/**
 * 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
 */
707
int
R
Roman Bogorodskiy 已提交
708
iptablesRemoveForwardRejectOut(int family,
709 710
                               const char *iface)
{
R
Roman Bogorodskiy 已提交
711
    return iptablesForwardRejectOut(family, iface, REMOVE);
712 713 714 715 716 717 718 719 720
}




/* Drop all traffic trying to forward to the bridge.
 * ie the bridge is the out interface
 */
static int
R
Roman Bogorodskiy 已提交
721
iptablesForwardRejectIn(int family,
722
                        const char *iface,
723 724
                        int action)
{
R
Roman Bogorodskiy 已提交
725
    return iptablesAddRemoveRule("filter", "FORWARD",
726
                                 family,
727 728 729 730 731 732
                                 action,
                                 "--out-interface", iface,
                                 "--jump", "REJECT",
                                 NULL);
}

733 734 735 736 737 738 739 740 741 742
/**
 * 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
 */
743
int
R
Roman Bogorodskiy 已提交
744
iptablesAddForwardRejectIn(int family,
745
                           const char *iface)
746
{
R
Roman Bogorodskiy 已提交
747
    return iptablesForwardRejectIn(family, iface, ADD);
748 749
}

750 751 752 753 754 755 756 757 758 759
/**
 * 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
 */
760
int
R
Roman Bogorodskiy 已提交
761
iptablesRemoveForwardRejectIn(int family,
762
                              const char *iface)
763
{
R
Roman Bogorodskiy 已提交
764
    return iptablesForwardRejectIn(family, iface, REMOVE);
765 766
}

767 768 769 770

/* Masquerade all traffic coming from the network associated
 * with the bridge
 */
771
static int
R
Roman Bogorodskiy 已提交
772
iptablesForwardMasquerade(virSocketAddr *netaddr,
773
                          unsigned int prefix,
774
                          const char *physdev,
775 776
                          virSocketAddrRangePtr addr,
                          virPortRangePtr port,
777 778
                          const char *protocol,
                          int action)
779
{
780 781 782 783
    int ret = -1;
    char *networkstr = NULL;
    char *addrStartStr = NULL;
    char *addrEndStr = NULL;
784
    char *portRangeStr = NULL;
785
    char *natRangeStr = NULL;
786
    virCommandPtr cmd = NULL;
787

788
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
789 790
        return -1;

791
    if (!VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET)) {
792
        /* Higher level code *should* guaranteee it's impossible to get here. */
793 794 795
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Attempted to NAT '%s'. NAT is only supported for IPv4."),
                       networkstr);
796 797 798
        goto cleanup;
    }

799 800
    if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->start, AF_INET)) {
        if (!(addrStartStr = virSocketAddrFormat(&addr->start)))
801
            goto cleanup;
802 803
        if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->end, AF_INET)) {
            if (!(addrEndStr = virSocketAddrFormat(&addr->end)))
804 805
                goto cleanup;
        }
806 807
    }

R
Roman Bogorodskiy 已提交
808
    cmd = iptablesCommandNew("nat", "POSTROUTING", AF_INET, action);
809 810 811 812 813 814 815 816 817 818
    virCommandAddArgList(cmd, "--source", networkstr, NULL);

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

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

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

819
    if (protocol && protocol[0]) {
820 821 822
        if (port->start == 0 && port->end == 0) {
            port->start = 1024;
            port->end = 65535;
823 824
        }

825 826
        if (port->start < port->end && port->end < 65536) {
            if (virAsprintf(&portRangeStr, ":%u-%u",
827
                            port->start, port->end) < 0)
828 829 830 831
                goto cleanup;
        } else {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Invalid port range '%u-%u'."),
832
                           port->start, port->end);
833 834 835
        }
    }

836 837 838
    /* Use --jump SNAT if public addr is specified */
    if (addrStartStr && addrStartStr[0]) {
        int r = 0;
839

840 841
        if (addrEndStr && addrEndStr[0]) {
            r = virAsprintf(&natRangeStr, "%s-%s%s", addrStartStr, addrEndStr,
842
                            portRangeStr ? portRangeStr : "");
843
        } else {
844 845
            r = virAsprintf(&natRangeStr, "%s%s", addrStartStr,
                            portRangeStr ? portRangeStr : "");
846 847
        }

848
        if (r < 0)
849 850 851 852 853 854 855
            goto cleanup;

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

856 857
         if (portRangeStr && portRangeStr[0])
             virCommandAddArgList(cmd, "--to-ports", &portRangeStr[1], NULL);
858 859 860 861 862
     }

    ret = virCommandRun(cmd, NULL);
cleanup:
    virCommandFree(cmd);
863
    VIR_FREE(networkstr);
864 865
    VIR_FREE(addrStartStr);
    VIR_FREE(addrEndStr);
866
    VIR_FREE(portRangeStr);
867
    VIR_FREE(natRangeStr);
868
    return ret;
869 870
}

871 872 873 874 875
/**
 * iptablesAddForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
876
 * @protocol: the network protocol or NULL
877
 *
878 879 880 881 882 883
 * 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
 */
884
int
R
Roman Bogorodskiy 已提交
885
iptablesAddForwardMasquerade(virSocketAddr *netaddr,
886
                             unsigned int prefix,
887
                             const char *physdev,
888 889
                             virSocketAddrRangePtr addr,
                             virPortRangePtr port,
890
                             const char *protocol)
891
{
R
Roman Bogorodskiy 已提交
892
    return iptablesForwardMasquerade(netaddr, prefix, physdev, addr, port,
893
                                     protocol, ADD);
894 895
}

896 897 898 899 900
/**
 * iptablesRemoveForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
901
 * @protocol: the network protocol or NULL
902
 *
903 904 905 906 907 908
 * 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
 */
909
int
R
Roman Bogorodskiy 已提交
910
iptablesRemoveForwardMasquerade(virSocketAddr *netaddr,
911
                                unsigned int prefix,
912
                                const char *physdev,
913 914
                                virSocketAddrRangePtr addr,
                                virPortRangePtr port,
915
                                const char *protocol)
916
{
R
Roman Bogorodskiy 已提交
917
    return iptablesForwardMasquerade(netaddr, prefix, physdev, addr, port,
918
                                     protocol, REMOVE);
919
}
920 921


922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
/* Don't masquerade traffic coming from the network associated with the bridge
 * if said traffic targets @destaddr.
 */
static int
iptablesForwardDontMasquerade(virSocketAddr *netaddr,
                              unsigned int prefix,
                              const char *physdev,
                              const char *destaddr,
                              int action)
{
    int ret = -1;
    char *networkstr = NULL;
    virCommandPtr cmd = 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;
    }

    cmd = iptablesCommandNew("nat", "POSTROUTING", AF_INET, action);

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

    virCommandAddArgList(cmd, "--source", networkstr,
                         "--destination", destaddr, "--jump", "RETURN", NULL);
    ret = virCommandRun(cmd, NULL);
cleanup:
    virCommandFree(cmd);
    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
iptablesAddDontMasquerade(virSocketAddr *netaddr,
                          unsigned int prefix,
                          const char *physdev,
                          const char *destaddr)
{
    return iptablesForwardDontMasquerade(netaddr, prefix, physdev, destaddr,
                                         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
iptablesRemoveDontMasquerade(virSocketAddr *netaddr,
                             unsigned int prefix,
                             const char *physdev,
                             const char *destaddr)
{
    return iptablesForwardDontMasquerade(netaddr, prefix, physdev, destaddr,
                                         REMOVE);
}


1010
static int
R
Roman Bogorodskiy 已提交
1011
iptablesOutputFixUdpChecksum(const char *iface,
1012 1013 1014 1015 1016 1017 1018 1019
                             int port,
                             int action)
{
    char portstr[32];

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

R
Roman Bogorodskiy 已提交
1020
    return iptablesAddRemoveRule("mangle", "POSTROUTING",
1021
                                 AF_INET,
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
                                 action,
                                 "--out-interface", iface,
                                 "--protocol", "udp",
                                 "--destination-port", portstr,
                                 "--jump", "CHECKSUM", "--checksum-fill",
                                 NULL);
}

/**
 * iptablesAddOutputFixUdpChecksum:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to match
 *
E
Eric Blake 已提交
1036
 * Add a rule to the mangle table's POSTROUTING chain that fixes up the
1037 1038 1039 1040 1041 1042 1043 1044 1045
 * checksum of packets with the given destination @port.
 * the given @iface interface for TCP packets.
 *
 * Returns 0 in case of success or an error code in case of error.
 * (NB: if the system's iptables does not support checksum mangling,
 * this will return an error, which should be ignored.)
 */

int
R
Roman Bogorodskiy 已提交
1046
iptablesAddOutputFixUdpChecksum(const char *iface,
1047 1048
                                int port)
{
R
Roman Bogorodskiy 已提交
1049
    return iptablesOutputFixUdpChecksum(iface, port, ADD);
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
}

/**
 * iptablesRemoveOutputFixUdpChecksum:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port of the rule to remove
 *
 * Removes the checksum fixup rule that was previous added with
 * iptablesAddOutputFixUdpChecksum.
 *
 * Returns 0 in case of success or an error code in case of error
 * (again, if iptables doesn't support checksum fixup, this will
 * return an error, which should be ignored)
 */
int
R
Roman Bogorodskiy 已提交
1066
iptablesRemoveOutputFixUdpChecksum(const char *iface,
1067 1068
                                   int port)
{
R
Roman Bogorodskiy 已提交
1069
    return iptablesOutputFixUdpChecksum(iface, port, REMOVE);
1070
}