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

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

static int
virIpTablesOnceInit(void)
{
62
    virCommandPtr cmd;
63
    int status;
64 65

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

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

    if (firewall_cmd_path)
        return 0;

#endif

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

VIR_ONCE_GLOBAL_INIT(virIpTables)

105
#define VIR_FROM_THIS VIR_FROM_NONE
106

107 108 109 110 111
enum {
    ADD = 0,
    REMOVE
};

112
static virCommandPtr
R
Roman Bogorodskiy 已提交
113
iptablesCommandNew(const char *table, const char *chain, int family, int action)
114
{
115 116
    virCommandPtr cmd = NULL;
    virIpTablesInitialize();
117
#if HAVE_FIREWALLD
118 119 120 121 122 123 124 125 126
    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)
127
                        ? IP6TABLES_PATH : IPTABLES_PATH);
128 129 130

        if (iptables_supports_xlock)
            virCommandAddArgList(cmd, "-w", NULL);
131
    }
132

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

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

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

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

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

164
    return iptablesCommandRunAndFree(cmd);
165 166 167
}

static int
R
Roman Bogorodskiy 已提交
168
iptablesInput(int family,
169 170 171 172 173 174 175 176 177 178
              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 已提交
179
    return iptablesAddRemoveRule("filter", "INPUT",
180
                                 family,
181 182 183 184 185 186
                                 action,
                                 "--in-interface", iface,
                                 "--protocol", tcp ? "tcp" : "udp",
                                 "--destination-port", portstr,
                                 "--jump", "ACCEPT",
                                 NULL);
187 188
}

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
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);
}

211 212 213 214 215 216 217 218 219 220 221 222
/**
 * 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
 */

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

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

250 251 252 253 254 255 256 257 258 259 260 261
/**
 * 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
 */

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

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

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
/**
 * 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);
}

328

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

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

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

349
    netstr = virSocketAddrFormat(&network);
350 351 352 353

    if (!netstr)
        return NULL;

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

    VIR_FREE(netstr);
    return ret;
}


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

375
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
376 377
        return -1;

R
Roman Bogorodskiy 已提交
378
    cmd = iptablesCommandNew("filter", "FORWARD",
379 380 381 382 383 384 385 386 387 388 389 390
                             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);
391 392
    VIR_FREE(networkstr);
    return ret;
393 394
}

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

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

439 440 441 442

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

453
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
454 455
        return -1;

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

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

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

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

538
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
539 540
        return -1;

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

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

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


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

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

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


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

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

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




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

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

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

765 766 767 768

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

786
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
787 788
        return -1;

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

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

R
Roman Bogorodskiy 已提交
806
    cmd = iptablesCommandNew("nat", "POSTROUTING", AF_INET, action);
807 808 809 810 811 812 813 814 815 816
    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);

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

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

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

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

846
        if (r < 0)
847 848 849 850 851 852 853
            goto cleanup;

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

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

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

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

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


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

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

R
Roman Bogorodskiy 已提交
1018
    return iptablesAddRemoveRule("mangle", "POSTROUTING",
1019
                                 AF_INET,
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
                                 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 已提交
1034
 * Add a rule to the mangle table's POSTROUTING chain that fixes up the
1035 1036 1037 1038 1039 1040 1041 1042 1043
 * 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 已提交
1044
iptablesAddOutputFixUdpChecksum(const char *iface,
1045 1046
                                int port)
{
R
Roman Bogorodskiy 已提交
1047
    return iptablesOutputFixUdpChecksum(iface, port, ADD);
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
}

/**
 * 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 已提交
1064
iptablesRemoveOutputFixUdpChecksum(const char *iface,
1065 1066
                                   int port)
{
R
Roman Bogorodskiy 已提交
1067
    return iptablesOutputFixUdpChecksum(iface, port, REMOVE);
1068
}