From 3ea88b568d7f5550ac399f310d2a4488bc31618d Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Wed, 14 Feb 2007 16:26:42 +0000 Subject: [PATCH] Tue Feb 14 16:23:25 IST 2007 Mark McLoughlin * 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 | 25 ++ configure.in | 25 ++ qemud/Makefile.am | 3 +- qemud/conf.c | 9 + qemud/internal.h | 2 + qemud/iptables.c | 699 ++++++++++++++++++++++++++++++++++++++++++++++ qemud/iptables.h | 71 +++++ qemud/qemud.c | 147 +++++++++- 8 files changed, 976 insertions(+), 5 deletions(-) create mode 100644 qemud/iptables.c create mode 100644 qemud/iptables.h diff --git a/ChangeLog b/ChangeLog index 730556b887..5cdbf5f5fb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +Tue Feb 14 16:23:25 IST 2007 Mark McLoughlin + + * 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 * src/xml.c: with connect the diff --git a/configure.in b/configure.in index 082576d637..f397f1dfc1 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/qemud/Makefile.am b/qemud/Makefile.am index 73243c10d0..1bc733d7ab 100644 --- a/qemud/Makefile.am +++ b/qemud/Makefile.am @@ -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) \ diff --git a/qemud/conf.c b/qemud/conf.c index 79340e5fd0..90e749e764 100644 --- a/qemud/conf.c +++ b/qemud/conf.c @@ -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) diff --git a/qemud/internal.h b/qemud/internal.h index 72281a19e3..a4764f5e65 100644 --- a/qemud/internal.h +++ b/qemud/internal.h @@ -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 index 0000000000..4863581bd7 --- /dev/null +++ b/qemud/iptables.c @@ -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 + */ + +#include + +#include "iptables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000000..3e1e79ec65 --- /dev/null +++ b/qemud/iptables.h @@ -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 + */ + +#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: + */ diff --git a/qemud/qemud.c b/qemud/qemud.c index 19cc5875cb..d56a61a2fd 100644 --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -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); }