[libvirt] [RFC PATCH 0/3] Add MAC filtering to qemu

The following series of patches is a prototype implementation of a ebtables based MAC address filter. I hope to have addressed all the previous comments and I am still interested to get more feedback/comments. At the moment, some defaults are set when libvirtd starts and when a domain is started or destroyed. The plan is to extend this filter capability to the API level and virsh command level. The plan is also to add more filtering features like VLAN filtering and QoS filtering. Thanks, Gerhard --- Gerhard Stenzel (3): add MAC address based port filtering to qemu add build support for ebtables wrapper add ebtables wrapper configure.in | 3 src/Makefile.am | 1 src/libvirt_private.syms | 27 + src/qemu/qemu.conf | 3 src/qemu/qemu_conf.c | 85 ++++ src/qemu/qemu_conf.h | 8 src/qemu/qemu_driver.c | 44 ++ src/util/ebtables.c | 982 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/ebtables.h | 134 ++++++ 9 files changed, 1287 insertions(+), 0 deletions(-) create mode 100644 src/util/ebtables.c create mode 100644 src/util/ebtables.h --

This patch adds the files which implement the ebtables wrapper. Signed-off-by: Gerhard Stenzel <gerhard.stenzel@de.ibm.com> --- src/util/ebtables.c | 982 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/util/ebtables.h | 134 +++++++ 2 files changed, 1116 insertions(+), 0 deletions(-) create mode 100644 src/util/ebtables.c create mode 100644 src/util/ebtables.h diff --git a/src/util/ebtables.c b/src/util/ebtables.c new file mode 100644 index 0000000..493b094 --- /dev/null +++ b/src/util/ebtables.c @@ -0,0 +1,982 @@ +/* + * 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 <gerhard.stenzel@de.ibm.com> + */ + +#include <config.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 <sys/types.h> +#include <sys/stat.h> + +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif + +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + +#include "internal.h" +#include "ebtables.h" +#include "util.h" +#include "memory.h" +#include "virterror_internal.h" +#include "logging.h" + +enum { + ADD = 0, + REMOVE, + CREATE, + POLICY, + INSERT +}; + + +#ifdef ENABLE_EBTABLES_LOKKIT +static void +notifyRulesUpdated(const char *table, + const char *path) +{ + char arg[PATH_MAX]; + const char *argv[4]; + + snprintf(arg, sizeof(arg), "--custom-rules=ipv4:%s:%s", table, path); + + argv[0] = (char *) LOKKIT_PATH; + argv[1] = (char *) "--nostart"; + argv[2] = arg; + argv[3] = NULL; + + char ebuf[1024]; + if (virRun(NULL, argv, NULL) < 0) + VIR_WARN(_("Failed to run '%s %s': %s"), + LOKKIT_PATH, arg, virStrerror(errno, ebuf, sizeof ebuf)); +} + +static int +stripLine(char *str, int len, const char *line) +{ + char *s, *p; + int changed; + + changed = 0; + s = str; + + while ((p = strchr(s, '\n'))) { + if (p == s || STRNEQLEN(s, line, p - s)) { + s = ++p; + continue; + } + + ++p; + memmove(s, p, len - (p - str) + 1); + len -= p - s; + changed = 1; + } + + if (STREQ(s, line)) { + *s = '\0'; + changed = 1; + } + + return changed; +} + +static void +notifyRulesRemoved(const char *table, + const char *path) +{ +/* 10 MB limit on config file size as a sanity check */ +#define MAX_FILE_LEN (1024*1024*10) + + char arg[PATH_MAX]; + char *content; + int len; + FILE *f = NULL; + + len = virFileReadAll(SYSCONF_DIR "/sysconfig/system-config-firewall", + MAX_FILE_LEN, &content); + if (len < 0) { + VIR_WARN("%s", _("Failed to read " SYSCONF_DIR + "/sysconfig/system-config-firewall")); + return; + } + + snprintf(arg, sizeof(arg), "--custom-rules=ipv4:%s:%s", table, path); + + if (!stripLine(content, len, arg)) { + VIR_FREE(content); + return; + } + + if (!(f = fopen(SYSCONF_DIR "/sysconfig/system-config-firewall", "w"))) + goto write_error; + + if (fputs(content, f) == EOF) + goto write_error; + + if (fclose(f) == EOF) { + f = NULL; + goto write_error; + } + + VIR_FREE(content); + + return; + + write_error:; + char ebuf[1024]; + VIR_WARN(_("Failed to write to " SYSCONF_DIR + "/sysconfig/system-config-firewall : %s"), + virStrerror(errno, ebuf, sizeof ebuf)); + if (f) + fclose(f); + VIR_FREE(content); + +#undef MAX_FILE_LEN +} + +static int +writeRules(const char *path, + const ebtRule *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].rule, 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; +} +#endif /* ENABLE_EBTABLES_LOKKIT */ + +static void +ebtRulesSave(ebtRules *rules) +{ +#ifdef ENABLE_EBTABLES_LOKKIT + int err; + + char ebuf[1024]; + if ((err = virFileMakePath(rules->dir))) { + VIR_WARN(_("Failed to create directory %s : %s"), + rules->dir, virStrerror(err, ebuf, sizeof ebuf)); + return; + } + + if ((err = writeRules(rules->path, rules->rules, rules->nrules))) { + VIR_WARN(_("Failed to saves ebtables rules to %s : %s"), + rules->path, virStrerror(err, ebuf, sizeof ebuf)); + return; + } + + if (rules->nrules > 0) + notifyRulesUpdated(rules->table, rules->path); + else + notifyRulesRemoved(rules->table, rules->path); +#else + (void) rules; +#endif /* ENABLE_EBTABLES_LOKKIT */ +} + +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; + } + +#ifdef ENABLE_EBTABLES_LOKKIT + rules->dir[0] = '\0'; + rules->path[0] = '\0'; +#endif /* ENABLE_EBTABLES_LOKKIT */ + + 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; + +#ifdef ENABLE_EBTABLES_LOKKIT + if (virFileBuildPath(LOCAL_STATE_DIR "/lib/libvirt/ebtables", table, NULL, + rules->dir, sizeof(rules->dir)) < 0) + goto error; + + if (virFileBuildPath(rules->dir, chain, ".chain", rules->path, sizeof(rules->path)) < 0) + goto error; +#endif /* ENABLE_EBTABLES_LOKKIT */ + + 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; + +/* + if (!(argv[n++] = strdup("--table"))) + goto error; + + if (!(argv[n++] = strdup(rules->table))) + 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 IP table context + * + * Free the resources associated with an IP 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 IP table context + * + * Saves all the IP 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); +} + +static void +ebtRulesReload(ebtRules *rules) +{ + int i; + char ebuf[1024]; + + for (i = 0; i < rules->nrules; i++) { + ebtRule *rule = &rules->rules[i]; + const char *orig; + + orig = rule->argv[rule->command_idx]; + rule->argv[rule->command_idx] = (char *) "--delete"; + + if (virRun(NULL, rule->argv, NULL) < 0) + VIR_WARN(_("Failed to remove ebtables rule '%s'" + " from chain '%s' in table '%s': %s"), + rule->rule, rules->chain, rules->table, + virStrerror(errno, ebuf, sizeof ebuf)); + + rule->argv[rule->command_idx] = orig; + } + + for (i = 0; i < rules->nrules; i++) + if (virRun(NULL, rules->rules[i].argv, NULL) < 0) + VIR_WARN(_("Failed to add ebtables rule '%s'" + " to chain '%s' in table '%s': %s"), + rules->rules[i].rule, rules->chain, rules->table, + virStrerror(errno, ebuf, sizeof ebuf)); +} + +/** + * ebtablesReloadRules: + * @ctx: pointer to the IP table context + * + * Reloads all the IP table rules associated to a context + */ +void +ebtablesReloadRules(ebtablesContext *ctx) +{ + ebtRulesReload(ctx->input_filter); + ebtRulesReload(ctx->forward_filter); + ebtRulesReload(ctx->nat_postrouting); +} + +/* +static int +ebtablesInput(ebtablesContext *ctx, + const char *iface, + int port, + int action, + int tcp) +{ + char portstr[32]; + + snprintf(portstr, sizeof(portstr), "%d", port); + portstr[sizeof(portstr) - 1] = '\0'; + + return ebtablesAddRemoveRule(ctx->input_filter, + action, + "--in-interface", iface, + "--protocol", tcp ? "tcp" : "udp", + "--destination-port", portstr, + "--jump", "ACCEPT", + NULL); +} +*/ +/* Allow all traffic coming from the bridge, with a valid network address + * to proceed to WAN + */ +static int +ebtablesForwardAllowOut(ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev, + int action) +{ + if (physdev && physdev[0]) { + return ebtablesAddRemoveRule(ctx->forward_filter, + action, + "--source", network, + "--in-interface", iface, + "--out-interface", physdev, + "--jump", "ACCEPT", + NULL); + } else { + return ebtablesAddRemoveRule(ctx->forward_filter, + action, + "--source", network, + "--in-interface", iface, + "--jump", "ACCEPT", + NULL); + } +} + +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); +} + +/** + * ebtablesAddForwardAllowOut: + * @ctx: pointer to the IP table context + * @network: the source network name + * @iface: the source interface name + * @physdev: the physical output device + * + * 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 + */ +int +ebtablesAddForwardAllowOut(ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev) +{ + return ebtablesForwardAllowOut(ctx, network, iface, physdev, ADD); +} + +/** + * ebtablesRemoveForwardAllowOut: + * @ctx: pointer to the IP table context + * @network: the source network name + * @iface: the source interface name + * @physdev: the physical output device + * + * 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 + */ +int +ebtablesRemoveForwardAllowOut(ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev) +{ + return ebtablesForwardAllowOut(ctx, network, iface, physdev, REMOVE); +} + + +/* Allow all traffic destined to the bridge, with a valid network address + * and associated with an existing connection + */ +static int +ebtablesForwardAllowRelatedIn(ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev, + int action) +{ + if (physdev && physdev[0]) { + return ebtablesAddRemoveRule(ctx->forward_filter, + action, + "--destination", network, + "--in-interface", physdev, + "--out-interface", iface, + "--match", "state", + "--state", "ESTABLISHED,RELATED", + "--jump", "ACCEPT", + NULL); + } else { + return ebtablesAddRemoveRule(ctx->forward_filter, + action, + "--destination", network, + "--out-interface", iface, + "--match", "state", + "--state", "ESTABLISHED,RELATED", + "--jump", "ACCEPT", + NULL); + } +} + +/** + * ebtablesAddForwardAllowRelatedIn: + * @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 +ebtablesAddForwardAllowRelatedIn(ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev) +{ + return ebtablesForwardAllowRelatedIn(ctx, network, iface, physdev, ADD); +} + +/** + * ebtablesRemoveForwardAllowRelatedIn: + * @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 +ebtablesRemoveForwardAllowRelatedIn(ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev) +{ + return ebtablesForwardAllowRelatedIn(ctx, network, iface, physdev, REMOVE); +} + +/* 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, +/* "--destination", network,*/ +/* "--insert", "FORWARD",*/ + "--in-interface", iface, +/* "--out-interface", network, */ + "--source", macaddr, + "--jump", "ACCEPT", + NULL); +} + +/** + * ebtablesAddForwardAllowIn: + * @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. This allow 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 char *physdev) +{ + return ebtablesForwardAllowIn(ctx, iface, physdev, ADD); +} + +/** + * ebtablesRemoveForwardAllowIn: + * @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. 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 char *physdev) +{ + return ebtablesForwardAllowIn(ctx, iface, physdev, REMOVE); +} + + +/* Allow all traffic between guests on the same bridge, + * with a valid network address + */ +static int +ebtablesForwardAllowCross(ebtablesContext *ctx, + const char *iface, + int action) +{ + return ebtablesAddRemoveRule(ctx->forward_filter, + action, + "--in-interface", iface, + "--out-interface", iface, + "--jump", "ACCEPT", + NULL); +} + +/** + * ebtablesAddForwardAllowCross: + * @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 + */ +int +ebtablesAddForwardAllowCross(ebtablesContext *ctx, + const char *iface) { + return ebtablesForwardAllowCross(ctx, iface, ADD); +} + +/** + * ebtablesRemoveForwardAllowCross: + * @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 + */ +int +ebtablesRemoveForwardAllowCross(ebtablesContext *ctx, + const char *iface) { + return ebtablesForwardAllowCross(ctx, iface, REMOVE); +} + + +/* Drop all traffic trying to forward from the bridge. + * ie the bridge is the in interface + */ +static int +ebtablesForwardRejectOut(ebtablesContext *ctx, + const char *iface, + int action) +{ + return ebtablesAddRemoveRule(ctx->forward_filter, + action, + "--in-interface", iface, + "--jump", "DROP", + NULL); +} + +/** + * ebtablesAddForwardRejectOut: + * @ctx: pointer to the EB table context + * @iface: the output interface name + * + * Add rules to the EB 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 + */ +int +ebtablesAddForwardRejectOut(ebtablesContext *ctx, + const char *iface) +{ + return ebtablesForwardRejectOut(ctx, iface, ADD); +} + +/** + * ebtablesRemoveForwardRejectOut: + * @ctx: pointer to the EB table context + * @iface: the output interface name + * + * Remove rules from the EB 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 + */ +int +ebtablesRemoveForwardRejectOut(ebtablesContext *ctx, + const char *iface) +{ + return ebtablesForwardRejectOut(ctx, iface, REMOVE); +} + + + + +/* Drop all traffic trying to forward to the bridge. + * ie the bridge is the out interface + */ +static int +ebtablesForwardRejectIn(ebtablesContext *ctx, + const char *iface, + int action) +{ + return ebtablesAddRemoveRule(ctx->forward_filter, + action, + "--out-interface", iface, + "--jump", "DROP", + NULL); +} + +/** + * ebtablesAddForwardRejectIn: + * @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 + */ +int +ebtablesAddForwardRejectIn(ebtablesContext *ctx, + const char *iface) +{ + return ebtablesForwardRejectIn(ctx, iface, ADD); +} + +/** + * ebtablesRemoveForwardRejectIn: + * @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 + */ +int +ebtablesRemoveForwardRejectIn(ebtablesContext *ctx, + const char *iface) +{ + return ebtablesForwardRejectIn(ctx, iface, REMOVE); +} diff --git a/src/util/ebtables.h b/src/util/ebtables.h new file mode 100644 index 0000000..a3f403a --- /dev/null +++ b/src/util/ebtables.h @@ -0,0 +1,134 @@ +/* + * 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 <gerhard.stenzel@de.ibm.com> + */ + +#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; + +#ifdef ENABLE_EBTABLES_LOKKIT + + char dir[PATH_MAX]; + char path[PATH_MAX]; + +#endif /* ENABLE_EBTABLES_LOKKIT */ + +} ebtRules; + +struct _ebtablesContext +{ + ebtRules *input_filter; + ebtRules *forward_filter; + ebtRules *nat_postrouting; +}; + +typedef struct _ebtablesContext ebtablesContext; + +ebtablesContext *ebtablesContextNew (const char *driver); +void ebtablesContextFree (ebtablesContext *ctx); + +void ebtablesSaveRules (ebtablesContext *ctx); +void ebtablesReloadRules (ebtablesContext *ctx); + +int ebtablesAddTcpInput (ebtablesContext *ctx, + const char *iface, + int port); +int ebtablesRemoveTcpInput (ebtablesContext *ctx, + const char *iface, + int port); + +int ebtablesAddUdpInput (ebtablesContext *ctx, + const char *iface, + int port); +int ebtablesRemoveUdpInput (ebtablesContext *ctx, + const char *iface, + int port); + +int ebtablesAddForwardAllowOut (ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev); +int ebtablesRemoveForwardAllowOut (ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev); + +int ebtablesAddForwardAllowRelatedIn(ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev); +int ebtablesRemoveForwardAllowRelatedIn(ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev); + +int ebtablesAddForwardAllowIn (ebtablesContext *ctx, + const char *iface, + const char *physdev); +int ebtablesRemoveForwardAllowIn (ebtablesContext *ctx, + const char *iface, + const char *physdev); + +int ebtablesAddForwardAllowCross (ebtablesContext *ctx, + const char *iface); +int ebtablesRemoveForwardAllowCross (ebtablesContext *ctx, + const char *iface); + +int ebtablesAddForwardRejectOut (ebtablesContext *ctx, + const char *iface); +int ebtablesRemoveForwardRejectOut (ebtablesContext *ctx, + const char *iface); + +int ebtablesAddForwardRejectIn (ebtablesContext *ctx, + const char *iface); +int ebtablesRemoveForwardRejectIn (ebtablesContext *ctx, + const char *iface); + +int ebtablesAddForwardMasquerade (ebtablesContext *ctx, + const char *network, + const char *physdev); +int ebtablesRemoveForwardMasquerade (ebtablesContext *ctx, + const char *network, + const char *physdev); + +int ebtablesAddForwardPolicyReject(ebtablesContext *ctx); + +int ebtablesRemoveForwardPolicyReject(ebtablesContext *ctx); + +int ebtablesForwardPolicyReject(ebtablesContext *ctx, + int action); + +#endif /* __QEMUD_ebtabLES_H__ */

On Tue, Oct 13, 2009 at 12:57:13PM +0200, Gerhard Stenzel wrote:
+#ifdef ENABLE_EBTABLES_LOKKIT +static void +notifyRulesUpdated(const char *table, + const char *path) +{ + char arg[PATH_MAX]; + const char *argv[4]; + + snprintf(arg, sizeof(arg), "--custom-rules=ipv4:%s:%s", table, path); + + argv[0] = (char *) LOKKIT_PATH; + argv[1] = (char *) "--nostart"; + argv[2] = arg; + argv[3] = NULL;
This is better declared upfront as const char *const argv[] = { LOKKIT_PATH, "--nostart", arg, NULL, };
+ + char ebuf[1024]; + if (virRun(NULL, argv, NULL) < 0) + VIR_WARN(_("Failed to run '%s %s': %s"), + LOKKIT_PATH, arg, virStrerror(errno, ebuf, sizeof ebuf)); +}
+ +static void +notifyRulesRemoved(const char *table, + const char *path) +{ +/* 10 MB limit on config file size as a sanity check */ +#define MAX_FILE_LEN (1024*1024*10) + + char arg[PATH_MAX]; + char *content; + int len; + FILE *f = NULL; + + len = virFileReadAll(SYSCONF_DIR "/sysconfig/system-config-firewall", + MAX_FILE_LEN, &content); + if (len < 0) { + VIR_WARN("%s", _("Failed to read " SYSCONF_DIR + "/sysconfig/system-config-firewall")); + return; + } + + snprintf(arg, sizeof(arg), "--custom-rules=ipv4:%s:%s", table, path); + + if (!stripLine(content, len, arg)) { + VIR_FREE(content); + return; + } + + if (!(f = fopen(SYSCONF_DIR "/sysconfig/system-config-firewall", "w"))) + goto write_error; + + if (fputs(content, f) == EOF) + goto write_error; + + if (fclose(f) == EOF) { + f = NULL; + goto write_error; + }
This fopen/fputs/fclose triple could be nicely replaced with a single call to if (virFileWriteStr(SYSCONF_DIR "/sysconfig/system-config-firewall", content) < 0) goto write_error;
+ + VIR_FREE(content); + + return; + + write_error:; + char ebuf[1024]; + VIR_WARN(_("Failed to write to " SYSCONF_DIR + "/sysconfig/system-config-firewall : %s"), + virStrerror(errno, ebuf, sizeof ebuf)); + if (f) + fclose(f); + VIR_FREE(content); + +#undef MAX_FILE_LEN +}
+ +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; + +#ifdef ENABLE_EBTABLES_LOKKIT + if (virFileBuildPath(LOCAL_STATE_DIR "/lib/libvirt/ebtables", table, NULL, + rules->dir, sizeof(rules->dir)) < 0) + goto error; + + if (virFileBuildPath(rules->dir, chain, ".chain", rules->path, sizeof(rules->path)) < 0) + goto error; +#endif /* ENABLE_EBTABLES_LOKKIT */
Don't forget to add a rule to src/Makefile.am to ensure that the new ebtables directory is created by the 'install-data-local' rule
index 0000000..a3f403a --- /dev/null +++ b/src/util/ebtables.h @@ -0,0 +1,134 @@ +/* + * 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 <gerhard.stenzel@de.ibm.com> + */ + +#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; + +#ifdef ENABLE_EBTABLES_LOKKIT + + char dir[PATH_MAX]; + char path[PATH_MAX]; + +#endif /* ENABLE_EBTABLES_LOKKIT */ + +} ebtRules; + +struct _ebtablesContext +{ + ebtRules *input_filter; + ebtRules *forward_filter; + ebtRules *nat_postrouting; +};
Since the caller of the API does not need to know the internals of the struct, it is better to move these 3 struct definitions into the ebtables.c file.
+typedef struct _ebtablesContext ebtablesContext; + +ebtablesContext *ebtablesContextNew (const char *driver); +void ebtablesContextFree (ebtablesContext *ctx); + +void ebtablesSaveRules (ebtablesContext *ctx); +void ebtablesReloadRules (ebtablesContext *ctx); + +int ebtablesAddTcpInput (ebtablesContext *ctx, + const char *iface, + int port); +int ebtablesRemoveTcpInput (ebtablesContext *ctx, + const char *iface, + int port); + +int ebtablesAddUdpInput (ebtablesContext *ctx, + const char *iface, + int port); +int ebtablesRemoveUdpInput (ebtablesContext *ctx, + const char *iface, + int port); + +int ebtablesAddForwardAllowOut (ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev); +int ebtablesRemoveForwardAllowOut (ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev); + +int ebtablesAddForwardAllowRelatedIn(ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev); +int ebtablesRemoveForwardAllowRelatedIn(ebtablesContext *ctx, + const char *network, + const char *iface, + const char *physdev); + +int ebtablesAddForwardAllowIn (ebtablesContext *ctx, + const char *iface, + const char *physdev); +int ebtablesRemoveForwardAllowIn (ebtablesContext *ctx, + const char *iface, + const char *physdev); + +int ebtablesAddForwardAllowCross (ebtablesContext *ctx, + const char *iface); +int ebtablesRemoveForwardAllowCross (ebtablesContext *ctx, + const char *iface); + +int ebtablesAddForwardRejectOut (ebtablesContext *ctx, + const char *iface); +int ebtablesRemoveForwardRejectOut (ebtablesContext *ctx, + const char *iface); + +int ebtablesAddForwardRejectIn (ebtablesContext *ctx, + const char *iface); +int ebtablesRemoveForwardRejectIn (ebtablesContext *ctx, + const char *iface); + +int ebtablesAddForwardMasquerade (ebtablesContext *ctx, + const char *network, + const char *physdev); +int ebtablesRemoveForwardMasquerade (ebtablesContext *ctx, + const char *network, + const char *physdev); + +int ebtablesAddForwardPolicyReject(ebtablesContext *ctx); + +int ebtablesRemoveForwardPolicyReject(ebtablesContext *ctx); + +int ebtablesForwardPolicyReject(ebtablesContext *ctx, + int action);
There seem like there's quite a lot of methods here that we don't actually need for MAC address filtering. It'd be a little clearer if you removed the ones that aren't relevant. Alot of the complexity of the iptables.h/c file on which this is based is relating to the masquerading, and punching holes for DHCP/DNS. ebtables ought to be alot simpler, since we're pretty much just doing a 'ALLOW <mac>' followed by 'DENY ALL' on the tap device in question. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

This patch adds build support for the ebtables wrapper. Signed-off-by: Gerhard Stenzel <gerhard.stenzel@de.ibm.com> --- configure.in | 3 +++ src/Makefile.am | 1 + src/libvirt_private.syms | 27 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 0 deletions(-) diff --git a/configure.in b/configure.in index 518859d..90c42cc 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 d0ef7d1..d9ed610 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 37395ab..6db2a57 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -230,6 +230,33 @@ iptablesRemoveUdpInput; iptablesSaveRules; +# ebtables.h +ebtablesAddForwardAllowCross; +ebtablesAddForwardAllowIn; +ebtablesAddForwardAllowOut; +ebtablesAddForwardAllowRelatedIn; +ebtablesAddForwardMasquerade; +ebtablesAddForwardRejectIn; +ebtablesAddForwardRejectOut; +ebtablesAddTcpInput; +ebtablesAddUdpInput; +ebtablesContextFree; +ebtablesContextNew; +ebtablesReloadRules; +ebtablesRemoveForwardAllowCross; +ebtablesRemoveForwardAllowIn; +ebtablesRemoveForwardAllowOut; +ebtablesRemoveForwardAllowRelatedIn; +ebtablesRemoveForwardMasquerade; +ebtablesRemoveForwardRejectIn; +ebtablesRemoveForwardRejectOut; +ebtablesRemoveTcpInput; +ebtablesRemoveUdpInput; +ebtablesSaveRules; +ebtablesAddForwardPolicyReject; +ebtablesRemoveForwardPolicyReject; +ebtablesForwardPolicyReject; + # libvirt_internal.h virStateInitialize; virStateCleanup;

On Tue, Oct 13, 2009 at 12:57:19PM +0200, Gerhard Stenzel wrote:
This patch adds build support for the ebtables wrapper.
Signed-off-by: Gerhard Stenzel <gerhard.stenzel@de.ibm.com> ---
configure.in | 3 +++ src/Makefile.am | 1 + src/libvirt_private.syms | 27 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/configure.in b/configure.in index 518859d..90c42cc 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 d0ef7d1..d9ed610 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 37395ab..6db2a57 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -230,6 +230,33 @@ iptablesRemoveUdpInput; iptablesSaveRules;
+# ebtables.h +ebtablesAddForwardAllowCross; +ebtablesAddForwardAllowIn; +ebtablesAddForwardAllowOut; +ebtablesAddForwardAllowRelatedIn; +ebtablesAddForwardMasquerade; +ebtablesAddForwardRejectIn; +ebtablesAddForwardRejectOut; +ebtablesAddTcpInput; +ebtablesAddUdpInput; +ebtablesContextFree; +ebtablesContextNew; +ebtablesReloadRules; +ebtablesRemoveForwardAllowCross; +ebtablesRemoveForwardAllowIn; +ebtablesRemoveForwardAllowOut; +ebtablesRemoveForwardAllowRelatedIn; +ebtablesRemoveForwardMasquerade; +ebtablesRemoveForwardRejectIn; +ebtablesRemoveForwardRejectOut; +ebtablesRemoveTcpInput; +ebtablesRemoveUdpInput; +ebtablesSaveRules; +ebtablesAddForwardPolicyReject; +ebtablesRemoveForwardPolicyReject; +ebtablesForwardPolicyReject; + # libvirt_internal.h virStateInitialize; virStateCleanup;
ACK, I'd actually recommend merging this patch with the first one - there isn't a compelling reason for them to be separate. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

This patch adds MAC address based port filtering to the qemu driver. Signed-off-by: Gerhard Stenzel <gerhard.stenzel@de.ibm.com> --- src/qemu/qemu.conf | 3 ++ src/qemu/qemu_conf.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_conf.h | 8 +++++ src/qemu/qemu_driver.c | 44 +++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 6d6b86a..53c4522 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -152,3 +152,6 @@ # in a location of $MOUNTPOINT/libvirt/qemu # hugetlbfs_mount = "/dev/hugepages" + +mac_filter = 1 + diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index ac63570..606152c 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -318,6 +318,20 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, } } + p = virConfGetValue (conf, "mac_filter"); + CHECK_TYPE ("mac_filter", VIR_CONF_LONG); + if (p) { + driver->macFilter = p->l; + if (!(driver->ebtables = ebtablesContextNew("qemu"))) { + driver->macFilter = 0; // TODO: we need to report an error here + } + + if ((errno = networkDisableAllFrames(driver))) { + virReportSystemError(NULL, errno, + _("failed to add rule to drop all frames in '%s'"), __FILE__); + } + } + virConfFree (conf); return 0; } @@ -1119,6 +1133,69 @@ int qemudExtractVersion(virConnectPtr conn, int +networkAddEbtablesRules(struct qemud_driver *driver) { + int err; + + /* Set forward policy to DROP */ + if ((err = ebtablesAddForwardPolicyReject(driver->ebtables))) { + virReportSystemError(NULL, err, + _("failed to add ebtables rule to set default policy to drop on '%s'"), + __FILE__); + return err; + } + ebtablesSaveRules(driver->ebtables); + + return 0; +} + + +int +networkDisableAllFrames(struct qemud_driver *driver) { + int err; + + /* add default rules */ + if ((err = networkAddEbtablesRules(driver))) { + virReportSystemError(NULL, err, + _("cannot filter mac addresses on bridge '%s'"), + __FILE__); + return err; + } + return 0; +} + +static int +networkAllowMacOnPort(virConnectPtr conn, + struct qemud_driver *driver, + char * ifname, + unsigned char * mac) { + + int err; + char *macaddr; + + if (virAsprintf(&macaddr, + "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], + mac[2], mac[3], + mac[4], mac[5]) < 0) { + virReportOOMError(conn); + return -1; + } + /* allow this combination of macaddr and ifname */ + + ebtablesContext * ebtablescontext = driver->ebtables; + if ((err = ebtablesAddForwardAllowIn(ebtablescontext, + ifname, + macaddr))) { + virReportSystemError(conn, err, + _("failed to add ebtables rule to allow routing to '%s'"), + ifname); + } + + return 0; +} + + +int qemudNetworkIfaceConnect(virConnectPtr conn, struct qemud_driver *driver, virDomainNetDefPtr net, @@ -1193,6 +1270,14 @@ qemudNetworkIfaceConnect(virConnectPtr conn, tapfd = -1; } + if (driver->macFilter) { + if ((err = networkAllowMacOnPort(conn, driver, net->ifname, net->mac))) { + virReportSystemError(conn, err, + _("failed to add ebtables rule to allow MAC address on '%s'"), + net->ifname); + } + } + cleanup: VIR_FREE(brname); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index f9a970f..3074ad1 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -26,6 +26,7 @@ #include <config.h> +#include "ebtables.h" #include "internal.h" #include "bridge.h" #include "capabilities.h" @@ -112,6 +113,9 @@ struct qemud_driver { char *hugetlbfs_mount; char *hugepage_path; + unsigned int macFilter : 1; + ebtablesContext *ebtables; + virCapsPtr caps; /* An array of callbacks */ @@ -205,4 +209,8 @@ virDomainDefPtr qemuParseCommandLineString(virConnectPtr conn, virCapsPtr caps, const char *args); + +int networkDisableAllFrames(struct qemud_driver *driver); +int networkAddEbtablesRules(struct qemud_driver *driver); + #endif /* __QEMUD_CONF_H */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 3812f91..8abb19a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2164,6 +2164,37 @@ cleanup: return -1; } +static int +networkDisallowMacOnPort(virConnectPtr conn, + struct qemud_driver *driver, + char * ifname, + unsigned char * mac) { + + int err; + char *macaddr; + + if (virAsprintf(&macaddr, + "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], + mac[2], mac[3], + mac[4], mac[5]) < 0) { + virReportOOMError(conn); + return -1; + } + /* disallow this combination of macaddr and ifname */ + + ebtablesContext * ebtablescontext = driver->ebtables; + if ((err = ebtablesRemoveForwardAllowIn(ebtablescontext, + ifname, + macaddr))) { + virReportSystemError(conn, err, + _("failed to add ebtables rule to allow routing to '%s'"), + ifname); + } + + return 0; +} + static void qemudShutdownVMDaemon(virConnectPtr conn, struct qemud_driver *driver, @@ -2176,6 +2207,19 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, VIR_DEBUG(_("Shutting down VM '%s'\n"), vm->def->name); + if (driver->macFilter) { + int i; + virDomainDefPtr def = vm->def; + for (i = 0 ; i < def->nnets ; i++) { + virDomainNetDefPtr net = def->nets[i]; + if ((errno = networkDisallowMacOnPort(conn, driver, net->ifname, net->mac))) { + virReportSystemError(conn, errno, + _("failed to remove ebtables rule to allow MAC address on '%s'"), + net->ifname); + } + } + } + if (virKillProcess(vm->pid, 0) == 0 && virKillProcess(vm->pid, SIGTERM) < 0) virReportSystemError(conn, errno,

On Tue, Oct 13, 2009 at 12:57:24PM +0200, Gerhard Stenzel wrote:
+static int +networkAllowMacOnPort(virConnectPtr conn, + struct qemud_driver *driver, + char * ifname, + unsigned char * mac) { + + int err; + char *macaddr; + + if (virAsprintf(&macaddr, + "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], + mac[2], mac[3], + mac[4], mac[5]) < 0) { + virReportOOMError(conn); + return -1; + } + /* allow this combination of macaddr and ifname */ + + ebtablesContext * ebtablescontext = driver->ebtables; + if ((err = ebtablesAddForwardAllowIn(ebtablescontext, + ifname, + macaddr))) { + virReportSystemError(conn, err, + _("failed to add ebtables rule to allow routing to '%s'"), + ifname); + }
I think it'd be good if the 'ebtablesAddForwardAllowIn' method actually too a 'unsigned char * mac' array, and did the conversion to printable string format itself. That would reduce the amount of work the QEMU driver needs todo in this case, which would be beneficial if we later add this MAC filtering to other drivers like LXC, UML, etc
+ + return 0; +} + + +int qemudNetworkIfaceConnect(virConnectPtr conn, struct qemud_driver *driver, virDomainNetDefPtr net, @@ -1193,6 +1270,14 @@ qemudNetworkIfaceConnect(virConnectPtr conn, tapfd = -1; }
+ if (driver->macFilter) { + if ((err = networkAllowMacOnPort(conn, driver, net->ifname, net->mac))) { + virReportSystemError(conn, err, + _("failed to add ebtables rule to allow MAC address on '%s'"), + net->ifname); + } + } + cleanup: VIR_FREE(brname);
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index f9a970f..3074ad1 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -26,6 +26,7 @@
#include <config.h>
+#include "ebtables.h" #include "internal.h" #include "bridge.h" #include "capabilities.h" @@ -112,6 +113,9 @@ struct qemud_driver { char *hugetlbfs_mount; char *hugepage_path;
+ unsigned int macFilter : 1; + ebtablesContext *ebtables; + virCapsPtr caps;
/* An array of callbacks */ @@ -205,4 +209,8 @@ virDomainDefPtr qemuParseCommandLineString(virConnectPtr conn, virCapsPtr caps, const char *args);
+ +int networkDisableAllFrames(struct qemud_driver *driver); +int networkAddEbtablesRules(struct qemud_driver *driver); + #endif /* __QEMUD_CONF_H */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 3812f91..8abb19a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2164,6 +2164,37 @@ cleanup: return -1; }
+static int +networkDisallowMacOnPort(virConnectPtr conn, + struct qemud_driver *driver, + char * ifname, + unsigned char * mac) { + + int err; + char *macaddr; + + if (virAsprintf(&macaddr, + "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], + mac[2], mac[3], + mac[4], mac[5]) < 0) { + virReportOOMError(conn); + return -1; + } + /* disallow this combination of macaddr and ifname */ + + ebtablesContext * ebtablescontext = driver->ebtables; + if ((err = ebtablesRemoveForwardAllowIn(ebtablescontext, + ifname, + macaddr))) { + virReportSystemError(conn, err, + _("failed to add ebtables rule to allow routing to '%s'"), + ifname); + } + + return 0; +}
Similar thought here - we should just pass the raw mac address into ebtablesRemoveForwardAllowIn() and have convert to printable format.
+
static void qemudShutdownVMDaemon(virConnectPtr conn, struct qemud_driver *driver, @@ -2176,6 +2207,19 @@ static void qemudShutdownVMDaemon(virConnectPtr conn,
VIR_DEBUG(_("Shutting down VM '%s'\n"), vm->def->name);
+ if (driver->macFilter) { + int i; + virDomainDefPtr def = vm->def; + for (i = 0 ; i < def->nnets ; i++) { + virDomainNetDefPtr net = def->nets[i]; + if ((errno = networkDisallowMacOnPort(conn, driver, net->ifname, net->mac))) {
NB, here you should add a if (net->ifname == NULL) continue; since, not all networking modes have a 'ifname' present
+ virReportSystemError(conn, errno, + _("failed to remove ebtables rule to allow MAC address on '%s'"), + net->ifname); + } + } + } +
All in all, this code looks much nicer now QEMU is calling the ebtables APIs directly Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
participants (2)
-
Daniel P. Berrange
-
Gerhard Stenzel