diff options
-rw-r--r-- | configure.ac | 12 | ||||
-rw-r--r-- | daemon/Makefile.am | 4 | ||||
-rw-r--r-- | daemon/libvirtd.c | 7 | ||||
-rw-r--r-- | po/POTFILES.in | 3 | ||||
-rwxr-xr-x | python/generator.py | 2 | ||||
-rw-r--r-- | src/Makefile.am | 21 | ||||
-rw-r--r-- | src/conf/nwfilter_conf.c | 37 | ||||
-rw-r--r-- | src/libvirt_private.syms | 36 | ||||
-rw-r--r-- | src/libvirt_public.syms | 1 | ||||
-rw-r--r-- | src/nwfilter/nwfilter_driver.c | 416 | ||||
-rw-r--r-- | src/nwfilter/nwfilter_driver.h | 36 | ||||
-rw-r--r-- | src/nwfilter/nwfilter_ebiptables_driver.c | 1414 | ||||
-rw-r--r-- | src/nwfilter/nwfilter_ebiptables_driver.h | 41 | ||||
-rw-r--r-- | src/nwfilter/nwfilter_gentech_driver.c | 683 | ||||
-rw-r--r-- | src/nwfilter/nwfilter_gentech_driver.h | 54 |
15 files changed, 2765 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac index 52b16eeb4d..2148ae039a 100644 --- a/configure.ac +++ b/configure.ac @@ -294,6 +294,9 @@ if test x"$with_rhel5_api" = x"yes"; then AC_DEFINE([WITH_RHEL5_API], [1], [whether building for the RHEL-5 API]) fi +AC_PATH_PROG([BASH_PATH], [bash], /bin/bash, [/bin:$PATH]) +AC_DEFINE_UNQUOTED([BASH_PATH], "$BASH_PATH", [path to bash binary]) + AC_PATH_PROG([IPTABLES_PATH], [iptables], /sbin/iptables, [/usr/sbin:$PATH]) AC_DEFINE_UNQUOTED([IPTABLES_PATH], "$IPTABLES_PATH", [path to iptables binary]) @@ -1268,6 +1271,15 @@ if test "$with_secrets" = "yes" ; then fi AM_CONDITIONAL([WITH_SECRETS], [test "$with_secrets" = "yes"]) +with_nwfilter=yes +if test "$with_libvirtd" = "no"; then + with_nwfilter=no +fi +if test "$with_nwfilter" = "yes" ; then + AC_DEFINE([WITH_NWFILTER], 1, [whether local network filter management driver is available]) +fi +AM_CONDITIONAL([WITH_NWFILTER], [test "$with_nwfilter" = "yes"]) + AC_ARG_WITH([storage-fs], AC_HELP_STRING([--with-storage-fs], [with FileSystem backend for the storage driver @<:@default=check@:>@]),[],[with_storage_fs=check]) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index a071609aa3..e117b97a38 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -116,6 +116,10 @@ endif if WITH_SECRETS libvirtd_LDADD += ../src/libvirt_driver_secret.la endif + +if WITH_NWFILTER + libvirtd_LDADD += ../src/libvirt_driver_nwfilter.la +endif endif libvirtd_LDADD += ../src/libvirt.la diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 0d89c534c6..bd45016bff 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -96,6 +96,9 @@ # ifdef WITH_SECRETS # include "secret/secret_driver.h" # endif +# ifdef WITH_NWFILTER +# include "nwfilter/nwfilter_driver.h" +# endif #endif @@ -876,6 +879,7 @@ static struct qemud_server *qemudInitialize(void) { virDriverLoadModule("lxc"); virDriverLoadModule("uml"); virDriverLoadModule("one"); + virDriverLoadModule("nwfilter"); #else # ifdef WITH_NETWORK networkRegister(); @@ -892,6 +896,9 @@ static struct qemud_server *qemudInitialize(void) { # ifdef WITH_SECRETS secretRegister(); # endif +# ifdef WITH_NWFILTER + nwfilterRegister(); +# endif # ifdef WITH_QEMU qemuRegister(); # endif diff --git a/po/POTFILES.in b/po/POTFILES.in index e14360efa3..d24cc1b0ff 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -28,6 +28,9 @@ src/node_device/node_device_driver.c src/node_device/node_device_linux_sysfs.c src/node_device/node_device_udev.c src/nodeinfo.c +src/nwfilter/nwfilter_driver.c +src/nwfilter/nwfilter_ebiptables_driver.c +src/nwfilter/nwfilter_gentech_driver.c src/opennebula/one_conf.c src/opennebula/one_driver.c src/openvz/openvz_conf.c diff --git a/python/generator.py b/python/generator.py index acc8c90cf7..a24e122165 100755 --- a/python/generator.py +++ b/python/generator.py @@ -175,6 +175,7 @@ skipped_types = { 'virConnectDomainEventIOErrorCallback': "No function types in python", 'virConnectDomainEventGraphicsCallback': "No function types in python", 'virEventAddHandleFunc': "No function types in python", + 'virNWFilterPtr': "No function types in python", } ####################################################################### @@ -273,6 +274,7 @@ skip_impl = ( 'virConnectListStorageVols', 'virConnectListDefinedStorageVols', 'virConnectListDefinedInterfaces', + 'virConnectListNWFilters', 'virConnGetLastError', 'virGetLastError', 'virDomainGetInfo', diff --git a/src/Makefile.am b/src/Makefile.am index b873d82d87..2409a57354 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -284,6 +284,11 @@ STORAGE_DRIVER_DISK_SOURCES = \ STORAGE_HELPER_DISK_SOURCES = \ storage/parthelper.c +# Network filters +NWFILTER_DRIVER_SOURCES = \ + nwfilter/nwfilter_driver.h nwfilter/nwfilter_driver.c \ + nwfilter/nwfilter_gentech_driver.c \ + nwfilter/nwfilter_ebiptables_driver.c # Security framework and drivers for various models SECURITY_DRIVER_SOURCES = \ @@ -727,6 +732,22 @@ endif endif +if WITH_NWFILTER +if WITH_DRIVER_MODULES +mod_LTLIBRARIES += libvirt_driver_nwfilter.la +else +libvirt_la_LIBADD += libvirt_driver_nwfilter.la +noinst_LTLIBRARIES += libvirt_driver_nwfilter.la +endif +libvirt_driver_nwfilter_la_CFLAGS = \ + -I@top_srcdir@/src/conf +if WITH_DRIVER_MODULES +libvirt_driver_nwfilter_la_LDFLAGS = -module -avoid-version ../gnulib/lib/libgnu.la +endif +libvirt_driver_nwfilter_la_SOURCES = $(NWFILTER_DRIVER_SOURCES) +endif + + libvirt_driver_security_la_SOURCES = $(SECURITY_DRIVER_SOURCES) noinst_LTLIBRARIES += libvirt_driver_security.la libvirt_la_LIBADD += libvirt_driver_security.la diff --git a/src/conf/nwfilter_conf.c b/src/conf/nwfilter_conf.c index e5d285dc13..a75522c240 100644 --- a/src/conf/nwfilter_conf.c +++ b/src/conf/nwfilter_conf.c @@ -39,6 +39,7 @@ #include "nwfilter_params.h" #include "nwfilter_conf.h" #include "domain_conf.h" +#include "nwfilter/nwfilter_gentech_driver.h" #define VIR_FROM_THIS VIR_FROM_NWFILTER @@ -1605,10 +1606,42 @@ struct cbStruct { }; static void -virNWFilterDomainFWUpdateCB(void *payload ATTRIBUTE_UNUSED, +virNWFilterDomainFWUpdateCB(void *payload, const char *name ATTRIBUTE_UNUSED, - void *data ATTRIBUTE_UNUSED) + void *data) { + virDomainObjPtr obj = payload; + virDomainDefPtr vm = obj->def; + struct cbStruct *cb = data; + int i; + + virDomainObjLock(obj); + + if (virDomainObjIsActive(obj)) { + for (i = 0; i < vm->nnets; i++) { + virDomainNetDefPtr net = vm->nets[i]; + if ((net->filter) && (net->ifname)) { + switch (cb->step) { + case STEP_APPLY_NEW: + cb->err = virNWFilterUpdateInstantiateFilter(cb->conn, + net); + break; + + case STEP_TEAR_NEW: + cb->err = virNWFilterRollbackUpdateFilter(cb->conn, net); + break; + + case STEP_TEAR_OLD: + cb->err = virNWFilterTearOldFilter(cb->conn, net); + break; + } + if (cb->err) + break; + } + } + } + + virDomainObjUnlock(obj); } diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4fe5427045..1bcab9fad1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -449,6 +449,42 @@ virNodeDeviceGetWWNs; virNodeDeviceGetParentHost; +# nwfilter_conf.h +virNWFilterPoolLoadAllConfigs; +virNWFilterPoolObjAssignDef; +virNWFilterPoolObjSaveDef; +virNWFilterPoolObjFindByName; +virNWFilterPoolObjFindByUUID; +virNWFilterPoolObjLock; +virNWFilterPoolObjUnlock; +virNWFilterPoolObjRemove; +virNWFilterDefFree; +virNWFilterDefParseString; +virNWFilterPoolObjDeleteDef; +virNWFilterPoolObjListFree; +virNWFilterDefFormat; +virNWFilterChainSuffixTypeToString; +virNWFilterRuleActionTypeToString; +virNWFilterJumpTargetTypeToString; +virNWFilterRegisterCallbackDriver; +virNWFilterTestUnassignDef; +virNWFilterConfLayerInit; +virNWFilterConfLayerShutdown; + + +#nwfilter_params.h +virNWFilterHashTableCreate; +virNWFilterHashTableFree; +virNWFilterHashTablePut; +virNWFilterHashTablePutAll; +virNWFilterHashTableRemoveEntry; + + +# nwfilter_gentech_driver.h +virNWFilterInstantiateFilter; +virNWFilterTeardownFilter; + + # pci.h pciGetDevice; pciFreeDevice; diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index ad7577cd30..2f812a1585 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -380,4 +380,5 @@ LIBVIRT_0.7.8 { virNWFilterUndefine; } LIBVIRT_0.7.7; + # .... define new API here using predicted next version number .... diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c new file mode 100644 index 0000000000..4bb30ce136 --- /dev/null +++ b/src/nwfilter/nwfilter_driver.c @@ -0,0 +1,416 @@ +/* + * 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; +} diff --git a/src/nwfilter/nwfilter_driver.h b/src/nwfilter/nwfilter_driver.h new file mode 100644 index 0000000000..b7d8668f45 --- /dev/null +++ b/src/nwfilter/nwfilter_driver.h @@ -0,0 +1,36 @@ +/* + * nwfilter_driver.h: core driver for nwfilter APIs + * (based on storage driver) + * + * Copyright (C) 2006-2008 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> + */ + +#ifndef __VIR_NWFILTER_DRIVER_H__ +#define __VIR_NWFILTER_DRIVER_H__ + +#include "nwfilter_params.h" +#include "nwfilter_conf.h" + +int nwfilterRegister(void); + +#endif /* __VIR_NWFILTER_DRIVER_H__ */ diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfilter_ebiptables_driver.c new file mode 100644 index 0000000000..7af269caaf --- /dev/null +++ b/src/nwfilter/nwfilter_ebiptables_driver.c @@ -0,0 +1,1414 @@ +/* + * 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, +}; diff --git a/src/nwfilter/nwfilter_ebiptables_driver.h b/src/nwfilter/nwfilter_ebiptables_driver.h new file mode 100644 index 0000000000..1635b1af79 --- /dev/null +++ b/src/nwfilter/nwfilter_ebiptables_driver.h @@ -0,0 +1,41 @@ +/* + * nwfilter_ebiptables_driver.h: ebtables/iptables driver support + * + * 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: Stefan Berger <stefanb@us.ibm.com> + */ +#ifndef VIR_NWFILTER_EBTABLES_DRIVER_H__ +#define VIR_NWFILTER_EBTABLES_DRIVER_H__ + +#define MAX_CHAINNAME_LENGTH 32 /* see linux/netfilter_bridge/ebtables.h */ + +typedef struct _ebiptablesRuleInst ebiptablesRuleInst; +typedef ebiptablesRuleInst *ebiptablesRuleInstPtr; +struct _ebiptablesRuleInst { + char *commandTemplate; + enum virNWFilterChainSuffixType neededProtocolChain; + char chainprefix; // I for incoming, O for outgoing + unsigned int priority; +}; + +extern virNWFilterTechDriver ebiptables_driver; + +#define EBIPTABLES_DRIVER_ID "ebiptables" + +#endif diff --git a/src/nwfilter/nwfilter_gentech_driver.c b/src/nwfilter/nwfilter_gentech_driver.c new file mode 100644 index 0000000000..e590952ae4 --- /dev/null +++ b/src/nwfilter/nwfilter_gentech_driver.c @@ -0,0 +1,683 @@ +/* + * 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; +} diff --git a/src/nwfilter/nwfilter_gentech_driver.h b/src/nwfilter/nwfilter_gentech_driver.h new file mode 100644 index 0000000000..a77d95a5c8 --- /dev/null +++ b/src/nwfilter/nwfilter_gentech_driver.h @@ -0,0 +1,54 @@ +/* + * nwfilter_gentech_driver.h: generic technology driver include file + * + * 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> + */ +#ifndef __NWFILTER_GENTECH_DRIVER_H +#define __NWFILTER_GENTECH_DRIVER_H + +virNWFilterTechDriverPtr virNWFilterTechDriverForName(const char *name); + +int virNWFilterRuleInstAddData(virConnectPtr conn, + virNWFilterRuleInstPtr res, + void *data); + + +enum instCase { + INSTANTIATE_ALWAYS, + INSTANTIATE_FOLLOW_NEWFILTER, +}; + + +int virNWFilterInstantiateFilter(virConnectPtr conn, + const virDomainNetDefPtr net); +int virNWFilterUpdateInstantiateFilter(virConnectPtr conn, + const virDomainNetDefPtr net); +int virNWFilterRollbackUpdateFilter(virConnectPtr conn, + const virDomainNetDefPtr net); + +int virNWFilterTearOldFilter(virConnectPtr conn, + const virDomainNetDefPtr net); + +int virNWFilterTeardownFilter(const virDomainNetDefPtr net); + +virNWFilterHashTablePtr virNWFilterCreateVarHashmap(virConnectPtr conn, + char *macaddr); + +#endif |