]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Tue Feb 14 16:23:25 IST 2007 Mark McLoughlin <markmc@redhat.com>
authorMark McLoughlin <markmc@redhat.com>
Wed, 14 Feb 2007 16:26:42 +0000 (16:26 +0000)
committerMark McLoughlin <markmc@redhat.com>
Wed, 14 Feb 2007 16:26:42 +0000 (16:26 +0000)
        * qemud/iptables.[ch]: add code for managing iptables
        rules.

        * qemud/Makefile.am: add iptables.[ch].

        * qemud/qemud.c: add and remove iptables rules as
        appropriate.

        * qemud/conf.c: when starting a guess, add a rule
        allowing it to forward packets across the networks
        bridge.

        * qemud/internal.h: add iptables context ptr

        * configure.in: add --with-iptables-dir and
        --with-iptables-prefix to allow us to put our rules
        in a chain with the given prefix and save the rules
        in files in the given dir so as to integrate with
        the proposed "service iptables restart" solution
        in:

           https://bugzilla.redhat.com/227011

ChangeLog
configure.in
qemud/Makefile.am
qemud/conf.c
qemud/internal.h
qemud/iptables.c [new file with mode: 0644]
qemud/iptables.h [new file with mode: 0644]
qemud/qemud.c

index 730556b8871eb48c81af37bdff6bb49081ba6384..5cdbf5f5fb894a79880a71e4d36f6b4b2620f269 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+Tue Feb 14 16:23:25 IST 2007 Mark McLoughlin <markmc@redhat.com>
+
+       * qemud/iptables.[ch]: add code for managing iptables
+       rules.
+
+       * qemud/Makefile.am: add iptables.[ch].
+       
+       * qemud/qemud.c: add and remove iptables rules as
+       appropriate.
+       
+       * qemud/conf.c: when starting a guess, add a rule
+       allowing it to forward packets across the networks
+       bridge.
+       
+       * qemud/internal.h: add iptables context ptr
+       
+       * configure.in: add --with-iptables-dir and
+       --with-iptables-prefix to allow us to put our rules
+       in a chain with the given prefix and save the rules
+       in files in the given dir so as to integrate with
+       the proposed "service iptables restart" solution
+       in:
+       
+          https://bugzilla.redhat.com/227011
+       
 Tue Feb 14 16:21:18 IST 2007 Mark McLoughlin <markmc@redhat.com>
 
        * src/xml.c: with <interface type="network"> connect the
index 082576d63799753cbf3a4b41a5f3295e34d4d46b..f397f1dfc10dacf66737e69d5772f5472d1f1f4f 100644 (file)
@@ -78,6 +78,31 @@ dnl
        CFLAGS="-g -O -W -Wformat -Wunused -Wimplicit -Wreturn-type -Wswitch -Wcomment -Wtrigraphs -Wformat -Wchar-subscripts -Wuninitialized -Wparentheses -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Waggregate-return -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline -Wredundant-decls -Wall"
     fi
 
+dnl
+dnl allow the creation of iptables rules in chains with a
+dnl specific prefix rather than in the standard toplevel chains
+dnl
+AC_ARG_WITH(iptables-prefix,
+            AC_HELP_STRING([--with-iptables-prefix=prefix],
+                           [prefix used for iptables chains, default is to use standard toplevel chains]),
+            [IPTABLES_PREFIX=$withval])
+AC_DEFINE_UNQUOTED(IPTABLES_PREFIX, "$IPTABLES_PREFIX", [prefix used for iptables chains])
+
+dnl
+dnl also support saving the various chains to files
+dnl in e.g. /etc/sysconfig/iptables.d
+dnl
+AC_ARG_WITH(iptables-dir,
+            AC_HELP_STRING([--with-iptables-dir=path],
+                           [directory used to save iptables chains, defaults to not saving]),
+            [IPTABLES_DIR=$withval])
+if test x"$IPTABLES_DIR" != "x"; then
+   AC_DEFINE_UNQUOTED(IPTABLES_DIR, "$IPTABLES_DIR", [directory used for saving iptables chains])
+fi
+
+AC_PATH_PROG(IPTABLES_PATH, iptables, /sbin/iptables)
+AC_DEFINE_UNQUOTED(IPTABLES_PATH, "$IPTABLES_PATH", [path to iptables binary])
+
 dnl
 dnl Specify the xen-distribution directory to be able to compile on a
 dnl non-xenified host
index 73243c10d07fe33067a3fd0a55306012c12a956b..1bc733d7abf7bd4cd49012c16bbf1eb96fb08ba0 100644 (file)
@@ -8,7 +8,8 @@ libvirt_qemud_SOURCES = qemud.c internal.h protocol.h \
                 driver.c driver.h \
                 dispatch.c dispatch.h \
                 conf.c conf.h \
-                bridge.c bridge.h
+                bridge.c bridge.h \
+                iptables.c iptables.h
 #-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L
 libvirt_qemud_CFLAGS = \
         -I$(top_srcdir)/include -I$(top_builddir)/include $(LIBXML_CFLAGS) \
index 79340e5fd0fa67f135663d697ea69133099e7fbc..90e749e764013e931e9a2e56cb9ae7fcd5cff543 100644 (file)
@@ -43,6 +43,7 @@
 #include "internal.h"
 #include "conf.h"
 #include "driver.h"
+#include "iptables.h"
 
 static int qemudParseUUID(const char *uuid,
                           unsigned char *rawuuid) {
@@ -860,6 +861,13 @@ qemudNetworkIfaceConnect(struct qemud_server *server,
         goto error;
     }
 
+    if ((err = iptablesAddPhysdevForward(server->iptables, net->dst.network.tapifname))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "Failed to add iptables rule to allow bridging from '%s' :%s",
+                         net->dst.network.tapifname, strerror(err));
+        goto error;
+    }
+
     snprintf(tapfdstr, sizeof(tapfdstr), "tap,fd=%d,script=", tapfd);
 
     if (!(retval = strdup(tapfdstr)))
@@ -875,6 +883,7 @@ qemudNetworkIfaceConnect(struct qemud_server *server,
     return retval;
 
  no_memory:
+    iptablesRemovePhysdevForward(server->iptables, net->dst.network.tapifname);
     qemudReportError(server, VIR_ERR_NO_MEMORY, "tapfds");
  error:
     if (retval)
index 72281a19e38ca2fd8bbc4565c82392f39e1c0185..a4764f5e65b5607f45ac93bb84e89fc2a4eafb3f 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "protocol.h"
 #include "bridge.h"
+#include "iptables.h"
 
 #ifdef __GNUC__
 #ifdef HAVE_ANSIDECL_H
@@ -283,6 +284,7 @@ struct qemud_server {
     int ninactivenetworks;
     struct qemud_network *inactivenetworks;
     brControl *brctl;
+    iptablesContext *iptables;
     char configDir[PATH_MAX];
     char networkConfigDir[PATH_MAX];
     char errorMessage[QEMUD_MAX_ERROR_LEN];
diff --git a/qemud/iptables.c b/qemud/iptables.c
new file mode 100644 (file)
index 0000000..4863581
--- /dev/null
@@ -0,0 +1,699 @@
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * 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
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Authors:
+ *     Mark McLoughlin <markmc@redhat.com>
+ */
+
+#include <config.h>
+
+#include "iptables.h"
+
+#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 <paths.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+enum {
+    ADD = 0,
+    REMOVE
+};
+
+enum {
+    WITH_ERRORS = 0,
+    NO_ERRORS
+};
+
+typedef struct
+{
+    char  *table;
+    char  *chain;
+
+#ifdef IPTABLES_DIR
+
+    char   dir[PATH_MAX];
+    char   path[PATH_MAX];
+
+    int    nrules;
+    char **rules;
+
+#endif /* IPTABLES_DIR */
+
+} iptRules;
+
+struct _iptablesContext
+{
+    iptRules *input_filter;
+    iptRules *forward_filter;
+    iptRules *nat_postrouting;
+};
+
+#ifdef IPTABLES_DIR
+static int
+writeRules(const char *path,
+           char * const *rules,
+           int nrules)
+{
+    char tmp[PATH_MAX];
+    FILE *f;
+    int istmp;
+    int i;
+
+    if (nrules == 0 && unlink(path) == 0)
+        return 0;
+
+    if (snprintf(tmp, PATH_MAX, "%s.new", path) >= PATH_MAX)
+        return EINVAL;
+
+    istmp = 1;
+
+    if (!(f = fopen(tmp, "w"))) {
+        istmp = 0;
+        if (!(f = fopen(path, "w")))
+            return errno;
+    }
+
+    for (i = 0; i < nrules; i++) {
+        if (fputs(rules[i], f) == EOF ||
+            fputc('\n', f) == EOF) {
+            fclose(f);
+            if (istmp)
+                unlink(tmp);
+            return errno;
+        }
+    }
+
+    fclose(f);
+
+    if (istmp && rename(tmp, path) < 0) {
+        unlink(tmp);
+        return errno;
+    }
+
+    if (istmp)
+        unlink(tmp);
+
+    return 0;
+}
+
+static int
+ensureDir(const char *path)
+{
+    struct stat st;
+    char parent[PATH_MAX];
+    char *p;
+    int err;
+
+    if (stat(path, &st) >= 0)
+        return 0;
+
+    strncpy(parent, path, PATH_MAX);
+    parent[PATH_MAX - 1] = '\0';
+
+    if (!(p = strrchr(parent, '/')))
+        return EINVAL;
+
+    if (p == parent)
+        return EPERM;
+
+    *p = '\0';
+
+    if ((err = ensureDir(parent)))
+        return err;
+
+    if (mkdir(path, 0700) < 0 && errno != EEXIST)
+        return errno;
+
+    return 0;
+}
+
+static int
+buildDir(const char *table,
+         char *path,
+         int maxlen)
+{
+    if (snprintf(path, maxlen, IPTABLES_DIR "/%s", table) >= maxlen)
+        return EINVAL;
+    else
+        return 0;
+}
+
+static int
+buildPath(const char *table,
+          const char *chain,
+          char *path,
+          int maxlen)
+{
+    if (snprintf(path, maxlen, IPTABLES_DIR "/%s/%s.chain", table, chain) >= maxlen)
+        return EINVAL;
+    else
+        return 0;
+}
+
+static int
+iptRulesAppend(iptRules *rules,
+               const char *rule)
+{
+    char **r;
+    int err;
+
+    if (!(r = (char **)realloc(rules->rules, sizeof(char *) * (rules->nrules+1))))
+        return ENOMEM;
+
+    rules->rules = r;
+
+    if (!(rules->rules[rules->nrules] = strdup(rule)))
+        return ENOMEM;
+
+    rules->nrules++;
+
+    if ((err = ensureDir(rules->dir)))
+        return err;
+
+    if ((err = writeRules(rules->path, rules->rules, rules->nrules)))
+        return err;
+
+    return 0;
+}
+
+static int
+iptRulesRemove(iptRules *rules,
+               const char *rule)
+{
+    int i;
+    int err;
+
+    for (i = 0; i < rules->nrules; i++)
+        if (!strcmp(rules->rules[i], rule))
+            break;
+
+    if (i >= rules->nrules)
+        return EINVAL;
+
+    free(rules->rules[i]);
+
+    memmove(&rules->rules[i],
+            &rules->rules[i+1],
+            (rules->nrules - i - 1) * sizeof (char *));
+
+    rules->nrules--;
+
+    if ((err = writeRules(rules->path, rules->rules, rules->nrules)))
+        return err;
+
+    return 0;
+}
+#endif /* IPTABLES_DIR */
+
+static void
+iptRulesFree(iptRules *rules)
+{
+    if (rules->table) {
+        free(rules->chain);
+        rules->chain = NULL;
+    }
+
+    if (rules->chain) {
+        free(rules->chain);
+        rules->chain = NULL;
+    }
+
+#ifdef IPTABLES_DIR
+    {
+        int i;
+
+        rules->dir[0] = '\0';
+        rules->path[0] = '\0';
+
+        for (i = 0; i < rules->nrules; i++) {
+            free(rules->rules[i]);
+            rules->rules[i] = NULL;
+        }
+
+        rules->nrules = 0;
+
+        if (rules->rules) {
+            free(rules->rules);
+            rules->rules = NULL;
+        }
+    }
+#endif /* IPTABLES_DIR */
+
+    free(rules);
+}
+
+static iptRules *
+iptRulesNew(const char *table,
+            const char *chain)
+{
+    iptRules *rules;
+
+    if (!(rules = (iptRules *)malloc(sizeof (iptRules))))
+        return NULL;
+
+    memset (rules, 0, sizeof (iptRules));
+
+    if (!(rules->table = strdup(table)))
+        goto error;
+
+    if (!(rules->chain = strdup(chain)))
+        goto error;
+
+#ifdef IPTABLES_DIR
+    if (buildDir(table, rules->dir, sizeof(rules->dir)))
+        goto error;
+
+    if (buildPath(table, chain, rules->path, sizeof(rules->path)))
+        goto error;
+
+    rules->rules = NULL;
+    rules->nrules = 0;
+#endif /* IPTABLES_DIR */
+
+    return rules;
+
+ error:
+    iptRulesFree(rules);
+    return NULL;
+}
+
+static int
+iptablesSpawn(int errors, char * const *argv)
+{
+    pid_t pid, ret;
+    int status;
+    int null = -1;
+
+    if (errors == NO_ERRORS && (null = open(_PATH_DEVNULL, O_RDONLY)) < 0)
+        return errno;
+
+    pid = fork();
+    if (pid == -1) {
+        if (errors == NO_ERRORS)
+            close(null);
+        return errno;
+    }
+
+    if (pid == 0) { /* child */
+        int i, open_max = sysconf(_SC_OPEN_MAX);
+
+        for (i = 0; i < open_max; i++) {
+            if (i != STDOUT_FILENO &&
+                i != STDERR_FILENO &&
+                i != STDIN_FILENO)
+                close(i);
+        else if (errors == NO_ERRORS)
+            dup2(null, i);
+        }
+
+        execvp(argv[0], argv);
+
+        _exit (1);
+    }
+
+    if (errors == NO_ERRORS)
+        close(null);
+
+    while ((ret = waitpid(pid, &status, 0) == -1) && errno == EINTR);
+    if (ret == -1)
+        return errno;
+
+    if (errors == NO_ERRORS)
+        return 0;
+    else
+        return (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? 0 : EINVAL;
+}
+
+static int
+iptablesAddRemoveChain(iptRules *rules, int action)
+{
+    char **argv;
+    int retval = ENOMEM;
+    int n;
+
+    n = 1 + /* /sbin/iptables    */
+        2 + /*   --table foo     */
+        2;  /*   --new-chain bar */
+
+    if (!(argv = (char **)malloc(sizeof(char *) * (n+1))))
+        goto error;
+
+    memset(argv, 0, sizeof(char *) * (n + 1));
+
+    n = 0;
+
+    if (!(argv[n++] = strdup(IPTABLES_PATH)))
+        goto error;
+
+    if (!(argv[n++] = strdup("--table")))
+        goto error;
+
+    if (!(argv[n++] = strdup(rules->table)))
+        goto error;
+
+    if (!(argv[n++] = strdup(action == ADD ? "--new-chain" : "--delete-chain")))
+        goto error;
+
+    if (!(argv[n++] = strdup(rules->chain)))
+        goto error;
+
+    retval = iptablesSpawn(NO_ERRORS, argv);
+
+ error:
+    if (argv) {
+        n = 0;
+        while (argv[n])
+            free(argv[n++]);
+        free(argv);
+    }
+
+    return retval;
+}
+
+static int
+iptablesAddRemoveRule(iptRules *rules, int action, const char *arg, ...)
+{
+    va_list args;
+    int retval = ENOMEM;
+    char **argv;
+    char *rule = NULL, *p;
+    const char *s;
+    int n, rulelen;
+
+    n = 1 + /* /sbin/iptables  */
+        2 + /*   --table foo   */
+        2 + /*   --insert bar  */
+        1;  /*   arg           */
+
+    rulelen = strlen(arg) + 1;
+
+    va_start(args, arg);
+    while ((s = va_arg(args, const char *))) {
+        n++;
+        rulelen += strlen(s) + 1;
+    }
+
+    va_end(args);
+
+    if (!(argv = (char **)malloc(sizeof(char *) * (n + 1))))
+        goto error;
+
+    if (!(rule = (char *)malloc(rulelen)))
+        goto error;
+
+    memset(argv, 0, sizeof(char *) * (n + 1));
+
+    n = 0;
+
+    if (!(argv[n++] = strdup(IPTABLES_PATH)))
+        goto error;
+
+    if (!(argv[n++] = strdup("--table")))
+        goto error;
+
+    if (!(argv[n++] = strdup(rules->table)))
+        goto error;
+
+    if (!(argv[n++] = strdup(action == ADD ? "--insert" : "--delete")))
+        goto error;
+
+    if (!(argv[n++] = strdup(rules->chain)))
+        goto error;
+
+    if (!(argv[n++] = strdup(arg)))
+        goto error;
+
+    p = strcpy(rule, arg);
+    p += strlen(arg);
+
+    va_start(args, arg);
+
+    while ((s = va_arg(args, const char *))) {
+        if (!(argv[n++] = strdup(s)))
+            goto error;
+
+        *(p++) = ' ';
+        strcpy(p, s);
+        p += strlen(s);
+    }
+
+    va_end(args);
+
+    *p = '\0';
+
+    if (action == ADD &&
+        (retval = iptablesAddRemoveChain(rules, action)))
+        goto error;
+
+    if ((retval = iptablesSpawn(WITH_ERRORS, argv)))
+        goto error;
+
+    if (action == REMOVE &&
+        (retval = iptablesAddRemoveChain(rules, action)))
+        goto error;
+
+#ifdef IPTABLES_DIR
+    if (action == ADD)
+        retval = iptRulesAppend(rules, rule);
+    else
+        retval = iptRulesRemove(rules, rule);
+#endif /* IPTABLES_DIR */
+
+ error:
+    if (rule)
+        free(rule);
+
+    if (argv) {
+        n = 0;
+        while (argv[n])
+            free(argv[n++]);
+        free(argv);
+    }
+
+    return retval;
+}
+
+iptablesContext *
+iptablesContextNew(void)
+{
+    iptablesContext *ctx;
+
+    if (!(ctx = (iptablesContext *) malloc(sizeof (iptablesContext))))
+        return NULL;
+
+    if (!(ctx->input_filter = iptRulesNew("filter", IPTABLES_PREFIX "INPUT")))
+        goto error;
+
+    if (!(ctx->forward_filter = iptRulesNew("filter", IPTABLES_PREFIX "FORWARD")))
+        goto error;
+
+    if (!(ctx->nat_postrouting = iptRulesNew("nat", IPTABLES_PREFIX "POSTROUTING")))
+        goto error;
+
+    return ctx;
+
+ error:
+    iptablesContextFree(ctx);
+    return NULL;
+}
+
+void
+iptablesContextFree(iptablesContext *ctx)
+{
+    iptRulesFree(ctx->input_filter);
+    iptRulesFree(ctx->forward_filter);
+    iptRulesFree(ctx->nat_postrouting);
+    free(ctx);
+}
+
+static int
+iptablesInput(iptablesContext *ctx,
+              const char *iface,
+              int port,
+              int action,
+              int tcp)
+{
+    char portstr[32];
+    int ret;
+
+    snprintf(portstr, sizeof(portstr), "%d", port);
+    portstr[sizeof(portstr) - 1] = '\0';
+
+    ret = iptablesAddRemoveRule(ctx->input_filter,
+                                action,
+                                "--in-interface", iface,
+                                "--protocol", tcp ? "tcp" : "udp",
+                                "--destination-port", portstr,
+                                "--jump", "ACCEPT",
+                                NULL);
+
+    return ret;
+}
+
+int
+iptablesAddTcpInput(iptablesContext *ctx,
+                    const char *iface,
+                    int port)
+{
+    return iptablesInput(ctx, iface, port, ADD, 1);
+}
+
+int
+iptablesRemoveTcpInput(iptablesContext *ctx,
+                       const char *iface,
+                       int port)
+{
+    return iptablesInput(ctx, iface, port, REMOVE, 1);
+}
+
+int
+iptablesAddUdpInput(iptablesContext *ctx,
+                    const char *iface,
+                    int port)
+{
+    return iptablesInput(ctx, iface, port, ADD, 0);
+}
+
+int
+iptablesRemoveUdpInput(iptablesContext *ctx,
+                       const char *iface,
+                       int port)
+{
+    return iptablesInput(ctx, iface, port, REMOVE, 0);
+}
+
+static int
+iptablesPhysdevForward(iptablesContext *ctx,
+                       const char *iface,
+                       int action)
+{
+    return iptablesAddRemoveRule(ctx->forward_filter,
+                                 action,
+                                 "--match", "physdev",
+                                 "--physdev-in", iface,
+                                 "--jump", "ACCEPT",
+                                 NULL);
+}
+
+int
+iptablesAddPhysdevForward(iptablesContext *ctx,
+                          const char *iface)
+{
+    return iptablesPhysdevForward(ctx, iface, ADD);
+}
+
+int
+iptablesRemovePhysdevForward(iptablesContext *ctx,
+                             const char *iface)
+{
+    return iptablesPhysdevForward(ctx, iface, REMOVE);
+}
+
+static int
+iptablesInterfaceForward(iptablesContext *ctx,
+                         const char *iface,
+                         int action)
+{
+    return iptablesAddRemoveRule(ctx->forward_filter,
+                                 action,
+                                 "--in-interface", iface,
+                                 "--jump", "ACCEPT",
+                                 NULL);
+}
+
+int
+iptablesAddInterfaceForward(iptablesContext *ctx,
+                            const char *iface)
+{
+    return iptablesInterfaceForward(ctx, iface, ADD);
+}
+
+int
+iptablesRemoveInterfaceForward(iptablesContext *ctx,
+                               const char *iface)
+{
+    return iptablesInterfaceForward(ctx, iface, REMOVE);
+}
+
+static int
+iptablesStateForward(iptablesContext *ctx,
+                     const char *iface,
+                     int action)
+{
+    return iptablesAddRemoveRule(ctx->forward_filter,
+                                 action,
+                                 "--out-interface", iface,
+                                 "--match", "state",
+                                 "--state", "ESTABLISHED,RELATED",
+                                 "--jump", "ACCEPT",
+                                 NULL);
+}
+
+int
+iptablesAddStateForward(iptablesContext *ctx,
+                        const char *iface)
+{
+    return iptablesStateForward(ctx, iface, ADD);
+}
+
+int
+iptablesRemoveStateForward(iptablesContext *ctx,
+                           const char *iface)
+{
+    return iptablesStateForward(ctx, iface, REMOVE);
+}
+
+static int
+iptablesNonBridgedMasq(iptablesContext *ctx,
+                       int action)
+{
+    return iptablesAddRemoveRule(ctx->nat_postrouting,
+                                 action,
+                                 "--match", "physdev",
+                                 "!", "--physdev-is-bridged",
+                                 "--jump", "MASQUERADE",
+                                 NULL);
+}
+
+int
+iptablesAddNonBridgedMasq(iptablesContext *ctx)
+{
+    return iptablesNonBridgedMasq(ctx, ADD);
+}
+
+int
+iptablesRemoveNonBridgedMasq(iptablesContext *ctx)
+{
+    return iptablesNonBridgedMasq(ctx, REMOVE);
+}
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/qemud/iptables.h b/qemud/iptables.h
new file mode 100644 (file)
index 0000000..3e1e79e
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * 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
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Authors:
+ *     Mark McLoughlin <markmc@redhat.com>
+ */
+
+#ifndef __QEMUD_IPTABLES_H__
+#define __QEMUD_IPTABLES_H__
+
+typedef struct _iptablesContext iptablesContext;
+
+iptablesContext *iptablesContextNew              (void);
+void             iptablesContextFree             (iptablesContext *ctx);
+
+int              iptablesAddTcpInput             (iptablesContext *ctx,
+                                                  const char *iface,
+                                                  int port);
+int              iptablesRemoveTcpInput          (iptablesContext *ctx,
+                                                  const char *iface,
+                                                  int port);
+
+int              iptablesAddUdpInput             (iptablesContext *ctx,
+                                                  const char *iface,
+                                                  int port);
+int              iptablesRemoveUdpInput          (iptablesContext *ctx,
+                                                  const char *iface,
+                                                  int port);
+
+int              iptablesAddPhysdevForward       (iptablesContext *ctx,
+                                                  const char *iface);
+int              iptablesRemovePhysdevForward    (iptablesContext *ctx,
+                                                  const char *iface);
+
+int              iptablesAddInterfaceForward     (iptablesContext *ctx,
+                                                  const char *iface);
+int              iptablesRemoveInterfaceForward  (iptablesContext *ctx,
+                                                  const char *iface);
+
+int              iptablesAddStateForward         (iptablesContext *ctx,
+                                                  const char *iface);
+int              iptablesRemoveStateForward      (iptablesContext *ctx,
+                                                  const char *iface);
+
+int              iptablesAddNonBridgedMasq       (iptablesContext *ctx);
+int              iptablesRemoveNonBridgedMasq    (iptablesContext *ctx);
+
+#endif /* __QEMUD_IPTABLES_H__ */
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
index 19cc5875cb32520d7549ae8801ff1c07f66baa0e..d56a61a2fdf43d9de75e945f7819d886cdc4b93c 100644 (file)
@@ -50,6 +50,7 @@
 #include "dispatch.h"
 #include "driver.h"
 #include "conf.h"
+#include "iptables.h"
 
 static void reapchild(int sig ATTRIBUTE_UNUSED) {
     /* We explicitly waitpid the child later */
@@ -659,11 +660,10 @@ static int qemudVMData(struct qemud_server *server ATTRIBUTE_UNUSED,
 }
 
 static void
-qemudNetworkIfaceDisconnect(struct qemud_server *server ATTRIBUTE_UNUSED,
+qemudNetworkIfaceDisconnect(struct qemud_server *server,
                             struct qemud_vm *vm ATTRIBUTE_UNUSED,
                             struct qemud_vm_net_def *net) {
-    /* FIXME: will be needed to remove iptables rules */
-    net = NULL;
+    iptablesRemovePhysdevForward(server->iptables, net->dst.network.tapifname);
 }
 
 int qemudShutdownVMDaemon(struct qemud_server *server, struct qemud_vm *vm) {
@@ -845,6 +845,129 @@ dhcpStartDhcpDaemon(struct qemud_server *server,
     return ret;
 }
 
+static int
+qemudAddIptablesRules(struct qemud_server *server,
+                      struct qemud_network *network) {
+    int err;
+
+    if (!server->iptables && !(server->iptables = iptablesContextNew())) {
+        qemudReportError(server, VIR_ERR_NO_MEMORY, "iptables support");
+        return 1;
+    }
+
+    /* allow bridging from the bridge interface itself */
+    if ((err = iptablesAddPhysdevForward(server->iptables, network->bridge))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "failed to add iptables rule to allow bridging from '%s' : %s\n",
+                         network->bridge, strerror(err));
+        goto err1;
+    }
+
+    /* allow forwarding packets from the bridge interface */
+    if ((err = iptablesAddInterfaceForward(server->iptables, network->bridge))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "failed to add iptables rule to allow forwarding from '%s' : %s\n",
+                         network->bridge, strerror(err));
+        goto err2;
+    }
+
+    /* allow forwarding packets to the bridge interface if they are part of an existing connection */
+    if ((err = iptablesAddStateForward(server->iptables, network->bridge))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "failed to add iptables rule to allow forwarding to '%s' : %s\n",
+                         network->bridge, strerror(err));
+        goto err3;
+    }
+
+    /* enable masquerading */
+    if ((err = iptablesAddNonBridgedMasq(server->iptables))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "failed to add iptables rule to enable masquerading : %s\n",
+                         strerror(err));
+        goto err4;
+    }
+
+    /* allow DHCP requests through to dnsmasq */
+    if ((err = iptablesAddTcpInput(server->iptables, network->bridge, 67))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "failed to add iptables rule to allow DHCP requests from '%s' : %s\n",
+                         network->bridge, strerror(err));
+        goto err5;
+    }
+
+    if ((err = iptablesAddUdpInput(server->iptables, network->bridge, 67))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "failed to add iptables rule to allow DHCP requests from '%s' : %s\n",
+                         network->bridge, strerror(err));
+        goto err6;
+    }
+
+    /* allow DNS requests through to dnsmasq */
+    if ((err = iptablesAddTcpInput(server->iptables, network->bridge, 53))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "failed to add iptables rule to allow DNS requests from '%s' : %s\n",
+                         network->bridge, strerror(err));
+        goto err7;
+    }
+
+    if ((err = iptablesAddUdpInput(server->iptables, network->bridge, 53))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "failed to add iptables rule to allow DNS requests from '%s' : %s\n",
+                         network->bridge, strerror(err));
+        goto err8;
+    }
+
+    return 1;
+
+ err8:
+    iptablesRemoveTcpInput(server->iptables, network->bridge, 53);
+ err7:
+    iptablesRemoveUdpInput(server->iptables, network->bridge, 67);
+ err6:
+    iptablesRemoveTcpInput(server->iptables, network->bridge, 67);
+ err5:
+    iptablesRemoveNonBridgedMasq(server->iptables);
+ err4:
+    iptablesRemoveStateForward(server->iptables, network->bridge);
+ err3:
+    iptablesRemoveInterfaceForward(server->iptables, network->bridge);
+ err2:
+    iptablesRemovePhysdevForward(server->iptables, network->bridge);
+ err1:
+    return 0;
+}
+
+static void
+qemudRemoveIptablesRules(struct qemud_server *server,
+                         struct qemud_network *network) {
+    iptablesRemoveUdpInput(server->iptables, network->bridge, 53);
+    iptablesRemoveTcpInput(server->iptables, network->bridge, 53);
+    iptablesRemoveUdpInput(server->iptables, network->bridge, 67);
+    iptablesRemoveTcpInput(server->iptables, network->bridge, 67);
+    iptablesRemoveNonBridgedMasq(server->iptables);
+    iptablesRemoveStateForward(server->iptables, network->bridge);
+    iptablesRemoveInterfaceForward(server->iptables, network->bridge);
+    iptablesRemovePhysdevForward(server->iptables, network->bridge);
+}
+
+static int
+qemudEnableIpForwarding(void)
+{
+#define PROC_IP_FORWARD "/proc/sys/net/ipv4/ip_forward"
+
+  int fd;
+
+  if ((fd = open(PROC_IP_FORWARD, O_WRONLY|O_TRUNC)) == -1 ||
+      write(fd, "1\n", 2) < 0)
+      return 0;
+
+  close (fd);
+
+  return 1;
+
+#undef PROC_IP_FORWARD
+}
+
 int qemudStartNetworkDaemon(struct qemud_server *server,
                             struct qemud_network *network) {
     const char *name;
@@ -899,14 +1022,26 @@ int qemudStartNetworkDaemon(struct qemud_server *server,
         goto err_delbr;
     }
 
+    if (!qemudAddIptablesRules(server, network))
+        goto err_delbr1;
+
+    if (!qemudEnableIpForwarding()) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "failed to enable IP forwarding : %s\n", strerror(err));
+        goto err_delbr2;
+    }
+
     if (network->def.ranges &&
         dhcpStartDhcpDaemon(server, network) < 0)
-        goto err_delbr1;
+        goto err_delbr2;
 
     network->active = 1;
 
     return 0;
 
+ err_delbr2:
+    qemudRemoveIptablesRules(server, network);
+
  err_delbr1:
     if (network->def.ipAddress[0] &&
         (err = brSetInterfaceUp(server->brctl, network->bridge, 0))) {
@@ -935,6 +1070,8 @@ int qemudShutdownNetworkDaemon(struct qemud_server *server,
     if (network->dnsmasqPid > 0)
         kill(network->dnsmasqPid, SIGTERM);
 
+    qemudRemoveIptablesRules(server, network);
+
     if (network->def.ipAddress[0] &&
         (err = brSetInterfaceUp(server->brctl, network->bridge, 0))) {
         printf("Damn! Failed to bring down bridge '%s' : %s\n",
@@ -1182,6 +1319,8 @@ static void qemudCleanup(struct qemud_server *server) {
     }
     if (server->brctl)
         brShutdown(server->brctl);
+    if (server->iptables)
+        iptablesContextFree(server->iptables);
     free(server);
 }