--- /dev/null
+/*
+ * nwfilter_driver.c: core driver for network filter APIs
+ * (based on storage_driver.c)
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ * Copyright (C) 2010 IBM Corporation
+ * Copyright (C) 2010 Stefan Berger
+ *
+ * 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
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ * Stefan Berger <stefanb@us.ibm.com>
+ */
+
+#include <config.h>
+
+#include "internal.h"
+
+#include "virterror_internal.h"
+#include "datatypes.h"
+#include "memory.h"
+#include "domain_conf.h"
+#include "nwfilter_driver.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_NWFILTER
+
+#define nwfilterLog(msg...) fprintf(stderr, msg)
+
+
+static virNWFilterDriverStatePtr driverState;
+
+static int nwfilterDriverShutdown(void);
+
+static void nwfilterDriverLock(virNWFilterDriverStatePtr driver)
+{
+ virMutexLock(&driver->lock);
+}
+static void nwfilterDriverUnlock(virNWFilterDriverStatePtr driver)
+{
+ virMutexUnlock(&driver->lock);
+}
+
+
+/**
+ * virNWFilterStartup:
+ *
+ * Initialization function for the QEmu daemon
+ */
+static int
+nwfilterDriverStartup(int privileged) {
+ char *base = NULL;
+
+ if (virNWFilterConfLayerInit() < 0)
+ return -1;
+
+ if (VIR_ALLOC(driverState) < 0)
+ goto alloc_err_exit;
+
+ if (virMutexInit(&driverState->lock) < 0)
+ goto alloc_err_exit;
+
+ nwfilterDriverLock(driverState);
+
+ if (privileged) {
+ if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL)
+ goto out_of_memory;
+ } else {
+ uid_t uid = geteuid();
+ char *userdir = virGetUserDirectory(uid);
+
+ if (!userdir)
+ goto error;
+
+ if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) {
+ nwfilterLog("out of memory in virAsprintf");
+ VIR_FREE(userdir);
+ goto out_of_memory;
+ }
+ VIR_FREE(userdir);
+ }
+
+ if (virAsprintf(&driverState->configDir,
+ "%s/nwfilter", base) == -1)
+ goto out_of_memory;
+
+ VIR_FREE(base);
+
+ if (virNWFilterPoolLoadAllConfigs(NULL,
+ &driverState->pools,
+ driverState->configDir) < 0)
+ goto error;
+
+ nwfilterDriverUnlock(driverState);
+
+ return 0;
+
+out_of_memory:
+ nwfilterLog("virNWFilterStartup: out of memory");
+
+error:
+ VIR_FREE(base);
+ nwfilterDriverUnlock(driverState);
+ nwfilterDriverShutdown();
+
+alloc_err_exit:
+ virNWFilterConfLayerShutdown();
+
+ return -1;
+}
+
+/**
+ * virNWFilterReload:
+ *
+ * Function to restart the nwfilter driver, it will recheck the configuration
+ * files and update its state
+ */
+static int
+nwfilterDriverReload(void) {
+ if (!driverState) {
+ return -1;
+ }
+
+ nwfilterDriverLock(driverState);
+ virNWFilterPoolLoadAllConfigs(NULL,
+ &driverState->pools,
+ driverState->configDir);
+ nwfilterDriverUnlock(driverState);
+
+ return 0;
+}
+
+/**
+ * virNWFilterActive:
+ *
+ * Checks if the nwfilter driver is active, i.e. has an active pool
+ *
+ * Returns 1 if active, 0 otherwise
+ */
+static int
+nwfilterDriverActive(void) {
+ if (!driverState->pools.count)
+ return 0;
+ return 1;
+}
+
+/**
+ * virNWFilterShutdown:
+ *
+ * Shutdown the nwfilter driver, it will stop all active nwfilter pools
+ */
+static int
+nwfilterDriverShutdown(void) {
+ if (!driverState)
+ return -1;
+
+ nwfilterDriverLock(driverState);
+
+ /* free inactive pools */
+ virNWFilterPoolObjListFree(&driverState->pools);
+
+ VIR_FREE(driverState->configDir);
+ nwfilterDriverUnlock(driverState);
+ virMutexDestroy(&driverState->lock);
+ VIR_FREE(driverState);
+
+ return 0;
+}
+
+
+static virNWFilterPtr
+nwfilterLookupByUUID(virConnectPtr conn,
+ const unsigned char *uuid) {
+ virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
+ virNWFilterPoolObjPtr pool;
+ virNWFilterPtr ret = NULL;
+
+ nwfilterDriverLock(driver);
+ pool = virNWFilterPoolObjFindByUUID(&driver->pools, uuid);
+ nwfilterDriverUnlock(driver);
+
+ if (!pool) {
+ virNWFilterReportError(conn, VIR_ERR_NO_NWFILTER,
+ "%s", _("no pool with matching uuid"));
+ goto cleanup;
+ }
+
+ ret = virGetNWFilter(conn, pool->def->name, pool->def->uuid);
+
+cleanup:
+ if (pool)
+ virNWFilterPoolObjUnlock(pool);
+ return ret;
+}
+
+
+static virNWFilterPtr
+nwfilterLookupByName(virConnectPtr conn,
+ const char *name) {
+ virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
+ virNWFilterPoolObjPtr pool;
+ virNWFilterPtr ret = NULL;
+
+ nwfilterDriverLock(driver);
+ pool = virNWFilterPoolObjFindByName(&driver->pools, name);
+ nwfilterDriverUnlock(driver);
+
+ if (!pool) {
+ virNWFilterReportError(conn, VIR_ERR_NO_NWFILTER,
+ _("no pool with matching name '%s'"), name);
+ goto cleanup;
+ }
+
+ ret = virGetNWFilter(conn, pool->def->name, pool->def->uuid);
+
+cleanup:
+ if (pool)
+ virNWFilterPoolObjUnlock(pool);
+ return ret;
+}
+
+
+static virDrvOpenStatus
+nwfilterOpen(virConnectPtr conn,
+ virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED) {
+ if (!driverState)
+ return VIR_DRV_OPEN_DECLINED;
+
+ conn->nwfilterPrivateData = driverState;
+ return VIR_DRV_OPEN_SUCCESS;
+}
+
+
+static int
+nwfilterClose(virConnectPtr conn) {
+ conn->nwfilterPrivateData = NULL;
+ return 0;
+}
+
+
+static int
+nwfilterNumNWFilters(virConnectPtr conn) {
+ virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
+ return driver->pools.count;
+}
+
+
+static int
+nwfilterListNWFilters(virConnectPtr conn,
+ char **const names,
+ int nnames) {
+ virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
+ int got = 0, i;
+
+ nwfilterDriverLock(driver);
+ for (i = 0 ; i < driver->pools.count && got < nnames ; i++) {
+ virNWFilterPoolObjLock(driver->pools.objs[i]);
+ if (!(names[got] = strdup(driver->pools.objs[i]->def->name))) {
+ virNWFilterPoolObjUnlock(driver->pools.objs[i]);
+ virReportOOMError();
+ goto cleanup;
+ }
+ got++;
+ virNWFilterPoolObjUnlock(driver->pools.objs[i]);
+ }
+ nwfilterDriverUnlock(driver);
+ return got;
+
+ cleanup:
+ nwfilterDriverUnlock(driver);
+ for (i = 0 ; i < got ; i++)
+ VIR_FREE(names[i]);
+ memset(names, 0, nnames * sizeof(*names));
+ return -1;
+}
+
+
+static virNWFilterPtr
+nwfilterDefine(virConnectPtr conn,
+ const char *xml,
+ unsigned int flags ATTRIBUTE_UNUSED) {
+ virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
+ virNWFilterDefPtr def;
+ virNWFilterPoolObjPtr pool = NULL;
+ virNWFilterPtr ret = NULL;
+
+ nwfilterDriverLock(driver);
+ if (!(def = virNWFilterDefParseString(conn, xml)))
+ goto cleanup;
+
+ if (!(pool = virNWFilterPoolObjAssignDef(conn, &driver->pools, def)))
+ goto cleanup;
+
+ if (virNWFilterPoolObjSaveDef(conn, driver, pool, def) < 0) {
+ virNWFilterPoolObjRemove(&driver->pools, pool);
+ def = NULL;
+ goto cleanup;
+ }
+ def = NULL;
+
+ ret = virGetNWFilter(conn, pool->def->name, pool->def->uuid);
+
+cleanup:
+ virNWFilterDefFree(def);
+ if (pool)
+ virNWFilterPoolObjUnlock(pool);
+ nwfilterDriverUnlock(driver);
+ return ret;
+}
+
+
+static int
+nwfilterUndefine(virNWFilterPtr obj) {
+ virNWFilterDriverStatePtr driver = obj->conn->nwfilterPrivateData;
+ virNWFilterPoolObjPtr pool;
+ int ret = -1;
+
+ nwfilterDriverLock(driver);
+ pool = virNWFilterPoolObjFindByUUID(&driver->pools, obj->uuid);
+ if (!pool) {
+ virNWFilterReportError(obj->conn, VIR_ERR_INVALID_NWFILTER,
+ "%s", _("no nwfilter pool with matching uuid"));
+ goto cleanup;
+ }
+
+ if (virNWFilterTestUnassignDef(obj->conn, pool)) {
+ virNWFilterReportError(obj->conn, VIR_ERR_INVALID_NWFILTER,
+ "%s",
+ _("nwfilter is in use"));
+ goto cleanup;
+ }
+
+ if (virNWFilterPoolObjDeleteDef(obj->conn, pool) < 0)
+ goto cleanup;
+
+ VIR_FREE(pool->configFile);
+
+ virNWFilterPoolObjRemove(&driver->pools, pool);
+ pool = NULL;
+ ret = 0;
+
+cleanup:
+ if (pool)
+ virNWFilterPoolObjUnlock(pool);
+ nwfilterDriverUnlock(driver);
+ return ret;
+}
+
+
+static char *
+nwfilterDumpXML(virNWFilterPtr obj,
+ unsigned int flags ATTRIBUTE_UNUSED) {
+ virNWFilterDriverStatePtr driver = obj->conn->nwfilterPrivateData;
+ virNWFilterPoolObjPtr pool;
+ char *ret = NULL;
+
+ nwfilterDriverLock(driver);
+ pool = virNWFilterPoolObjFindByUUID(&driver->pools, obj->uuid);
+ nwfilterDriverUnlock(driver);
+
+ if (!pool) {
+ virNWFilterReportError(obj->conn, VIR_ERR_INVALID_NWFILTER,
+ "%s", _("no nwfilter pool with matching uuid"));
+ goto cleanup;
+ }
+
+ ret = virNWFilterDefFormat(obj->conn, pool->def);
+
+cleanup:
+ if (pool)
+ virNWFilterPoolObjUnlock(pool);
+ return ret;
+}
+
+
+static virNWFilterDriver nwfilterDriver = {
+ .name = "nwfilter",
+ .open = nwfilterOpen,
+ .close = nwfilterClose,
+ .numOfNWFilters = nwfilterNumNWFilters,
+ .listNWFilters = nwfilterListNWFilters,
+ .nwfilterLookupByName = nwfilterLookupByName,
+ .nwfilterLookupByUUID = nwfilterLookupByUUID,
+ .defineXML = nwfilterDefine,
+ .undefine = nwfilterUndefine,
+ .getXMLDesc = nwfilterDumpXML,
+};
+
+
+static virStateDriver stateDriver = {
+ .name = "NWFilter",
+ .initialize = nwfilterDriverStartup,
+ .cleanup = nwfilterDriverShutdown,
+ .reload = nwfilterDriverReload,
+ .active = nwfilterDriverActive,
+};
+
+int nwfilterRegister(void) {
+ virRegisterNWFilterDriver(&nwfilterDriver);
+ virRegisterStateDriver(&stateDriver);
+ return 0;
+}
--- /dev/null
+/*
+ * nwfilter_ebiptables_driver.c: driver for ebtables/iptables on tap devices
+ *
+ * Copyright (C) 2010 IBM Corp.
+ * Copyright (C) 2010 Stefan Berger
+ *
+ * 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
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ */
+
+#include <config.h>
+
+#include <sys/stat.h>
+
+#include "internal.h"
+
+#include "buf.h"
+#include "memory.h"
+#include "logging.h"
+#include "virterror_internal.h"
+#include "domain_conf.h"
+#include "nwfilter_gentech_driver.h"
+#include "nwfilter_ebiptables_driver.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_NWFILTER
+
+
+#define EBTABLES_DEFAULT_TABLE "nat"
+#define EBTABLES_CHAIN_INCOMING "PREROUTING"
+#define EBTABLES_CHAIN_OUTGOING "POSTROUTING"
+
+#define CHAINPREFIX_HOST_IN 'I'
+#define CHAINPREFIX_HOST_OUT 'O'
+#define CHAINPREFIX_HOST_IN_TEMP 'J'
+#define CHAINPREFIX_HOST_OUT_TEMP 'P'
+
+
+#define CMD_SEPARATOR "\n"
+#define CMD_DEF_PRE "cmd=\""
+#define CMD_DEF_POST "\""
+#define CMD_DEF(X) CMD_DEF_PRE X CMD_DEF_POST
+#define CMD_EXEC "res=`${cmd}`" CMD_SEPARATOR
+#define CMD_STOPONERR(X) \
+ X ? "if [ $? -ne 0 ]; then" \
+ " echo \"Failure to execute command '${cmd}'.\";" \
+ " exit 1;" \
+ "fi" CMD_SEPARATOR \
+ : ""
+
+
+#define EBTABLES_CMD EBTABLES_PATH
+#define BASH_CMD BASH_PATH
+
+#define PRINT_ROOT_CHAIN(buf, prefix, ifname) \
+ snprintf(buf, sizeof(buf), "libvirt-%c-%s", prefix, ifname)
+#define PRINT_CHAIN(buf, prefix, ifname, suffix) \
+ snprintf(buf, sizeof(buf), "%c-%s-%s", prefix, ifname, suffix)
+
+
+static const char *supported_protocols[] = {
+ "ipv4",
+ "arp",
+ NULL,
+};
+
+
+static int
+printVar(virConnectPtr conn,
+ virNWFilterHashTablePtr vars,
+ char *buf, int bufsize,
+ nwItemDescPtr item,
+ int *done)
+{
+ *done = 0;
+
+ if ((item->flags & NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR)) {
+ char *val = (char *)virHashLookup(vars->hashTable, item->var);
+ if (!val) {
+ virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER,
+ _("cannot find value for '%s'"),
+ item->var);
+ return 1;
+ }
+
+ if (!virStrcpy(buf, val, bufsize)) {
+ virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER,
+ _("Buffer to small to print MAC address "
+ "'%s' into"),
+ item->var);
+ return 1;
+ }
+
+ *done = 1;
+ }
+ return 0;
+}
+
+
+static int
+printDataType(virConnectPtr conn,
+ virNWFilterHashTablePtr vars,
+ char *buf, int bufsize,
+ nwItemDescPtr item)
+{
+ int done;
+ if (printVar(conn, vars, buf, bufsize, item, &done))
+ return 1;
+
+ if (done)
+ return 0;
+
+ switch (item->datatype) {
+ case DATATYPE_IPADDR:
+ if (snprintf(buf, bufsize, "%d.%d.%d.%d",
+ item->u.ipaddr.addr.ipv4Addr[0],
+ item->u.ipaddr.addr.ipv4Addr[1],
+ item->u.ipaddr.addr.ipv4Addr[2],
+ item->u.ipaddr.addr.ipv4Addr[3]) >= bufsize) {
+ virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER,
+ _("Buffer too small for IP address"));
+ return 1;
+ }
+ break;
+
+ case DATATYPE_MACADDR:
+ if (bufsize < VIR_MAC_STRING_BUFLEN) {
+ virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER,
+ _("Buffer too small for MAC address"));
+ return 1;
+ }
+
+ virFormatMacAddr(item->u.macaddr.addr, buf);
+ break;
+
+ case DATATYPE_UINT16:
+ if (snprintf(buf, bufsize, "%d",
+ item->u.u16) >= bufsize) {
+ virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER,
+ _("Buffer too small for uint16 type"));
+ return 1;
+ }
+ break;
+
+ case DATATYPE_IPMASK:
+ case DATATYPE_UINT8:
+ if (snprintf(buf, bufsize, "%d",
+ item->u.u8) >= bufsize) {
+ virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER,
+ _("Buffer too small for uint8 type"));
+ return 1;
+ }
+ break;
+
+ default:
+ virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER,
+ _("Unhandled datatype %x"), item->datatype);
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+
+static void
+ebiptablesRuleInstFree(ebiptablesRuleInstPtr inst)
+{
+ if (!inst)
+ return;
+
+ VIR_FREE(inst->commandTemplate);
+ VIR_FREE(inst);
+}
+
+
+static int
+ebiptablesAddRuleInst(virConnectPtr conn,
+ virNWFilterRuleInstPtr res,
+ char *commandTemplate,
+ enum virNWFilterChainSuffixType neededChain,
+ char chainprefix,
+ unsigned int priority)
+{
+ ebiptablesRuleInstPtr inst;
+
+ if (VIR_ALLOC(inst) < 0) {
+ virReportOOMError();
+ return 1;
+ }
+
+ inst->commandTemplate = commandTemplate;
+ inst->neededProtocolChain = neededChain;
+ inst->chainprefix = chainprefix;
+ inst->priority = priority;
+
+ return virNWFilterRuleInstAddData(conn, res, inst);
+}
+
+
+static int
+ebtablesHandleEthHdr(virConnectPtr conn,
+ virBufferPtr buf,
+ virNWFilterHashTablePtr vars,
+ ethHdrDataDefPtr ethHdr)
+{
+ char macaddr[VIR_MAC_STRING_BUFLEN];
+
+ if (HAS_ENTRY_ITEM(ðHdr->dataSrcMACAddr)) {
+ if (printDataType(conn,
+ vars,
+ macaddr, sizeof(macaddr),
+ ðHdr->dataSrcMACAddr))
+ goto err_exit;
+
+ virBufferVSprintf(buf,
+ " -s %s %s",
+ ENTRY_GET_NEG_SIGN(ðHdr->dataSrcMACAddr),
+ macaddr);
+
+ if (HAS_ENTRY_ITEM(ðHdr->dataSrcMACMask)) {
+ if (printDataType(conn,
+ vars,
+ macaddr, sizeof(macaddr),
+ ðHdr->dataSrcMACMask))
+ goto err_exit;
+
+ virBufferVSprintf(buf,
+ "/%s",
+ macaddr);
+ }
+ }
+
+ if (HAS_ENTRY_ITEM(ðHdr->dataDstMACAddr)) {
+ if (printDataType(conn,
+ vars,
+ macaddr, sizeof(macaddr),
+ ðHdr->dataDstMACAddr))
+ goto err_exit;
+
+ virBufferVSprintf(buf,
+ " -d %s %s",
+ ENTRY_GET_NEG_SIGN(ðHdr->dataDstMACAddr),
+ macaddr);
+
+ if (HAS_ENTRY_ITEM(ðHdr->dataDstMACMask)) {
+ if (printDataType(conn,
+ vars,
+ macaddr, sizeof(macaddr),
+ ðHdr->dataDstMACMask))
+ goto err_exit;
+
+ virBufferVSprintf(buf,
+ "/%s",
+ macaddr);
+ }
+ }
+
+ return 0;
+
+ err_exit:
+ virBufferFreeAndReset(buf);
+
+ return 1;
+}
+
+/*
+ * ebtablesCreateRuleInstance:
+ * @conn : Pointer to a virConnect object
+ * @chainPrefix : The prefix to put in front of the name of the chain
+ * @nwfilter : The filter
+ * @rule: The rule of the filter to convert
+ * @ifname : The name of the interface to apply the rule to
+ * @vars : A map containing the variables to resolve
+ * @res : The data structure to store the result(s) into
+ *
+ * Convert a single rule into its representation for later instantiation
+ *
+ * Returns 0 in case of success with the result stored in the data structure
+ * pointed to by res, != 0 otherwise with the error message stored in the
+ * virConnect object.
+ */
+static int
+ebtablesCreateRuleInstance(virConnectPtr conn,
+ char chainPrefix,
+ virNWFilterDefPtr nwfilter,
+ virNWFilterRuleDefPtr rule,
+ const char *ifname,
+ virNWFilterHashTablePtr vars,
+ virNWFilterRuleInstPtr res)
+{
+ char macaddr[VIR_MAC_STRING_BUFLEN],
+ ipaddr[INET_ADDRSTRLEN],
+ number[20];
+ char chain[MAX_CHAINNAME_LENGTH];
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (nwfilter->chainsuffix == VIR_NWFILTER_CHAINSUFFIX_ROOT)
+ PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
+ else
+ PRINT_CHAIN(chain, chainPrefix, ifname,
+ virNWFilterChainSuffixTypeToString(nwfilter->chainsuffix));
+
+
+ switch (rule->prtclType) {
+ case VIR_NWFILTER_RULE_PROTOCOL_MAC:
+
+ virBufferVSprintf(&buf,
+ CMD_DEF_PRE EBTABLES_CMD " -t %s -%%c %s %%s",
+ EBTABLES_DEFAULT_TABLE, chain);
+
+
+ if (ebtablesHandleEthHdr(conn,
+ &buf,
+ vars,
+ &rule->p.ethHdrFilter.ethHdr))
+ goto err_exit;
+
+ if (HAS_ENTRY_ITEM(&rule->p.ethHdrFilter.dataProtocolID)) {
+ if (printDataType(conn,
+ vars,
+ number, sizeof(number),
+ &rule->p.ethHdrFilter.dataProtocolID))
+ goto err_exit;
+ virBufferVSprintf(&buf,
+ " -p %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.ethHdrFilter.dataProtocolID),
+ number);
+ }
+ break;
+
+ case VIR_NWFILTER_RULE_PROTOCOL_ARP:
+
+ virBufferVSprintf(&buf,
+ CMD_DEF_PRE EBTABLES_CMD " -t %s -%%c %s %%s",
+ EBTABLES_DEFAULT_TABLE, chain);
+
+ if (ebtablesHandleEthHdr(conn,
+ &buf,
+ vars,
+ &rule->p.arpHdrFilter.ethHdr))
+ goto err_exit;
+
+ virBufferAddLit(&buf, " -p arp");
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataHWType)) {
+ if (printDataType(conn,
+ vars,
+ number, sizeof(number),
+ &rule->p.arpHdrFilter.dataHWType))
+ goto err_exit;
+ virBufferVSprintf(&buf,
+ " --arp-htype %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataHWType),
+ number);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataOpcode)) {
+ if (printDataType(conn,
+ vars,
+ number, sizeof(number),
+ &rule->p.arpHdrFilter.dataOpcode))
+ goto err_exit;
+ virBufferVSprintf(&buf,
+ " --arp-opcode %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataOpcode),
+ number);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataProtocolType)) {
+ if (printDataType(conn,
+ vars,
+ number, sizeof(number),
+ &rule->p.arpHdrFilter.dataProtocolType))
+ goto err_exit;
+ virBufferVSprintf(&buf,
+ " --arp-ptype %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataProtocolType),
+ number);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataARPSrcIPAddr)) {
+ if (printDataType(conn,
+ vars,
+ ipaddr, sizeof(ipaddr),
+ &rule->p.arpHdrFilter.dataARPSrcIPAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --arp-ip-src %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataARPSrcIPAddr),
+ ipaddr);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataARPDstIPAddr)) {
+ if (printDataType(conn,
+ vars,
+ ipaddr, sizeof(ipaddr),
+ &rule->p.arpHdrFilter.dataARPDstIPAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --arp-ip-dst %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataARPDstIPAddr),
+ ipaddr);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataARPSrcMACAddr)) {
+ if (printDataType(conn,
+ vars,
+ macaddr, sizeof(macaddr),
+ &rule->p.arpHdrFilter.dataARPSrcMACAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --arp-mac-src %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataARPSrcMACAddr),
+ macaddr);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataARPDstMACAddr)) {
+ if (printDataType(conn,
+ vars,
+ macaddr, sizeof(macaddr),
+ &rule->p.arpHdrFilter.dataARPDstMACAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --arp-mac-dst %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataARPDstMACAddr),
+ macaddr);
+ }
+ break;
+
+ case VIR_NWFILTER_RULE_PROTOCOL_IP:
+ virBufferVSprintf(&buf,
+ CMD_DEF_PRE EBTABLES_CMD " -t %s -%%c %s %%s",
+ EBTABLES_DEFAULT_TABLE, chain);
+
+ if (ebtablesHandleEthHdr(conn,
+ &buf,
+ vars,
+ &rule->p.ipHdrFilter.ethHdr))
+ goto err_exit;
+
+ virBufferAddLit(&buf,
+ " -p ipv4");
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataSrcIPAddr)) {
+ if (printDataType(conn,
+ vars,
+ ipaddr, sizeof(ipaddr),
+ &rule->p.ipHdrFilter.ipHdr.dataSrcIPAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --ip-source %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.ipHdrFilter.ipHdr.dataSrcIPAddr),
+ ipaddr);
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataSrcIPMask)) {
+ if (printDataType(conn,
+ vars,
+ number, sizeof(number),
+ &rule->p.ipHdrFilter.ipHdr.dataSrcIPMask))
+ goto err_exit;
+ virBufferVSprintf(&buf,
+ "/%s",
+ number);
+ }
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataDstIPAddr)) {
+
+ if (printDataType(conn,
+ vars,
+ ipaddr, sizeof(ipaddr),
+ &rule->p.ipHdrFilter.ipHdr.dataDstIPAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --ip-destination %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.ipHdrFilter.ipHdr.dataDstIPAddr),
+ ipaddr);
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataDstIPMask)) {
+ if (printDataType(conn,
+ vars,
+ number, sizeof(number),
+ &rule->p.ipHdrFilter.ipHdr.dataDstIPMask))
+ goto err_exit;
+ virBufferVSprintf(&buf,
+ "/%s",
+ number);
+ }
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataProtocolID)) {
+ if (printDataType(conn,
+ vars,
+ number, sizeof(number),
+ &rule->p.ipHdrFilter.ipHdr.dataProtocolID))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --ip-protocol %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.ipHdrFilter.ipHdr.dataProtocolID),
+ number);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.portData.dataSrcPortStart)) {
+
+ if (printDataType(conn,
+ vars,
+ number, sizeof(number),
+ &rule->p.ipHdrFilter.portData.dataSrcPortStart))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --ip-source-port %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.ipHdrFilter.portData.dataSrcPortStart),
+ number);
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.portData.dataSrcPortEnd)) {
+ if (printDataType(conn,
+ vars,
+ number, sizeof(number),
+ &rule->p.ipHdrFilter.portData.dataSrcPortEnd))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ ":%s",
+ number);
+ }
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.portData.dataDstPortStart)) {
+
+ if (printDataType(conn,
+ vars,
+ number, sizeof(number),
+ &rule->p.ipHdrFilter.portData.dataDstPortStart))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --ip-destination-port %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.ipHdrFilter.portData.dataDstPortStart),
+ number);
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.portData.dataDstPortEnd)) {
+ if (printDataType(conn,
+ vars,
+ number, sizeof(number),
+ &rule->p.ipHdrFilter.portData.dataDstPortEnd))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ ":%s",
+ number);
+ }
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataDSCP)) {
+ if (printDataType(conn,
+ vars,
+ number, sizeof(number),
+ &rule->p.ipHdrFilter.ipHdr.dataDSCP))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --ip-tos %s %s",
+ ENTRY_GET_NEG_SIGN(&rule->p.ipHdrFilter.ipHdr.dataDSCP),
+ number);
+ }
+ break;
+
+ case VIR_NWFILTER_RULE_PROTOCOL_NONE:
+ virBufferVSprintf(&buf,
+ CMD_DEF_PRE EBTABLES_CMD " -t %s -%%c %s %%s",
+ EBTABLES_DEFAULT_TABLE, chain);
+ break;
+ }
+
+ virBufferVSprintf(&buf,
+ " -j %s" CMD_DEF_POST CMD_SEPARATOR
+ CMD_EXEC,
+ virNWFilterJumpTargetTypeToString(rule->action));
+
+ if (virBufferError(&buf)) {
+ virBufferFreeAndReset(&buf);
+ virReportOOMError();
+ return -1;
+ }
+
+ return ebiptablesAddRuleInst(conn,
+ res,
+ virBufferContentAndReset(&buf),
+ nwfilter->chainsuffix,
+ chainPrefix,
+ rule->priority);
+
+err_exit:
+ virBufferFreeAndReset(&buf);
+
+ return -1;
+}
+
+
+/*
+ * ebiptablesCreateRuleInstance:
+ * @conn : Pointer to a virConnect object
+ * @nwfilter : The filter
+ * @rule: The rule of the filter to convert
+ * @ifname : The name of the interface to apply the rule to
+ * @vars : A map containing the variables to resolve
+ * @res : The data structure to store the result(s) into
+ *
+ * Convert a single rule into its representation for later instantiation
+ *
+ * Returns 0 in case of success with the result stored in the data structure
+ * pointed to by res, != 0 otherwise with the error message stored in the
+ * virConnect object.
+ */
+static int
+ebiptablesCreateRuleInstance(virConnectPtr conn,
+ enum virDomainNetType nettype ATTRIBUTE_UNUSED,
+ virNWFilterDefPtr nwfilter,
+ virNWFilterRuleDefPtr rule,
+ const char *ifname,
+ virNWFilterHashTablePtr vars,
+ virNWFilterRuleInstPtr res)
+{
+ int rc = 0;
+
+ switch (rule->prtclType) {
+ case VIR_NWFILTER_RULE_PROTOCOL_IP:
+ case VIR_NWFILTER_RULE_PROTOCOL_MAC:
+ case VIR_NWFILTER_RULE_PROTOCOL_ARP:
+ case VIR_NWFILTER_RULE_PROTOCOL_NONE:
+
+ if (rule->tt == VIR_NWFILTER_RULE_DIRECTION_OUT ||
+ rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT) {
+ rc = ebtablesCreateRuleInstance(conn,
+ CHAINPREFIX_HOST_IN_TEMP,
+ nwfilter,
+ rule,
+ ifname,
+ vars,
+ res);
+ if (rc)
+ return rc;
+ }
+
+ if (rule->tt == VIR_NWFILTER_RULE_DIRECTION_IN ||
+ rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT) {
+ rc = ebtablesCreateRuleInstance(conn,
+ CHAINPREFIX_HOST_OUT_TEMP,
+ nwfilter,
+ rule,
+ ifname,
+ vars,
+ res);
+ }
+ break;
+ }
+
+ return rc;
+}
+
+
+static int
+ebiptablesFreeRuleInstance(void *_inst)
+{
+ ebiptablesRuleInstFree((ebiptablesRuleInstPtr)_inst);
+ return 0;
+}
+
+
+static int
+ebiptablesDisplayRuleInstance(virConnectPtr conn ATTRIBUTE_UNUSED,
+ void *_inst)
+{
+ ebiptablesRuleInstPtr inst = (ebiptablesRuleInstPtr)_inst;
+ printf("Command Template: %s\nNeeded protocol: %s\n\n",
+ inst->commandTemplate,
+ virNWFilterChainSuffixTypeToString(inst->neededProtocolChain));
+ return 0;
+}
+
+
+/**
+ * ebiptablesWriteToTempFile:
+ * @conn: pointer to virConnect object
+ * @string : the string to write into the file
+ *
+ * Returns the tempory filename where the string was written into,
+ * NULL in case of error with the error reported.
+ *
+ * Write the string into a temporary file and return the name of
+ * the temporary file. The string is assumed to contain executable
+ * commands. A line '#!/bin/bash' will automatically be written
+ * as the first line in the file. The permissions of the file are
+ * set so that the file can be run as an executable script.
+ */
+static char *
+ebiptablesWriteToTempFile(virConnectPtr conn,
+ const char *string) {
+ char filename[] = "/tmp/virtdXXXXXX";
+ int len;
+ char *filnam;
+ const char header[] = "#!" BASH_CMD "\n";
+ size_t written;
+
+ int fd = mkstemp(filename);
+
+ if (fd < 0) {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s",
+ _("cannot create temporary file"));
+ return NULL;
+ }
+
+ if (fchmod(fd, S_IXUSR| S_IRUSR | S_IWUSR) < 0) {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s",
+ _("cannot change permissions on temp. file"));
+ goto err_exit;
+ }
+
+ len = strlen(header);
+ written = safewrite(fd, header, len);
+ if (written != len) {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s",
+ _("cannot write string to file"));
+ goto err_exit;
+ }
+
+ len = strlen(string);
+ written = safewrite(fd, string, len);
+ if (written != len) {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s",
+ _("cannot write string to file"));
+ goto err_exit;
+ }
+
+ filnam = strdup(filename);
+ if (!filnam) {
+ virReportOOMError();
+ goto err_exit;
+ }
+
+ close(fd);
+ return filnam;
+
+err_exit:
+ close(fd);
+ unlink(filename);
+ return NULL;
+}
+
+
+/**
+ * ebiptablesExecCLI:
+ * @conn : pointer to virConnect object
+ * @buf : pointer to virBuffer containing the string with the commands to
+ * execute.
+ * @status: Pointer to an integer for returning the status of the
+ * commands executed via the script the was run.
+ *
+ * Returns 0 in case of success, != 0 in case of an error. The returned
+ * value is NOT the result of running the commands inside the bash
+ * script.
+ *
+ * Execute a sequence of commands (held in the given buffer) as a bash
+ * script and return the status of the execution.
+ */
+static int
+ebiptablesExecCLI(virConnectPtr conn,
+ virBufferPtr buf,
+ int *status)
+{
+ char *cmds;
+ char *filename;
+ int rc;
+ const char *argv[] = {NULL, NULL};
+
+ if (virBufferError(buf)) {
+ virReportOOMError();
+ virBufferFreeAndReset(buf);
+ return 1;
+ }
+
+ *status = 0;
+
+ cmds = virBufferContentAndReset(buf);
+
+ VIR_DEBUG("%s", cmds);
+
+ if (!cmds)
+ return 0;
+
+ filename = ebiptablesWriteToTempFile(conn, cmds);
+ VIR_FREE(cmds);
+
+ if (!filename)
+ return 1;
+
+ argv[0] = filename;
+ rc = virRun(argv, status);
+
+ *status >>= 8;
+
+ VIR_DEBUG("rc = %d, status = %d\n",rc, *status);
+
+ unlink(filename);
+
+ VIR_FREE(filename);
+
+ return rc;
+}
+
+
+static int
+ebtablesCreateTmpRootChain(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virBufferPtr buf,
+ int incoming, const char *ifname,
+ int stopOnError)
+{
+ char chain[MAX_CHAINNAME_LENGTH];
+ char chainPrefix = (incoming) ? CHAINPREFIX_HOST_IN_TEMP
+ : CHAINPREFIX_HOST_OUT_TEMP;
+
+ PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
+
+ virBufferVSprintf(buf,
+ CMD_DEF(EBTABLES_CMD " -t %s -N %s") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+ EBTABLES_DEFAULT_TABLE, chain,
+ CMD_STOPONERR(stopOnError));
+
+ return 0;
+}
+
+
+static int
+ebtablesLinkTmpRootChain(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virBufferPtr buf,
+ int incoming, const char *ifname,
+ int stopOnError)
+{
+ char chain[MAX_CHAINNAME_LENGTH];
+ char chainPrefix = (incoming) ? CHAINPREFIX_HOST_IN_TEMP
+ : CHAINPREFIX_HOST_OUT_TEMP;
+ char iodev = (incoming) ? 'i' : 'o';
+
+ PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
+
+ virBufferVSprintf(buf,
+ CMD_DEF(EBTABLES_CMD " -t %s -A %s -%c %s -j %s") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+ EBTABLES_DEFAULT_TABLE,
+ (incoming) ? EBTABLES_CHAIN_INCOMING
+ : EBTABLES_CHAIN_OUTGOING,
+ iodev, ifname, chain,
+
+ CMD_STOPONERR(stopOnError));
+
+ return 0;
+}
+
+
+static int
+_ebtablesRemoveRootChain(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virBufferPtr buf,
+ int incoming, const char *ifname,
+ int isTempChain)
+{
+ char chain[MAX_CHAINNAME_LENGTH];
+ char chainPrefix;
+ if (isTempChain)
+ chainPrefix = (incoming) ? CHAINPREFIX_HOST_IN_TEMP
+ : CHAINPREFIX_HOST_OUT_TEMP;
+ else
+ chainPrefix = (incoming) ? CHAINPREFIX_HOST_IN
+ : CHAINPREFIX_HOST_OUT;
+
+ PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
+
+ virBufferVSprintf(buf,
+ EBTABLES_CMD " -t %s -F %s" CMD_SEPARATOR
+ EBTABLES_CMD " -t %s -X %s" CMD_SEPARATOR,
+ EBTABLES_DEFAULT_TABLE, chain,
+ EBTABLES_DEFAULT_TABLE, chain);
+
+ return 0;
+}
+
+
+static int
+ebtablesRemoveRootChain(virConnectPtr conn,
+ virBufferPtr buf,
+ int incoming, const char *ifname)
+{
+ return _ebtablesRemoveRootChain(conn, buf, incoming, ifname, 0);
+}
+
+
+static int
+ebtablesRemoveTmpRootChain(virConnectPtr conn,
+ virBufferPtr buf,
+ int incoming, const char *ifname)
+{
+ return _ebtablesRemoveRootChain(conn, buf, incoming, ifname, 1);
+}
+
+
+static int
+_ebtablesUnlinkRootChain(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virBufferPtr buf,
+ int incoming, const char *ifname,
+ int isTempChain)
+{
+ char chain[MAX_CHAINNAME_LENGTH];
+ char iodev = (incoming) ? 'i' : 'o';
+ char chainPrefix;
+
+ if (isTempChain) {
+ chainPrefix = (incoming) ? CHAINPREFIX_HOST_IN_TEMP
+ : CHAINPREFIX_HOST_OUT_TEMP;
+ } else {
+ chainPrefix = (incoming) ? CHAINPREFIX_HOST_IN
+ : CHAINPREFIX_HOST_OUT;
+ }
+
+ PRINT_ROOT_CHAIN(chain, chainPrefix, ifname);
+
+ virBufferVSprintf(buf,
+ EBTABLES_CMD " -t %s -D %s -%c %s -j %s" CMD_SEPARATOR,
+ EBTABLES_DEFAULT_TABLE,
+ (incoming) ? EBTABLES_CHAIN_INCOMING
+ : EBTABLES_CHAIN_OUTGOING,
+ iodev, ifname, chain);
+
+ return 0;
+}
+
+
+static int
+ebtablesUnlinkRootChain(virConnectPtr conn,
+ virBufferPtr buf,
+ int incoming, const char *ifname)
+{
+ return _ebtablesUnlinkRootChain(conn, buf, incoming, ifname, 0);
+}
+
+
+static int
+ebtablesUnlinkTmpRootChain(virConnectPtr conn,
+ virBufferPtr buf,
+ int incoming, const char *ifname)
+{
+ return _ebtablesUnlinkRootChain(conn, buf, incoming, ifname, 1);
+}
+
+
+static int
+ebtablesCreateTmpSubChain(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virBufferPtr buf,
+ int incoming,
+ const char *ifname,
+ const char *protocol,
+ int stopOnError)
+{
+ char rootchain[MAX_CHAINNAME_LENGTH], chain[MAX_CHAINNAME_LENGTH];
+ char chainPrefix = (incoming) ? CHAINPREFIX_HOST_IN_TEMP
+ : CHAINPREFIX_HOST_OUT_TEMP;
+
+ PRINT_ROOT_CHAIN(rootchain, chainPrefix, ifname);
+ PRINT_CHAIN(chain, chainPrefix, ifname, protocol);
+
+ virBufferVSprintf(buf,
+ CMD_DEF(EBTABLES_CMD " -t %s -N %s") CMD_SEPARATOR
+ CMD_EXEC
+ "%s"
+ CMD_DEF(EBTABLES_CMD " -t %s -A %s -p %s -j %s") CMD_SEPARATOR
+ CMD_EXEC
+ "%s",
+
+ EBTABLES_DEFAULT_TABLE, chain,
+
+ CMD_STOPONERR(stopOnError),
+
+ EBTABLES_DEFAULT_TABLE,
+ rootchain,
+ protocol, chain,
+
+ CMD_STOPONERR(stopOnError));
+
+ return 0;
+}
+
+
+static int
+_ebtablesRemoveSubChain(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virBufferPtr buf,
+ int incoming,
+ const char *ifname,
+ const char *protocol,
+ int isTempChain)
+{
+ char rootchain[MAX_CHAINNAME_LENGTH], chain[MAX_CHAINNAME_LENGTH];
+ char chainPrefix;
+ if (isTempChain) {
+ chainPrefix =(incoming) ? CHAINPREFIX_HOST_IN_TEMP
+ : CHAINPREFIX_HOST_OUT_TEMP;
+ } else {
+ chainPrefix =(incoming) ? CHAINPREFIX_HOST_IN
+ : CHAINPREFIX_HOST_OUT;
+ }
+
+ PRINT_ROOT_CHAIN(rootchain, chainPrefix, ifname);
+ PRINT_CHAIN(chain, chainPrefix, ifname, protocol);
+
+ virBufferVSprintf(buf,
+ EBTABLES_CMD " -t %s -D %s -p %s -j %s" CMD_SEPARATOR
+ EBTABLES_CMD " -t %s -F %s" CMD_SEPARATOR
+ EBTABLES_CMD " -t %s -X %s" CMD_SEPARATOR,
+ EBTABLES_DEFAULT_TABLE,
+ rootchain,
+ protocol, chain,
+
+ EBTABLES_DEFAULT_TABLE, chain,
+
+ EBTABLES_DEFAULT_TABLE, chain);
+
+ return 0;
+}
+
+
+static int
+ebtablesRemoveSubChain(virConnectPtr conn,
+ virBufferPtr buf,
+ int incoming,
+ const char *ifname,
+ const char *protocol)
+{
+ return _ebtablesRemoveSubChain(conn, buf,
+ incoming, ifname, protocol, 0);
+}
+
+
+static int
+ebtablesRemoveSubChains(virConnectPtr conn,
+ virBufferPtr buf,
+ const char *ifname)
+{
+ int i;
+ for (i = 0; supported_protocols[i]; i++) {
+ ebtablesRemoveSubChain(conn, buf, 1, ifname, supported_protocols[i]);
+ ebtablesRemoveSubChain(conn, buf, 0, ifname, supported_protocols[i]);
+ }
+
+ return 0;
+}
+
+
+static int
+ebtablesRemoveTmpSubChain(virConnectPtr conn,
+ virBufferPtr buf,
+ int incoming,
+ const char *ifname,
+ const char *protocol)
+{
+ return _ebtablesRemoveSubChain(conn, buf,
+ incoming, ifname, protocol, 1);
+}
+
+
+static int
+ebtablesRemoveTmpSubChains(virConnectPtr conn,
+ virBufferPtr buf,
+ const char *ifname)
+{
+ int i;
+ for (i = 0; supported_protocols[i]; i++) {
+ ebtablesRemoveTmpSubChain(conn, buf, 1, ifname,
+ supported_protocols[i]);
+ ebtablesRemoveTmpSubChain(conn, buf, 0, ifname,
+ supported_protocols[i]);
+ }
+
+ return 0;
+}
+
+
+static int
+ebtablesRenameTmpSubChain(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virBufferPtr buf,
+ int incoming,
+ const char *ifname,
+ const char *protocol)
+{
+ char tmpchain[MAX_CHAINNAME_LENGTH], chain[MAX_CHAINNAME_LENGTH];
+ char tmpChainPrefix = (incoming) ? CHAINPREFIX_HOST_IN_TEMP
+ : CHAINPREFIX_HOST_OUT_TEMP;
+ char chainPrefix = (incoming) ? CHAINPREFIX_HOST_IN
+ : CHAINPREFIX_HOST_OUT;
+
+ if (protocol) {
+ PRINT_CHAIN(tmpchain, tmpChainPrefix, ifname, protocol);
+ PRINT_CHAIN( chain, chainPrefix, ifname, protocol);
+ } else {
+ PRINT_ROOT_CHAIN(tmpchain, tmpChainPrefix, ifname);
+ PRINT_ROOT_CHAIN( chain, chainPrefix, ifname);
+ }
+
+ virBufferVSprintf(buf,
+ EBTABLES_CMD " -t %s -E %s %s" CMD_SEPARATOR,
+ EBTABLES_DEFAULT_TABLE,
+ tmpchain,
+ chain);
+ return 0;
+}
+
+
+static int
+ebtablesRenameTmpSubChains(virConnectPtr conn,
+ virBufferPtr buf,
+ const char *ifname)
+{
+ int i;
+ for (i = 0; supported_protocols[i]; i++) {
+ ebtablesRenameTmpSubChain (conn, buf, 1, ifname,
+ supported_protocols[i]);
+ ebtablesRenameTmpSubChain (conn, buf, 0, ifname,
+ supported_protocols[i]);
+ }
+
+ return 0;
+}
+
+
+static int
+ebtablesRenameTmpRootChain(virConnectPtr conn,
+ virBufferPtr buf,
+ int incoming,
+ const char *ifname)
+{
+ return ebtablesRenameTmpSubChain(conn, buf, incoming, ifname, NULL);
+}
+
+
+static void
+ebiptablesInstCommand(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virBufferPtr buf,
+ const char *templ, char cmd, int pos,
+ int stopOnError)
+{
+ char position[10] = { 0 };
+ if (pos >= 0)
+ snprintf(position, sizeof(position), "%d", pos);
+ virBufferVSprintf(buf, templ, cmd, position);
+ virBufferVSprintf(buf, CMD_SEPARATOR "%s",
+ CMD_STOPONERR(stopOnError));
+}
+
+
+static int
+ebiptablesRuleOrderSort(const void *a, const void *b)
+{
+ const ebiptablesRuleInstPtr *insta = a;
+ const ebiptablesRuleInstPtr *instb = b;
+ return ((*insta)->priority - (*instb)->priority);
+}
+
+
+static int
+ebiptablesApplyNewRules(virConnectPtr conn,
+ const char *ifname,
+ int nruleInstances,
+ void **_inst)
+{
+ int i;
+ int cli_status;
+ ebiptablesRuleInstPtr *inst = (ebiptablesRuleInstPtr *)_inst;
+ int chains_in = 0, chains_out = 0;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ if (inst)
+ qsort(inst, nruleInstances, sizeof(inst[0]),
+ ebiptablesRuleOrderSort);
+
+ for (i = 0; i < nruleInstances; i++) {
+ if (inst[i]->chainprefix == CHAINPREFIX_HOST_IN_TEMP)
+ chains_in |= (1 << inst[i]->neededProtocolChain);
+ else
+ chains_out |= (1 << inst[i]->neededProtocolChain);
+ }
+
+ ebtablesUnlinkTmpRootChain(conn, &buf, 1, ifname);
+ ebtablesUnlinkTmpRootChain(conn, &buf, 0, ifname);
+ ebtablesRemoveTmpSubChains(conn, &buf, ifname);
+ ebtablesRemoveTmpRootChain(conn, &buf, 1, ifname);
+ ebtablesRemoveTmpRootChain(conn, &buf, 0, ifname);
+
+ if (chains_in != 0)
+ ebtablesCreateTmpRootChain(conn, &buf, 1, ifname, 1);
+ if (chains_out != 0)
+ ebtablesCreateTmpRootChain(conn, &buf, 0, ifname, 1);
+
+ if (chains_in & (1 << VIR_NWFILTER_CHAINSUFFIX_IPv4))
+ ebtablesCreateTmpSubChain(conn, &buf, 1, ifname, "ipv4", 1);
+ if (chains_out & (1 << VIR_NWFILTER_CHAINSUFFIX_IPv4))
+ ebtablesCreateTmpSubChain(conn, &buf, 0, ifname, "ipv4", 1);
+
+ // keep arp as last
+ if (chains_in & (1 << VIR_NWFILTER_CHAINSUFFIX_ARP))
+ ebtablesCreateTmpSubChain(conn, &buf, 1, ifname, "arp", 1);
+ if (chains_out & (1 << VIR_NWFILTER_CHAINSUFFIX_ARP))
+ ebtablesCreateTmpSubChain(conn, &buf, 0, ifname, "arp", 1);
+
+ if (ebiptablesExecCLI(conn, &buf, &cli_status) || cli_status != 0)
+ goto tear_down_tmpebchains;
+
+ for (i = 0; i < nruleInstances; i++)
+ ebiptablesInstCommand(conn, &buf,
+ inst[i]->commandTemplate,
+ 'A', -1, 1);
+
+ if (ebiptablesExecCLI(conn, &buf, &cli_status) || cli_status != 0)
+ goto tear_down_tmpebchains;
+
+ // FIXME: establishment of iptables user define table tree goes here
+
+ // END IPTABLES stuff
+
+ if (chains_in != 0)
+ ebtablesLinkTmpRootChain(conn, &buf, 1, ifname, 1);
+ if (chains_out != 0)
+ ebtablesLinkTmpRootChain(conn, &buf, 0, ifname, 1);
+
+ if (ebiptablesExecCLI(conn, &buf, &cli_status) || cli_status != 0)
+ goto tear_down_ebsubchains_and_unlink;
+
+ return 0;
+
+tear_down_ebsubchains_and_unlink:
+ ebtablesUnlinkTmpRootChain(conn, &buf, 1, ifname);
+ ebtablesUnlinkTmpRootChain(conn, &buf, 0, ifname);
+
+tear_down_tmpebchains:
+ ebtablesRemoveTmpSubChains(conn, &buf, ifname);
+ ebtablesRemoveTmpRootChain(conn, &buf, 1, ifname);
+ ebtablesRemoveTmpRootChain(conn, &buf, 0, ifname);
+
+ ebiptablesExecCLI(conn, &buf, &cli_status);
+
+ virNWFilterReportError(conn, VIR_ERR_BUILD_FIREWALL,
+ "%s",
+ _("Some rules could not be created."));
+
+ return 1;
+}
+
+
+static int
+ebiptablesTearNewRules(virConnectPtr conn,
+ const char *ifname)
+{
+ int cli_status;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ ebtablesUnlinkTmpRootChain(conn, &buf, 1, ifname);
+ ebtablesUnlinkTmpRootChain(conn, &buf, 0, ifname);
+
+ ebtablesRemoveTmpSubChains(conn, &buf, ifname);
+ ebtablesRemoveTmpRootChain(conn, &buf, 1, ifname);
+ ebtablesRemoveTmpRootChain(conn, &buf, 0, ifname);
+
+ ebiptablesExecCLI(conn, &buf, &cli_status);
+
+ return 0;
+}
+
+
+static int
+ebiptablesTearOldRules(virConnectPtr conn,
+ const char *ifname)
+{
+ int cli_status;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ ebtablesUnlinkRootChain(conn, &buf, 1, ifname);
+ ebtablesUnlinkRootChain(conn, &buf, 0, ifname);
+
+ ebtablesRemoveSubChains(conn, &buf, ifname);
+
+ ebtablesRemoveRootChain(conn, &buf, 1, ifname);
+ ebtablesRemoveRootChain(conn, &buf, 0, ifname);
+
+ ebtablesRenameTmpSubChains(conn, &buf, ifname);
+ ebtablesRenameTmpRootChain(conn, &buf, 1, ifname);
+ ebtablesRenameTmpRootChain(conn, &buf, 0, ifname);
+
+ ebiptablesExecCLI(conn, &buf, &cli_status);
+
+ return 0;
+}
+
+
+/**
+ * ebiptablesRemoveRules:
+ * @conn : pointer to virConnect object
+ * @ifname : the name of the interface to which the rules apply
+ * @nRuleInstance : the number of given rules
+ * @_inst : array of rule instantiation data
+ *
+ * Remove all rules one after the other
+ *
+ * Return 0 on success, 1 if execution of one or more cleanup
+ * commands failed.
+ */
+static int
+ebiptablesRemoveRules(virConnectPtr conn,
+ const char *ifname ATTRIBUTE_UNUSED,
+ int nruleInstances,
+ void **_inst)
+{
+ int rc = 0;
+ int cli_status;
+ int i;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ ebiptablesRuleInstPtr *inst = (ebiptablesRuleInstPtr *)_inst;
+
+ for (i = 0; i < nruleInstances; i++)
+ ebiptablesInstCommand(conn, &buf,
+ inst[i]->commandTemplate,
+ 'D', -1,
+ 0);
+
+ if (ebiptablesExecCLI(conn, &buf, &cli_status))
+ goto err_exit;
+
+ if (cli_status) {
+ virNWFilterReportError(conn, VIR_ERR_BUILD_FIREWALL,
+ "%s",
+ _("error while executing CLI commands"));
+ rc = 1;
+ }
+
+err_exit:
+ return rc;
+}
+
+
+/**
+ * ebiptablesAllTeardown:
+ * @ifname : the name of the interface to which the rules apply
+ *
+ * Unconditionally remove all possible user defined tables and rules
+ * that were created for the given interface (ifname).
+ *
+ * Always returns 0.
+ */
+static int
+ebiptablesAllTeardown(const char *ifname)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ int cli_status;
+ virConnectPtr conn = NULL;
+
+ ebtablesUnlinkRootChain(conn, &buf, 1, ifname);
+ ebtablesUnlinkRootChain(conn, &buf, 0, ifname);
+
+ ebtablesRemoveRootChain(conn, &buf, 1, ifname);
+ ebtablesRemoveRootChain(conn, &buf, 0, ifname);
+
+ ebtablesRemoveSubChains(conn, &buf, ifname);
+
+ ebiptablesExecCLI(conn, &buf, &cli_status);
+
+ return 0;
+}
+
+
+virNWFilterTechDriver ebiptables_driver = {
+ .name = EBIPTABLES_DRIVER_ID,
+
+ .createRuleInstance = ebiptablesCreateRuleInstance,
+ .applyNewRules = ebiptablesApplyNewRules,
+ .tearNewRules = ebiptablesTearNewRules,
+ .tearOldRules = ebiptablesTearOldRules,
+ .allTeardown = ebiptablesAllTeardown,
+ .removeRules = ebiptablesRemoveRules,
+ .freeRuleInstance = ebiptablesFreeRuleInstance,
+ .displayRuleInstance = ebiptablesDisplayRuleInstance,
+};
--- /dev/null
+/*
+ * nwfilter_gentech_driver.c: generic technology driver
+ *
+ * Copyright (C) 2010 IBM Corp.
+ * Copyright (C) 2010 Stefan Berger
+ *
+ * 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
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ */
+
+#include <config.h>
+
+#include <stdint.h>
+
+#include "internal.h"
+
+#include "memory.h"
+#include "logging.h"
+#include "datatypes.h"
+#include "domain_conf.h"
+#include "virterror_internal.h"
+#include "nwfilter_gentech_driver.h"
+#include "nwfilter_ebiptables_driver.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_NWFILTER
+
+
+#define NWFILTER_STD_VAR_MAC "MAC"
+
+
+static virNWFilterTechDriverPtr filter_tech_drivers[] = {
+ &ebiptables_driver,
+ NULL
+};
+
+
+virNWFilterTechDriverPtr
+virNWFilterTechDriverForName(const char *name) {
+ int i = 0;
+ while (filter_tech_drivers[i]) {
+ if (STREQ(filter_tech_drivers[i]->name, name))
+ return filter_tech_drivers[i];
+ i++;
+ }
+ return NULL;
+}
+
+
+/**
+ * virNWFilterRuleInstAddData:
+ * @conn : pointer to virConnect object
+ * @res : pointer to virNWFilterRuleInst object collecting the instantiation
+ * data of a single firewall rule.
+ * @data : the opaque data that the driver wants to add
+ *
+ * Add instantiation data to a firewall rule. An instantiated firewall
+ * rule may hold multiple data structure representing its instantiation
+ * data. This may for example be the case if a rule has been defined
+ * for bidirectional traffic and data needs to be added to the incoming
+ * and outgoing chains.
+ *
+ * Returns 0 in case of success, 1 in case of an error with the error
+ * message attached to the virConnect object.
+ */
+int
+virNWFilterRuleInstAddData(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virNWFilterRuleInstPtr res,
+ void *data)
+{
+ if (VIR_REALLOC_N(res->data, res->ndata+1) < 0) {
+ virReportOOMError();
+ return 1;
+ }
+ res->data[res->ndata++] = data;
+ return 0;
+}
+
+
+static void
+virNWFilterRuleInstFree(virNWFilterRuleInstPtr inst)
+{
+ int i;
+ if (!inst)
+ return;
+
+ for (i = 0; i < inst->ndata; i++)
+ inst->techdriver->freeRuleInstance(inst->data[i]);
+
+ VIR_FREE(inst->data);
+ VIR_FREE(inst);
+}
+
+
+/**
+ * virNWFilterVarHashmapAddStdValues:
+ * @conn: Poijter to virConnect object
+ * @tables: pointer to hash tabel to add values to
+ * @macaddr: The string of the MAC address to add to the hash table,
+ * may be NULL
+ *
+ * Returns 0 in case of success, 1 in case an error happened with
+ * error having been reported.
+ *
+ * Adds a couple of standard keys (MAC, IP) to the hash table.
+ */
+static int
+virNWFilterVarHashmapAddStdValues(virConnectPtr conn,
+ virNWFilterHashTablePtr table,
+ char *macaddr)
+{
+ if (macaddr) {
+ if (virHashAddEntry(table->hashTable,
+ NWFILTER_STD_VAR_MAC,
+ macaddr) < 0) {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Could not add variable 'MAC' to hashmap"));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * virNWFilterCreateVarHashmap:
+ * @conn: pointer to virConnect object
+ * @macaddr: pointer to string containing formatted MAC address of interface
+ *
+ * Create a hashmap used for evaluating the firewall rules. Initializes
+ * it with the standard variable 'MAC'.
+ *
+ * Returns pointer to hashmap, NULL if an error occcurred and error message
+ * is attached to the virConnect object.
+ */
+virNWFilterHashTablePtr
+virNWFilterCreateVarHashmap(virConnectPtr conn,
+ char *macaddr) {
+ virNWFilterHashTablePtr table = virNWFilterHashTableCreate(0);
+ if (!table) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ if (virNWFilterVarHashmapAddStdValues(conn, table, macaddr)) {
+ virNWFilterHashTableFree(table);
+ return NULL;
+ }
+ return table;
+}
+
+
+/**
+ * virNWFilterRuleInstantiate:
+ * @conn: pointer to virConnect object
+ * @techdriver: the driver to use for instantiation
+ * @filter: The filter the rule is part of
+ * @rule : The rule that is to be instantiated
+ * @ifname: The name of the interface
+ * @vars: map containing variable names and value used for instantiation
+ *
+ * Returns virNWFilterRuleInst object on success, NULL on error with
+ * error reported.
+ *
+ * Instantiate a single rule. Return a pointer to virNWFilterRuleInst
+ * object that will hold an array of driver-specific data resulting
+ * from the instantiation. Returns NULL on error with error reported.
+ */
+static virNWFilterRuleInstPtr
+virNWFilterRuleInstantiate(virConnectPtr conn,
+ virNWFilterTechDriverPtr techdriver,
+ enum virDomainNetType nettype,
+ virNWFilterDefPtr filter,
+ virNWFilterRuleDefPtr rule,
+ const char *ifname,
+ virNWFilterHashTablePtr vars)
+{
+ int rc;
+ int i;
+ virNWFilterRuleInstPtr ret;
+
+ if (VIR_ALLOC(ret) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ ret->techdriver = techdriver;
+
+ rc = techdriver->createRuleInstance(conn, nettype, filter,
+ rule, ifname, vars, ret);
+
+ if (rc) {
+ for (i = 0; i < ret->ndata; i++)
+ techdriver->freeRuleInstance(ret->data[i]);
+ VIR_FREE(ret);
+ ret = NULL;
+ }
+
+ return ret;
+}
+
+
+/**
+ * virNWFilterCreateVarsFrom:
+ * @conn: pointer to virConnect object
+ * @vars1: pointer to hash table
+ * @vars2: pointer to hash table
+ *
+ * Returns pointer to new hashtable or NULL in case of error with
+ * error already reported.
+ *
+ * Creates a new hash table with contents of var1 and var2 added where
+ * contents of var2 will overwrite those of var1.
+ */
+static virNWFilterHashTablePtr
+virNWFilterCreateVarsFrom(virConnectPtr conn,
+ virNWFilterHashTablePtr vars1,
+ virNWFilterHashTablePtr vars2)
+{
+ virNWFilterHashTablePtr res = virNWFilterHashTableCreate(0);
+ if (!res) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ if (virNWFilterHashTablePutAll(conn, vars1, res))
+ goto err_exit;
+
+ if (virNWFilterHashTablePutAll(conn, vars2, res))
+ goto err_exit;
+
+ return res;
+
+err_exit:
+ virNWFilterHashTableFree(res);
+ return NULL;
+}
+
+
+/**
+ * _virNWFilterPoolInstantiateRec:
+ * @conn: pointer to virConnect object
+ * @techdriver: The driver to use for instantiation
+ * @filter: The filter to instantiate
+ * @ifname: The name of the interface to apply the rules to
+ * @vars: A map holding variable names and values used for instantiating
+ * the filter and its subfilters.
+ * @nEntries: number of virNWFilterInst objects collected
+ * @insts: pointer to array for virNWFilterIns object pointers
+ * @useNewFilter: instruct whether to use a newDef pointer rather than a
+ * def ptr which is useful during a filter update
+ * @foundNewFilter: pointer to int indivating whether a newDef pointer was
+ * ever used; variable expected to be initialized to 0 by caller
+ *
+ * Returns 0 on success, a value otherwise.
+ *
+ * Recursively instantiate a filter by instantiating the given filter along
+ * with all its subfilters in a depth-first traversal of the tree of
+ * referenced filters. The name of the interface to which the rules belong
+ * must be provided. Apply the values of variables as needed. Terminate with
+ * error when a referenced filter is missing or a variable could not be
+ * resolved -- among other reasons.
+ */
+static int
+_virNWFilterInstantiateRec(virConnectPtr conn,
+ virNWFilterTechDriverPtr techdriver,
+ enum virDomainNetType nettype,
+ virNWFilterDefPtr filter,
+ const char *ifname,
+ virNWFilterHashTablePtr vars,
+ int *nEntries,
+ virNWFilterRuleInstPtr **insts,
+ enum instCase useNewFilter, int *foundNewFilter)
+{
+ virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
+ virNWFilterPoolObjPtr obj;
+ int rc = 0;
+ int i;
+ virNWFilterRuleInstPtr inst;
+ virNWFilterDefPtr next_filter;
+
+ for (i = 0; i < filter->nentries; i++) {
+ virNWFilterRuleDefPtr rule = filter->filterEntries[i]->rule;
+ virNWFilterIncludeDefPtr inc = filter->filterEntries[i]->include;
+ if (rule) {
+ inst = virNWFilterRuleInstantiate(conn,
+ techdriver,
+ nettype,
+ filter,
+ rule,
+ ifname,
+ vars);
+ if (!inst) {
+ rc = 1;
+ break;
+ }
+
+ if (VIR_REALLOC_N(*insts, (*nEntries)+1) < 0) {
+ virReportOOMError();
+ rc = 1;
+ break;
+ }
+
+ (*insts)[(*nEntries)++] = inst;
+
+ } else if (inc) {
+ VIR_DEBUG("Instantiating filter %s\n", inc->filterref);
+ obj = virNWFilterPoolObjFindByName(&driver->pools,
+ inc->filterref);
+ if (obj) {
+
+ if (obj->wantRemoved) {
+ virNWFilterReportError(conn, VIR_ERR_NO_NWFILTER,
+ _("Filter '%s' is in use."),
+ inc->filterref);
+ rc = 1;
+ virNWFilterPoolObjUnlock(obj);
+ break;
+ }
+
+ // create a temporary hashmap for depth-first tree traversal
+ virNWFilterHashTablePtr tmpvars =
+ virNWFilterCreateVarsFrom(conn,
+ inc->params,
+ vars);
+ if (!tmpvars) {
+ virReportOOMError();
+ rc = 1;
+ virNWFilterPoolObjUnlock(obj);
+ break;
+ }
+
+ next_filter = obj->def;
+
+ switch (useNewFilter) {
+ case INSTANTIATE_FOLLOW_NEWFILTER:
+ if (obj->newDef) {
+ next_filter = obj->newDef;
+ *foundNewFilter = 1;
+ }
+ break;
+ case INSTANTIATE_ALWAYS:
+ break;
+ }
+
+ rc = _virNWFilterInstantiateRec(conn,
+ techdriver,
+ nettype,
+ next_filter,
+ ifname,
+ tmpvars,
+ nEntries, insts,
+ useNewFilter,
+ foundNewFilter);
+
+ virNWFilterHashTableFree(tmpvars);
+
+ virNWFilterPoolObjUnlock(obj);
+ if (rc)
+ break;
+ } else {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("referenced filter '%s' is missing"),
+ inc->filterref);
+ rc = 1;
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+
+static int
+virNWFilterRuleInstancesToArray(int nEntries,
+ virNWFilterRuleInstPtr *insts,
+ void ***ptrs,
+ int *nptrs)
+{
+ int i,j;
+
+ *nptrs = 0;
+
+ for (j = 0; j < nEntries; j++)
+ (*nptrs) += insts[j]->ndata;
+
+ if ((*nptrs) == 0)
+ return 0;
+
+ if (VIR_ALLOC_N((*ptrs), (*nptrs)) < 0) {
+ virReportOOMError();
+ return 1;
+ }
+
+ (*nptrs) = 0;
+
+ for (j = 0; j < nEntries; j++)
+ for (i = 0; i < insts[j]->ndata; i++)
+ (*ptrs)[(*nptrs)++] = insts[j]->data[i];
+
+ return 0;
+}
+
+
+/**
+ * virNWFilterInstantiate:
+ * @conn: pointer to virConnect object
+ * @techdriver: The driver to use for instantiation
+ * @filter: The filter to instantiate
+ * @ifname: The name of the interface to apply the rules to
+ * @vars: A map holding variable names and values used for instantiating
+ * the filter and its subfilters.
+ *
+ * Returns 0 on success, a value otherwise.
+ *
+ * Instantiate a filter by instantiating the filter itself along with
+ * all its subfilters in a depth-first traversal of the tree of referenced
+ * filters. The name of the interface to which the rules belong must be
+ * provided. Apply the values of variables as needed.
+ */
+static int
+virNWFilterInstantiate(virConnectPtr conn,
+ virNWFilterTechDriverPtr techdriver,
+ enum virDomainNetType nettype,
+ virNWFilterDefPtr filter,
+ const char *ifname,
+ virNWFilterHashTablePtr vars,
+ enum instCase useNewFilter, int *foundNewFilter,
+ bool teardownOld)
+{
+ int rc;
+ int j, nptrs;
+ int nEntries = 0;
+ virNWFilterRuleInstPtr *insts = NULL;
+ void **ptrs = NULL;
+ int instantiate = 1;
+
+ rc = _virNWFilterInstantiateRec(conn,
+ techdriver,
+ nettype,
+ filter,
+ ifname,
+ vars,
+ &nEntries, &insts,
+ useNewFilter, foundNewFilter);
+
+ if (rc)
+ goto err_exit;
+
+ switch (useNewFilter) {
+ case INSTANTIATE_FOLLOW_NEWFILTER:
+ instantiate = *foundNewFilter;
+ break;
+ case INSTANTIATE_ALWAYS:
+ instantiate = 1;
+ break;
+ }
+
+ if (instantiate) {
+
+ rc = virNWFilterRuleInstancesToArray(nEntries, insts,
+ &ptrs, &nptrs);
+ if (rc)
+ goto err_exit;
+
+ rc = techdriver->applyNewRules(conn, ifname, nptrs, ptrs);
+
+ if (teardownOld && rc == 0)
+ techdriver->tearOldRules(conn, ifname);
+
+ VIR_FREE(ptrs);
+ }
+
+err_exit:
+
+ for (j = 0; j < nEntries; j++)
+ virNWFilterRuleInstFree(insts[j]);
+
+ VIR_FREE(insts);
+
+ return rc;
+}
+
+
+static int
+_virNWFilterInstantiateFilter(virConnectPtr conn,
+ const virDomainNetDefPtr net,
+ bool teardownOld,
+ enum instCase useNewFilter)
+{
+ int rc;
+ const char *drvname = EBIPTABLES_DRIVER_ID;
+ virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData;
+ virNWFilterTechDriverPtr techdriver;
+ virNWFilterPoolObjPtr obj;
+ virNWFilterHashTablePtr vars, vars1;
+ virNWFilterDefPtr filter;
+ char vmmacaddr[VIR_MAC_STRING_BUFLEN] = {0};
+ int foundNewFilter = 0;
+ char *str_macaddr = NULL;
+
+ techdriver = virNWFilterTechDriverForName(drvname);
+
+ if (!techdriver) {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Could not get access to ACL tech "
+ "driver '%s'"),
+ drvname);
+ return 1;
+ }
+
+ VIR_DEBUG("filter name: %s\n", net->filter);
+
+ obj = virNWFilterPoolObjFindByName(&driver->pools, net->filter);
+ if (!obj) {
+ virNWFilterReportError(conn, VIR_ERR_NO_NWFILTER,
+ _("Could not find filter '%s'"),
+ net->filter);
+ return 1;
+ }
+
+ if (obj->wantRemoved) {
+ virNWFilterReportError(conn, VIR_ERR_NO_NWFILTER,
+ _("Filter '%s' is in use."),
+ net->filter);
+ rc = 1;
+ goto err_exit;
+ }
+
+ virFormatMacAddr(net->mac, vmmacaddr);
+ str_macaddr = strdup(vmmacaddr);
+ if (!str_macaddr) {
+ virReportOOMError();
+ rc = 1;
+ goto err_exit;
+ }
+
+ vars1 = virNWFilterCreateVarHashmap(conn,
+ str_macaddr);
+ if (!vars1) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ str_macaddr = NULL;
+
+ vars = virNWFilterCreateVarsFrom(conn,
+ vars1,
+ net->filterparams);
+ if (!vars) {
+ rc = 1;
+ goto err_exit_vars1;
+ }
+
+ filter = obj->def;
+
+ switch (useNewFilter) {
+ case INSTANTIATE_FOLLOW_NEWFILTER:
+ if (obj->newDef) {
+ filter = obj->newDef;
+ foundNewFilter = 1;
+ }
+ break;
+
+ case INSTANTIATE_ALWAYS:
+ break;
+ }
+
+ rc = virNWFilterInstantiate(conn,
+ techdriver,
+ net->type,
+ filter,
+ net->ifname,
+ vars,
+ useNewFilter, &foundNewFilter,
+ teardownOld);
+
+ virNWFilterHashTableFree(vars);
+
+err_exit_vars1:
+ virNWFilterHashTableFree(vars1);
+
+err_exit:
+
+ virNWFilterPoolObjUnlock(obj);
+
+ VIR_FREE(str_macaddr);
+
+ return rc;
+}
+
+
+int
+virNWFilterInstantiateFilter(virConnectPtr conn,
+ const virDomainNetDefPtr net)
+{
+ return _virNWFilterInstantiateFilter(conn, net,
+ 1,
+ INSTANTIATE_ALWAYS);
+}
+
+
+int
+virNWFilterUpdateInstantiateFilter(virConnectPtr conn,
+ const virDomainNetDefPtr net)
+{
+ return _virNWFilterInstantiateFilter(conn, net,
+ 0,
+ INSTANTIATE_FOLLOW_NEWFILTER);
+}
+
+int virNWFilterRollbackUpdateFilter(virConnectPtr conn,
+ const virDomainNetDefPtr net)
+{
+ const char *drvname = EBIPTABLES_DRIVER_ID;
+ virNWFilterTechDriverPtr techdriver;
+ techdriver = virNWFilterTechDriverForName(drvname);
+ if (!techdriver) {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Could not get access to ACL tech "
+ "driver '%s'"),
+ drvname);
+ return 1;
+ }
+
+ return techdriver->tearNewRules(conn, net->ifname);
+}
+
+
+int
+virNWFilterTearOldFilter(virConnectPtr conn,
+ virDomainNetDefPtr net)
+{
+ const char *drvname = EBIPTABLES_DRIVER_ID;
+ virNWFilterTechDriverPtr techdriver;
+ techdriver = virNWFilterTechDriverForName(drvname);
+ if (!techdriver) {
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Could not get access to ACL tech "
+ "driver '%s'"),
+ drvname);
+ return 1;
+ }
+
+ return techdriver->tearOldRules(conn, net->ifname);
+}
+
+
+int
+virNWFilterTeardownFilter(const virDomainNetDefPtr net)
+{
+ const char *drvname = EBIPTABLES_DRIVER_ID;
+ virNWFilterTechDriverPtr techdriver;
+ techdriver = virNWFilterTechDriverForName(drvname);
+
+ if (!techdriver) {
+#if 0
+ virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Could not get access to ACL tech "
+ "driver '%s'"),
+ drvname);
+#endif
+ return 1;
+ }
+
+ techdriver->allTeardown(net->ifname);
+
+ return 0;
+}