diff --git a/configure.in b/configure.in index b7d0d7b29e..7afbc7c798 100644 --- a/configure.in +++ b/configure.in @@ -287,6 +287,9 @@ fi AC_PATH_PROG([IPTABLES_PATH], [iptables], /sbin/iptables, [/usr/sbin:$PATH]) AC_DEFINE_UNQUOTED([IPTABLES_PATH], "$IPTABLES_PATH", [path to iptables binary]) +AC_PATH_PROG([EBTABLES_PATH], [ebtables], /sbin/ebtables, [/usr/sbin:$PATH]) +AC_DEFINE_UNQUOTED([EBTABLES_PATH], "$EBTABLES_PATH", [path to ebtables binary]) + if test "$with_openvz" = "yes"; then AC_DEFINE_UNQUOTED([WITH_OPENVZ], 1, [whether OpenVZ driver is enabled]) fi diff --git a/src/Makefile.am b/src/Makefile.am index 8fbfb5f0a6..27973525a8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -51,6 +51,7 @@ UTIL_SOURCES = \ util/event.c util/event.h \ util/hash.c util/hash.h \ util/iptables.c util/iptables.h \ + util/ebtables.c util/ebtables.h \ util/logging.c util/logging.h \ util/memory.c util/memory.h \ util/pci.c util/pci.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 15d75fdd1b..215addbc08 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -174,6 +174,14 @@ virDomainEventDispatch; virDomainEventQueueDispatch; +# ebtables.h +ebtablesAddForwardAllowIn; +ebtablesAddForwardPolicyReject; +ebtablesContextNew; +ebtablesRemoveForwardAllowIn; +ebtablesSaveRules; + + # event.h virEventAddHandle; virEventAddTimeout; @@ -213,6 +221,7 @@ virInterfaceObjListFree; # interface_driver.h interfaceRegister; + # iptables.h iptablesAddForwardAllowCross; iptablesAddForwardAllowIn; diff --git a/src/util/ebtables.c b/src/util/ebtables.c new file mode 100644 index 0000000000..60427d7099 --- /dev/null +++ b/src/util/ebtables.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2009 IBM Corp. + * Copyright (C) 2007-2009 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 + * + * based on iptables.c + * Authors: + * Gerhard Stenzel + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#ifdef HAVE_PATHS_H +#include +#endif + +#include "internal.h" +#include "ebtables.h" +#include "util.h" +#include "memory.h" +#include "virterror_internal.h" +#include "logging.h" + +struct _ebtablesContext +{ + ebtRules *input_filter; + ebtRules *forward_filter; + ebtRules *nat_postrouting; +}; + +enum { + ADD = 0, + REMOVE, + CREATE, + POLICY, + INSERT +}; + +static void +ebtRulesSave(ebtRules *rules) +{ + (void) rules; +} + +static void +ebtRuleFree(ebtRule *rule) +{ + VIR_FREE(rule->rule); + + if (rule->argv) { + int i = 0; + while (rule->argv[i]) + VIR_FREE(rule->argv[i++]); + VIR_FREE(rule->argv); + } +} + +static int +ebtRulesAppend(ebtRules *rules, + char *rule, + const char **argv, + int command_idx) +{ + if (VIR_REALLOC_N(rules->rules, rules->nrules+1) < 0) { + int i = 0; + while (argv[i]) + VIR_FREE(argv[i++]); + VIR_FREE(argv); + return ENOMEM; + } + + rules->rules[rules->nrules].rule = rule; + rules->rules[rules->nrules].argv = argv; + rules->rules[rules->nrules].command_idx = command_idx; + + rules->nrules++; + + return 0; +} + +static int +ebtRulesRemove(ebtRules *rules, + char *rule) +{ + int i; + + for (i = 0; i < rules->nrules; i++) + if (STREQ(rules->rules[i].rule, rule)) + break; + + if (i >= rules->nrules) + return EINVAL; + + ebtRuleFree(&rules->rules[i]); + + memmove(&rules->rules[i], + &rules->rules[i+1], + (rules->nrules - i - 1) * sizeof (ebtRule)); + + rules->nrules--; + + return 0; +} + +static void +ebtRulesFree(ebtRules *rules) +{ + int i; + + VIR_FREE(rules->table); + VIR_FREE(rules->chain); + + if (rules->rules) { + for (i = 0; i < rules->nrules; i++) + ebtRuleFree(&rules->rules[i]); + + VIR_FREE(rules->rules); + + rules->nrules = 0; + } + + VIR_FREE(rules); +} + +static ebtRules * +ebtRulesNew(const char *table, + const char *chain) +{ + ebtRules *rules; + + if (VIR_ALLOC(rules) < 0) + return NULL; + + if (!(rules->table = strdup(table))) + goto error; + + if (!(rules->chain = strdup(chain))) + goto error; + + rules->rules = NULL; + rules->nrules = 0; + + return rules; + + error: + ebtRulesFree(rules); + return NULL; +} + +static int +ebtablesAddRemoveRule(ebtRules *rules, int action, const char *arg, ...) +{ + va_list args; + int retval = ENOMEM; + const char **argv; + char *rule = NULL; + const char *s; + int n, command_idx; + + n = 1 + /* /sbin/ebtables */ + 2 + /* --table foo */ + 2 + /* --insert bar */ + 1; /* arg */ + + va_start(args, arg); + while ((s = va_arg(args, const char *))) + n++; + + va_end(args); + + if (VIR_ALLOC_N(argv, n + 1) < 0) + goto error; + + n = 0; + + if (!(argv[n++] = strdup(EBTABLES_PATH))) + goto error; + + command_idx = n; + + if(action == ADD || action == REMOVE) { + if (!(argv[n++] = strdup("--insert"))) + goto error; + + if (!(argv[n++] = strdup(rules->chain))) + goto error; + } + + if (!(argv[n++] = strdup(arg))) + goto error; + + va_start(args, arg); + + while ((s = va_arg(args, const char *))) + if (!(argv[n++] = strdup(s))) + goto error; + + va_end(args); + + if (!(rule = virArgvToString(&argv[command_idx]))) + goto error; + + if (action == REMOVE) { + VIR_FREE(argv[command_idx]); + if (!(argv[command_idx] = strdup("--delete"))) + goto error; + } + + if (virRun(NULL, argv, NULL) < 0) { + retval = errno; + goto error; + } + + if (action == ADD || action == CREATE || action == POLICY || + action == INSERT) { + retval = ebtRulesAppend(rules, rule, argv, command_idx); + rule = NULL; + argv = NULL; + } else { + retval = ebtRulesRemove(rules, rule); + } + + error: + VIR_FREE(rule); + + if (argv) { + n = 0; + while (argv[n]) + VIR_FREE(argv[n++]); + VIR_FREE(argv); + } + + return retval; +} + + +/** + * ebtablesContextNew: + * + * Create a new ebtable context + * + * Returns a pointer to the new structure or NULL in case of error + */ +ebtablesContext * +ebtablesContextNew(const char *driver) +{ + ebtablesContext *ctx; + char chain[PATH_MAX]; + + if (VIR_ALLOC(ctx) < 0) + return NULL; + + snprintf(chain, sizeof(chain), "libvirt_%s_INPUT", driver); + if (!(ctx->input_filter = ebtRulesNew("filter", chain))) + goto error; + + snprintf(chain, sizeof(chain), "libvirt_%s_FORWARD", driver); + if (!(ctx->forward_filter = ebtRulesNew("filter", chain))) + goto error; + + snprintf(chain, sizeof(chain), "libvirt_%s_POSTROUTING", driver); + if (!(ctx->nat_postrouting = ebtRulesNew("nat", chain))) + goto error; + + return ctx; + + error: + ebtablesContextFree(ctx); + return NULL; +} + +/** + * ebtablesContextFree: + * @ctx: pointer to the EB table context + * + * Free the resources associated with an EB table context + */ +void +ebtablesContextFree(ebtablesContext *ctx) +{ + if (ctx->input_filter) + ebtRulesFree(ctx->input_filter); + if (ctx->forward_filter) + ebtRulesFree(ctx->forward_filter); + if (ctx->nat_postrouting) + ebtRulesFree(ctx->nat_postrouting); + VIR_FREE(ctx); +} + +/** + * ebtablesSaveRules: + * @ctx: pointer to the EB table context + * + * Saves all the EB table rules associated with a context + * to disk so that if ebtables is restarted, the rules + * will automatically be reload. + */ +void +ebtablesSaveRules(ebtablesContext *ctx) +{ + ebtRulesSave(ctx->input_filter); + ebtRulesSave(ctx->forward_filter); + ebtRulesSave(ctx->nat_postrouting); +} + +int +ebtablesAddForwardPolicyReject(ebtablesContext *ctx) +{ + return ebtablesForwardPolicyReject(ctx, ADD); +} + + +int +ebtablesRemoveForwardPolicyReject(ebtablesContext *ctx) +{ + return ebtablesForwardPolicyReject(ctx, REMOVE); +} + +int +ebtablesForwardPolicyReject(ebtablesContext *ctx, + int action) +{ + /* create it, if it does not exist */ + if (action == ADD) { + ebtablesAddRemoveRule(ctx->forward_filter, + CREATE, + "--new-chain", ctx->forward_filter->chain, NULL, + NULL); + ebtablesAddRemoveRule(ctx->forward_filter, + INSERT, + "--insert", "FORWARD", "--jump", + ctx->forward_filter->chain, NULL); + } + + return ebtablesAddRemoveRule(ctx->forward_filter, + POLICY, + "-P", ctx->forward_filter->chain, "DROP", + NULL); +} + +/* + * Allow all traffic destined to the bridge, with a valid network address + */ +static int +ebtablesForwardAllowIn(ebtablesContext *ctx, + const char *iface, + const char *macaddr, + int action) +{ + return ebtablesAddRemoveRule(ctx->forward_filter, + action, + "--in-interface", iface, + "--source", macaddr, + "--jump", "ACCEPT", + NULL); +} + +/** + * ebtablesAddForwardAllowIn: + * @ctx: pointer to the EB table context + * @iface: the output interface name + * @physdev: the physical input device or NULL + * + * Add rules to the EB table context to allow the traffic on + * @physdev device to be forwarded to interface @iface. This allows + * the inbound traffic on a bridge. + * + * Returns 0 in case of success or an error code otherwise + */ +int +ebtablesAddForwardAllowIn(ebtablesContext *ctx, + const char *iface, + const unsigned char *mac) +{ + char *macaddr; + + if (virAsprintf(&macaddr, + "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], + mac[2], mac[3], + mac[4], mac[5]) < 0) { + return -1; + } + return ebtablesForwardAllowIn(ctx, iface, macaddr, ADD); +} + +/** + * ebtablesRemoveForwardAllowIn: + * @ctx: pointer to the EB table context + * @iface: the output interface name + * @physdev: the physical input device or NULL + * + * Remove rules from the EB table context hence forbidding the traffic + * on the @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 + */ +int +ebtablesRemoveForwardAllowIn(ebtablesContext *ctx, + const char *iface, + const unsigned char *mac) +{ + char *macaddr; + + if (virAsprintf(&macaddr, + "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], + mac[2], mac[3], + mac[4], mac[5]) < 0) { + return -1; + } + return ebtablesForwardAllowIn(ctx, iface, macaddr, REMOVE); +} diff --git a/src/util/ebtables.h b/src/util/ebtables.h new file mode 100644 index 0000000000..cafe41817f --- /dev/null +++ b/src/util/ebtables.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009 IBM Corp. + * Copyright (C) 2007, 2008 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 + * + * based on iptables.h + * Authors: + * Gerhard Stenzel + */ + +#ifndef __QEMUD_EBTABLES_H__ +#define __QEMUD_EBTABLES_H__ + +typedef struct +{ + char *rule; + const char **argv; + int command_idx; +} ebtRule; + +typedef struct +{ + char *table; + char *chain; + + int nrules; + ebtRule *rules; + +} ebtRules; + +typedef struct _ebtablesContext ebtablesContext; + +ebtablesContext *ebtablesContextNew (const char *driver); +void ebtablesContextFree (ebtablesContext *ctx); + +void ebtablesSaveRules (ebtablesContext *ctx); + +int ebtablesAddForwardAllowIn (ebtablesContext *ctx, + const char *iface, + const unsigned char *mac); +int ebtablesRemoveForwardAllowIn (ebtablesContext *ctx, + const char *iface, + const unsigned char *mac); + +int ebtablesAddForwardPolicyReject(ebtablesContext *ctx); + +int ebtablesRemoveForwardPolicyReject(ebtablesContext *ctx); + +int ebtablesForwardPolicyReject(ebtablesContext *ctx, + int action); + +#endif /* __QEMUD_ebtabLES_H__ */