viriptables.c 30.5 KB
Newer Older
1
/*
2 3
 * viriptables.c: helper APIs for managing iptables
 *
4
 * Copyright (C) 2007-2013 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 63 64 65
    virCommandPtr cmd;
    int status;

#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 74

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

    if (firewall_cmd_path)
        return 0;

#endif

    cmd = virCommandNew(IPTABLES_PATH);
    virCommandAddArgList(cmd, "-w", "-L", "-n", NULL);
    if (virCommandRun(cmd, &status) < 0 || status != 0) {
        VIR_INFO("xtables locking not supported by your iptables");
    } else {
        VIR_INFO("using xtables locking for iptables");
        iptables_supports_xlock = true;
    }
    virCommandFree(cmd);
98 99 100 101 102
    return 0;
}

VIR_ONCE_GLOBAL_INIT(virIpTables)

103
#define VIR_FROM_THIS VIR_FROM_NONE
104

105 106 107 108 109
enum {
    ADD = 0,
    REMOVE
};

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

        if (iptables_supports_xlock)
            virCommandAddArgList(cmd, "-w", NULL);
129
    }
130

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

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

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

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

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

162
    return iptablesCommandRunAndFree(cmd);
163 164 165
}

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

187 188 189 190 191 192 193 194 195 196 197 198
/**
 * 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
 */

199
int
R
Roman Bogorodskiy 已提交
200
iptablesAddTcpInput(int family,
201 202 203
                    const char *iface,
                    int port)
{
R
Roman Bogorodskiy 已提交
204
    return iptablesInput(family, iface, port, ADD, 1);
205 206
}

207 208 209 210 211 212
/**
 * iptablesRemoveTcpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the TCP port to remove
 *
R
Richard W.M. Jones 已提交
213
 * Removes an input from the IP table, hence forbidding access to the given
214 215 216 217
 * @port on the given @iface interface for TCP packets
 *
 * Returns 0 in case of success or an error code in case of error
 */
218
int
R
Roman Bogorodskiy 已提交
219
iptablesRemoveTcpInput(int family,
220 221 222
                       const char *iface,
                       int port)
{
R
Roman Bogorodskiy 已提交
223
    return iptablesInput(family, iface, port, REMOVE, 1);
224 225
}

226 227 228 229 230 231 232 233 234 235 236 237
/**
 * 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
 */

238
int
R
Roman Bogorodskiy 已提交
239
iptablesAddUdpInput(int family,
240 241 242
                    const char *iface,
                    int port)
{
R
Roman Bogorodskiy 已提交
243
    return iptablesInput(family, iface, port, ADD, 0);
244 245
}

246 247 248 249 250 251
/**
 * iptablesRemoveUdpInput:
 * @ctx: pointer to the IP table context
 * @iface: the interface name
 * @port: the UDP port to remove
 *
R
Richard W.M. Jones 已提交
252
 * Removes an input from the IP table, hence forbidding access to the given
253 254 255 256
 * @port on the given @iface interface for UDP packets
 *
 * Returns 0 in case of success or an error code in case of error
 */
257
int
R
Roman Bogorodskiy 已提交
258
iptablesRemoveUdpInput(int family,
259 260 261
                       const char *iface,
                       int port)
{
R
Roman Bogorodskiy 已提交
262
    return iptablesInput(family, iface, port, REMOVE, 0);
263 264 265
}


266
static char *iptablesFormatNetwork(virSocketAddr *netaddr,
267
                                   unsigned int prefix)
268 269 270 271 272
{
    virSocketAddr network;
    char *netstr;
    char *ret;

273 274
    if (!(VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET) ||
          VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET6))) {
275 276
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Only IPv4 or IPv6 addresses can be used with iptables"));
277 278 279
        return NULL;
    }

280
    if (virSocketAddrMaskByPrefix(netaddr, prefix, &network) < 0) {
281 282
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failure to mask address"));
283 284
        return NULL;
    }
285

286
    netstr = virSocketAddrFormat(&network);
287 288 289 290

    if (!netstr)
        return NULL;

291
    ignore_value(virAsprintf(&ret, "%s/%d", netstr, prefix));
292 293 294 295 296 297

    VIR_FREE(netstr);
    return ret;
}


298 299 300
/* Allow all traffic coming from the bridge, with a valid network address
 * to proceed to WAN
 */
301
static int
R
Roman Bogorodskiy 已提交
302
iptablesForwardAllowOut(virSocketAddr *netaddr,
303
                        unsigned int prefix,
304 305 306
                        const char *iface,
                        const char *physdev,
                        int action)
307
{
308 309
    int ret;
    char *networkstr;
310
    virCommandPtr cmd = NULL;
311

312
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
313 314
        return -1;

R
Roman Bogorodskiy 已提交
315
    cmd = iptablesCommandNew("filter", "FORWARD",
316 317 318 319 320 321 322 323 324 325 326 327
                             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);
328 329
    VIR_FREE(networkstr);
    return ret;
330 331
}

332 333 334 335 336 337
/**
 * iptablesAddForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
338
 *
339 340 341 342 343 344
 * 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
 */
345
int
R
Roman Bogorodskiy 已提交
346
iptablesAddForwardAllowOut(virSocketAddr *netaddr,
347
                           unsigned int prefix,
348 349
                           const char *iface,
                           const char *physdev)
350
{
R
Roman Bogorodskiy 已提交
351
    return iptablesForwardAllowOut(netaddr, prefix, iface, physdev, ADD);
352 353
}

354 355 356 357 358 359
/**
 * iptablesRemoveForwardAllowOut:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @iface: the source interface name
 * @physdev: the physical output device
360
 *
361 362 363 364 365 366
 * 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
 */
367
int
R
Roman Bogorodskiy 已提交
368
iptablesRemoveForwardAllowOut(virSocketAddr *netaddr,
369
                              unsigned int prefix,
370 371
                              const char *iface,
                              const char *physdev)
372
{
R
Roman Bogorodskiy 已提交
373
    return iptablesForwardAllowOut(netaddr, prefix, iface, physdev, REMOVE);
374 375
}

376 377 378 379

/* Allow all traffic destined to the bridge, with a valid network address
 * and associated with an existing connection
 */
380
static int
R
Roman Bogorodskiy 已提交
381
iptablesForwardAllowRelatedIn(virSocketAddr *netaddr,
382
                              unsigned int prefix,
383 384 385
                              const char *iface,
                              const char *physdev,
                              int action)
386
{
387 388 389
    int ret;
    char *networkstr;

390
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
391 392
        return -1;

393
    if (physdev && physdev[0]) {
R
Roman Bogorodskiy 已提交
394
        ret = iptablesAddRemoveRule("filter", "FORWARD",
395
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
396 397 398 399
                                    action,
                                    "--destination", networkstr,
                                    "--in-interface", physdev,
                                    "--out-interface", iface,
S
Stefan Seyfried 已提交
400 401
                                    "--match", "conntrack",
                                    "--ctstate", "ESTABLISHED,RELATED",
402 403
                                    "--jump", "ACCEPT",
                                    NULL);
404
    } else {
R
Roman Bogorodskiy 已提交
405
        ret = iptablesAddRemoveRule("filter", "FORWARD",
406
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
407 408 409
                                    action,
                                    "--destination", networkstr,
                                    "--out-interface", iface,
S
Stefan Seyfried 已提交
410 411
                                    "--match", "conntrack",
                                    "--ctstate", "ESTABLISHED,RELATED",
412 413
                                    "--jump", "ACCEPT",
                                    NULL);
414
    }
415 416
    VIR_FREE(networkstr);
    return ret;
417 418
}

419 420 421 422 423 424 425 426 427 428 429 430 431 432
/**
 * 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 已提交
433
iptablesAddForwardAllowRelatedIn(virSocketAddr *netaddr,
434
                                 unsigned int prefix,
435 436
                                 const char *iface,
                                 const char *physdev)
437
{
R
Roman Bogorodskiy 已提交
438
    return iptablesForwardAllowRelatedIn(netaddr, prefix, iface, physdev, ADD);
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
}

/**
 * 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 已提交
455
iptablesRemoveForwardAllowRelatedIn(virSocketAddr *netaddr,
456
                                    unsigned int prefix,
457 458
                                    const char *iface,
                                    const char *physdev)
459
{
R
Roman Bogorodskiy 已提交
460
    return iptablesForwardAllowRelatedIn(netaddr, prefix, iface, physdev, REMOVE);
461 462 463 464 465
}

/* Allow all traffic destined to the bridge, with a valid network address
 */
static int
R
Roman Bogorodskiy 已提交
466
iptablesForwardAllowIn(virSocketAddr *netaddr,
467
                       unsigned int prefix,
468 469 470 471
                       const char *iface,
                       const char *physdev,
                       int action)
{
472 473 474
    int ret;
    char *networkstr;

475
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
476 477
        return -1;

478
    if (physdev && physdev[0]) {
R
Roman Bogorodskiy 已提交
479
        ret = iptablesAddRemoveRule("filter", "FORWARD",
480
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
481 482 483 484 485 486
                                    action,
                                    "--destination", networkstr,
                                    "--in-interface", physdev,
                                    "--out-interface", iface,
                                    "--jump", "ACCEPT",
                                    NULL);
487
    } else {
R
Roman Bogorodskiy 已提交
488
        ret = iptablesAddRemoveRule("filter", "FORWARD",
489
                                    VIR_SOCKET_ADDR_FAMILY(netaddr),
490 491 492 493 494
                                    action,
                                    "--destination", networkstr,
                                    "--out-interface", iface,
                                    "--jump", "ACCEPT",
                                    NULL);
495
    }
496 497
    VIR_FREE(networkstr);
    return ret;
498 499
}

500 501 502 503 504 505
/**
 * 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
506
 *
507 508 509 510 511 512
 * 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
 */
513
int
R
Roman Bogorodskiy 已提交
514
iptablesAddForwardAllowIn(virSocketAddr *netaddr,
515
                          unsigned int prefix,
516 517 518
                          const char *iface,
                          const char *physdev)
{
R
Roman Bogorodskiy 已提交
519
    return iptablesForwardAllowIn(netaddr, prefix, iface, physdev, ADD);
520 521
}

522 523 524 525 526 527
/**
 * 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
528
 *
529 530 531 532 533 534
 * 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
 */
535
int
R
Roman Bogorodskiy 已提交
536
iptablesRemoveForwardAllowIn(virSocketAddr *netaddr,
537
                             unsigned int prefix,
538 539 540
                             const char *iface,
                             const char *physdev)
{
R
Roman Bogorodskiy 已提交
541
    return iptablesForwardAllowIn(netaddr, prefix, iface, physdev, REMOVE);
542 543 544 545 546 547 548
}


/* Allow all traffic between guests on the same bridge,
 * with a valid network address
 */
static int
R
Roman Bogorodskiy 已提交
549
iptablesForwardAllowCross(int family,
550 551 552
                          const char *iface,
                          int action)
{
R
Roman Bogorodskiy 已提交
553
    return iptablesAddRemoveRule("filter", "FORWARD",
554
                                 family,
555 556 557 558 559 560 561
                                 action,
                                 "--in-interface", iface,
                                 "--out-interface", iface,
                                 "--jump", "ACCEPT",
                                 NULL);
}

562 563 564 565 566 567 568 569 570 571 572
/**
 * 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
 */
573
int
R
Roman Bogorodskiy 已提交
574
iptablesAddForwardAllowCross(int family,
575 576
                             const char *iface)
{
R
Roman Bogorodskiy 已提交
577
    return iptablesForwardAllowCross(family, iface, ADD);
578 579
}

580 581 582 583 584 585 586 587 588 589 590
/**
 * 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
 */
591
int
R
Roman Bogorodskiy 已提交
592
iptablesRemoveForwardAllowCross(int family,
593 594
                                const char *iface)
{
R
Roman Bogorodskiy 已提交
595
    return iptablesForwardAllowCross(family, iface, REMOVE);
596 597 598 599 600 601 602
}


/* Drop all traffic trying to forward from the bridge.
 * ie the bridge is the in interface
 */
static int
R
Roman Bogorodskiy 已提交
603
iptablesForwardRejectOut(int family,
604 605 606
                         const char *iface,
                         int action)
{
R
Roman Bogorodskiy 已提交
607
    return iptablesAddRemoveRule("filter", "FORWARD",
608 609 610 611 612
                                 family,
                                 action,
                                 "--in-interface", iface,
                                 "--jump", "REJECT",
                                 NULL);
613 614
}

615 616 617 618 619 620 621 622 623 624
/**
 * 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
 */
625
int
R
Roman Bogorodskiy 已提交
626
iptablesAddForwardRejectOut(int family,
627 628
                            const char *iface)
{
R
Roman Bogorodskiy 已提交
629
    return iptablesForwardRejectOut(family, iface, ADD);
630 631
}

632 633 634 635 636 637 638 639 640 641
/**
 * 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
 */
642
int
R
Roman Bogorodskiy 已提交
643
iptablesRemoveForwardRejectOut(int family,
644 645
                               const char *iface)
{
R
Roman Bogorodskiy 已提交
646
    return iptablesForwardRejectOut(family, iface, REMOVE);
647 648 649 650 651 652 653 654 655
}




/* Drop all traffic trying to forward to the bridge.
 * ie the bridge is the out interface
 */
static int
R
Roman Bogorodskiy 已提交
656
iptablesForwardRejectIn(int family,
657
                        const char *iface,
658 659
                        int action)
{
R
Roman Bogorodskiy 已提交
660
    return iptablesAddRemoveRule("filter", "FORWARD",
661
                                 family,
662 663 664 665 666 667
                                 action,
                                 "--out-interface", iface,
                                 "--jump", "REJECT",
                                 NULL);
}

668 669 670 671 672 673 674 675 676 677
/**
 * 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
 */
678
int
R
Roman Bogorodskiy 已提交
679
iptablesAddForwardRejectIn(int family,
680
                           const char *iface)
681
{
R
Roman Bogorodskiy 已提交
682
    return iptablesForwardRejectIn(family, iface, ADD);
683 684
}

685 686 687 688 689 690 691 692 693 694
/**
 * 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
 */
695
int
R
Roman Bogorodskiy 已提交
696
iptablesRemoveForwardRejectIn(int family,
697
                              const char *iface)
698
{
R
Roman Bogorodskiy 已提交
699
    return iptablesForwardRejectIn(family, iface, REMOVE);
700 701
}

702 703 704 705

/* Masquerade all traffic coming from the network associated
 * with the bridge
 */
706
static int
R
Roman Bogorodskiy 已提交
707
iptablesForwardMasquerade(virSocketAddr *netaddr,
708
                          unsigned int prefix,
709
                          const char *physdev,
710 711
                          virSocketAddrRangePtr addr,
                          virPortRangePtr port,
712 713
                          const char *protocol,
                          int action)
714
{
715 716 717 718
    int ret = -1;
    char *networkstr = NULL;
    char *addrStartStr = NULL;
    char *addrEndStr = NULL;
719
    char *portRangeStr = NULL;
720
    char *natRangeStr = NULL;
721
    virCommandPtr cmd = NULL;
722

723
    if (!(networkstr = iptablesFormatNetwork(netaddr, prefix)))
724 725
        return -1;

726
    if (!VIR_SOCKET_ADDR_IS_FAMILY(netaddr, AF_INET)) {
727
        /* Higher level code *should* guaranteee it's impossible to get here. */
728 729 730
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Attempted to NAT '%s'. NAT is only supported for IPv4."),
                       networkstr);
731 732 733
        goto cleanup;
    }

734 735
    if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->start, AF_INET)) {
        if (!(addrStartStr = virSocketAddrFormat(&addr->start)))
736
            goto cleanup;
737 738
        if (VIR_SOCKET_ADDR_IS_FAMILY(&addr->end, AF_INET)) {
            if (!(addrEndStr = virSocketAddrFormat(&addr->end)))
739 740
                goto cleanup;
        }
741 742
    }

R
Roman Bogorodskiy 已提交
743
    cmd = iptablesCommandNew("nat", "POSTROUTING", AF_INET, action);
744 745 746 747 748 749 750 751 752 753
    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);

754
    if (protocol && protocol[0]) {
755 756 757
        if (port->start == 0 && port->end == 0) {
            port->start = 1024;
            port->end = 65535;
758 759
        }

760 761
        if (port->start < port->end && port->end < 65536) {
            if (virAsprintf(&portRangeStr, ":%u-%u",
762
                            port->start, port->end) < 0)
763 764 765 766
                goto cleanup;
        } else {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Invalid port range '%u-%u'."),
767
                           port->start, port->end);
768 769 770
        }
    }

771 772 773
    /* Use --jump SNAT if public addr is specified */
    if (addrStartStr && addrStartStr[0]) {
        int r = 0;
774

775 776
        if (addrEndStr && addrEndStr[0]) {
            r = virAsprintf(&natRangeStr, "%s-%s%s", addrStartStr, addrEndStr,
777
                            portRangeStr ? portRangeStr : "");
778
        } else {
779 780
            r = virAsprintf(&natRangeStr, "%s%s", addrStartStr,
                            portRangeStr ? portRangeStr : "");
781 782
        }

783
        if (r < 0)
784 785 786 787 788 789 790
            goto cleanup;

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

791 792
         if (portRangeStr && portRangeStr[0])
             virCommandAddArgList(cmd, "--to-ports", &portRangeStr[1], NULL);
793 794 795 796 797
     }

    ret = virCommandRun(cmd, NULL);
cleanup:
    virCommandFree(cmd);
798
    VIR_FREE(networkstr);
799 800
    VIR_FREE(addrStartStr);
    VIR_FREE(addrEndStr);
801
    VIR_FREE(portRangeStr);
802
    VIR_FREE(natRangeStr);
803
    return ret;
804 805
}

806 807 808 809 810
/**
 * iptablesAddForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
811
 * @protocol: the network protocol or NULL
812
 *
813 814 815 816 817 818
 * 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
 */
819
int
R
Roman Bogorodskiy 已提交
820
iptablesAddForwardMasquerade(virSocketAddr *netaddr,
821
                             unsigned int prefix,
822
                             const char *physdev,
823 824
                             virSocketAddrRangePtr addr,
                             virPortRangePtr port,
825
                             const char *protocol)
826
{
R
Roman Bogorodskiy 已提交
827
    return iptablesForwardMasquerade(netaddr, prefix, physdev, addr, port,
828
                                     protocol, ADD);
829 830
}

831 832 833 834 835
/**
 * iptablesRemoveForwardMasquerade:
 * @ctx: pointer to the IP table context
 * @network: the source network name
 * @physdev: the physical input device or NULL
836
 * @protocol: the network protocol or NULL
837
 *
838 839 840 841 842 843
 * 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
 */
844
int
R
Roman Bogorodskiy 已提交
845
iptablesRemoveForwardMasquerade(virSocketAddr *netaddr,
846
                                unsigned int prefix,
847
                                const char *physdev,
848 849
                                virSocketAddrRangePtr addr,
                                virPortRangePtr port,
850
                                const char *protocol)
851
{
R
Roman Bogorodskiy 已提交
852
    return iptablesForwardMasquerade(netaddr, prefix, physdev, addr, port,
853
                                     protocol, REMOVE);
854
}
855 856


857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 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
/* 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);
}


945
static int
R
Roman Bogorodskiy 已提交
946
iptablesOutputFixUdpChecksum(const char *iface,
947 948 949 950 951 952 953 954
                             int port,
                             int action)
{
    char portstr[32];

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

R
Roman Bogorodskiy 已提交
955
    return iptablesAddRemoveRule("mangle", "POSTROUTING",
956
                                 AF_INET,
957 958 959 960 961 962 963 964 965 966 967 968 969 970
                                 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 已提交
971
 * Add a rule to the mangle table's POSTROUTING chain that fixes up the
972 973 974 975 976 977 978 979 980
 * 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 已提交
981
iptablesAddOutputFixUdpChecksum(const char *iface,
982 983
                                int port)
{
R
Roman Bogorodskiy 已提交
984
    return iptablesOutputFixUdpChecksum(iface, port, ADD);
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
}

/**
 * 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 已提交
1001
iptablesRemoveOutputFixUdpChecksum(const char *iface,
1002 1003
                                   int port)
{
R
Roman Bogorodskiy 已提交
1004
    return iptablesOutputFixUdpChecksum(iface, port, REMOVE);
1005
}