[libvirt] [PATCH v4 00/15] Network filtering (ACL) extensions for libvirt

Hi! This is a repost of this set of patches with some of the suggested fixes applied and ipv6 support on the ebtables layer added. Between V3 and V4 of this patch series the following changes were made: - occurrences of typo 'scp' were changed to 'sctp' - the root ebtables chain for each interface now has the previx of 'libvirt-' - additional calls into tear-down functions in case something goes wrong while starting the qemu/kvm VM in 2nd level error paths - additional functions in the driver interface to split up the application of firewall rules into - creation of new firewall rules 'tree' - switch-over to new firewall rules 'tree', tear down of old one and renaming of new firewall 'tree' - tear down of new firewall rules 'tree' in case an error happend during update of several VMs. - additional patch with example filters The following set of patches add network filtering (ACL) extensions to libvirt and enable network traffic filtering for VMs using ebtables and, depending on the networking technology being used (tap, but not macvtap), also iptables. Usage of either is optional and controlled through filters that a VM is referencing. The ebtables-level filtering is based on the XML derived from the CIM network slide 10 (filtering) from the DMTF website (http://www.dmtf.org/standards/cim/cim_schema_v2230/CIM_Network.pdf). The XML we derived from this was discussed on the list before. On the ebtables level we currently handle filtering of IPv4 and ARP traffic. The iptables-level filtering is based on similar XML where XML nodes described the particular protocol to filter for. Its extensions enable the filtering of traffic using iptables for tcp, udp, icmp, igmp, sctp and 'all' types of traffic. This list of protocols maps to the features supported by iptables and only excludes protocols like 'esp', 'ah' and 'udplite'. Currently only bridging mode is supported and based on availability of the physdev match. The filtering framework adds new libvirt virsh commands for managing the filters. The 5 new commands are: - virsh nwfilter-list - virsh nwfilter-dumpxml <name of filter> - virsh nwfilter-define <name of file containing filter desc.> - virsh nwfilter-undefine <name of filter> - virsh nwfilter-edit <name of filter> Above commands are similar to commands for already existing pools and as such much of the code directly related to the above commands could be borrowed from other drivers. The network filters can either contain rules using the above mentioned XML or contain references to other filters in order to build more complex filters that form some sort of filter tree or can contain both. An example for a filter referencing other filters would be this one here: <filter name='demofilter4' chain='root'> <uuid>66f62d1d-34c1-1421-824f-c62d5ee5e8b6</uuid> <filterref filter='no-mac-spoofing'/> <filterref filter='no-mac-broadcast'/> <filterref filter='no-arp-spoofing'/> <filterref filter='allow-dhcp'> <parameter name='DHCPSERVER' value='10.0.0.1'/> </filterref> <filterref filter='no-other-l2-traffic'/> <filterref filter='recv-only-vm-ipaddress'/> <filterref filter='recv-only-vm-macaddress'/> <filterref filter='l3-test'/> <filterref filter='ipv6test'/> </filter> A filter containing actual rules would look like this: <filter name='no-mac-broadcast' chain='ipv4'> <uuid>ffe2ccd6-edec-7360-1852-6b5ccb553234</uuid> <rule action='drop' direction='out' priority='500'> <mac dstmacaddr='ff:ff:ff:ff:ff:ff'/> </rule> </filter> The filter XML now also holds a priority attribute in the rule. This provides control over the ordering of the applied ebtables/iptables rules beyond their appearance in the XML. The domain XML has been extended to reference a top level filter from within each <interface> XML node. A valid reference to such a top level filter looks like this: <interface type='bridge'> <source bridge='static'/> <filterref filter='demofilter4'> <parameter name='IP' value='9.59.241.151'/> </filterref> </interface> In this XML a parameter IP is passed for instantiation of the referenced filters, that may require the availability of this parameter. In the above case the IP parameter's value describes the value of the IP address of the VM and allows to enable those filters to be instantiated that require this 'IP' variable. If a filter requires a parameter that is not provided, the VM will not start or the interface will not attach to a running VM. Any names of parameters can be provided for instantiation of filters and their names and values only need to pass a regular expression test. In a subsequent patch we will be adding capability to allow users to omit the IP parameter (only) and enable libvirt to learn the IP address of the VM and have it instantiate the filter once it knows it. While virtual machines are running, it is possible to update their filters. For that all running VMs' filter 'trees' are traversed to detect whether the updated filter is referenced by the VM. If so, its ebtables/iptable rules are applied. If one of the VMs' update fails allupdates are rolled back and the filter XML update is rejected. One comment about the instantiation of the rules: Since the XML allows to create nearly any possible combination of parameters to ebtables or iptables commands, I haven't used the ebtables or iptables wrappers. Instead, I am writing ebtables/iptables command into a buffer, add command line options to each one of them as described in the rule's XML, write the buffer into a file and run it as a script. For those commands that are not allowed to fail I am using the following format to run them: cmd="ebtables <some options>" r=`${cmd}` if [ $? -ne 0 ]; then echo "Failure in command ${cmd}." exit 1 fi cmd="..." [...] If one of the command fails in such a batch, the libvirt code is going pick up the error code '1', tear down anything previously established and report an error back. The actual error message shown above is currently not reported back, but can be later on with some changes to the commands running external programs that need to read the script's stdout. One comment to patch 14: It currently #include's a .c file into a .c file only for the reason so I don't have to change too much code once I change code in the underlying patch. So this has to be changed. The patch series works without patch 13, but then only supports ebtables. The patches apply to the current tip. They pass 'make syntax-check' and have been frequently run in valgrind for memory leak checks. Looking forward to your feedback on the patches. Thanks and regards, Stefan and Gerhard

This patch adds recursive locks necessary due to the processing of network filter XML that can reference other network filters, including references that cause looks. Loops in the XML are prevented but their detection requires recursive locks. Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- src/util/threads-pthread.c | 13 +++++++++++++ src/util/threads-win32.c | 5 +++++ src/util/threads.h | 1 + 3 files changed, 19 insertions(+) Index: libvirt-acl/src/util/threads-pthread.c =================================================================== --- libvirt-acl.orig/src/util/threads-pthread.c +++ libvirt-acl/src/util/threads-pthread.c @@ -46,6 +46,19 @@ int virMutexInit(virMutexPtr m) return 0; } +int virMutexInitRecursive(virMutexPtr m) +{ + int ret; + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if ((ret = pthread_mutex_init(&m->lock, &attr)) != 0) { + errno = ret; + return -1; + } + return 0; +} + void virMutexDestroy(virMutexPtr m) { pthread_mutex_destroy(&m->lock); Index: libvirt-acl/src/util/threads.h =================================================================== --- libvirt-acl.orig/src/util/threads.h +++ libvirt-acl/src/util/threads.h @@ -38,6 +38,7 @@ int virThreadInitialize(void) ATTRIBUTE_ void virThreadOnExit(void); int virMutexInit(virMutexPtr m) ATTRIBUTE_RETURN_CHECK; +int virMutexInitRecursive(virMutexPtr m) ATTRIBUTE_RETURN_CHECK; void virMutexDestroy(virMutexPtr m); void virMutexLock(virMutexPtr m); Index: libvirt-acl/src/util/threads-win32.c =================================================================== --- libvirt-acl.orig/src/util/threads-win32.c +++ libvirt-acl/src/util/threads-win32.c @@ -69,6 +69,11 @@ void virThreadOnExit(void) int virMutexInit(virMutexPtr m) { + virMutexInitRecursive(m); +} + +int virMutexInitRecursive(virMutexPtr m) +{ if (!(m->lock = CreateMutex(NULL, FALSE, NULL))) { errno = ESRCH; return -1;

This patch adds extensions to libvirt's public API necessary for controlling the new functionality from remote for example. Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- include/libvirt/libvirt.h.in | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) Index: libvirt-acl/include/libvirt/libvirt.h.in =================================================================== --- libvirt-acl.orig/include/libvirt/libvirt.h.in +++ libvirt-acl/include/libvirt/libvirt.h.in @@ -1861,4 +1861,67 @@ int virDomainAbortJob(virDomainPtr dom); } #endif + +/** + * virNWFilter: + * + * a virNWFilter is a private structure representing a network filter + */ +typedef struct _virNWFilter virNWFilter; + +/** + * virNWFilterPtr: + * + * a virNWFilterPtr is pointer to a virNWFilter private structure, + * this is the type used to reference a network filter in the API. + */ +typedef virNWFilter *virNWFilterPtr; + + +/* + * List NWFilters + */ +int virConnectNumOfNWFilters (virConnectPtr conn); +int virConnectListNWFilters (virConnectPtr conn, + char **const names, + int maxnames); + +/* + * Lookup nwfilter by name or uuid + */ +virNWFilterPtr virNWFilterLookupByName (virConnectPtr conn, + const char *name); +virNWFilterPtr virNWFilterLookupByUUID (virConnectPtr conn, + const unsigned char *uuid); +virNWFilterPtr virNWFilterLookupByUUIDString (virConnectPtr conn, + const char *uuid); + +/* + * Define persistent nwfilter + */ +virNWFilterPtr virNWFilterDefineXML (virConnectPtr conn, + const char *xmlDesc); + +/* + * Delete persistent nwfilter + */ +int virNWFilterUndefine (virNWFilterPtr nwfilter); + +/* + * NWFilter destroy/free + */ +int virNWFilterRef (virNWFilterPtr nwfilter); +int virNWFilterFree (virNWFilterPtr nwfilter); + +/* + * NWFilter information + */ +const char* virNWFilterGetName (virNWFilterPtr nwfilter); +int virNWFilterGetUUID (virNWFilterPtr nwfilter, + unsigned char *uuid); +int virNWFilterGetUUIDString (virNWFilterPtr nwfilter, + char *buf); +char * virNWFilterGetXMLDesc (virNWFilterPtr nwfilter, + int flags); + #endif /* __VIR_VIRLIB_H__ */

This patch adds the internal API extensions for network filtering (ACL) support. Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- src/driver.h | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) Index: libvirt-acl/src/driver.h =================================================================== --- libvirt-acl.orig/src/driver.h +++ libvirt-acl/src/driver.h @@ -994,6 +994,58 @@ struct _virStreamDriver { }; +typedef int + (*virDrvConnectNumOfNWFilters) (virConnectPtr conn); +typedef int + (*virDrvConnectListNWFilters) (virConnectPtr conn, + char **const names, + int maxnames); +typedef virNWFilterPtr + (*virDrvNWFilterLookupByName) (virConnectPtr conn, + const char *name); +typedef virNWFilterPtr + (*virDrvNWFilterLookupByUUID) (virConnectPtr conn, + const unsigned char *uuid); +typedef virNWFilterPtr + (*virDrvNWFilterDefineXML) (virConnectPtr conn, + const char *xmlDesc, + unsigned int flags); +typedef int + (*virDrvNWFilterUndefine) (virNWFilterPtr pool); + +typedef char * + (*virDrvNWFilterGetXMLDesc) (virNWFilterPtr pool, + unsigned int flags); + + +typedef struct _virNWFilterDriver virNWFilterDriver; +typedef virNWFilterDriver *virNWFilterDriverPtr; + +/** + * _virNWFilterDriver: + * + * Structure associated to a network filter driver, defining the various + * entry points for it. + * + * All drivers must support the following fields/methods: + * - open + * - close + */ +struct _virNWFilterDriver { + const char * name; /* the name of the driver */ + virDrvOpen open; + virDrvClose close; + + virDrvConnectNumOfNWFilters numOfNWFilters; + virDrvConnectListNWFilters listNWFilters; + virDrvNWFilterLookupByName nwfilterLookupByName; + virDrvNWFilterLookupByUUID nwfilterLookupByUUID; + virDrvNWFilterDefineXML defineXML; + virDrvNWFilterUndefine undefine; + virDrvNWFilterGetXMLDesc getXMLDesc; +}; + + /* * Registration * TODO: also need ways to (des)activate a given driver @@ -1005,6 +1057,7 @@ int virRegisterInterfaceDriver(virInterf int virRegisterStorageDriver(virStorageDriverPtr); int virRegisterDeviceMonitor(virDeviceMonitorPtr); int virRegisterSecretDriver(virSecretDriverPtr); +int virRegisterNWFilterDriver(virNWFilterDriverPtr); # ifdef WITH_LIBVIRTD int virRegisterStateDriver(virStateDriverPtr); # endif

This patch adds the implementation of the public API for the network filtering (ACL) extensions to libvirt.c . Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- src/libvirt.c | 586 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 586 insertions(+) Index: libvirt-acl/src/libvirt.c =================================================================== --- libvirt-acl.orig/src/libvirt.c +++ libvirt-acl/src/libvirt.c @@ -91,6 +91,8 @@ static virDeviceMonitorPtr virDeviceMoni static int virDeviceMonitorTabCount = 0; static virSecretDriverPtr virSecretDriverTab[MAX_DRIVERS]; static int virSecretDriverTabCount = 0; +static virNWFilterDriverPtr virNWFilterDriverTab[MAX_DRIVERS]; +static int virNWFilterDriverTabCount = 0; #ifdef WITH_LIBVIRTD static virStateDriverPtr virStateDriverTab[MAX_DRIVERS]; static int virStateDriverTabCount = 0; @@ -655,6 +657,32 @@ virLibSecretError(virSecretPtr secret, v } /** + * virLibNWFilterError: + * @conn: the connection if available + * @error: the error number + * @info: extra information string + * + * Handle an error at the connection level + */ +static void +virLibNWFilterError(virNWFilterPtr pool, virErrorNumber error, + const char *info) +{ + virConnectPtr conn = NULL; + const char *errmsg; + + if (error == VIR_ERR_OK) + return; + + errmsg = virErrorMsg(error, info); + if (error != VIR_ERR_INVALID_NWFILTER) + conn = pool->conn; + + virRaiseError(conn, NULL, NULL, VIR_FROM_NWFILTER, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +/** * virRegisterNetworkDriver: * @driver: pointer to a network driver block * @@ -810,6 +838,38 @@ virRegisterSecretDriver(virSecretDriverP } /** + * virRegisterNWFilterDriver: + * @driver: pointer to a network filter driver block + * + * Register a network filter virtualization driver + * + * Returns the driver priority or -1 in case of error. + */ +int +virRegisterNWFilterDriver(virNWFilterDriverPtr driver) +{ + if (virInitialize() < 0) + return -1; + + if (driver == NULL) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return -1; + } + + if (virNWFilterDriverTabCount >= MAX_DRIVERS) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return -1; + } + + DEBUG ("registering %s as network filter driver %d", + driver->name, virNWFilterDriverTabCount); + + virNWFilterDriverTab[virNWFilterDriverTabCount] = driver; + return virNWFilterDriverTabCount++; +} + + +/** * virRegisterDriver: * @driver: pointer to a driver block * @@ -1253,6 +1313,26 @@ do_open (const char *name, } } + /* Network filter driver. Optional */ + for (i = 0; i < virNWFilterDriverTabCount; i++) { + res = virNWFilterDriverTab[i]->open (ret, auth, flags); + DEBUG("nwfilter driver %d %s returned %s", + i, virNWFilterDriverTab[i]->name, + res == VIR_DRV_OPEN_SUCCESS ? "SUCCESS" : + (res == VIR_DRV_OPEN_DECLINED ? "DECLINED" : + (res == VIR_DRV_OPEN_ERROR ? "ERROR" : "unknown status"))); + if (res == VIR_DRV_OPEN_ERROR) { + if (STREQ(virNWFilterDriverTab[i]->name, "remote")) { + virLibConnWarning (NULL, VIR_WAR_NO_NWFILTER, + _("Is the daemon running ?")); + } + break; + } else if (res == VIR_DRV_OPEN_SUCCESS) { + ret->nwfilterDriver = virNWFilterDriverTab[i]; + break; + } + } + return ret; failed: @@ -11011,6 +11091,512 @@ error: } + +/** + * virConnectNumOfNWFilters: + * @conn: pointer to the hypervisor connection + * + * Provides the number of nwfilters. + * + * Returns the number of nwfilters found or -1 in case of error + */ +int +virConnectNumOfNWFilters(virConnectPtr conn) +{ + DEBUG("conn=%p", conn); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->nwfilterDriver && conn->nwfilterDriver->numOfNWFilters) { + int ret; + ret = conn->nwfilterDriver->numOfNWFilters (conn); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + + +/** + * virConnectListNWFilters: + * @conn: pointer to the hypervisor connection + * @names: array to collect the list of names of network filters + * @maxnames: size of @names + * + * Collect the list of network filters, and store their names in @names + * + * Returns the number of network filters found or -1 in case of error + */ +int +virConnectListNWFilters(virConnectPtr conn, char **const names, int maxnames) +{ + DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if ((names == NULL) || (maxnames < 0)) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (conn->nwfilterDriver && conn->nwfilterDriver->listNWFilters) { + int ret; + ret = conn->nwfilterDriver->listNWFilters (conn, names, maxnames); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + + +/** + * virNWFilterLookupByName: + * @conn: pointer to the hypervisor connection + * @name: name for the network filter + * + * Try to lookup a network filter on the given hypervisor based on its name. + * + * Returns a new nwfilter object or NULL in case of failure. If the + * network filter cannot be found, then VIR_ERR_NO_NWFILTER error is raised. + */ +virNWFilterPtr +virNWFilterLookupByName(virConnectPtr conn, const char *name) +{ + DEBUG("conn=%p, name=%s", conn, name); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return (NULL); + } + if (name == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (conn->nwfilterDriver && conn->nwfilterDriver->nwfilterLookupByName) { + virNWFilterPtr ret; + ret = conn->nwfilterDriver->nwfilterLookupByName (conn, name); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return NULL; +} + +/** + * virNWFilterLookupByUUID: + * @conn: pointer to the hypervisor connection + * @uuid: the raw UUID for the network filter + * + * Try to lookup a network filter on the given hypervisor based on its UUID. + * + * Returns a new nwfilter object or NULL in case of failure. If the + * nwfdilter cannot be found, then VIR_ERR_NO_NWFILTER error is raised. + */ +virNWFilterPtr +virNWFilterLookupByUUID(virConnectPtr conn, const unsigned char *uuid) +{ + DEBUG("conn=%p, uuid=%s", conn, uuid); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return (NULL); + } + if (uuid == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (conn->nwfilterDriver && conn->nwfilterDriver->nwfilterLookupByUUID){ + virNWFilterPtr ret; + ret = conn->nwfilterDriver->nwfilterLookupByUUID (conn, uuid); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return NULL; +} + +/** + * virNWFIlterLookupByUUIDString: + * @conn: pointer to the hypervisor connection + * @uuidstr: the string UUID for the nwfilter + * + * Try to lookup an nwfilter on the given hypervisor based on its UUID. + * + * Returns a new nwfilter object or NULL in case of failure. If the + * nwfilter cannot be found, then VIR_ERR_NO_NWFILTER error is raised. + */ +virNWFilterPtr +virNWFilterLookupByUUIDString(virConnectPtr conn, const char *uuidstr) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + DEBUG("conn=%p, uuidstr=%s", conn, uuidstr); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return (NULL); + } + if (uuidstr == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (virUUIDParse(uuidstr, uuid) < 0) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + return virNWFilterLookupByUUID(conn, &uuid[0]); + +error: + virDispatchError(conn); + return NULL; +} + +/** + * virNWFilterFree: + * @nwfilter: a nwfilter object + * + * Free the nwfilter object. The running instance is kept alive. + * The data structure is freed and should not be used thereafter. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virNWFilterFree(virNWFilterPtr nwfilter) +{ + DEBUG("nwfilter=%p", nwfilter); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_NWFILTER(nwfilter)) { + virLibNWFilterError(NULL, VIR_ERR_INVALID_NWFILTER, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (virUnrefNWFilter(nwfilter) < 0) { + virDispatchError(NULL); + return -1; + } + return 0; +} + +/** + * virNWFilterGetName: + * @nwfilter: a nwfilter object + * + * Get the public name for the network filter + * + * Returns a pointer to the name or NULL, the string need not be deallocated + * its lifetime will be the same as the nwfilter object. + */ +const char * +virNWFilterGetName(virNWFilterPtr nwfilter) +{ + DEBUG("nwfilter=%p", nwfilter); + + virResetLastError(); + + if (!VIR_IS_NWFILTER(nwfilter)) { + virLibNWFilterError(NULL, VIR_ERR_INVALID_NWFILTER, __FUNCTION__); + virDispatchError(NULL); + return (NULL); + } + return (nwfilter->name); +} + +/** + * virNWFilterGetUUID: + * @nwfilter: a nwfilter object + * @uuid: pointer to a VIR_UUID_BUFLEN bytes array + * + * Get the UUID for a network filter + * + * Returns -1 in case of error, 0 in case of success + */ +int +virNWFilterGetUUID(virNWFilterPtr nwfilter, unsigned char *uuid) +{ + DEBUG("nwfilter=%p, uuid=%p", nwfilter, uuid); + + virResetLastError(); + + if (!VIR_IS_NWFILTER(nwfilter)) { + virLibNWFilterError(NULL, VIR_ERR_INVALID_NWFILTER, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (uuid == NULL) { + virLibNWFilterError(nwfilter, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + memcpy(uuid, &nwfilter->uuid[0], VIR_UUID_BUFLEN); + + return 0; + +error: + virDispatchError(nwfilter->conn); + return -1; +} + +/** + * virNWFilterGetUUIDString: + * @nwfilter: a nwfilter object + * @buf: pointer to a VIR_UUID_STRING_BUFLEN bytes array + * + * Get the UUID for a network filter as string. For more information about + * UUID see RFC4122. + * + * Returns -1 in case of error, 0 in case of success + */ +int +virNWFilterGetUUIDString(virNWFilterPtr nwfilter, char *buf) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + DEBUG("nwfilter=%p, buf=%p", nwfilter, buf); + + virResetLastError(); + + if (!VIR_IS_NWFILTER(nwfilter)) { + virLibNWFilterError(NULL, VIR_ERR_INVALID_NWFILTER, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (buf == NULL) { + virLibNWFilterError(nwfilter, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (virNWFilterGetUUID(nwfilter, &uuid[0])) + goto error; + + virUUIDFormat(uuid, buf); + return 0; + +error: + virDispatchError(nwfilter->conn); + return -1; +} + + +/** + * virNWFilterDefineXML: + * @conn: pointer to the hypervisor connection + * @xmlDesc: an XML description of the nwfilter + * + * Define a new network filter, based on an XML description + * similar to the one returned by virNWFilterGetXMLDesc() + * + * Returns a new nwfilter object or NULL in case of failure + */ +virNWFilterPtr +virNWFilterDefineXML(virConnectPtr conn, const char *xmlDesc) +{ + DEBUG("conn=%p, xmlDesc=%s", conn, xmlDesc); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return (NULL); + } + if (xmlDesc == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->nwfilterDriver && conn->nwfilterDriver->defineXML) { + virNWFilterPtr ret; + ret = conn->nwfilterDriver->defineXML (conn, xmlDesc, 0); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return NULL; +} + + +/** + * virNWFilterUndefine: + * @nwfilter: a nwfilter object + * + * Undefine the nwfilter object. This call will not succeed if + * a running VM is referencing the filter. This does not free the + * associated virNWFilterPtr object. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +virNWFilterUndefine(virNWFilterPtr nwfilter) +{ + virConnectPtr conn; + DEBUG("nwfilter=%p", nwfilter); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_NWFILTER(nwfilter)) { + virLibNWFilterError(NULL, VIR_ERR_INVALID_NWFILTER, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = nwfilter->conn; + if (conn->flags & VIR_CONNECT_RO) { + virLibNWFilterError(nwfilter, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->nwfilterDriver && conn->nwfilterDriver->undefine) { + int ret; + ret = conn->nwfilterDriver->undefine (nwfilter); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(nwfilter->conn); + return -1; +} + + +/** + * virNWFilterGetXMLDesc: + * @nwfilter: a nwfilter object + * @flags: an OR'ed set of extraction flags, not used yet + * + * Provide an XML description of the network filter. The description may be + * reused later to redefine the network filter with virNWFilterCreateXML(). + * + * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case of error. + * the caller must free() the returned value. + */ +char * +virNWFilterGetXMLDesc(virNWFilterPtr nwfilter, int flags) +{ + virConnectPtr conn; + DEBUG("nwfilter=%p, flags=%d", nwfilter, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_NWFILTER(nwfilter)) { + virLibNWFilterError(NULL, VIR_ERR_INVALID_NWFILTER, __FUNCTION__); + virDispatchError(NULL); + return (NULL); + } + if (flags != 0) { + virLibNWFilterError(nwfilter, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + conn = nwfilter->conn; + + if (conn->nwfilterDriver && conn->nwfilterDriver->getXMLDesc) { + char *ret; + ret = conn->nwfilterDriver->getXMLDesc (nwfilter, flags); + if (!ret) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(nwfilter->conn); + return NULL; +} + + +/** + * virNWFilterRef: + * @nwfilter: the nwfilter to hold a reference on + * + * Increment the reference count on the nwfilter. For each + * additional call to this method, there shall be a corresponding + * call to virNWFilterFree to release the reference count, once + * the caller no longer needs the reference to this object. + * + * This method is typically useful for applications where multiple + * threads are using a connection, and it is required that the + * connection remain open until all threads have finished using + * it. ie, each new thread using an nwfilter would increment + * the reference count. + * + * Returns 0 in case of success, -1 in case of failure. + */ +int +virNWFilterRef(virNWFilterPtr nwfilter) +{ + if ((!VIR_IS_CONNECTED_NWFILTER(nwfilter))) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + virMutexLock(&nwfilter->conn->lock); + DEBUG("nwfilter=%p refs=%d", nwfilter, nwfilter->refs); + nwfilter->refs++; + virMutexUnlock(&nwfilter->conn->lock); + return 0; +} + + /** * virInterfaceIsActive: * @iface: pointer to the interface object

This patch adds the definition of the wire format for RPC calls for the new network filtering (ACL) functionality added to libvirt. Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- src/remote/remote_protocol.x | 72 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) Index: libvirt-acl/src/remote/remote_protocol.x =================================================================== --- libvirt-acl.orig/src/remote/remote_protocol.x +++ libvirt-acl/src/remote/remote_protocol.x @@ -103,6 +103,9 @@ const REMOTE_NODE_DEVICE_NAME_LIST_MAX = /* Upper limit on lists of node device capabilities. */ const REMOTE_NODE_DEVICE_CAPS_LIST_MAX = 16384; +/* Upper limit on lists of network filter names. */ +const REMOTE_NWFILTER_NAME_LIST_MAX = 1024; + /* Upper limit on list of scheduler parameters. */ const REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX = 16; @@ -176,6 +179,12 @@ struct remote_nonnull_network { remote_uuid uuid; }; +/* A network filter which may not be NULL. */ +struct remote_nonnull_nwfilter { + remote_nonnull_string name; + remote_uuid uuid; +}; + /* An interface which may not be NULL. */ struct remote_nonnull_interface { remote_nonnull_string name; @@ -210,6 +219,7 @@ struct remote_nonnull_secret { /* A domain or network which may be NULL. */ typedef remote_nonnull_domain *remote_domain; typedef remote_nonnull_network *remote_network; +typedef remote_nonnull_nwfilter *remote_nwfilter; typedef remote_nonnull_storage_pool *remote_storage_pool; typedef remote_nonnull_storage_vol *remote_storage_vol; typedef remote_nonnull_node_device *remote_node_device; @@ -234,6 +244,7 @@ struct remote_error { int int1; int int2; remote_network net; + remote_nwfilter nwfilter; }; /* Authentication types available thus far.... */ @@ -843,6 +854,57 @@ struct remote_network_set_autostart_args int autostart; }; +/* network filter calls */ + +struct remote_num_of_nwfilters_ret { + int num; +}; + +struct remote_list_nwfilters_args { + int maxnames; +}; + +struct remote_list_nwfilters_ret { + remote_nonnull_string names<REMOTE_NWFILTER_NAME_LIST_MAX>; +}; + +struct remote_nwfilter_lookup_by_uuid_args { + remote_uuid uuid; +}; + +struct remote_nwfilter_lookup_by_uuid_ret { + remote_nonnull_nwfilter nwfilter; +}; + +struct remote_nwfilter_lookup_by_name_args { + remote_nonnull_string name; +}; + +struct remote_nwfilter_lookup_by_name_ret { + remote_nonnull_nwfilter nwfilter; +}; + +struct remote_nwfilter_define_xml_args { + remote_nonnull_string xml; +}; + +struct remote_nwfilter_define_xml_ret { + remote_nonnull_nwfilter nwfilter; +}; + +struct remote_nwfilter_undefine_args { + remote_nonnull_nwfilter nwfilter; +}; + +struct remote_nwfilter_get_xml_desc_args { + remote_nonnull_nwfilter nwfilter; + int flags; +}; + +struct remote_nwfilter_get_xml_desc_ret { + remote_nonnull_string xml; +}; + /* Interface calls: */ @@ -1717,7 +1779,15 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_JOB_INFO = 163, REMOTE_PROC_DOMAIN_ABORT_JOB = 164, REMOTE_PROC_STORAGE_VOL_WIPE = 165, - REMOTE_PROC_DOMAIN_MIGRATE_SET_MAX_DOWNTIME = 166 + REMOTE_PROC_DOMAIN_MIGRATE_SET_MAX_DOWNTIME = 166, + REMOTE_PROC_NWFILTER_LOOKUP_BY_NAME = 167, + REMOTE_PROC_NWFILTER_LOOKUP_BY_UUID = 168, + REMOTE_PROC_NWFILTER_GET_XML_DESC = 169, + REMOTE_PROC_NUM_OF_NWFILTERS = 170, + + REMOTE_PROC_LIST_NWFILTERS = 171, + REMOTE_PROC_NWFILTER_DEFINE_XML = 172, + REMOTE_PROC_NWFILTER_UNDEFINE = 173 /* * Notice how the entries are grouped in sets of 10 ?

This patch extends the RPC client for the new network filtering (ACL) functionality. Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- daemon/remote.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) Index: libvirt-acl/daemon/remote.c =================================================================== --- libvirt-acl.orig/daemon/remote.c +++ libvirt-acl/daemon/remote.c @@ -66,6 +66,7 @@ static virInterfacePtr get_nonnull_inter static virStoragePoolPtr get_nonnull_storage_pool (virConnectPtr conn, remote_nonnull_storage_pool pool); static virStorageVolPtr get_nonnull_storage_vol (virConnectPtr conn, remote_nonnull_storage_vol vol); static virSecretPtr get_nonnull_secret (virConnectPtr conn, remote_nonnull_secret secret); +static virNWFilterPtr get_nonnull_nwfilter (virConnectPtr conn, remote_nonnull_nwfilter nwfilter); static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src); static void make_nonnull_network (remote_nonnull_network *net_dst, virNetworkPtr net_src); static void make_nonnull_interface (remote_nonnull_interface *interface_dst, virInterfacePtr interface_src); @@ -73,6 +74,7 @@ static void make_nonnull_storage_pool (r static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); static void make_nonnull_node_device (remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src); static void make_nonnull_secret (remote_nonnull_secret *secret_dst, virSecretPtr secret_src); +static void make_nonnull_nwfilter (remote_nonnull_nwfilter *net_dst, virNWFilterPtr nwfilter_src); #include "remote_dispatch_prototypes.h" @@ -5524,6 +5526,185 @@ remoteDispatchDomainMigrateSetMaxDowntim } + +static int +remoteDispatchNwfilterLookupByName (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_nwfilter_lookup_by_name_args *args, + remote_nwfilter_lookup_by_name_ret *ret) +{ + virNWFilterPtr nwfilter; + + nwfilter = virNWFilterLookupByName (conn, args->name); + if (nwfilter == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + make_nonnull_nwfilter (&ret->nwfilter, nwfilter); + virNWFilterFree(nwfilter); + return 0; +} + +static int +remoteDispatchNwfilterLookupByUuid (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_nwfilter_lookup_by_uuid_args *args, + remote_nwfilter_lookup_by_uuid_ret *ret) +{ + virNWFilterPtr nwfilter; + + nwfilter = virNWFilterLookupByUUID (conn, (unsigned char *) args->uuid); + if (nwfilter == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + make_nonnull_nwfilter (&ret->nwfilter, nwfilter); + virNWFilterFree(nwfilter); + return 0; +} + + +static int +remoteDispatchNwfilterDefineXml (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_nwfilter_define_xml_args *args, + remote_nwfilter_define_xml_ret *ret) +{ + virNWFilterPtr nwfilter; + + nwfilter = virNWFilterDefineXML (conn, args->xml); + if (nwfilter == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + make_nonnull_nwfilter (&ret->nwfilter, nwfilter); + virNWFilterFree(nwfilter); + return 0; +} + + +static int +remoteDispatchNwfilterUndefine (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_nwfilter_undefine_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + virNWFilterPtr nwfilter; + + nwfilter = get_nonnull_nwfilter (conn, args->nwfilter); + if (nwfilter == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + if (virNWFilterUndefine (nwfilter) == -1) { + virNWFilterFree(nwfilter); + remoteDispatchConnError(rerr, conn); + return -1; + } + virNWFilterFree(nwfilter); + return 0; +} + +static int +remoteDispatchListNwfilters (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_list_nwfilters_args *args, + remote_list_nwfilters_ret *ret) +{ + + if (args->maxnames > REMOTE_NWFILTER_NAME_LIST_MAX) { + remoteDispatchFormatError (rerr, + "%s", _("maxnames > REMOTE_NWFILTER_NAME_LIST_MAX")); + return -1; + } + + /* Allocate return buffer. */ + if (VIR_ALLOC_N(ret->names.names_val, args->maxnames) < 0) { + remoteDispatchOOMError(rerr); + return -1; + } + + ret->names.names_len = + virConnectListNWFilters (conn, + ret->names.names_val, args->maxnames); + if (ret->names.names_len == -1) { + VIR_FREE(ret->names.names_len); + remoteDispatchConnError(rerr, conn); + return -1; + } + + return 0; +} + + +static int +remoteDispatchNwfilterGetXmlDesc (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + remote_nwfilter_get_xml_desc_args *args, + remote_nwfilter_get_xml_desc_ret *ret) +{ + virNWFilterPtr nwfilter; + + nwfilter = get_nonnull_nwfilter (conn, args->nwfilter); + if (nwfilter == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + /* remoteDispatchClientRequest will free this. */ + ret->xml = virNWFilterGetXMLDesc (nwfilter, args->flags); + if (!ret->xml) { + virNWFilterFree(nwfilter); + remoteDispatchConnError(rerr, conn); + return -1; + } + virNWFilterFree(nwfilter); + return 0; +} + + +static int +remoteDispatchNumOfNwfilters (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + void *args ATTRIBUTE_UNUSED, + remote_num_of_nwfilters_ret *ret) +{ + + ret->num = virConnectNumOfNWFilters (conn); + if (ret->num == -1) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + return 0; +} + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire @@ -5576,6 +5757,12 @@ get_nonnull_secret (virConnectPtr conn, return virGetSecret (conn, BAD_CAST secret.uuid, secret.usageType, secret.usageID); } +static virNWFilterPtr +get_nonnull_nwfilter (virConnectPtr conn, remote_nonnull_nwfilter nwfilter) +{ + return virGetNWFilter (conn, nwfilter.name, BAD_CAST nwfilter.uuid); +} + /* Make remote_nonnull_domain and remote_nonnull_network. */ static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src) @@ -5628,3 +5815,10 @@ make_nonnull_secret (remote_nonnull_secr secret_dst->usageType = secret_src->usageType; secret_dst->usageID = strdup (secret_src->usageID); } + +static void +make_nonnull_nwfilter (remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwfilter_src) +{ + nwfilter_dst->name = strdup (nwfilter_src->name); + memcpy (nwfilter_dst->uuid, nwfilter_src->uuid, VIR_UUID_BUFLEN); +}

This patch extends the RPC dispatcher to support the newly added RPC calls for network filtering (ACL) support. Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- src/remote/remote_driver.c | 311 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) Index: libvirt-acl/src/remote/remote_driver.c =================================================================== --- libvirt-acl.orig/src/remote/remote_driver.c +++ libvirt-acl/src/remote/remote_driver.c @@ -248,6 +248,7 @@ static int remoteAuthPolkit (virConnectP static virDomainPtr get_nonnull_domain (virConnectPtr conn, remote_nonnull_domain domain); static virNetworkPtr get_nonnull_network (virConnectPtr conn, remote_nonnull_network network); +static virNWFilterPtr get_nonnull_nwfilter (virConnectPtr conn, remote_nonnull_nwfilter nwfilter); static virInterfacePtr get_nonnull_interface (virConnectPtr conn, remote_nonnull_interface iface); static virStoragePoolPtr get_nonnull_storage_pool (virConnectPtr conn, remote_nonnull_storage_pool pool); static virStorageVolPtr get_nonnull_storage_vol (virConnectPtr conn, remote_nonnull_storage_vol vol); @@ -259,6 +260,7 @@ static void make_nonnull_interface (remo static void make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr vol_src); static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); static void make_nonnull_secret (remote_nonnull_secret *secret_dst, virSecretPtr secret_src); +static void make_nonnull_nwfilter (remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwfilter_src); void remoteDomainEventFired(int watch, int fd, int event, void *data); static void remoteDomainQueueEvent(virConnectPtr conn, XDR *xdr); void remoteDomainEventQueueFlush(int timer, void *opaque); @@ -6086,6 +6088,287 @@ done: return rv; } +/* ------------------------------------------------------------- */ + +static virDrvOpenStatus ATTRIBUTE_NONNULL (1) +remoteNWFilterOpen (virConnectPtr conn, + virConnectAuthPtr auth, + int flags) +{ + if (inside_daemon) + return VIR_DRV_OPEN_DECLINED; + + if (conn->driver && + STREQ (conn->driver->name, "remote")) { + struct private_data *priv; + + /* If we're here, the remote driver is already + * in use due to a) a QEMU uri, or b) a remote + * URI. So we can re-use existing connection + */ + priv = conn->privateData; + remoteDriverLock(priv); + priv->localUses++; + conn->nwfilterPrivateData = priv; + remoteDriverUnlock(priv); + return VIR_DRV_OPEN_SUCCESS; + } else { + /* Using a non-remote driver, so we need to open a + * new connection for network filtering APIs, forcing it to + * use the UNIX transport. This handles Xen driver + * which doesn't have its own impl of the network filtering APIs. + */ + struct private_data *priv; + int ret; + ret = remoteOpenSecondaryDriver(conn, + auth, + flags, + &priv); + if (ret == VIR_DRV_OPEN_SUCCESS) + conn->nwfilterPrivateData = priv; + return ret; + } +} + +static int +remoteNWFilterClose (virConnectPtr conn) +{ + int rv = 0; + struct private_data *priv = conn->nwfilterPrivateData; + + remoteDriverLock(priv); + priv->localUses--; + if (!priv->localUses) { + rv = doRemoteClose(conn, priv); + conn->nwfilterPrivateData = NULL; + remoteDriverUnlock(priv); + virMutexDestroy(&priv->lock); + VIR_FREE(priv); + } + if (priv) + remoteDriverUnlock(priv); + return rv; +} + + +static int +remoteNumOfNWFilters (virConnectPtr conn) +{ + int rv = -1; + remote_num_of_nwfilters_ret ret; + struct private_data *priv = conn->nwfilterPrivateData; + + remoteDriverLock(priv); + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_NUM_OF_NWFILTERS, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_remote_num_of_nwfilters_ret, (char *) &ret) == -1) + goto done; + + rv = ret.num; + +done: + remoteDriverUnlock(priv); + return rv; +} + + +static virNWFilterPtr +remoteNWFilterDefineXML (virConnectPtr conn, const char *xmlDesc, + unsigned int flags ATTRIBUTE_UNUSED) +{ + virNWFilterPtr net = NULL; + remote_nwfilter_define_xml_args args; + remote_nwfilter_define_xml_ret ret; + struct private_data *priv = conn->nwfilterPrivateData; + + remoteDriverLock(priv); + + args.xml = (char *) xmlDesc; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_NWFILTER_DEFINE_XML, + (xdrproc_t) xdr_remote_nwfilter_define_xml_args, (char *) &args, + (xdrproc_t) xdr_remote_nwfilter_define_xml_ret, (char *) &ret) == -1) + goto done; + + net = get_nonnull_nwfilter (conn, ret.nwfilter); + xdr_free ((xdrproc_t) &xdr_remote_nwfilter_define_xml_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return net; +} + + +static int +remoteNWFilterUndefine (virNWFilterPtr nwfilter) +{ + int rv = -1; + remote_nwfilter_undefine_args args; + struct private_data *priv = nwfilter->conn->nwfilterPrivateData; + + remoteDriverLock(priv); + + make_nonnull_nwfilter (&args.nwfilter, nwfilter); + + if (call (nwfilter->conn, priv, 0, REMOTE_PROC_NWFILTER_UNDEFINE, + (xdrproc_t) xdr_remote_nwfilter_undefine_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto done; + + rv = 0; + +done: + remoteDriverUnlock(priv); + return rv; +} + + +static int +remoteListNWFilters (virConnectPtr conn, char **const names, int maxnames) +{ + int rv = -1; + int i; + remote_list_nwfilters_args args; + remote_list_nwfilters_ret ret; + struct private_data *priv = conn->nwfilterPrivateData; + + remoteDriverLock(priv); + + if (maxnames > REMOTE_NWFILTER_NAME_LIST_MAX) { + errorf (conn, VIR_ERR_RPC, + _("too many remote nwfilters: %d > %d"), + maxnames, REMOTE_NWFILTER_NAME_LIST_MAX); + goto done; + } + args.maxnames = maxnames; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_LIST_NWFILTERS, + (xdrproc_t) xdr_remote_list_nwfilters_args, (char *) &args, + (xdrproc_t) xdr_remote_list_nwfilters_ret, (char *) &ret) == -1) + goto done; + + if (ret.names.names_len > maxnames) { + errorf (conn, VIR_ERR_RPC, + _("too many remote nwfilters: %d > %d"), + ret.names.names_len, maxnames); + goto cleanup; + } + + /* This call is caller-frees (although that isn't clear from + * the documentation). However xdr_free will free up both the + * names and the list of pointers, so we have to strdup the + * names here. + */ + for (i = 0; i < ret.names.names_len; ++i) { + names[i] = strdup (ret.names.names_val[i]); + + if (names[i] == NULL) { + for (--i; i >= 0; --i) + VIR_FREE(names[i]); + + virReportOOMError(); + goto cleanup; + } + } + + rv = ret.names.names_len; + +cleanup: + xdr_free ((xdrproc_t) xdr_remote_list_nwfilters_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + + + +static virNWFilterPtr +remoteNWFilterLookupByUUID (virConnectPtr conn, + const unsigned char *uuid) +{ + virNWFilterPtr net = NULL; + remote_nwfilter_lookup_by_uuid_args args; + remote_nwfilter_lookup_by_uuid_ret ret; + struct private_data *priv = conn->nwfilterPrivateData; + + remoteDriverLock(priv); + + memcpy (args.uuid, uuid, VIR_UUID_BUFLEN); + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_NWFILTER_LOOKUP_BY_UUID, + (xdrproc_t) xdr_remote_nwfilter_lookup_by_uuid_args, (char *) &args, + (xdrproc_t) xdr_remote_nwfilter_lookup_by_uuid_ret, (char *) &ret) == -1) + goto done; + + net = get_nonnull_nwfilter (conn, ret.nwfilter); + xdr_free ((xdrproc_t) &xdr_remote_nwfilter_lookup_by_uuid_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return net; +} + +static virNWFilterPtr +remoteNWFilterLookupByName (virConnectPtr conn, + const char *name) +{ + virNWFilterPtr net = NULL; + remote_nwfilter_lookup_by_name_args args; + remote_nwfilter_lookup_by_name_ret ret; + struct private_data *priv = conn->nwfilterPrivateData; + + remoteDriverLock(priv); + + args.name = (char *) name; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_NWFILTER_LOOKUP_BY_NAME, + (xdrproc_t) xdr_remote_nwfilter_lookup_by_name_args, (char *) &args, + (xdrproc_t) xdr_remote_nwfilter_lookup_by_name_ret, (char *) &ret) == -1) + goto done; + + net = get_nonnull_nwfilter (conn, ret.nwfilter); + xdr_free ((xdrproc_t) &xdr_remote_nwfilter_lookup_by_name_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return net; +} + + +static char * +remoteNWFilterGetXMLDesc (virNWFilterPtr nwfilter, unsigned int flags) +{ + char *rv = NULL; + remote_nwfilter_get_xml_desc_args args; + remote_nwfilter_get_xml_desc_ret ret; + struct private_data *priv = nwfilter->conn->nwfilterPrivateData; + + remoteDriverLock(priv); + + make_nonnull_nwfilter (&args.nwfilter, nwfilter); + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (nwfilter->conn, priv, 0, REMOTE_PROC_NWFILTER_GET_XML_DESC, + (xdrproc_t) xdr_remote_nwfilter_get_xml_desc_args, (char *) &args, + (xdrproc_t) xdr_remote_nwfilter_get_xml_desc_ret, (char *) &ret) == -1) + goto done; + + /* Caller frees. */ + rv = ret.xml; + +done: + remoteDriverUnlock(priv); + return rv; +} + /*----------------------------------------------------------------------*/ @@ -9053,6 +9336,13 @@ get_nonnull_secret (virConnectPtr conn, return virGetSecret(conn, BAD_CAST secret.uuid, secret.usageType, secret.usageID); } +static virNWFilterPtr +get_nonnull_nwfilter (virConnectPtr conn, remote_nonnull_nwfilter nwfilter) +{ + return virGetNWFilter (conn, nwfilter.name, BAD_CAST nwfilter.uuid); +} + + /* Make remote_nonnull_domain and remote_nonnull_network. */ static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr dom_src) @@ -9100,6 +9390,13 @@ make_nonnull_secret (remote_nonnull_secr secret_dst->usageID = secret_src->usageID; } +static void +make_nonnull_nwfilter (remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwfilter_src) +{ + nwfilter_dst->name = nwfilter_src->name; + memcpy (nwfilter_dst->uuid, nwfilter_src->uuid, VIR_UUID_BUFLEN); +} + /*----------------------------------------------------------------------*/ unsigned long remoteVersion(void) @@ -9303,6 +9600,19 @@ static virDeviceMonitor dev_monitor = { .deviceDestroy = remoteNodeDeviceDestroy }; +static virNWFilterDriver nwfilter_driver = { + .name = "remote", + .open = remoteNWFilterOpen, + .close = remoteNWFilterClose, + .nwfilterLookupByUUID = remoteNWFilterLookupByUUID, + .nwfilterLookupByName = remoteNWFilterLookupByName, + .getXMLDesc = remoteNWFilterGetXMLDesc, + .defineXML = remoteNWFilterDefineXML, + .undefine = remoteNWFilterUndefine, + .numOfNWFilters = remoteNumOfNWFilters, + .listNWFilters = remoteListNWFilters, +}; + #ifdef WITH_LIBVIRTD static virStateDriver state_driver = { @@ -9327,6 +9637,7 @@ remoteRegister (void) if (virRegisterStorageDriver (&storage_driver) == -1) return -1; if (virRegisterDeviceMonitor (&dev_monitor) == -1) return -1; if (virRegisterSecretDriver (&secret_driver) == -1) return -1; + if (virRegisterNWFilterDriver(&nwfilter_driver) == -1) return -1; #ifdef WITH_LIBVIRTD if (virRegisterStateDriver (&state_driver) == -1) return -1; #endif

This patch adds virsh support for the five new CLI commands to manage network filters. Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- tools/virsh.c | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 349 insertions(+) Index: libvirt-acl/tools/virsh.c =================================================================== --- libvirt-acl.orig/tools/virsh.c +++ libvirt-acl/tools/virsh.c @@ -254,6 +254,14 @@ static virNetworkPtr vshCommandOptNetwor vshCommandOptNetworkBy(_ctl, _cmd, _name, \ VSH_BYUUID|VSH_BYNAME) +static virNWFilterPtr vshCommandOptNWFilterBy(vshControl *ctl, const vshCmd *cmd, + char **name, int flag); + +/* default is lookup by Name and UUID */ +#define vshCommandOptNWFilter(_ctl, _cmd, _name) \ + vshCommandOptNWFilterBy(_ctl, _cmd, _name, \ + VSH_BYUUID|VSH_BYNAME) + static virInterfacePtr vshCommandOptInterfaceBy(vshControl *ctl, const vshCmd *cmd, char **name, int flag); @@ -3850,6 +3858,300 @@ cmdInterfaceDestroy(vshControl *ctl, con return ret; } + +/* + * "nwfilter-define" command + */ +static const vshCmdInfo info_nwfilter_define[] = { + {"help", N_("define or update a network filter from an XML file")}, + {"desc", N_("Define a new network filter or update an existing one.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_nwfilter_define[] = { + {"file", VSH_OT_DATA, VSH_OFLAG_REQ, N_("file containing an XML network filter description")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNWFilterDefine(vshControl *ctl, const vshCmd *cmd) +{ + virNWFilterPtr nwfilter; + char *from; + int found; + int ret = TRUE; + char *buffer; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + from = vshCommandOptString(cmd, "file", &found); + if (!found) + return FALSE; + + if (virFileReadAll(from, VIRSH_MAX_XML_FILE, &buffer) < 0) + return FALSE; + + nwfilter = virNWFilterDefineXML(ctl->conn, buffer); + VIR_FREE(buffer); + + if (nwfilter != NULL) { + vshPrint(ctl, _("Network filter %s defined from %s\n"), + virNWFilterGetName(nwfilter), from); + virNWFilterFree(nwfilter); + } else { + vshError(ctl, _("Failed to define network filter from %s"), from); + ret = FALSE; + } + return ret; +} + + +/* + * "nwfilter-undefine" command + */ +static const vshCmdInfo info_nwfilter_undefine[] = { + {"help", N_("undefine a network filter")}, + {"desc", N_("Undefine a given network filter.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_nwfilter_undefine[] = { + {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNWFilterUndefine(vshControl *ctl, const vshCmd *cmd) +{ + virNWFilterPtr nwfilter; + int ret = TRUE; + char *name; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, &name))) + return FALSE; + + if (virNWFilterUndefine(nwfilter) == 0) { + vshPrint(ctl, _("Network filter %s undefined\n"), name); + } else { + vshError(ctl, _("Failed to undefine network filter %s"), name); + ret = FALSE; + } + + virNWFilterFree(nwfilter); + return ret; +} + + +/* + * "nwfilter-dumpxml" command + */ +static const vshCmdInfo info_nwfilter_dumpxml[] = { + {"help", N_("network filter information in XML")}, + {"desc", N_("Output the network filter information as an XML dump to stdout.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_nwfilter_dumpxml[] = { + {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNWFilterDumpXML(vshControl *ctl, const vshCmd *cmd) +{ + virNWFilterPtr nwfilter; + int ret = TRUE; + char *dump; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(nwfilter = vshCommandOptNWFilter(ctl, cmd, NULL))) + return FALSE; + + dump = virNWFilterGetXMLDesc(nwfilter, 0); + if (dump != NULL) { + printf("%s", dump); + VIR_FREE(dump); + } else { + ret = FALSE; + } + + virNWFilterFree(nwfilter); + return ret; +} + +/* + * "nwfilter-list" command + */ +static const vshCmdInfo info_nwfilter_list[] = { + {"help", N_("list network filters")}, + {"desc", N_("Returns list of network filters.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_nwfilter_list[] = { + {NULL, 0, 0, NULL} +}; + +static int +cmdNWFilterList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) +{ + int numfilters, i; + char **names; + unsigned char uuid[VIR_UUID_STRING_BUFLEN]; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + numfilters = virConnectNumOfNWFilters(ctl->conn); + if (numfilters < 0) { + vshError(ctl, "%s", _("Failed to list network filters")); + return FALSE; + } + + names = vshMalloc(ctl, sizeof(char *) * numfilters); + + if ((numfilters = virConnectListNWFilters(ctl->conn, names, + numfilters)) < 0) { + vshError(ctl, "%s", _("Failed to list network filters")); + VIR_FREE(names); + return FALSE; + } + + qsort(&names[0], numfilters, sizeof(char *), namesorter); + + vshPrintExtra(ctl, "%-36s %-20s \n", _("UUID"), _("Name")); + vshPrintExtra(ctl, + "----------------------------------------------------------------\n"); + + for (i = 0; i < numfilters; i++) { + virNWFilterPtr nwfilter = + virNWFilterLookupByName(ctl->conn, names[i]); + + /* this kind of work with networks is not atomic operation */ + if (!nwfilter) { + VIR_FREE(names[i]); + continue; + } + + virNWFilterGetUUIDString(nwfilter, uuid); + vshPrint(ctl, "%-36s %-20s\n", + uuid, + virNWFilterGetName(nwfilter)); + virNWFilterFree(nwfilter); + VIR_FREE(names[i]); + } + + VIR_FREE(names); + return TRUE; +} + + +/* + * "nwfilter-edit" command + */ +static const vshCmdInfo info_nwfilter_edit[] = { + {"help", N_("edit XML configuration for a network filter")}, + {"desc", N_("Edit the XML configuration for a network filter.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_nwfilter_edit[] = { + {"nwfilter", VSH_OT_DATA, VSH_OFLAG_REQ, N_("network filter name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdNWFilterEdit (vshControl *ctl, const vshCmd *cmd) +{ + int ret = FALSE; + virNWFilterPtr nwfilter = NULL; + char *tmp = NULL; + char *doc = NULL; + char *doc_edited = NULL; + char *doc_reread = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + nwfilter = vshCommandOptNWFilter (ctl, cmd, NULL); + if (nwfilter == NULL) + goto cleanup; + + /* Get the XML configuration of the interface. */ + doc = virNWFilterGetXMLDesc (nwfilter, 0); + if (!doc) + goto cleanup; + + /* Create and open the temporary file. */ + tmp = editWriteToTempFile (ctl, doc); + if (!tmp) goto cleanup; + + /* Start the editor. */ + if (editFile (ctl, tmp) == -1) goto cleanup; + + /* Read back the edited file. */ + doc_edited = editReadBackFile (ctl, tmp); + if (!doc_edited) goto cleanup; + + unlink (tmp); + tmp = NULL; + + /* Compare original XML with edited. Has it changed at all? */ + if (STREQ (doc, doc_edited)) { + vshPrint (ctl, _("Network filter %s XML configuration not changed.\n"), + virNWFilterGetName (nwfilter)); + ret = TRUE; + goto cleanup; + } + + /* Now re-read the network filter XML. Did someone else change it while + * it was being edited? This also catches problems such as us + * losing a connection or the interface going away. + */ + doc_reread = virNWFilterGetXMLDesc (nwfilter, 0); + if (!doc_reread) + goto cleanup; + + if (STRNEQ (doc, doc_reread)) { + vshError(ctl, "%s", + _("ERROR: the XML configuration was changed by another user")); + goto cleanup; + } + + /* Everything checks out, so redefine the interface. */ + virNWFilterFree (nwfilter); + nwfilter = virNWFilterDefineXML (ctl->conn, doc_edited); + if (!nwfilter) + goto cleanup; + + vshPrint (ctl, _("Network filter %s XML configuration edited.\n"), + virNWFilterGetName(nwfilter)); + + ret = TRUE; + +cleanup: + if (nwfilter) + virNWFilterFree (nwfilter); + + VIR_FREE(doc); + VIR_FREE(doc_edited); + VIR_FREE(doc_reread); + + if (tmp) { + unlink (tmp); + VIR_FREE(tmp); + } + + return ret; +} + + /**************************************************************************/ /* * "pool-autostart" command @@ -7848,6 +8150,12 @@ static const vshCmdDef commands[] = { {"nodedev-create", cmdNodeDeviceCreate, opts_node_device_create, info_node_device_create}, {"nodedev-destroy", cmdNodeDeviceDestroy, opts_node_device_destroy, info_node_device_destroy}, + {"nwfilter-define", cmdNWFilterDefine, opts_nwfilter_define, info_nwfilter_define}, + {"nwfilter-undefine", cmdNWFilterUndefine, opts_nwfilter_undefine, info_nwfilter_undefine}, + {"nwfilter-dumpxml", cmdNWFilterDumpXML, opts_nwfilter_dumpxml, info_nwfilter_dumpxml}, + {"nwfilter-list", cmdNWFilterList, opts_nwfilter_list, info_nwfilter_list}, + {"nwfilter-edit", cmdNWFilterEdit, opts_nwfilter_edit, info_nwfilter_edit}, + {"pool-autostart", cmdPoolAutostart, opts_pool_autostart, info_pool_autostart}, {"pool-build", cmdPoolBuild, opts_pool_build, info_pool_build}, {"pool-create", cmdPoolCreate, opts_pool_create, info_pool_create}, @@ -8320,6 +8628,47 @@ vshCommandOptNetworkBy(vshControl *ctl, return network; } + +static virNWFilterPtr +vshCommandOptNWFilterBy(vshControl *ctl, const vshCmd *cmd, + char **name, int flag) +{ + virNWFilterPtr nwfilter = NULL; + char *n; + const char *optname = "nwfilter"; + if (!cmd_has_option (ctl, cmd, optname)) + return NULL; + + if (!(n = vshCommandOptString(cmd, optname, NULL))) { + vshError(ctl, "%s", _("undefined nwfilter name")); + return NULL; + } + + vshDebug(ctl, 5, "%s: found option <%s>: %s\n", + cmd->def->name, optname, n); + + if (name) + *name = n; + + /* try it by UUID */ + if ((flag & VSH_BYUUID) && (strlen(n) == VIR_UUID_STRING_BUFLEN-1)) { + vshDebug(ctl, 5, "%s: <%s> trying as nwfilter UUID\n", + cmd->def->name, optname); + nwfilter = virNWFilterLookupByUUIDString(ctl->conn, n); + } + /* try it by NAME */ + if (nwfilter == NULL && (flag & VSH_BYNAME)) { + vshDebug(ctl, 5, "%s: <%s> trying as nwfilter NAME\n", + cmd->def->name, optname); + nwfilter = virNWFilterLookupByName(ctl->conn, n); + } + + if (!nwfilter) + vshError(ctl, _("failed to get nwfilter '%s'"), n); + + return nwfilter; +} + static virInterfacePtr vshCommandOptInterfaceBy(vshControl *ctl, const vshCmd *cmd, char **name, int flag)

This patch extends the domain XML processing to parse the top level referenced filter along with potentially provided parameters and also converts the internal data back into XML representation. Signed-off-by: Stefan Berger <stefanb@us.ibm.com> Signed-off-by: Gerhard Stenzel <gerhard.stenzel@de.ibm.com> --- src/conf/domain_conf.c | 39 +++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 4 ++++ 2 files changed, 43 insertions(+) Index: libvirt-acl/src/conf/domain_conf.c =================================================================== --- libvirt-acl.orig/src/conf/domain_conf.c +++ libvirt-acl/src/conf/domain_conf.c @@ -42,6 +42,7 @@ #include "logging.h" #include "network.h" #include "macvtap.h" +#include "nwfilter_conf.h" #define VIR_FROM_THIS VIR_FROM_DOMAIN @@ -456,6 +457,9 @@ void virDomainNetDefFree(virDomainNetDef virDomainDeviceInfoClear(&def->info); + VIR_FREE(def->filter); + virNWFilterHashTableFree(def->filterparams); + VIR_FREE(def); } @@ -1729,9 +1733,11 @@ virDomainNetDefParseXML(virCapsPtr caps, char *address = NULL; char *port = NULL; char *model = NULL; + char *filter = NULL; char *internal = NULL; char *devaddr = NULL; char *mode = NULL; + virNWFilterHashTablePtr filterparams = NULL; if (VIR_ALLOC(def) < 0) { virReportOOMError(); @@ -1800,6 +1806,9 @@ virDomainNetDefParseXML(virCapsPtr caps, script = virXMLPropString(cur, "path"); } else if (xmlStrEqual (cur->name, BAD_CAST "model")) { model = virXMLPropString(cur, "type"); + } else if (xmlStrEqual (cur->name, BAD_CAST "filterref")) { + filter = virXMLPropString(cur, "filter"); + filterparams = virNWFilterParseParamAttributes(cur); } else if ((flags & VIR_DOMAIN_XML_INTERNAL_STATUS) && xmlStrEqual(cur->name, BAD_CAST "state")) { /* Legacy back-compat. Don't add any more attributes here */ @@ -1975,6 +1984,22 @@ virDomainNetDefParseXML(virCapsPtr caps, model = NULL; } + if (filter != NULL) { + switch (def->type) { + case VIR_DOMAIN_NET_TYPE_ETHERNET: + case VIR_DOMAIN_NET_TYPE_NETWORK: + case VIR_DOMAIN_NET_TYPE_BRIDGE: + case VIR_DOMAIN_NET_TYPE_DIRECT: + def->filter = filter; + filter = NULL; + def->filterparams = filterparams; + filterparams = NULL; + break; + default: + break; + } + } + cleanup: VIR_FREE(macaddr); VIR_FREE(network); @@ -1985,10 +2010,12 @@ cleanup: VIR_FREE(script); VIR_FREE(bridge); VIR_FREE(model); + VIR_FREE(filter); VIR_FREE(type); VIR_FREE(internal); VIR_FREE(devaddr); VIR_FREE(mode); + virNWFilterHashTableFree(filterparams); return def; @@ -4795,6 +4822,7 @@ virDomainNetDefFormat(virBufferPtr buf, int flags) { const char *type = virDomainNetTypeToString(def->type); + char *attrs; if (!type) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, @@ -4869,6 +4897,17 @@ virDomainNetDefFormat(virBufferPtr buf, if (def->model) virBufferEscapeString(buf, " <model type='%s'/>\n", def->model); + if (def->filter) { + virBufferEscapeString(buf, " <filterref filter='%s'", + def->filter); + attrs = virNWFilterFormatParamAttributes(def->filterparams, + " "); + if (!attrs || strlen(attrs) <= 1) + virBufferAddLit(buf, "/>\n"); + else + virBufferVSprintf(buf, ">\n%s </filterref>\n", attrs); + VIR_FREE(attrs); + } if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0) return -1; Index: libvirt-acl/src/conf/domain_conf.h =================================================================== --- libvirt-acl.orig/src/conf/domain_conf.h +++ libvirt-acl/src/conf/domain_conf.h @@ -36,6 +36,8 @@ # include "threads.h" # include "hash.h" # include "network.h" +# include "nwfilter_params.h" +# include "nwfilter_conf.h" /* Private component of virDomainXMLFlags */ typedef enum { @@ -282,6 +284,8 @@ struct _virDomainNetDef { } data; char *ifname; virDomainDeviceInfo info; + char *filter; + virNWFilterHashTablePtr filterparams; }; enum virDomainChrTargetType {

Add support for Qemu to have firewall rules applied and removed on VM startup and shutdown respectively. This patch also provides support for the updating of a filter that causes all VMs that reference the filter to have their ebtables/iptables rules updated. Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- src/qemu/qemu_conf.c | 29 +++++++++++++++++++++++++++++ src/qemu/qemu_driver.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) Index: libvirt-acl/src/qemu/qemu_conf.c =================================================================== --- libvirt-acl.orig/src/qemu/qemu_conf.c +++ libvirt-acl/src/qemu/qemu_conf.c @@ -54,6 +54,7 @@ #include "network.h" #include "macvtap.h" #include "cpu/cpu.h" +#include "nwfilter/nwfilter_gentech_driver.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -1472,6 +1473,17 @@ qemudPhysIfaceConnect(virConnectPtr conn net->ifname); } } + + if (rc >= 0) { + if ((net->filter) && (net->ifname)) { + err = virNWFilterInstantiateFilter(conn, net); + if (err) { + close(rc); + rc = -1; + delMacvtap(net->ifname); + } + } + } #else (void)conn; (void)net; @@ -1594,6 +1606,16 @@ qemudNetworkIfaceConnect(virConnectPtr c } } + if (tapfd >= 0) { + if ((net->filter) && (net->ifname)) { + err = virNWFilterInstantiateFilter(conn, net); + if (err) { + close(tapfd); + tapfd = -1; + } + } + } + cleanup: VIR_FREE(brname); @@ -3301,6 +3323,7 @@ int qemudBuildCommandLine(virConnectPtr char domid[50]; char *cpu; char *smp; + int last_good_net = -1; uname_normalize(&ut); @@ -3955,6 +3978,7 @@ int qemudBuildCommandLine(virConnectPtr goto error; ADD_ARG(host); } + last_good_net = i; } } @@ -4415,6 +4439,11 @@ int qemudBuildCommandLine(virConnectPtr VIR_FREE((qenv)[i]); VIR_FREE(qenv); } + for (i = 0; i <= last_good_net; i++) { + virDomainNetDefPtr net = def->nets[i]; + if ((net->filter) && (net->ifname)) + virNWFilterTeardownFilter(net); + } return -1; #undef ADD_ARG Index: libvirt-acl/src/qemu/qemu_driver.c =================================================================== --- libvirt-acl.orig/src/qemu/qemu_driver.c +++ libvirt-acl/src/qemu/qemu_driver.c @@ -83,6 +83,7 @@ #include "xml.h" #include "cpu/cpu.h" #include "macvtap.h" +#include "nwfilter/nwfilter_gentech_driver.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -2644,6 +2645,21 @@ cleanup: } +static void +qemuTearNWFilter(virDomainNetDefPtr net) { + if ((net->filter) && (net->ifname)) + virNWFilterTeardownFilter(net); +} + + +static void +qemuTearVMNWFilters(virDomainObjPtr vm) { + int i; + for (i = 0; i < vm->def->nnets; i++) + qemuTearNWFilter(vm->def->nets[i]); +} + + struct qemudHookData { virConnectPtr conn; virDomainObjPtr vm; @@ -2993,6 +3009,8 @@ cleanup: /* We jump here if we failed to start the VM for any reason * XXX investigate if we can kill this block and safely call * qemudShutdownVMDaemon even though no PID is running */ + qemuTearVMNWFilters(vm); + qemuDomainReAttachHostDevices(driver, vm->def); if (driver->securityDriver && @@ -3041,6 +3059,8 @@ static void qemudShutdownVMDaemon(struct * reporting so we don't squash a legit error. */ orig_err = virSaveLastError(); + qemuTearVMNWFilters(vm); + if (driver->macFilter) { def = vm->def; for (i = 0 ; i < def->nnets ; i++) { @@ -6433,6 +6453,8 @@ cleanup: qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &net->info) < 0) VIR_WARN0("Unable to release PCI address on NIC"); + qemuTearNWFilter(net); + VIR_FREE(nicstr); VIR_FREE(netstr); VIR_FREE(tapfd_name); @@ -7052,6 +7074,8 @@ qemudDomainDetachNetDevice(struct qemud_ } } + qemuTearNWFilter(detach); + if (vm->def->nnets > 1) { memmove(vm->def->nets + i, vm->def->nets + i + 1, @@ -9687,8 +9711,24 @@ static virStateDriver qemuStateDriver = .active = qemudActive, }; +static int +qemudVMFilterRebuild(virConnectPtr conn, + virHashIterator iter, void *data) +{ + (void)conn; + virHashForEach(qemu_driver->domains.objs, iter, data); + return 0; +} + + +static virNWFilterCallbackDriver qemuCallbackDriver = { + .name = "QEMU", + .vmFilterRebuild = qemudVMFilterRebuild, +}; + int qemuRegister(void) { virRegisterDriver(&qemuDriver); virRegisterStateDriver(&qemuStateDriver); + virNWFilterRegisterCallbackDriver(&qemuCallbackDriver); return 0; }

This patch implements the core driver and provides - management functionality for managing the filter XMLs - compiling the internal filter representation into ebtables rules - applying ebtables rules on a network (tap,macvtap) interface - tearing down ebtables rules that were applied on behalf of an interface - updating of filters while VMs are running and causing the firewalls to be rebuilt - other bits and pieces Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- daemon/libvirtd.c | 7 include/libvirt/virterror.h | 7 python/generator.py | 2 src/conf/nwfilter_conf.c | 2244 ++++++++++++++++++++++++++++++ src/conf/nwfilter_conf.h | 465 ++++++ src/conf/nwfilter_params.c | 318 ++++ src/conf/nwfilter_params.h | 53 src/datatypes.c | 142 + src/datatypes.h | 32 src/nwfilter/nwfilter_driver.c | 416 +++++ src/nwfilter/nwfilter_driver.h | 36 src/nwfilter/nwfilter_ebiptables_driver.c | 1414 ++++++++++++++++++ src/nwfilter/nwfilter_ebiptables_driver.h | 41 src/nwfilter/nwfilter_gentech_driver.c | 698 +++++++++ src/nwfilter/nwfilter_gentech_driver.h | 55 src/util/virterror.c | 27 16 files changed, 5956 insertions(+), 1 deletion(-) Index: libvirt-acl/src/conf/nwfilter_conf.c =================================================================== --- /dev/null +++ libvirt-acl/src/conf/nwfilter_conf.c @@ -0,0 +1,2244 @@ +/* + * nwfilter_conf.c: network filter XML processing + * (derived from storage_conf.c) + * + * Copyright (C) 2006-2010 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * Copyright (C) 2010 IBM Corporation + * + * 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 <fcntl.h> +#include <dirent.h> +#include <net/ethernet.h> + +#include "internal.h" + +#include "uuid.h" +#include "memory.h" +#include "virterror_internal.h" +#include "datatypes.h" +#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 + +#define virNWFilterError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_NWFILTER, code, __FILE__,\ + __FUNCTION__, __LINE__, fmt) + +VIR_ENUM_IMPL(virNWFilterRuleAction, VIR_NWFILTER_RULE_ACTION_LAST, + "drop", + "accept"); + +VIR_ENUM_IMPL(virNWFilterJumpTarget, VIR_NWFILTER_RULE_ACTION_LAST, + "DROP", + "ACCEPT"); + +VIR_ENUM_IMPL(virNWFilterRuleDirection, VIR_NWFILTER_RULE_DIRECTION_LAST, + "in", + "out", + "inout"); + +VIR_ENUM_IMPL(virNWFilterChainPolicy, VIR_NWFILTER_CHAIN_POLICY_LAST, + "ACCEPT", + "DROP"); + +VIR_ENUM_IMPL(virNWFilterEbtablesTable, VIR_NWFILTER_EBTABLES_TABLE_LAST, + "filter", + "nat", + "broute"); + +VIR_ENUM_IMPL(virNWFilterChainSuffix, VIR_NWFILTER_CHAINSUFFIX_LAST, + "root", + "arp", + "ipv4"); + + +/* + * a map entry for a simple static int-to-string map + */ +struct int_map { + int32_t attr; + const char *val; +}; + + +/* + * only one filter update allowed + */ +static virMutex updateMutex; + +static void +virNWFilterLockFilterUpdates(void) { + virMutexLock(&updateMutex); +} + +static void +virNWFilterUnlockFilterUpdates(void) { + virMutexUnlock(&updateMutex); +} + + +/* + * attribute names for the rules XML + */ +static const char srcmacaddr_str[] = "srcmacaddr"; +static const char srcmacmask_str[] = "srcmacmask"; +static const char dstmacaddr_str[] = "dstmacaddr"; +static const char dstmacmask_str[] = "dstmacmask"; +static const char arpsrcmacaddr_str[]= "arpsrcmacaddr"; +static const char arpdstmacaddr_str[]= "arpdstmacaddr"; +static const char arpsrcipaddr_str[] = "arpsrcipaddr"; +static const char arpdstipaddr_str[] = "arpdstipaddr"; +static const char srcipaddr_str[] = "srcipaddr"; +static const char srcipmask_str[] = "srcipmask"; +static const char dstipaddr_str[] = "dstipaddr"; +static const char dstipmask_str[] = "dstipmask"; +static const char srcportstart_str[] = "srcportstart"; +static const char srcportend_str[] = "srcportend"; +static const char dstportstart_str[] = "dstportstart"; +static const char dstportend_str[] = "dstportend"; +static const char dscp_str[] = "dscp"; + +#define SRCMACADDR srcmacaddr_str +#define SRCMACMASK srcmacmask_str +#define DSTMACADDR dstmacaddr_str +#define DSTMACMASK dstmacmask_str +#define ARPSRCMACADDR arpsrcmacaddr_str +#define ARPDSTMACADDR arpdstmacaddr_str +#define ARPSRCIPADDR arpsrcipaddr_str +#define ARPDSTIPADDR arpdstipaddr_str +#define SRCIPADDR srcipaddr_str +#define SRCIPMASK srcipmask_str +#define DSTIPADDR dstipaddr_str +#define DSTIPMASK dstipmask_str +#define SRCPORTSTART srcportstart_str +#define SRCPORTEND srcportend_str +#define DSTPORTSTART dstportstart_str +#define DSTPORTEND dstportend_str +#define DSCP dscp_str + + +/** + * intMapGetByInt: + * @intmap: Pointer to int-to-string map + * @attr: The attribute to look up + * @res: Pointer to string pointer for result + * + * Returns 1 if value was found with result returned, 0 otherwise. + * + * lookup a map entry given the integer. + */ +static bool +intMapGetByInt(const struct int_map *intmap, int32_t attr, const char **res) +{ + int i = 0; + bool found = 0; + while (intmap[i].val && !found) { + if (intmap[i].attr == attr) { + *res = intmap[i].val; + found = 1; + } + i++; + } + return found; +} + + +/** + * intMapGetByString: + * @intmap: Pointer to int-to-string map + * @str: Pointer to string for which to find the entry + * @casecmp : Whether to ignore case when doing string matching + * @result: Pointer to int for result + * + * Returns 0 if no entry was found, 1 otherwise. + * + * Do a lookup in the map trying to find an integer key using the string + * value. Returns 1 if entry was found with result returned, 0 otherwise. + */ +static bool +intMapGetByString(const struct int_map *intmap, const char *str, int casecmp, + int32_t *result) +{ + int i = 0; + bool found = 0; + while (intmap[i].val && !found) { + if ( (casecmp && STRCASEEQ(intmap[i].val, str)) || + STREQ (intmap[i].val, str) ) { + *result = intmap[i].attr; + found = 1; + } + i++; + } + return found; +} + + +void +virNWFilterRuleDefFree(virNWFilterRuleDefPtr def) { + int i; + if (!def) + return; + + for (i = 0; i < def->nvars; i++) + VIR_FREE(def->vars[i]); + + VIR_FREE(def->vars); + + VIR_FREE(def); +} + + +static void +virNWFilterIncludeDefFree(virNWFilterIncludeDefPtr inc) { + if (!inc) + return; + virNWFilterHashTableFree(inc->params); + VIR_FREE(inc->filterref); + VIR_FREE(inc); +} + + +static void +virNWFilterEntryFree(virNWFilterEntryPtr entry) { + if (!entry) + return; + + virNWFilterRuleDefFree(entry->rule); + virNWFilterIncludeDefFree(entry->include); + VIR_FREE(entry); +} + + +void +virNWFilterDefFree(virNWFilterDefPtr def) { + int i; + if (!def) + return; + + VIR_FREE(def->name); + + for (i = 0; i < def->nentries; i++) + virNWFilterEntryFree(def->filterEntries[i]); + + VIR_FREE(def->filterEntries); + + VIR_FREE(def); +} + + +void +virNWFilterPoolObjFree(virNWFilterPoolObjPtr obj) { + if (!obj) + return; + + virNWFilterDefFree(obj->def); + virNWFilterDefFree(obj->newDef); + + VIR_FREE(obj->configFile); + + virMutexDestroy(&obj->lock); + + VIR_FREE(obj); +} + + +void +virNWFilterPoolObjListFree(virNWFilterPoolObjListPtr pools) +{ + unsigned int i; + for (i = 0 ; i < pools->count ; i++) + virNWFilterPoolObjFree(pools->objs[i]); + VIR_FREE(pools->objs); + pools->count = 0; +} + + +static int +virNWFilterRuleDefAddVar(virConnectPtr conn ATTRIBUTE_UNUSED, + virNWFilterRuleDefPtr nwf, + nwItemDesc *item, + const char *var) +{ + int i = 0; + + if (nwf->vars) { + for (i = 0; i < nwf->nvars; i++) + if (STREQ(nwf->vars[i], var)) { + item->var = nwf->vars[i]; + return 0; + } + } + + if (VIR_REALLOC_N(nwf->vars, nwf->nvars+1) < 0) { + virReportOOMError(); + return 1; + } + + nwf->vars[nwf->nvars] = strdup(var); + + if (!nwf->vars[nwf->nvars]) { + virReportOOMError(); + return 1; + } + + item->var = nwf->vars[nwf->nvars++]; + + return 0; +} + + +void +virNWFilterPoolObjRemove(virNWFilterPoolObjListPtr pools, + virNWFilterPoolObjPtr pool) +{ + unsigned int i; + + virNWFilterPoolObjUnlock(pool); + + for (i = 0 ; i < pools->count ; i++) { + virNWFilterPoolObjLock(pools->objs[i]); + if (pools->objs[i] == pool) { + virNWFilterPoolObjUnlock(pools->objs[i]); + virNWFilterPoolObjFree(pools->objs[i]); + + if (i < (pools->count - 1)) + memmove(pools->objs + i, pools->objs + i + 1, + sizeof(*(pools->objs)) * (pools->count - (i + 1))); + + if (VIR_REALLOC_N(pools->objs, pools->count - 1) < 0) { + ; /* Failure to reduce memory allocation isn't fatal */ + } + pools->count--; + + break; + } + virNWFilterPoolObjUnlock(pools->objs[i]); + } +} + + + +typedef bool (*valueValidator)(enum attrDatatype datatype, void *valptr, + virNWFilterRuleDefPtr nwf); +typedef bool (*valueFormatter)(virBufferPtr buf, + virNWFilterRuleDefPtr nwf); + +typedef struct _virXMLAttr2Struct virXMLAttr2Struct; +struct _virXMLAttr2Struct +{ + const char *name; // attribute name + enum attrDatatype datatype; + int dataIdx; // offset of the hasXYZ boolean + valueValidator validator; // beyond-standard checkers + valueFormatter formatter; // beyond-standard formatter +}; + + +static const struct int_map macProtoMap[] = { + { + .attr = ETHERTYPE_ARP, + .val = "arp", + }, { + .attr = ETHERTYPE_IP, + .val = "ipv4", + }, { + .val = NULL, + } +}; + + +static bool +checkMacProtocolID(enum attrDatatype datatype, void *value, + virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED) +{ + int32_t res = -1; + const char *str; + + if (datatype == DATATYPE_STRING) { + if (intMapGetByString(macProtoMap, (char *)value, 1, &res) == 0) + res = -1; + } else if (datatype == DATATYPE_UINT16) { + if (intMapGetByInt(macProtoMap, + (int32_t)*(uint16_t *)value, &str) == 0) + res = -1; + } + + if (res != -1) { + nwf->p.ethHdrFilter.dataProtocolID.u.u16 = res; + return 1; + } + + return 0; +} + + +static bool +macProtocolIDFormatter(virBufferPtr buf, + virNWFilterRuleDefPtr nwf) +{ + const char *str = NULL; + + if (intMapGetByInt(macProtoMap, + nwf->p.ethHdrFilter.dataProtocolID.u.u16, + &str)) { + virBufferVSprintf(buf, "%s", str); + return 1; + } + return 0; +} + + +/* generic function to check for a valid (ipv4,ipv6, mac) mask + * A mask is valid of there is a sequence of 1's followed by a sequence + * of 0s or only 1s or only 0s + */ +static bool +checkValidMask(unsigned char *data, int len) +{ + uint32_t idx = 0; + uint8_t mask = 0x80; + int checkones = 1; + + while ((idx >> 3) < len) { + if (checkones) { + if (!(data[idx>>3] & mask)) + checkones = 0; + } else { + if ((data[idx>>3] & mask)) + return 0; + } + + idx++; + mask >>= 1; + if (!mask) + mask = 0x80; + } + return 1; +} + + +/* check for a valid IPv4 mask */ +static bool +checkIPv4Mask(enum attrDatatype datatype ATTRIBUTE_UNUSED, void *maskptr, + virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED) +{ + return checkValidMask(maskptr, 4); +} + + +static bool +checkMACMask(enum attrDatatype datatype ATTRIBUTE_UNUSED, + void *macMask, + virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED) +{ + return checkValidMask((unsigned char *)macMask, 6); +} + + +static int getMaskNumBits(const unsigned char *mask, int len) { + int i = 0; + while (i < (len << 3)) { + if (!(mask[i>>3] & (0x80 >> (i & 3)))) + break; + i++; + } + return i; +} + +/* + * supported arp opcode -- see 'ebtables -h arp' for the naming + */ +static const struct int_map arpOpcodeMap[] = { + { + .attr = 1, + .val = "Request", + } , { + .attr = 2, + .val = "Reply", + } , { + .attr = 3, + .val = "Request_Reverse", + } , { + .attr = 4, + .val = "Reply_Reverse", + } , { + .attr = 5, + .val = "DRARP_Request", + } , { + .attr = 6, + .val = "DRARP_Reply", + } , { + .attr = 7, + .val = "DRARP_Error", + } , { + .attr = 8, + .val = "InARP_Request", + } , { + .attr = 9, + .val = "ARP_NAK", + } , { + .val = NULL, + } +}; + + +static bool +arpOpcodeValidator(enum attrDatatype datatype, + void *value, + virNWFilterRuleDefPtr nwf) +{ + int32_t res = -1; + const char *str; + + if (datatype == DATATYPE_STRING) { + if (intMapGetByString(arpOpcodeMap, (char *)value, 1, &res) == 0) + res = -1; + } else if (datatype == DATATYPE_UINT16) { + if (intMapGetByInt(arpOpcodeMap, + (uint32_t)*(uint16_t *)value, &str) == 0) + res = -1; + } + + if (res != -1) { + nwf->p.arpHdrFilter.dataOpcode.u.u16 = res; + nwf->p.arpHdrFilter.dataOpcode.datatype = DATATYPE_UINT16; + return 1; + } + return 0; +} + + +static bool +arpOpcodeFormatter(virBufferPtr buf, + virNWFilterRuleDefPtr nwf) +{ + const char *str = NULL; + + if (intMapGetByInt(arpOpcodeMap, + nwf->p.arpHdrFilter.dataOpcode.u.u16, + &str)) { + virBufferVSprintf(buf, "%s", str); + return 1; + } + return 0; +} + + +static const struct int_map ipProtoMap[] = { + { + .attr = IPPROTO_TCP, + .val = "tcp", + } , { + .attr = IPPROTO_UDP, + .val = "udp", + } , { + .attr = IPPROTO_ICMP, + .val = "icmp", + } , { + .attr = IPPROTO_IGMP, + .val = "igmp", +#ifdef IPPROTO_SCTP + } , { + .attr = IPPROTO_SCTP, + .val = "sctp", +#endif + } , { + .val = NULL, + } +}; + + +static bool checkIPProtocolID(enum attrDatatype datatype, + void *value, + virNWFilterRuleDefPtr nwf) +{ + int32_t res = -1; + const char *str; + + if (datatype == DATATYPE_STRING) { + if (intMapGetByString(ipProtoMap, (char *)value, 1, &res) == 0) + res = -1; + } else if (datatype == DATATYPE_UINT8) { + // may just accept what user provides and not test... + if (intMapGetByInt(ipProtoMap, + (uint32_t)*(uint16_t *)value, &str) == 0) + res = -1; + } + + if (res != -1) { + nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u8 = res; + nwf->p.ipHdrFilter.ipHdr.dataProtocolID.datatype = DATATYPE_UINT8; + return 1; + } + return 0; +} + + +static bool +formatIPProtocolID(virBufferPtr buf, + virNWFilterRuleDefPtr nwf) +{ + const char *str = NULL; + + if (intMapGetByInt(ipProtoMap, + nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u8, + &str)) { + virBufferVSprintf(buf, "%s", str); + return 1; + } + return 0; +} + + +static bool +dscpValidator(enum attrDatatype datatype ATTRIBUTE_UNUSED, void *val, + virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED) +{ + uint8_t dscp = *(uint16_t *)val; + if (dscp > 63) + return 0; + return 1; +} + +#define COMMON_MAC_PROPS(STRUCT) \ + {\ + .name = SRCMACADDR,\ + .datatype = DATATYPE_MACADDR,\ + .dataIdx = offsetof(virNWFilterRuleDef,p.STRUCT.ethHdr.dataSrcMACAddr),\ + },\ + {\ + .name = SRCMACMASK,\ + .datatype = DATATYPE_MACMASK,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ethHdr.dataSrcMACMask),\ + },\ + {\ + .name = DSTMACADDR,\ + .datatype = DATATYPE_MACADDR,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ethHdr.dataDstMACAddr),\ + },\ + {\ + .name = DSTMACMASK,\ + .datatype = DATATYPE_MACMASK,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ethHdr.dataDstMACMask),\ + } + + +static const virXMLAttr2Struct macAttributes[] = { + COMMON_MAC_PROPS(ethHdrFilter), + { + .name = "protocolid", + .datatype = DATATYPE_UINT16 | DATATYPE_STRING, + .dataIdx = offsetof(virNWFilterRuleDef, p.ethHdrFilter.dataProtocolID), + .validator= checkMacProtocolID, + .formatter= macProtocolIDFormatter, + }, + { + .name = NULL, + } +}; + +static const virXMLAttr2Struct arpAttributes[] = { + COMMON_MAC_PROPS(arpHdrFilter), + { + .name = "hwtype", + .datatype = DATATYPE_UINT16, + .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataHWType), + }, { + .name = "protocoltype", + .datatype = DATATYPE_UINT16, + .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataProtocolType), + }, { + .name = "opcode", + .datatype = DATATYPE_UINT16 | DATATYPE_STRING, + .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataOpcode), + .validator= arpOpcodeValidator, + .formatter= arpOpcodeFormatter, + }, { + .name = ARPSRCMACADDR, + .datatype = DATATYPE_MACADDR, + .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPSrcMACAddr), + }, { + .name = ARPDSTMACADDR, + .datatype = DATATYPE_MACADDR, + .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPDstMACAddr), + }, { + .name = ARPSRCIPADDR, + .datatype = DATATYPE_IPADDR, + .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPSrcIPAddr), + }, { + .name = ARPDSTIPADDR, + .datatype = DATATYPE_IPADDR, + .dataIdx = offsetof(virNWFilterRuleDef, p.arpHdrFilter.dataARPDstIPAddr), + }, + { + .name = NULL, + } +}; + +static const virXMLAttr2Struct ipAttributes[] = { + COMMON_MAC_PROPS(ipHdrFilter), + { + .name = "version", + .datatype = DATATYPE_UINT8, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataIPVersion), + }, + { + .name = SRCIPADDR, + .datatype = DATATYPE_IPADDR, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataSrcIPAddr), + }, + { + .name = DSTIPADDR, + .datatype = DATATYPE_IPADDR, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataDstIPAddr), + }, + { + .name = SRCIPMASK, + .datatype = DATATYPE_IPMASK, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataSrcIPMask), + }, + { + .name = DSTIPMASK, + .datatype = DATATYPE_IPMASK, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataDstIPMask), + }, + { + .name = "protocol", + .datatype = DATATYPE_STRING, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataProtocolID), + .validator= checkIPProtocolID, + .formatter= formatIPProtocolID, + }, + { + .name = SRCPORTSTART, + .datatype = DATATYPE_UINT16, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataSrcPortStart), + }, + { + .name = SRCPORTEND, + .datatype = DATATYPE_UINT16, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataSrcPortEnd), + }, + { + .name = DSTPORTSTART, + .datatype = DATATYPE_UINT16, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataDstPortStart), + }, + { + .name = DSTPORTEND, + .datatype = DATATYPE_UINT16, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.portData.dataDstPortEnd), + }, + { + .name = DSCP, + .datatype = DATATYPE_UINT8, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipHdrFilter.ipHdr.dataDSCP), + .validator = dscpValidator, + }, + { + .name = NULL, + } +}; + + +typedef struct _virAttributes virAttributes; +struct _virAttributes { + const char *id; + const virXMLAttr2Struct *att; + enum virNWFilterRuleProtocolType prtclType; +}; + + +static const virAttributes virAttr[] = { + { + .id = "arp", + .att = arpAttributes, + .prtclType = VIR_NWFILTER_RULE_PROTOCOL_ARP, + }, { + .id = "mac", + .att = macAttributes, + .prtclType = VIR_NWFILTER_RULE_PROTOCOL_MAC, + }, { + .id = "ip", + .att = ipAttributes, + .prtclType = VIR_NWFILTER_RULE_PROTOCOL_IP, + }, { + .id = NULL, + } +}; + + +static bool +virNWMACAddressParser(const char *input, + nwMACAddressPtr output) +{ + if (virParseMacAddr(input, &output->addr[0]) == 0) + return 1; + return 0; +} + + +static bool +virNWIPv4AddressParser(const char *input, + nwIPAddressPtr output) +{ + int i; + char *endptr; + const char *n = input; + long int d; + + for (i = 0; i < 4; i++) { + d = strtol(n, &endptr, 10); + if (d < 0 || d > 255 || + (endptr - n > 3 ) || + (i <= 2 && *endptr != '.' ) || + (i == 3 && *endptr != '\0')) + return 0; + output->addr.ipv4Addr[i] = (unsigned char)d; + n = endptr + 1; + } + return 1; +} + + +static int +virNWFilterRuleDetailsParse(virConnectPtr conn ATTRIBUTE_UNUSED, + xmlNodePtr node, + virNWFilterRuleDefPtr nwf, + const virXMLAttr2Struct *att) +{ + int rc = 0; + int idx = 0; + char *prop; + int found = 0; + enum attrDatatype datatype, att_datatypes; + enum virNWFilterEntryItemFlags *flags ,match_flag = 0, flags_set = 0; + nwItemDesc *item; + int int_val; + void *data_ptr, *storage_ptr; + valueValidator validator; + char *match = virXMLPropString(node, "match"); + nwIPAddress ipaddr; + + if (match && STREQ(match, "no")) + match_flag = NWFILTER_ENTRY_ITEM_FLAG_IS_NEG; + VIR_FREE(match); + match = NULL; + + while (att[idx].name != NULL && rc == 0) { + prop = virXMLPropString(node, att[idx].name); + + item = (nwItemDesc *)((char *)nwf + att[idx].dataIdx); + flags = &item->flags; + flags_set = match_flag; + + if (prop) { + found = 0; + + validator = NULL; + + if (STRPREFIX(prop, "$")) { + flags_set |= NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR; + storage_ptr = NULL; + + if (virNWFilterRuleDefAddVar(conn, + nwf, + item, + &prop[1])) + rc = -1; + found = 1; + } + + datatype = 1; + + att_datatypes = att[idx].datatype; + + while (datatype <= DATATYPE_LAST && found == 0 && rc == 0) { + if ((att_datatypes & datatype)) { + + att_datatypes ^= datatype; + + validator = att[idx].validator; + + switch (datatype) { + + case DATATYPE_UINT8: + storage_ptr = &item->u.u8; + if (sscanf(prop, "%d", &int_val) == 1) { + if (int_val >= 0 && int_val <= 0xff) { + if (!validator) + *(uint8_t *)storage_ptr = int_val; + found = 1; + data_ptr = &int_val; + } else + rc = -1; + } else + rc = -1; + break; + + case DATATYPE_UINT16: + storage_ptr = &item->u.u16; + if (sscanf(prop, "%d", &int_val) == 1) { + if (int_val >= 0 && int_val <= 0xffff) { + if (!validator) + *(uint16_t *)storage_ptr = int_val; + found = 1; + data_ptr = &int_val; + } else + rc = -1; + } else + rc = -1; + break; + + case DATATYPE_IPADDR: + storage_ptr = &item->u.ipaddr; + if (!virNWIPv4AddressParser(prop, + (nwIPAddressPtr)storage_ptr)) { + rc = -1; + } + found = 1; + break; + + case DATATYPE_IPMASK: + storage_ptr = &item->u.u8; + if (!virNWIPv4AddressParser(prop, &ipaddr)) { + if (sscanf(prop, "%d", &int_val) == 1) { + if (int_val >= 0 && int_val <= 32) { + if (!validator) + *(uint8_t *)storage_ptr = + (uint8_t)int_val; + found = 1; + data_ptr = &int_val; + } else + rc = -1; + } else + rc = -1; + } else { + if (checkIPv4Mask(datatype, + ipaddr.addr.ipv4Addr, nwf)) + *(uint8_t *)storage_ptr = + getMaskNumBits(ipaddr.addr.ipv4Addr, + sizeof(ipaddr.addr.ipv4Addr)); + else + rc = -1; + found = 1; + } + break; + + case DATATYPE_MACADDR: + storage_ptr = &item->u.macaddr; + if (!virNWMACAddressParser(prop, + (nwMACAddressPtr)storage_ptr)) { + rc = -1; + } + found = 1; + break; + + case DATATYPE_MACMASK: + validator = checkMACMask; + storage_ptr = &item->u.macaddr; + if (!virNWMACAddressParser(prop, + (nwMACAddressPtr)storage_ptr)) { + rc = -1; + } + data_ptr = storage_ptr; + found = 1; + break; + + case DATATYPE_STRING: + if (!validator) { + // not supported + rc = -1; + break; + } + data_ptr = prop; + found = 1; + break; + + case DATATYPE_LAST: + default: + break; + } + } + + if (rc != 0 && att_datatypes != 0) { + rc = 0; + found = 0; + } + + datatype <<= 1; + } /* while */ + + if (found == 1 && rc == 0) { + *flags = NWFILTER_ENTRY_ITEM_FLAG_EXISTS | flags_set; + item->datatype = datatype >> 1; + if (validator) { + if (!validator(datatype >> 1, data_ptr, nwf)) { + rc = -1; + *flags = 0; + } + } + } + + if (!found || rc) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("%s has illegal value %s"), + att[idx].name, prop); + rc = -1; + } + VIR_FREE(prop); + } + idx++; + } + + return rc; +} + + + + +static virNWFilterIncludeDefPtr +virNWFilterIncludeParse(virConnectPtr conn, + xmlNodePtr cur) +{ + virNWFilterIncludeDefPtr ret; + + if (VIR_ALLOC(ret) < 0) { + virReportOOMError(); + return NULL; + } + + ret->filterref = virXMLPropString(cur, "filter"); + if (!ret->filterref) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", + _("rule node requires action attribute")); + goto err_exit; + } + + ret->params = virNWFilterParseParamAttributes(cur); + if (!ret->params) + goto err_exit; + +cleanup: + return ret; + +err_exit: + virNWFilterIncludeDefFree(ret); + ret = NULL; + goto cleanup; +} + + +static void +virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule) +{ +#define COPY_NEG_SIGN(A, B) \ + (A).flags = ((A).flags & ~NWFILTER_ENTRY_ITEM_FLAG_IS_NEG) | \ + ((B).flags & NWFILTER_ENTRY_ITEM_FLAG_IS_NEG); + + switch (rule->prtclType) { + case VIR_NWFILTER_RULE_PROTOCOL_MAC: + COPY_NEG_SIGN(rule->p.ethHdrFilter.ethHdr.dataSrcMACMask, + rule->p.ethHdrFilter.ethHdr.dataSrcMACAddr); + COPY_NEG_SIGN(rule->p.ethHdrFilter.ethHdr.dataDstMACMask, + rule->p.ethHdrFilter.ethHdr.dataDstMACAddr); + break; + + case VIR_NWFILTER_RULE_PROTOCOL_IP: + COPY_NEG_SIGN(rule->p.ipHdrFilter.ipHdr.dataSrcIPMask, + rule->p.ipHdrFilter.ipHdr.dataSrcIPAddr); + COPY_NEG_SIGN(rule->p.ipHdrFilter.ipHdr.dataDstIPMask, + rule->p.ipHdrFilter.ipHdr.dataDstIPAddr); + break; + + case VIR_NWFILTER_RULE_PROTOCOL_ARP: + case VIR_NWFILTER_RULE_PROTOCOL_NONE: + break; + } + +#undef COPY_NEG_SIGN +} + + +static virNWFilterRuleDefPtr +virNWFilterRuleParse(virConnectPtr conn, + xmlNodePtr node) +{ + char *action; + char *direction; + char *prio; + int found; + int found_i; + unsigned int priority; + + xmlNodePtr cur; + virNWFilterRuleDefPtr ret; + + if (VIR_ALLOC(ret) < 0) { + virReportOOMError(); + return NULL; + } + + action = virXMLPropString(node, "action"); + direction = virXMLPropString(node, "direction"); + prio = virXMLPropString(node, "priority"); + + if (!action) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", + _("rule node requires action attribute")); + goto err_exit; + } + + if ((ret->action = virNWFilterRuleActionTypeFromString(action)) < 0) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", + _("unknown rule action attribute value")); + goto err_exit; + } + + if (!direction) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", + _("rule node requires direction attribute")); + goto err_exit; + } + + if ((ret->tt = virNWFilterRuleDirectionTypeFromString(direction)) < 0) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", + _("unknown rule direction attribute value")); + goto err_exit; + } + + ret->priority = MAX_RULE_PRIORITY / 2; + + if (prio) { + if (sscanf(prio, "%d", (int *)&priority) == 1) { + if ((int)priority >= 0 && priority <= MAX_RULE_PRIORITY) + ret->priority = priority; + } + } + + cur = node->children; + + found = 0; + + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + int i = 0; + while (1) { + if (found) + i = found_i; + + if (xmlStrEqual(cur->name, BAD_CAST virAttr[i].id)) { + + found_i = i; + found = 1; + ret->prtclType = virAttr[i].prtclType; + + if (virNWFilterRuleDetailsParse(conn, + cur, + ret, + virAttr[i].att) < 0) { + /* we ignore malformed rules + goto err_exit; + */ + } + break; + } + if (!found) { + i++; + if (!virAttr[i].id) + break; + } else + break; + } + } + + cur = cur->next; + } + + virNWFilterRuleDefFixup(ret); + +cleanup: + VIR_FREE(prio); + VIR_FREE(action); + VIR_FREE(direction); + + return ret; + +err_exit: + virNWFilterRuleDefFree(ret); + ret = NULL; + goto cleanup; +} + + +static virNWFilterDefPtr +virNWFilterDefParseXML(virConnectPtr conn, + xmlXPathContextPtr ctxt) { + virNWFilterDefPtr ret; + xmlNodePtr curr = ctxt->node; + char *uuid = NULL; + char *chain = NULL; + virNWFilterEntryPtr entry; + + if (VIR_ALLOC(ret) < 0) { + virReportOOMError(); + return NULL; + } + + ret->name = virXPathString("string(./@name)", ctxt); + if (!ret->name) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("filter has no name")); + goto cleanup; + } + + ret->chainsuffix = VIR_NWFILTER_CHAINSUFFIX_ROOT; + chain = virXPathString("string(./@chain)", ctxt); + if (chain) { + if ((ret->chainsuffix = + virNWFilterChainSuffixTypeFromString(chain)) < 0) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown chain suffix '%s'"), chain); + goto cleanup; + } + } + + uuid = virXPathString("string(./uuid)", ctxt); + if (uuid == NULL) { + if (virUUIDGenerate(ret->uuid) < 0) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("unable to generate uuid")); + goto cleanup; + } + } else { + if (virUUIDParse(uuid, ret->uuid) < 0) { + virNWFilterReportError(conn, VIR_ERR_XML_ERROR, + "%s", _("malformed uuid element")); + goto cleanup; + } + VIR_FREE(uuid); + } + + curr = curr->children; + + while (curr != NULL) { + if (curr->type == XML_ELEMENT_NODE) { + if (VIR_ALLOC(entry) < 0) { + virReportOOMError(); + goto cleanup; + } + + /* ignore malformed rule and include elements */ + if (xmlStrEqual(curr->name, BAD_CAST "rule")) + entry->rule = virNWFilterRuleParse(conn, curr); + else if (xmlStrEqual(curr->name, BAD_CAST "filterref")) + entry->include = virNWFilterIncludeParse(conn, curr); + + if (entry->rule || entry->include) { + if (VIR_REALLOC_N(ret->filterEntries, ret->nentries+1) < 0) { + VIR_FREE(entry); + virReportOOMError(); + goto cleanup; + } + ret->filterEntries[ret->nentries++] = entry; + } else + VIR_FREE(entry); + } + curr = curr->next; + } + + VIR_FREE(chain); + + return ret; + + cleanup: + VIR_FREE(chain); + VIR_FREE(uuid); + return NULL; +} + + +/* Called from SAX on parsing errors in the XML. */ +static void +catchXMLError (void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) +{ + xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; + + if (ctxt) { + virConnectPtr conn = ctxt->_private; + + if (conn && + conn->err.code == VIR_ERR_NONE && + ctxt->lastError.level == XML_ERR_FATAL && + ctxt->lastError.message != NULL) { + virNWFilterReportError(conn, VIR_ERR_XML_DETAIL, + _("at line %d: %s"), + ctxt->lastError.line, + ctxt->lastError.message); + } + } +} + + +virNWFilterDefPtr +virNWFilterDefParseNode(virConnectPtr conn, + xmlDocPtr xml, + xmlNodePtr root) { + xmlXPathContextPtr ctxt = NULL; + virNWFilterDefPtr def = NULL; + + if (STRNEQ((const char *)root->name, "filter")) { + virNWFilterReportError(conn, VIR_ERR_XML_ERROR, + "%s", + _("unknown root element for nw filter pool")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virReportOOMError(); + goto cleanup; + } + + ctxt->node = root; + def = virNWFilterDefParseXML(conn, ctxt); + +cleanup: + xmlXPathFreeContext(ctxt); + return def; +} + + +static virNWFilterDefPtr +virNWFilterDefParse(virConnectPtr conn, + const char *xmlStr, + const char *filename) { + virNWFilterDefPtr ret = NULL; + xmlParserCtxtPtr pctxt; + xmlDocPtr xml = NULL; + xmlNodePtr node = NULL; + + /* Set up a parser context so we can catch the details of XML errors. */ + pctxt = xmlNewParserCtxt (); + if (!pctxt || !pctxt->sax) + goto cleanup; + pctxt->sax->error = catchXMLError; + pctxt->_private = conn; + + if (conn) virResetError (&conn->err); + if (filename) { + xml = xmlCtxtReadFile (pctxt, filename, NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOWARNING); + } else { + xml = xmlCtxtReadDoc (pctxt, BAD_CAST xmlStr, + "nwfilter.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOWARNING); + } + + if (!xml) { + if (conn && conn->err.code == VIR_ERR_NONE) + virNWFilterReportError(conn, VIR_ERR_XML_ERROR, + "%s",_("failed to parse xml document")); + goto cleanup; + } + + node = xmlDocGetRootElement(xml); + if (node == NULL) { + virNWFilterReportError(conn, VIR_ERR_XML_ERROR, + "%s", _("missing root element")); + goto cleanup; + } + + ret = virNWFilterDefParseNode(conn, xml, node); + + xmlFreeParserCtxt (pctxt); + xmlFreeDoc(xml); + + return ret; + + cleanup: + xmlFreeParserCtxt (pctxt); + xmlFreeDoc(xml); + return NULL; +} + + +virNWFilterDefPtr +virNWFilterDefParseString(virConnectPtr conn, + const char *xmlStr) +{ + return virNWFilterDefParse(conn, xmlStr, NULL); +} + + +virNWFilterDefPtr +virNWFilterDefParseFile(virConnectPtr conn, + const char *filename) +{ + return virNWFilterDefParse(conn, NULL, filename); +} + + +virNWFilterPoolObjPtr +virNWFilterPoolObjFindByUUID(virNWFilterPoolObjListPtr pools, + const unsigned char *uuid) +{ + unsigned int i; + + for (i = 0 ; i < pools->count ; i++) { + virNWFilterPoolObjLock(pools->objs[i]); + if (!memcmp(pools->objs[i]->def->uuid, uuid, VIR_UUID_BUFLEN)) + return pools->objs[i]; + virNWFilterPoolObjUnlock(pools->objs[i]); + } + + return NULL; +} + + +virNWFilterPoolObjPtr +virNWFilterPoolObjFindByName(virNWFilterPoolObjListPtr pools, + const char *name) +{ + unsigned int i; + + for (i = 0 ; i < pools->count ; i++) { + virNWFilterPoolObjLock(pools->objs[i]); + if (STREQ(pools->objs[i]->def->name, name)) + return pools->objs[i]; + virNWFilterPoolObjUnlock(pools->objs[i]); + } + + return NULL; +} + + +int virNWFilterSaveXML(virConnectPtr conn, + const char *configDir, + virNWFilterDefPtr def, + const char *xml) +{ + char *configFile = NULL; + int fd = -1, ret = -1; + size_t towrite; + int err; + + if ((configFile = virNWFilterConfigFile(conn, configDir, def->name)) == NULL) + goto cleanup; + + if ((err = virFileMakePath(configDir))) { + virReportSystemError(err, + _("cannot create config directory '%s'"), + configDir); + goto cleanup; + } + + if ((fd = open(configFile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR )) < 0) { + virReportSystemError(errno, + _("cannot create config file '%s'"), + configFile); + goto cleanup; + } + + towrite = strlen(xml); + if (safewrite(fd, xml, towrite) < 0) { + virReportSystemError(errno, + _("cannot write config file '%s'"), + configFile); + goto cleanup; + } + + if (close(fd) < 0) { + virReportSystemError(errno, + _("cannot save config file '%s'"), + configFile); + goto cleanup; + } + + ret = 0; + + cleanup: + if (fd != -1) + close(fd); + + VIR_FREE(configFile); + + return ret; +} + + +int virNWFilterSaveConfig(virConnectPtr conn, + const char *configDir, + virNWFilterDefPtr def) +{ + int ret = -1; + char *xml; + + if (!(xml = virNWFilterDefFormat(conn, def))) + goto cleanup; + + if (virNWFilterSaveXML(conn, configDir, def, xml)) + goto cleanup; + + ret = 0; +cleanup: + VIR_FREE(xml); + return ret; +} + + +static int +_virNWFilterDefLoopDetect(virConnectPtr conn, + virNWFilterPoolObjListPtr pools, + virNWFilterDefPtr def, + const char *filtername) +{ + int rc = 0; + int i; + virNWFilterEntryPtr entry; + virNWFilterPoolObjPtr obj; + + if (!def) + return 0; + + for (i = 0; i < def->nentries; i++) { + entry = def->filterEntries[i]; + if (entry->include) { + + if (STREQ(filtername, entry->include->filterref)) { + rc = 1; + break; + } + + obj = virNWFilterPoolObjFindByName(pools, + entry->include->filterref); + if (obj) { + rc = _virNWFilterDefLoopDetect(conn, + pools, + obj->def, filtername); + + virNWFilterPoolObjUnlock(obj); + if (rc) + break; + } + } + } + + return rc; +} + + +/* + * virNWFilterDefLoopDetect: + * @conn: pointer to virConnect object + * @pools : the pools to search + * @def : the filter definiton that may add a loop and is to be tested + * + * Detect a loop introduced through the filters being able to + * reference each other. + * + * Returns 0 in case no loop was detected, 1 otherwise. + */ +static int +virNWFilterDefLoopDetect(virConnectPtr conn, + virNWFilterPoolObjListPtr pools, + virNWFilterDefPtr def) +{ + return _virNWFilterDefLoopDetect(conn, pools, def, def->name); +} + +int nCallbackDriver; +#define MAX_CALLBACK_DRIVER 10 +static virNWFilterCallbackDriverPtr callbackDrvArray[MAX_CALLBACK_DRIVER]; + +void +virNWFilterRegisterCallbackDriver(virNWFilterCallbackDriverPtr cbd) +{ + if (nCallbackDriver < MAX_CALLBACK_DRIVER) { + callbackDrvArray[nCallbackDriver++] = cbd; + } +} + + +enum UpdateStep { + STEP_APPLY_NEW, + STEP_TEAR_NEW, + STEP_TEAR_OLD, +}; + +struct cbStruct { + virConnectPtr conn; + enum UpdateStep step; + int err; +}; + +static void +virNWFilterDomainFWUpdateCB(void *payload, + const char *name 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); +} + + +static int +virNWFilterTriggerVMFilterRebuild(virConnectPtr conn) +{ + int i; + int err; + struct cbStruct cb = { + .conn = conn, + .err = 0, + .step = STEP_APPLY_NEW, + }; + + for (i = 0; i < nCallbackDriver; i++) { + callbackDrvArray[i]->vmFilterRebuild(conn, + virNWFilterDomainFWUpdateCB, + &cb); + } + + err = cb.err; + + if (err) { + cb.step = STEP_TEAR_NEW; // rollback + cb.err = 0; + + for (i = 0; i < nCallbackDriver; i++) + callbackDrvArray[i]->vmFilterRebuild(conn, + virNWFilterDomainFWUpdateCB, + &cb); + } else { + cb.step = STEP_TEAR_OLD; // switch over + + for (i = 0; i < nCallbackDriver; i++) + callbackDrvArray[i]->vmFilterRebuild(conn, + virNWFilterDomainFWUpdateCB, + &cb); + } + + return err; +} + + +int +virNWFilterTestUnassignDef(virConnectPtr conn, + virNWFilterPoolObjPtr pool) +{ + int rc = 0; + + virNWFilterLockFilterUpdates(); + + pool->wantRemoved = 1; + // trigger the update on VMs referencing the filter + if (virNWFilterTriggerVMFilterRebuild(conn)) + rc = 1; + + pool->wantRemoved = 0; + virNWFilterUnlockFilterUpdates(); + return rc; +} + + +virNWFilterPoolObjPtr +virNWFilterPoolObjAssignDef(virConnectPtr conn, + virNWFilterPoolObjListPtr pools, + virNWFilterDefPtr def) +{ + virNWFilterPoolObjPtr pool; + + if (virNWFilterDefLoopDetect(conn, pools, def)) { + virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER, + "%s", _("filter would introduce loop")); + return NULL; + } + + if ((pool = virNWFilterPoolObjFindByName(pools, def->name))) { + virNWFilterLockFilterUpdates(); + pool->newDef = def; + // trigger the update on VMs referencing the filter + if (virNWFilterTriggerVMFilterRebuild(conn)) { + pool->newDef = NULL; + virNWFilterUnlockFilterUpdates(); + virNWFilterPoolObjUnlock(pool); + return NULL; + } + + virNWFilterDefFree(pool->def); + pool->def = def; + pool->newDef = NULL; + virNWFilterUnlockFilterUpdates(); + return pool; + } + + if (VIR_ALLOC(pool) < 0) { + virReportOOMError(); + return NULL; + } + + if (virMutexInitRecursive(&pool->lock) < 0) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(pool); + return NULL; + } + virNWFilterPoolObjLock(pool); + pool->active = 0; + pool->def = def; + + if (VIR_REALLOC_N(pools->objs, pools->count+1) < 0) { + pool->def = NULL; + virNWFilterPoolObjUnlock(pool); + virNWFilterPoolObjFree(pool); + virReportOOMError(); + return NULL; + } + pools->objs[pools->count++] = pool; + + return pool; +} + + +static virNWFilterPoolObjPtr +virNWFilterPoolObjLoad(virConnectPtr conn, + virNWFilterPoolObjListPtr pools, + const char *file, + const char *path) +{ + virNWFilterDefPtr def; + virNWFilterPoolObjPtr pool; + + if (!(def = virNWFilterDefParseFile(conn, path))) { + return NULL; + } + + if (!virFileMatchesNameSuffix(file, def->name, ".xml")) { + virNWFilterError(conn, VIR_ERR_INVALID_NWFILTER, + "NWFilter pool config filename '%s' does not match pool name '%s'", + path, def->name); + virNWFilterDefFree(def); + return NULL; + } + + if (!(pool = virNWFilterPoolObjAssignDef(conn, pools, def))) { + virNWFilterDefFree(def); + return NULL; + } + + pool->configFile = strdup(path); + if (pool->configFile == NULL) { + virReportOOMError(); + virNWFilterDefFree(def); + return NULL; + } + + return pool; +} + + +int +virNWFilterPoolLoadAllConfigs(virConnectPtr conn, + virNWFilterPoolObjListPtr pools, + const char *configDir) +{ + DIR *dir; + struct dirent *entry; + + if (!(dir = opendir(configDir))) { + if (errno == ENOENT) { + return 0; + } + virReportSystemError(errno, _("Failed to open dir '%s'"), + configDir); + return -1; + } + + while ((entry = readdir(dir))) { + char path[PATH_MAX]; + virNWFilterPoolObjPtr pool; + + if (entry->d_name[0] == '.') + continue; + + if (!virFileHasSuffix(entry->d_name, ".xml")) + continue; + + if (virFileBuildPath(configDir, entry->d_name, + NULL, path, PATH_MAX) < 0) { + virNWFilterError(conn, VIR_ERR_INTERNAL_ERROR, + "Config filename '%s/%s' is too long", + configDir, entry->d_name); + continue; + } + + pool = virNWFilterPoolObjLoad(conn, pools, entry->d_name, path); + if (pool) + virNWFilterPoolObjUnlock(pool); + } + + closedir(dir); + + return 0; +} + + +int +virNWFilterPoolObjSaveDef(virConnectPtr conn, + virNWFilterDriverStatePtr driver, + virNWFilterPoolObjPtr pool, + virNWFilterDefPtr def) +{ + char *xml; + int fd = -1, ret = -1; + ssize_t towrite; + + if (!pool->configFile) { + int err; + char path[PATH_MAX]; + + if ((err = virFileMakePath(driver->configDir))) { + virReportSystemError(err, + _("cannot create config directory %s"), + driver->configDir); + return -1; + } + + if (virFileBuildPath(driver->configDir, def->name, ".xml", + path, sizeof(path)) < 0) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot construct config file path")); + return -1; + } + if (!(pool->configFile = strdup(path))) { + virReportOOMError(); + return -1; + } + } + + if (!(xml = virNWFilterDefFormat(conn, def))) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to generate XML")); + return -1; + } + + if ((fd = open(pool->configFile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR )) < 0) { + virReportSystemError(errno, + _("cannot create config file %s"), + pool->configFile); + goto cleanup; + } + + towrite = strlen(xml); + if (safewrite(fd, xml, towrite) != towrite) { + virReportSystemError(errno, + _("cannot write config file %s"), + pool->configFile); + goto cleanup; + } + + if (close(fd) < 0) { + virReportSystemError(errno, + _("cannot save config file %s"), + pool->configFile); + goto cleanup; + } + + ret = 0; + + cleanup: + if (fd != -1) + close(fd); + + VIR_FREE(xml); + + return ret; +} + + +int +virNWFilterPoolObjDeleteDef(virConnectPtr conn, + virNWFilterPoolObjPtr pool) +{ + if (!pool->configFile) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("no config file for %s"), pool->def->name); + return -1; + } + + if (unlink(pool->configFile) < 0) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot remove config for %s"), + pool->def->name); + return -1; + } + + return 0; +} + + +static void +virNWIPAddressFormat(virBufferPtr buf, nwIPAddressPtr ipaddr) +{ + if (!ipaddr->isIPv6) { + virBufferVSprintf(buf, "%d.%d.%d.%d", + ipaddr->addr.ipv4Addr[0], + ipaddr->addr.ipv4Addr[1], + ipaddr->addr.ipv4Addr[2], + ipaddr->addr.ipv4Addr[3]); + } else { + virBufferAddLit(buf, "MISSING IPv6 ADDRESS FORMATTER"); + } +} + + +static void +virNWFilterRuleDefDetailsFormat(virConnectPtr conn, + virBufferPtr buf, + const char *type, + const virXMLAttr2Struct *att, + virNWFilterRuleDefPtr def) +{ + int i, j; + bool typeShown = 0; + bool neverShown = 1; + enum match { + MATCH_NONE = 0, + MATCH_YES, + MATCH_NO + } matchShown = MATCH_NONE; + nwItemDesc *item; + + while (att[i].name) { + item = (nwItemDesc *)((char *)def + att[i].dataIdx); + enum virNWFilterEntryItemFlags flags = item->flags; + void *storage_ptr; + if ((flags & NWFILTER_ENTRY_ITEM_FLAG_EXISTS)) { + if (!typeShown) { + virBufferVSprintf(buf, " <%s", type); + typeShown = 1; + neverShown = 0; + } + + if ((flags & NWFILTER_ENTRY_ITEM_FLAG_IS_NEG)) { + if (matchShown == MATCH_NONE) { + virBufferAddLit(buf, " match='no'"); + matchShown = MATCH_NO; + } else if (matchShown == MATCH_YES) { + virBufferAddLit(buf, "/>\n"); + typeShown = 0; + matchShown = MATCH_NONE; + continue; + } + } else { + if (matchShown == MATCH_NO) { + virBufferAddLit(buf, "/>\n"); + typeShown = 0; + matchShown = MATCH_NONE; + continue; + } + matchShown = MATCH_YES; + } + + virBufferVSprintf(buf, " %s='", + att[i].name); + if (att[i].formatter) { + if (!att[i].formatter(buf, def)) { + virNWFilterReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("formatter for %s %s reported error"), + type, + att[i].name); + goto err_exit; + } + } else if ((flags & NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR)) { + virBufferVSprintf(buf, "$%s", item->var); + } else { + switch (att[i].datatype) { + + case DATATYPE_IPMASK: + // display all masks in CIDR format + case DATATYPE_UINT8: + storage_ptr = &item->u.u8; + virBufferVSprintf(buf, "%d", *(uint8_t *)storage_ptr); + break; + + case DATATYPE_UINT16: + storage_ptr = &item->u.u16; + virBufferVSprintf(buf, "%d", *(uint16_t *)storage_ptr); + break; + + case DATATYPE_IPADDR: + storage_ptr = &item->u.ipaddr; + virNWIPAddressFormat(buf, + (nwIPAddressPtr)storage_ptr); + break; + + case DATATYPE_MACMASK: + case DATATYPE_MACADDR: + storage_ptr = &item->u.macaddr; + for (j = 0; j < 6; j++) + virBufferVSprintf(buf, "%02x%s", + ((nwMACAddressPtr)storage_ptr)->addr[j], + (j < 5) ? ":" : ""); + break; + + case DATATYPE_STRING: + default: + virBufferVSprintf(buf, + "UNSUPPORTED DATATYPE 0x%02x\n", + att[i].datatype); + } + } + virBufferAddLit(buf, "'"); + } + i++; + } + if (typeShown) + virBufferAddLit(buf, "/>\n"); + + if (neverShown) + virBufferVSprintf(buf, + " <%s/>\n", type); + +err_exit: + return; +} + + +static char * +virNWFilterRuleDefFormat(virConnectPtr conn, + virNWFilterRuleDefPtr def) +{ + int i; + virBuffer buf = VIR_BUFFER_INITIALIZER; + virBuffer buf2 = VIR_BUFFER_INITIALIZER; + char *data; + + virBufferVSprintf(&buf, " <rule action='%s' direction='%s' priority='%d'", + virNWFilterRuleActionTypeToString(def->action), + virNWFilterRuleDirectionTypeToString(def->tt), + def->priority); + + i = 0; + while (virAttr[i].id) { + if (virAttr[i].prtclType == def->prtclType) { + virNWFilterRuleDefDetailsFormat(conn, + &buf2, + virAttr[i].id, + virAttr[i].att, + def); + break; + } + i++; + } + + if (virBufferError(&buf2)) + goto no_memory; + + data = virBufferContentAndReset(&buf2); + + if (data) { + virBufferAddLit(&buf, ">\n"); + virBufferVSprintf(&buf, "%s </rule>\n", data); + VIR_FREE(data); + } else + virBufferAddLit(&buf, "/>\n"); + + if (virBufferError(&buf)) + goto no_memory; + + return virBufferContentAndReset(&buf); + +no_memory: + virReportOOMError(); + virBufferFreeAndReset(&buf); + virBufferFreeAndReset(&buf2); + + return NULL; +} + + +static char * +virNWFilterIncludeDefFormat(virNWFilterIncludeDefPtr inc) +{ + char *attrs; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferVSprintf(&buf," <filterref filter='%s'", + inc->filterref); + + attrs = virNWFilterFormatParamAttributes(inc->params, " "); + + if (!attrs || strlen(attrs) <= 1) + virBufferAddLit(&buf, "/>\n"); + else + virBufferVSprintf(&buf, ">\n%s </filterref>\n", attrs); + + if (virBufferError(&buf)) { + virReportOOMError(); + virBufferFreeAndReset(&buf); + return NULL; + } + + return virBufferContentAndReset(&buf); +} + + +static char * +virNWFilterEntryFormat(virConnectPtr conn, + virNWFilterEntryPtr entry) +{ + if (entry->rule) + return virNWFilterRuleDefFormat(conn, entry->rule); + return virNWFilterIncludeDefFormat(entry->include); +} + + +char * +virNWFilterDefFormat(virConnectPtr conn, + virNWFilterDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char uuid[VIR_UUID_STRING_BUFLEN]; + int i; + char *xml; + + virBufferVSprintf(&buf, "<filter name='%s' chain='%s'", + def->name, + virNWFilterChainSuffixTypeToString(def->chainsuffix)); + virBufferAddLit(&buf, ">\n"); + + virUUIDFormat(def->uuid, uuid); + virBufferVSprintf(&buf," <uuid>%s</uuid>\n", uuid); + + for (i = 0; i < def->nentries; i++) { + xml = virNWFilterEntryFormat(conn, def->filterEntries[i]); + if (!xml) + goto err_exit; + virBufferVSprintf(&buf, "%s", xml); + VIR_FREE(xml); + } + + virBufferAddLit(&buf, "</filter>\n"); + + if (virBufferError(&buf)) + goto no_memory; + + return virBufferContentAndReset(&buf); + + no_memory: + virReportOOMError(); + + err_exit: + virBufferFreeAndReset(&buf); + return NULL; +} + + +char *virNWFilterConfigFile(virConnectPtr conn ATTRIBUTE_UNUSED, + const char *dir, + const char *name) +{ + char *ret = NULL; + + if (virAsprintf(&ret, "%s/%s.xml", dir, name) < 0) { + virReportOOMError(); + return NULL; + } + + return ret; +} + + +int virNWFilterConfLayerInit(void) +{ + if (virMutexInit(&updateMutex)) + return 1; + + if (virNWFilterParamConfLayerInit()) + return 1; + + return 0; +} + + +void virNWFilterConfLayerShutdown(void) +{ + virNWFilterParamConfLayerShutdown(); +} + + +void virNWFilterPoolObjLock(virNWFilterPoolObjPtr obj) +{ + virMutexLock(&obj->lock); +} + +void virNWFilterPoolObjUnlock(virNWFilterPoolObjPtr obj) +{ + virMutexUnlock(&obj->lock); +} Index: libvirt-acl/src/conf/nwfilter_conf.h =================================================================== --- /dev/null +++ libvirt-acl/src/conf/nwfilter_conf.h @@ -0,0 +1,465 @@ +/* + * nwfilter_conf.h: network filter XML processing + * (derived from storage_conf.h) + * + * Copyright (C) 2006-2010 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * Copyright (C) 2010 IBM Corporation + * + * 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_CONF_H +#define NWFILTER_CONF_H + +#include <stdint.h> +#include <stddef.h> + +#include "internal.h" +#include "util.h" +#include "hash.h" +#include "xml.h" + +/** + * Chain suffix size is: + * max. user define table name length - + * sizeof("FO-") - + * max. interface name size - + * sizeof("-") - + * terminating '0' = + * 32-3-15-1-1 = 12 + */ +#define MAX_CHAIN_SUFFIX_SIZE 12 + + +enum virNWFilterEntryItemFlags { + NWFILTER_ENTRY_ITEM_FLAG_EXISTS = 1 << 0, + NWFILTER_ENTRY_ITEM_FLAG_IS_NEG = 1 << 1, + NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR = 1 << 2, +}; + + +#define HAS_ENTRY_ITEM(data) \ + (((data)->flags) & NWFILTER_ENTRY_ITEM_FLAG_EXISTS) + +#define ENTRY_GET_NEG_SIGN(data) \ + ((((data)->flags) & NWFILTER_ENTRY_ITEM_FLAG_IS_NEG) ? "!" : "") + +// datatypes appearing in rule attributes +enum attrDatatype { + DATATYPE_UINT16 = (1 << 0), + DATATYPE_UINT8 = (1 << 1), + DATATYPE_MACADDR = (1 << 2), + DATATYPE_MACMASK = (1 << 3), + DATATYPE_IPADDR = (1 << 4), + DATATYPE_IPMASK = (1 << 5), + DATATYPE_STRING = (1 << 6), + + DATATYPE_LAST = (1 << 7), +}; + + +typedef struct _nwMACAddress nwMACAddress; +typedef nwMACAddress *nwMACAddressPtr; +struct _nwMACAddress { + unsigned char addr[6]; +}; + + +typedef struct _nwIPAddress nwIPAddress; +typedef nwIPAddress *nwIPAddressPtr; +struct _nwIPAddress { + int isIPv6; + union { + unsigned char ipv4Addr[4]; + /* unsigned char ipv6Addr[16]; future :-) */ + } addr; +}; + + +typedef struct _nwItemDesc nwItemDesc; +typedef nwItemDesc *nwItemDescPtr; +struct _nwItemDesc { + enum virNWFilterEntryItemFlags flags; + char *var; + enum attrDatatype datatype; + union { + nwMACAddress macaddr; + nwIPAddress ipaddr; + uint8_t u8; + uint16_t u16; + char protocolID[10]; + } u; +}; + + +typedef struct _ethHdrDataDef ethHdrDataDef; +typedef ethHdrDataDef *ethHdrDataDefPtr; +struct _ethHdrDataDef { + nwItemDesc dataSrcMACAddr; + nwItemDesc dataSrcMACMask; + nwItemDesc dataDstMACAddr; + nwItemDesc dataDstMACMask; +}; + + +typedef struct _ethHdrFilterDef ethHdrFilterDef; +typedef ethHdrFilterDef *ethHdrFilterDefPtr; +struct _ethHdrFilterDef { + ethHdrDataDef ethHdr; + nwItemDesc dataProtocolID; +}; + + +typedef struct _arpHdrFilterDef arpHdrFilterDef; +typedef arpHdrFilterDef *arpHdrFilterDefPtr; +struct _arpHdrFilterDef { + ethHdrDataDef ethHdr; + nwItemDesc dataHWType; + nwItemDesc dataProtocolType; + nwItemDesc dataOpcode; + nwItemDesc dataARPSrcMACAddr; + nwItemDesc dataARPSrcIPAddr; + nwItemDesc dataARPDstMACAddr; + nwItemDesc dataARPDstIPAddr; +}; + + +typedef struct _ipHdrDataDef ipHdrDataDef; +typedef ipHdrDataDef *ipHdrDataDefPtr; +struct _ipHdrDataDef { + nwItemDesc dataIPVersion; + nwItemDesc dataSrcIPAddr; + nwItemDesc dataSrcIPMask; + nwItemDesc dataDstIPAddr; + nwItemDesc dataDstIPMask; + nwItemDesc dataProtocolID; + nwItemDesc dataDSCP; +}; + + +typedef struct _portDataDef portDataDef; +typedef portDataDef *portDataDefPtr; +struct _portDataDef { + nwItemDesc dataSrcPortStart; + nwItemDesc dataSrcPortEnd; + nwItemDesc dataDstPortStart; + nwItemDesc dataDstPortEnd; +}; + + +typedef struct _ipHdrFilterDef ipHdrFilterDef; +typedef ipHdrFilterDef *ipHdrFilterDefPtr; +struct _ipHdrFilterDef { + ethHdrDataDef ethHdr; + ipHdrDataDef ipHdr; + portDataDef portData; +}; + + +enum virNWFilterRuleActionType { + VIR_NWFILTER_RULE_ACTION_DROP = 0, + VIR_NWFILTER_RULE_ACTION_ACCEPT, + + VIR_NWFILTER_RULE_ACTION_LAST, +}; + +enum virNWFilterRuleDirectionType { + VIR_NWFILTER_RULE_DIRECTION_IN = 0, + VIR_NWFILTER_RULE_DIRECTION_OUT, + VIR_NWFILTER_RULE_DIRECTION_INOUT, + + VIR_NWFILTER_RULE_DIRECTION_LAST, +}; + +enum virNWFilterChainPolicyType { + VIR_NWFILTER_CHAIN_POLICY_ACCEPT = 0, + VIR_NWFILTER_CHAIN_POLICY_DROP, + + VIR_NWFILTER_CHAIN_POLICY_LAST, +}; + +enum virNWFilterRuleProtocolType { + VIR_NWFILTER_RULE_PROTOCOL_NONE = 0, + VIR_NWFILTER_RULE_PROTOCOL_MAC, + VIR_NWFILTER_RULE_PROTOCOL_ARP, + VIR_NWFILTER_RULE_PROTOCOL_IP, +}; + +enum virNWFilterEbtablesTableType { + VIR_NWFILTER_EBTABLES_TABLE_FILTER = 0, + VIR_NWFILTER_EBTABLES_TABLE_NAT, + VIR_NWFILTER_EBTABLES_TABLE_BROUTE, + + VIR_NWFILTER_EBTABLES_TABLE_LAST, +}; + + +#define MAX_RULE_PRIORITY 1000 + + +typedef struct _virNWFilterRuleDef virNWFilterRuleDef; +typedef virNWFilterRuleDef *virNWFilterRuleDefPtr; +struct _virNWFilterRuleDef { + unsigned int priority; + int action; /*enum virNWFilterRuleActionType*/ + int tt; /*enum virNWFilterRuleDirectionType*/ + enum virNWFilterRuleProtocolType prtclType; + union { + ethHdrFilterDef ethHdrFilter; + arpHdrFilterDef arpHdrFilter; + ipHdrFilterDef ipHdrFilter; + } p; + + int nvars; + char **vars; +}; + + +typedef struct _virNWFilterIncludeDef virNWFilterIncludeDef; +typedef virNWFilterIncludeDef *virNWFilterIncludeDefPtr; +struct _virNWFilterIncludeDef { + char *filterref; + virNWFilterHashTablePtr params; +}; + + +typedef struct _virNWFilterEntry virNWFilterEntry; +typedef virNWFilterEntry *virNWFilterEntryPtr; +struct _virNWFilterEntry { + virNWFilterRuleDef *rule; + virNWFilterIncludeDef *include; +}; + +enum virNWFilterChainSuffixType { + VIR_NWFILTER_CHAINSUFFIX_ROOT = 0, + VIR_NWFILTER_CHAINSUFFIX_ARP, + VIR_NWFILTER_CHAINSUFFIX_IPv4, + + VIR_NWFILTER_CHAINSUFFIX_LAST, +}; + + +typedef struct _virNWFilterDef virNWFilterDef; +typedef virNWFilterDef *virNWFilterDefPtr; + +struct _virNWFilterDef { + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; + + int chainsuffix; /*enum virNWFilterChainSuffixType */ + + int nentries; + virNWFilterEntryPtr *filterEntries; +}; + + +typedef struct _virNWFilterPoolObj virNWFilterPoolObj; +typedef virNWFilterPoolObj *virNWFilterPoolObjPtr; + +struct _virNWFilterPoolObj { + virMutex lock; + + char *configFile; + int active; + int wantRemoved; + + virNWFilterDefPtr def; + virNWFilterDefPtr newDef; +}; + + +typedef struct _virNWFilterPoolObjList virNWFilterPoolObjList; +typedef virNWFilterPoolObjList *virNWFilterPoolObjListPtr; +struct _virNWFilterPoolObjList { + unsigned int count; + virNWFilterPoolObjPtr *objs; +}; + + +typedef struct _virNWFilterDriverState virNWFilterDriverState; +typedef virNWFilterDriverState *virNWFilterDriverStatePtr; +struct _virNWFilterDriverState { + virMutex lock; + + virNWFilterPoolObjList pools; + + char *configDir; +}; + + +typedef struct _virNWFilterTechDriver virNWFilterTechDriver; +typedef virNWFilterTechDriver *virNWFilterTechDriverPtr; + + +typedef struct _virNWFilterRuleInst virNWFilterRuleInst; +typedef virNWFilterRuleInst *virNWFilterRuleInstPtr; +struct _virNWFilterRuleInst { + int ndata; + void **data; + virNWFilterTechDriverPtr techdriver; +}; + + +enum virDomainNetType; + +typedef int (*virNWFilterRuleCreateInstance)(virConnectPtr conn, + enum virDomainNetType nettype, + virNWFilterDefPtr filter, + virNWFilterRuleDefPtr rule, + const char *ifname, + virNWFilterHashTablePtr vars, + virNWFilterRuleInstPtr res); + +typedef int (*virNWFilterRuleApplyNewRules)(virConnectPtr conn, + const char *ifname, + int nruleInstances, + void **_inst); + +typedef int (*virNWFilterRuleTeardownNewRules)(virConnectPtr conn, + const char *ifname); + +typedef int (*virNWFilterRuleTeardownOldRules)(virConnectPtr conn, + const char *ifname); + +typedef int (*virNWFilterRuleRemoveRules)(virConnectPtr conn, + const char *ifname, + int nruleInstances, + void **_inst); + +typedef int (*virNWFilterRuleAllTeardown)(const char *ifname); + +typedef int (*virNWFilterRuleFreeInstanceData)(void * _inst); + +typedef int (*virNWFilterRuleDisplayInstanceData)(virConnectPtr conn, + void *_inst); + + +struct _virNWFilterTechDriver { + const char *name; + + virNWFilterRuleCreateInstance createRuleInstance; + virNWFilterRuleApplyNewRules applyNewRules; + virNWFilterRuleTeardownNewRules tearNewRules; + virNWFilterRuleTeardownOldRules tearOldRules; + virNWFilterRuleRemoveRules removeRules; + virNWFilterRuleAllTeardown allTeardown; + virNWFilterRuleFreeInstanceData freeRuleInstance; + virNWFilterRuleDisplayInstanceData displayRuleInstance; +}; + + + +void virNWFilterRuleDefFree(virNWFilterRuleDefPtr def); + +void virNWFilterDefFree(virNWFilterDefPtr def); +void virNWFilterPoolObjListFree(virNWFilterPoolObjListPtr pools); +void virNWFilterPoolObjRemove(virNWFilterPoolObjListPtr pools, + virNWFilterPoolObjPtr pool); + +void virNWFilterPoolObjFree(virNWFilterPoolObjPtr obj); + +virNWFilterPoolObjPtr + virNWFilterPoolObjFindByUUID(virNWFilterPoolObjListPtr pools, + const unsigned char *uuid); + +virNWFilterPoolObjPtr + virNWFilterPoolObjFindByName(virNWFilterPoolObjListPtr pools, + const char *name); + + +int virNWFilterPoolObjSaveDef(virConnectPtr conn, + virNWFilterDriverStatePtr driver, + virNWFilterPoolObjPtr pool, + virNWFilterDefPtr def); + +int virNWFilterPoolObjDeleteDef(virConnectPtr conn, + virNWFilterPoolObjPtr pool); + +virNWFilterPoolObjPtr virNWFilterPoolObjAssignDef(virConnectPtr conn, + virNWFilterPoolObjListPtr pools, + virNWFilterDefPtr def); + +int virNWFilterTestUnassignDef(virConnectPtr conn, + virNWFilterPoolObjPtr pool); + +virNWFilterDefPtr virNWFilterDefParseNode(virConnectPtr conn, + xmlDocPtr xml, + xmlNodePtr root); + +char *virNWFilterDefFormat(virConnectPtr conn, + virNWFilterDefPtr def); + +int virNWFilterSaveXML(virConnectPtr conn, + const char *configDir, + virNWFilterDefPtr def, + const char *xml); + +int virNWFilterSaveConfig(virConnectPtr conn, + const char *configDir, + virNWFilterDefPtr def); + +int virNWFilterPoolLoadAllConfigs(virConnectPtr conn, + virNWFilterPoolObjListPtr pools, + const char *configDir); + +char *virNWFilterConfigFile(virConnectPtr conn, + const char *dir, + const char *name); + +virNWFilterDefPtr virNWFilterDefParseString(virConnectPtr conn, + const char *xml); +virNWFilterDefPtr virNWFilterDefParseFile(virConnectPtr conn, + const char *filename); + +void virNWFilterPoolObjLock(virNWFilterPoolObjPtr obj); +void virNWFilterPoolObjUnlock(virNWFilterPoolObjPtr obj); + +int virNWFilterConfLayerInit(void); +void virNWFilterConfLayerShutdown(void); + +int virNWFilterParamConfLayerInit(void); +void virNWFilterParamConfLayerShutdown(void); + +#define virNWFilterReportError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_NWFILTER, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + + +typedef int (*virNWFilterRebuild)(virConnectPtr conn, + virHashIterator, void *data); + +typedef struct _virNWFilterCallbackDriver virNWFilterCallbackDriver; +typedef virNWFilterCallbackDriver *virNWFilterCallbackDriverPtr; +struct _virNWFilterCallbackDriver { + const char *name; + + virNWFilterRebuild vmFilterRebuild; +}; + +void virNWFilterRegisterCallbackDriver(virNWFilterCallbackDriverPtr); + + +VIR_ENUM_DECL(virNWFilterRuleAction); +VIR_ENUM_DECL(virNWFilterRuleDirection); +VIR_ENUM_DECL(virNWFilterRuleProtocol); +VIR_ENUM_DECL(virNWFilterJumpTarget); +VIR_ENUM_DECL(virNWFilterChainPolicy); +VIR_ENUM_DECL(virNWFilterEbtablesTable); +VIR_ENUM_DECL(virNWFilterChainSuffix); + +#endif /* NWFILTER_CONF_H */ Index: libvirt-acl/include/libvirt/virterror.h =================================================================== --- libvirt-acl.orig/include/libvirt/virterror.h +++ libvirt-acl/include/libvirt/virterror.h @@ -69,7 +69,8 @@ typedef enum { VIR_FROM_PHYP, /* Error from IBM power hypervisor */ VIR_FROM_SECRET, /* Error from secret storage */ VIR_FROM_CPU, /* Error from CPU driver */ - VIR_FROM_XENAPI /* Error from XenAPI */ + VIR_FROM_XENAPI, /* Error from XenAPI */ + VIR_FROM_NWFILTER /* Error from network filter driver */ } virErrorDomain; @@ -169,6 +170,10 @@ typedef enum { VIR_ERR_NO_INTERFACE, /* interface driver not running */ VIR_ERR_INVALID_INTERFACE, /* invalid interface object */ VIR_ERR_MULTIPLE_INTERFACES, /* more than one matching interface found */ + VIR_WAR_NO_NWFILTER, /* failed to start nwfilter driver */ + VIR_ERR_INVALID_NWFILTER, /* invalid nwfilter object */ + VIR_ERR_NO_NWFILTER, /* nw filter pool not found */ + VIR_ERR_BUILD_FIREWALL, /* nw filter pool not found */ VIR_WAR_NO_SECRET, /* failed to start secret storage */ VIR_ERR_INVALID_SECRET, /* invalid secret */ VIR_ERR_NO_SECRET, /* secret not found */ Index: libvirt-acl/src/util/virterror.c =================================================================== --- libvirt-acl.orig/src/util/virterror.c +++ libvirt-acl/src/util/virterror.c @@ -178,6 +178,9 @@ static const char *virErrorDomainName(vi case VIR_FROM_CPU: dom = "CPU "; break; + case VIR_FROM_NWFILTER: + dom = "Network Filter"; + break; } return(dom); } @@ -1100,6 +1103,30 @@ virErrorMsg(virErrorNumber error, const else errmsg = _("Secret not found: %s"); break; + case VIR_WAR_NO_NWFILTER: + if (info == NULL) + errmsg = _("Failed to start the nwfilter driver"); + else + errmsg = _("Failed to start the nwfilter driver: %s"); + break; + case VIR_ERR_INVALID_NWFILTER: + if (info == NULL) + errmsg = _("Invalid network filter"); + else + errmsg = _("Invalid network filter: %s"); + break; + case VIR_ERR_NO_NWFILTER: + if (info == NULL) + errmsg = _("Network filter not found"); + else + errmsg = _("Network filter not found: %s"); + break; + case VIR_ERR_BUILD_FIREWALL: + if (info == NULL) + errmsg = _("Error while building firewall"); + else + errmsg = _("Error while building firewall: %s"); + break; case VIR_ERR_CONFIG_UNSUPPORTED: if (info == NULL) errmsg = _("unsupported configuration"); Index: libvirt-acl/src/nwfilter/nwfilter_driver.c =================================================================== --- /dev/null +++ libvirt-acl/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; +} Index: libvirt-acl/src/nwfilter/nwfilter_driver.h =================================================================== --- /dev/null +++ libvirt-acl/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__ */ Index: libvirt-acl/src/datatypes.h =================================================================== --- libvirt-acl.orig/src/datatypes.h +++ libvirt-acl/src/datatypes.h @@ -121,6 +121,17 @@ /** + * VIR_NWFILTER_MAGIC: + * + * magic value used to protect the API when pointers to network filter + * pool structures are passed down by the users. + */ +#define VIR_NWFILTER_MAGIC 0xDEAD7777 +#define VIR_IS_NWFILTER(obj) ((obj) && (obj)->magic==VIR_NWFILTER_MAGIC) +#define VIR_IS_CONNECTED_NWFILTER(obj) (VIR_IS_NWFILTER(obj) && VIR_IS_CONNECT((obj)->conn)) + + +/** * _virConnect: * * Internal structure associated to a connection @@ -141,6 +152,7 @@ struct _virConnect { virStorageDriverPtr storageDriver; virDeviceMonitorPtr deviceMonitor; virSecretDriverPtr secretDriver; + virNWFilterDriverPtr nwfilterDriver; /* Private data pointer which can be used by driver and * network driver as they wish. @@ -152,6 +164,7 @@ struct _virConnect { void * storagePrivateData; void * devMonPrivateData; void * secretPrivateData; + void * nwfilterPrivateData; /* * The lock mutex must be acquired before accessing/changing @@ -173,6 +186,7 @@ struct _virConnect { virHashTablePtr storageVols;/* hash table for known storage vols */ virHashTablePtr nodeDevices; /* hash table for known node devices */ virHashTablePtr secrets; /* hash taboe for known secrets */ + virHashTablePtr nwfilterPools; /* hash tables ofr known nw filter pools */ int refs; /* reference count */ }; @@ -336,4 +350,22 @@ int virUnrefSecret(virSecretPtr secret); virStreamPtr virGetStream(virConnectPtr conn); int virUnrefStream(virStreamPtr st); +/** +* _virNWFilter: +* +* Internal structure associated to a network filter +*/ +struct _virNWFilter { + unsigned int magic; /* specific value to check */ + int refs; /* reference count */ + virConnectPtr conn; /* pointer back to the connection */ + char *name; /* the network filter external name */ + unsigned char uuid[VIR_UUID_BUFLEN]; /* the network filter unique identifier */ +}; + +virNWFilterPtr virGetNWFilter(virConnectPtr conn, + const char *name, + const unsigned char *uuid); +int virUnrefNWFilter(virNWFilterPtr pool); + #endif Index: libvirt-acl/src/datatypes.c =================================================================== --- libvirt-acl.orig/src/datatypes.c +++ libvirt-acl/src/datatypes.c @@ -175,6 +175,9 @@ virGetConnect(void) { ret->secrets = virHashCreate(20); if (ret->secrets == NULL) goto failed; + ret->nwfilterPools = virHashCreate(20); + if (ret->nwfilterPools == NULL) + goto failed; ret->refs = 1; return(ret); @@ -1362,3 +1365,142 @@ int virUnrefStream(virStreamPtr st) { virMutexUnlock(&st->conn->lock); return (refs); } + + +/** + * virGetNWFilter: + * @conn: the hypervisor connection + * @name: pointer to the network filter pool name + * @uuid: pointer to the uuid + * + * Lookup if the network filter is already registered for that connection, + * if yes return a new pointer to it, if no allocate a new structure, + * and register it in the table. In any case a corresponding call to + * virFreeNWFilterPool() is needed to not leak data. + * + * Returns a pointer to the network, or NULL in case of failure + */ +virNWFilterPtr +virGetNWFilter(virConnectPtr conn, const char *name, const unsigned char *uuid) { + virNWFilterPtr ret = NULL; + + if ((!VIR_IS_CONNECT(conn)) || (name == NULL) || (uuid == NULL)) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(NULL); + } + virMutexLock(&conn->lock); + + /* TODO search by UUID first as they are better differenciators */ + + ret = (virNWFilterPtr) virHashLookup(conn->nwfilterPools, name); + /* TODO check the UUID */ + if (ret == NULL) { + if (VIR_ALLOC(ret) < 0) { + virMutexUnlock(&conn->lock); + virReportOOMError(); + goto error; + } + ret->name = strdup(name); + if (ret->name == NULL) { + virMutexUnlock(&conn->lock); + virReportOOMError(); + goto error; + } + ret->magic = VIR_NWFILTER_MAGIC; + ret->conn = conn; + if (uuid != NULL) + memcpy(&(ret->uuid[0]), uuid, VIR_UUID_BUFLEN); + + if (virHashAddEntry(conn->nwfilterPools, name, ret) < 0) { + virMutexUnlock(&conn->lock); + virLibConnError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to add network filter pool to connection hash table")); + goto error; + } + conn->refs++; + } + ret->refs++; + virMutexUnlock(&conn->lock); + return(ret); + +error: + if (ret != NULL) { + VIR_FREE(ret->name); + VIR_FREE(ret); + } + return(NULL); +} + + +/** + * virReleaseNWFilterPool: + * @pool: the pool to release + * + * Unconditionally release all memory associated with a pool. + * The conn.lock mutex must be held prior to calling this, and will + * be released prior to this returning. The pool obj must not + * be used once this method returns. + * + * It will also unreference the associated connection object, + * which may also be released if its ref count hits zero. + */ +static void +virReleaseNWFilterPool(virNWFilterPtr pool) { + virConnectPtr conn = pool->conn; + DEBUG("release pool %p %s", pool, pool->name); + + /* TODO search by UUID first as they are better differenciators */ + if (virHashRemoveEntry(conn->nwfilterPools, pool->name, NULL) < 0) { + virMutexUnlock(&conn->lock); + virLibConnError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("pool missing from connection hash table")); + conn = NULL; + } + + pool->magic = -1; + VIR_FREE(pool->name); + VIR_FREE(pool); + + if (conn) { + DEBUG("unref connection %p %d", conn, conn->refs); + conn->refs--; + if (conn->refs == 0) { + virReleaseConnect(conn); + /* Already unlocked mutex */ + return; + } + virMutexUnlock(&conn->lock); + } +} + + +/** + * virUnrefNWFilter: + * @pool: the nwfilter to unreference + * + * Unreference the networkf itler. If the use count drops to zero, the + * structure is actually freed. + * + * Returns the reference count or -1 in case of failure. + */ +int +virUnrefNWFilter(virNWFilterPtr pool) { + int refs; + + if (!VIR_IS_CONNECTED_NWFILTER(pool)) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return -1; + } + virMutexLock(&pool->conn->lock); + DEBUG("unref pool %p %s %d", pool, pool->name, pool->refs); + pool->refs--; + refs = pool->refs; + if (refs == 0) { + virReleaseNWFilterPool(pool); + /* Already unlocked mutex */ + return (0); + } + + virMutexUnlock(&pool->conn->lock); + return (refs); +} Index: libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c =================================================================== --- /dev/null +++ libvirt-acl/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, +}; Index: libvirt-acl/src/nwfilter/nwfilter_gentech_driver.c =================================================================== --- /dev/null +++ libvirt-acl/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 (!strcmp(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; +} Index: libvirt-acl/src/nwfilter/nwfilter_gentech_driver.h =================================================================== --- /dev/null +++ libvirt-acl/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 Index: libvirt-acl/daemon/libvirtd.c =================================================================== --- libvirt-acl.orig/daemon/libvirtd.c +++ libvirt-acl/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 *qemudInitial virDriverLoadModule("lxc"); virDriverLoadModule("uml"); virDriverLoadModule("one"); + virDriverLoadModule("nwfilter"); #else # ifdef WITH_NETWORK networkRegister(); @@ -892,6 +896,9 @@ static struct qemud_server *qemudInitial # ifdef WITH_SECRETS secretRegister(); # endif +# ifdef WITH_NWFILTER + nwfilterRegister(); +# endif # ifdef WITH_QEMU qemuRegister(); # endif Index: libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.h =================================================================== --- /dev/null +++ libvirt-acl/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 Index: libvirt-acl/python/generator.py =================================================================== --- libvirt-acl.orig/python/generator.py +++ libvirt-acl/python/generator.py @@ -170,6 +170,7 @@ skipped_types = { # 'int *': "usually a return type", 'virConnectDomainEventCallback': "No function types in python", 'virEventAddHandleFunc': "No function types in python", + 'virNWFilterPtr': "No function types in python", } ####################################################################### @@ -268,6 +269,7 @@ skip_impl = ( 'virConnectListStorageVols', 'virConnectListDefinedStorageVols', 'virConnectListDefinedInterfaces', + 'virConnectListNWFilters', 'virConnGetLastError', 'virGetLastError', 'virDomainGetInfo', Index: libvirt-acl/src/conf/nwfilter_params.c =================================================================== --- /dev/null +++ libvirt-acl/src/conf/nwfilter_params.c @@ -0,0 +1,318 @@ +/* + * nwfilter_params.c: parsing and data maintenance of filter parameters + * + * Copyright (C) 2010 IBM Corporation + * + * 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 <regex.h> + +#include "internal.h" + +#include "memory.h" +#include "virterror_internal.h" +#include "datatypes.h" +#include "nwfilter_params.h" +#include "domain_conf.h" + + +#define VIR_FROM_THIS VIR_FROM_NWFILTER + +/* + * regular expressions for parameter names and values + */ +static regex_t regex_nam; +static regex_t regex_val; + + +static void +hashDealloc(void *payload, const char *name ATTRIBUTE_UNUSED) +{ + VIR_FREE(payload); +} + + +/** + * virNWFilterHashTablePut: + * @table: Pointer to a virNWFilterHashTable + * @name: name of the key to enter + * @val: The value associated with the key + * @freeName: Whether the name must be freed on table destruction + * + * Returns 0 on success, 1 on failure. + * + * Put an entry into the hashmap replacing and freeing an existing entry + * if one existed. + */ +int +virNWFilterHashTablePut(virNWFilterHashTablePtr table, + const char *name, + char *val, + int copyName) +{ + if (!virHashLookup(table->hashTable, name)) { + if (copyName) { + name = strdup(name); + if (!name) + return 1; + + if (VIR_REALLOC_N(table->names, table->nNames + 1) < 0) { + VIR_FREE(name); + return 1; + } + table->names[table->nNames++] = (char *)name; + } + + if (virHashAddEntry(table->hashTable, name, val) != 0) { + if (copyName) { + VIR_FREE(name); + table->nNames--; + } + return 1; + } + } else { + if (virHashUpdateEntry(table->hashTable, name, val, hashDealloc) != 0) { + return 1; + } + } + return 0; +} + + +/** + * virNWFilterHashTableFree: + * @table: Pointer to virNWFilterHashTable + * + * Free a hashtable de-allocating memory for all its entries. + * + * All hash tables within the NWFilter driver must use this + * function to deallocate and free their content. + */ +void +virNWFilterHashTableFree(virNWFilterHashTablePtr table) +{ + int i; + if (!table) + return; + virHashFree(table->hashTable, hashDealloc); + + for (i = 0; i < table->nNames; i++) + VIR_FREE(table->names[i]); + VIR_FREE(table->names); + VIR_FREE(table); +} + + +virNWFilterHashTablePtr +virNWFilterHashTableCreate(int n) { + virNWFilterHashTablePtr ret; + + if (VIR_ALLOC(ret) < 0) { + virReportOOMError(); + return NULL; + } + ret->hashTable = virHashCreate(n); + if (!ret->hashTable) { + virReportOOMError(); + VIR_FREE(ret); + return NULL; + } + return ret; +} + + +int +virNWFilterHashTableRemoveEntry(virNWFilterHashTablePtr ht, + const char *entry) +{ + int i; + int rc = virHashRemoveEntry(ht->hashTable, entry, hashDealloc); + + if (rc == 0) { + for (i = 0; i < ht->nNames; i++) { + if (STREQ(ht->names[i], entry)) { + VIR_FREE(ht->names[i]); + ht->names[i] = ht->names[--ht->nNames]; + ht->names[ht->nNames] = NULL; + break; + } + } + } + return rc; +} + + +struct addToTableStruct { + virNWFilterHashTablePtr target; + int errOccurred; + virConnectPtr conn; +}; + + +static void +addToTable(void *payload, const char *name, void *data) +{ + struct addToTableStruct *atts = (struct addToTableStruct *)data; + char *val; + + if (atts->errOccurred) + return; + + val = strdup((char *)payload); + if (!val) { + virReportOOMError(); + atts->errOccurred = 1; + return; + } + + if (virNWFilterHashTablePut(atts->target, name, val, 1) != 0) { + virNWFilterReportError(atts->conn, VIR_ERR_INTERNAL_ERROR, + _("Could not put variable '%s' into hashmap"), + name); + atts->errOccurred = 1; + VIR_FREE(val); + } +} + + +int +virNWFilterHashTablePutAll(virConnectPtr conn, + virNWFilterHashTablePtr src, + virNWFilterHashTablePtr dest) +{ + struct addToTableStruct atts = { + .target = dest, + .errOccurred = 0, + .conn = conn, + }; + + virHashForEach(src->hashTable, addToTable, &atts); + if (atts.errOccurred) + goto err_exit; + + return 0; + +err_exit: + return 1; +} + + +virNWFilterHashTablePtr +virNWFilterParseParamAttributes(xmlNodePtr cur) +{ + char *nam, *val; + + virNWFilterHashTablePtr table = virNWFilterHashTableCreate(0); + if (!table) { + virReportOOMError(); + return NULL; + } + + cur = cur->children; + + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if (xmlStrEqual(cur->name, BAD_CAST "parameter")) { + nam = virXMLPropString(cur, "name"); + val = virXMLPropString(cur, "value"); + if (nam != NULL && val != NULL) { + if (regexec(®ex_nam, nam, 0, NULL, 0) != 0) + goto skip_entry; + if (regexec(®ex_val, val, 0, NULL, 0) != 0) + goto skip_entry; + if (virNWFilterHashTablePut(table, nam, val, 1)) { + VIR_FREE(nam); + VIR_FREE(val); + virNWFilterHashTableFree(table); + return NULL; + } + val = NULL; + } +skip_entry: + VIR_FREE(nam); + VIR_FREE(val); + } + } + cur = cur->next; + } + return table; +} + + +struct formatterParam { + virBufferPtr buf; + const char *indent; +}; + + +static void +_formatParameterAttrs(void *payload, const char *name, void *data) +{ + struct formatterParam *fp = (struct formatterParam *)data; + + virBufferVSprintf(fp->buf, "%s<parameter name='%s' value='%s'/>\n", + fp->indent, + name, + (char *)payload); +} + + +char * +virNWFilterFormatParamAttributes(virNWFilterHashTablePtr table, + const char *indent) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + struct formatterParam fp = { + .buf = &buf, + .indent = indent, + }; + + virHashForEach(table->hashTable, _formatParameterAttrs, &fp); + + if (virBufferError(&buf)) { + virReportOOMError(); + virBufferFreeAndReset(&buf); + return NULL; + } + + return virBufferContentAndReset(&buf); +} + + +int virNWFilterParamConfLayerInit(void) { + + if (regcomp(®ex_nam, "^[a-zA-Z0-9_]+$" , + REG_NOSUB|REG_EXTENDED) != 0) + return 1; + + if (regcomp(®ex_val, "^[a-zA-Z0-9_.:]+$", + REG_NOSUB|REG_EXTENDED) != 0) { + regfree(®ex_nam); + return 1; + } + + return 0; +} + + +void virNWFilterParamConfLayerShutdown(void) { + regfree(®ex_nam); + regfree(®ex_val); +} Index: libvirt-acl/src/conf/nwfilter_params.h =================================================================== --- /dev/null +++ libvirt-acl/src/conf/nwfilter_params.h @@ -0,0 +1,53 @@ +/* + * nwfilter_params.h: parsing and data maintenance of filter parameters + * + * Copyright (C) 2010 IBM Corporation + * + * 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_PARAMS_H +#define NWFILTER_PARAMS_H + +#include "hash.h" + +typedef struct _virNWFilterHashTable virNWFilterHashTable; +typedef virNWFilterHashTable *virNWFilterHashTablePtr; +struct _virNWFilterHashTable { + virHashTablePtr hashTable; + + int nNames; + char **names; +}; + + +virNWFilterHashTablePtr virNWFilterParseParamAttributes(xmlNodePtr cur); +char * virNWFilterFormatParamAttributes(virNWFilterHashTablePtr table, + const char *indent); + +virNWFilterHashTablePtr virNWFilterHashTableCreate(int n); +void virNWFilterHashTableFree(virNWFilterHashTablePtr table); +int virNWFilterHashTablePut(virNWFilterHashTablePtr table, + const char *name, + char *val, + int freeName); +int virNWFilterHashTableRemoveEntry(virNWFilterHashTablePtr table, + const char *name); +int virNWFilterHashTablePutAll(virConnectPtr conn, + virNWFilterHashTablePtr src, + virNWFilterHashTablePtr dest); + +#endif /* NWFILTER_PARAMS_H */

This patch adds build support for the network filtering framework. Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- configure.ac | 12 ++++++++++++ daemon/Makefile.am | 4 ++++ src/Makefile.am | 34 +++++++++++++++++++++++++++++++++- src/libvirt_private.syms | 39 +++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 14 ++++++++++++++ 5 files changed, 102 insertions(+), 1 deletion(-) Index: libvirt-acl/src/Makefile.am =================================================================== --- libvirt-acl.orig/src/Makefile.am +++ libvirt-acl/src/Makefile.am @@ -97,9 +97,17 @@ DOMAIN_EVENT_SOURCES = \ conf/domain_event.c conf/domain_event.h # Network driver generic impl APIs -NETWORK_CONF_SOURCES = \ +NETWORK_CONF_SOURCES = \ conf/network_conf.c conf/network_conf.h +# Network filter driver generic impl APIs +NWFILTER_PARAM_CONF_SOURCES = \ + conf/nwfilter_params.c conf/nwfilter_conf.h + +NWFILTER_CONF_SOURCES = \ + $(NWFILTER_PARAM_CONF_SOURCES) \ + conf/nwfilter_conf.c conf/nwfilter_conf.h + # Storage driver generic impl APIs STORAGE_CONF_SOURCES = \ conf/storage_conf.h conf/storage_conf.c @@ -126,6 +134,7 @@ CONF_SOURCES = \ $(DOMAIN_CONF_SOURCES) \ $(DOMAIN_EVENT_SOURCES) \ $(NETWORK_CONF_SOURCES) \ + $(NWFILTER_CONF_SOURCES) \ $(NODE_DEVICE_CONF_SOURCES) \ $(STORAGE_CONF_SOURCES) \ $(ENCRYPTION_CONF_SOURCES) \ @@ -275,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 = \ @@ -718,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 @@ -761,6 +791,7 @@ EXTRA_DIST += \ $(NODE_DEVICE_DRIVER_SOURCES) \ $(NODE_DEVICE_DRIVER_HAL_SOURCES) \ $(NODE_DEVICE_DRIVER_UDEV_SOURCES) \ + $(NWFILTER_DRIVER_SOURCES) \ $(SECURITY_DRIVER_SELINUX_SOURCES) \ $(SECURITY_DRIVER_APPARMOR_SOURCES) \ $(SECRET_DRIVER_SOURCES) \ @@ -903,6 +934,7 @@ libvirt_lxc_SOURCES = \ $(NODE_INFO_SOURCES) \ $(ENCRYPTION_CONF_SOURCES) \ $(DOMAIN_CONF_SOURCES) \ + $(NWFILTER_PARAM_CONF_SOURCES) \ $(CPU_CONF_SOURCES) libvirt_lxc_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDCFLAGS) $(CAPNG_LIBS) $(YAJL_LIBS) libvirt_lxc_LDADD = $(LIBXML_LIBS) $(NUMACTL_LIBS) ../gnulib/lib/libgnu.la Index: libvirt-acl/src/libvirt_private.syms =================================================================== --- libvirt-acl.orig/src/libvirt_private.syms +++ libvirt-acl/src/libvirt_private.syms @@ -105,6 +105,8 @@ virUnrefConnect; virUnrefSecret; virGetStream; virUnrefStream; +virGetNWFilter; +virUnrefNWFiler; # domain_conf.h @@ -294,6 +296,7 @@ virRegisterNetworkDriver; virRegisterStorageDriver; virRegisterDeviceMonitor; virRegisterSecretDriver; +virRegisterNWFilterDriver; # json.h @@ -429,6 +432,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; Index: libvirt-acl/daemon/Makefile.am =================================================================== --- libvirt-acl.orig/daemon/Makefile.am +++ libvirt-acl/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 Index: libvirt-acl/configure.ac =================================================================== --- libvirt-acl.orig/configure.ac +++ libvirt-acl/configure.ac @@ -294,6 +294,9 @@ if test x"$with_rhel5_api" = x"yes"; the 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]) Index: libvirt-acl/src/libvirt_public.syms =================================================================== --- libvirt-acl.orig/src/libvirt_public.syms +++ libvirt-acl/src/libvirt_public.syms @@ -362,6 +362,20 @@ LIBVIRT_0.7.8 { global: virStorageVolWipe; virDomainMigrateSetMaxDowntime; + virConnectListNWFilters; + virConnectNumOfNWFilters; + virNWFilterLookupByName; + virNWFilterLookupByUUID; + virNWFilterLookupByUUIDString; + virNWFilterFree; + virNWFilterGetName; + virNWFilterGetUUID; + virNWFilterGetUUIDString; + virNWFilterGetXMLDesc; + virNWFilterRef; + virNWFilterDefineXML; + virNWFilterUndefine; } LIBVIRT_0.7.7; + # .... define new API here using predicted next version number ....

This patch adds IPv6 support for the ebtables layer. Since the parser etc. are all parameterized, it was fairly easy to add this... Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- src/conf/nwfilter_conf.c | 230 +++++++++++++++++++++++++++++- src/conf/nwfilter_conf.h | 18 ++ src/nwfilter/nwfilter_ebiptables_driver.c | 155 ++++++++++++++++++++ 3 files changed, 399 insertions(+), 4 deletions(-) Index: libvirt-acl/src/conf/nwfilter_conf.c =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_conf.c +++ libvirt-acl/src/conf/nwfilter_conf.c @@ -73,7 +73,8 @@ VIR_ENUM_IMPL(virNWFilterEbtablesTable, VIR_ENUM_IMPL(virNWFilterChainSuffix, VIR_NWFILTER_CHAINSUFFIX_LAST, "root", "arp", - "ipv4"); + "ipv4", + "ipv6"); /* @@ -366,6 +367,9 @@ static const struct int_map macProtoMap[ .attr = ETHERTYPE_IP, .val = "ipv4", }, { + .attr = ETHERTYPE_IPV6, + .val = "ipv6", + }, { .val = NULL, } }; @@ -449,6 +453,13 @@ checkIPv4Mask(enum attrDatatype datatype return checkValidMask(maskptr, 4); } +static bool +checkIPv6Mask(enum attrDatatype datatype ATTRIBUTE_UNUSED, void *maskptr, + virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED) +{ + return checkValidMask(maskptr, 16); +} + static bool checkMACMask(enum attrDatatype datatype ATTRIBUTE_UNUSED, @@ -765,6 +776,61 @@ static const virXMLAttr2Struct ipAttribu }; +static const virXMLAttr2Struct ipv6Attributes[] = { + COMMON_MAC_PROPS(ipv6HdrFilter), + { + .name = SRCIPADDR, + .datatype = DATATYPE_IPV6ADDR, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataSrcIPAddr), + }, + { + .name = DSTIPADDR, + .datatype = DATATYPE_IPV6ADDR, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataDstIPAddr), + }, + { + .name = SRCIPMASK, + .datatype = DATATYPE_IPV6MASK, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataSrcIPMask), + }, + { + .name = DSTIPMASK, + .datatype = DATATYPE_IPV6MASK, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataDstIPMask), + }, + { + .name = "protocol", + .datatype = DATATYPE_STRING, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataProtocolID), + .validator= checkIPProtocolID, + .formatter= formatIPProtocolID, + }, + { + .name = SRCPORTSTART, + .datatype = DATATYPE_UINT16, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataSrcPortStart), + }, + { + .name = SRCPORTEND, + .datatype = DATATYPE_UINT16, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataSrcPortEnd), + }, + { + .name = DSTPORTSTART, + .datatype = DATATYPE_UINT16, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataDstPortStart), + }, + { + .name = DSTPORTEND, + .datatype = DATATYPE_UINT16, + .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataDstPortEnd), + }, + { + .name = NULL, + } +}; + + typedef struct _virAttributes virAttributes; struct _virAttributes { const char *id; @@ -787,6 +853,10 @@ static const virAttributes virAttr[] = { .att = ipAttributes, .prtclType = VIR_NWFILTER_RULE_PROTOCOL_IP, }, { + .id = "ipv6", + .att = ipv6Attributes, + .prtclType = VIR_NWFILTER_RULE_PROTOCOL_IPV6, + }, { .id = NULL, } }; @@ -825,6 +895,89 @@ virNWIPv4AddressParser(const char *input } +static bool +virNWIPv6AddressParser(const char *input, + nwIPAddressPtr output) +{ + int i, j, pos; + uint16_t n; + int shiftpos = -1; + char prevchar; + char base; + + memset(output, 0x0, sizeof(*output)); + + output->isIPv6 = 1; + + pos = 0; + i = 0; + + while (i < 8) { + j = 0; + n = 0; + while (1) { + prevchar = input[pos++]; + if (prevchar == ':' || prevchar == 0) { + if (j > 0) { + output->addr.ipv6Addr[i * 2 + 0] = n >> 8; + output->addr.ipv6Addr[i * 2 + 1] = n; + i++; + } + break; + } + + if (j >= 4) + return 0; + + if (prevchar >= '0' && prevchar <= '9') + base = '0'; + else if (prevchar >= 'a' && prevchar <= 'f') + base = 'a' - 10; + else if (prevchar >= 'A' && prevchar <= 'F') + base = 'A' - 10; + else + return 0; + n <<= 4; + n |= (prevchar - base); + j++; + } + + if (prevchar == 0) + break; + + if (input[pos] == ':') { + pos ++; + // sequence of zeros + if (prevchar != ':') + return 0; + + if (shiftpos != -1) + return 0; + + shiftpos = i; + } + } + + if (shiftpos != -1) { + if (i >= 7) + return 0; + i--; + j = 0; + while (i >= shiftpos) { + output->addr.ipv6Addr[15 - (j*2) - 1] = + output->addr.ipv6Addr[i * 2 + 0]; + output->addr.ipv6Addr[15 - (j*2) - 0] = + output->addr.ipv6Addr[i * 2 + 1]; + output->addr.ipv6Addr[i * 2 + 0] = 0; + output->addr.ipv6Addr[i * 2 + 1] = 0; + i--; + j++; + } + } + return 1; +} + + static int virNWFilterRuleDetailsParse(virConnectPtr conn ATTRIBUTE_UNUSED, xmlNodePtr node, @@ -969,6 +1122,41 @@ virNWFilterRuleDetailsParse(virConnectPt found = 1; break; + case DATATYPE_IPV6ADDR: + storage_ptr = &item->u.ipaddr; + if (!virNWIPv6AddressParser(prop, + (nwIPAddressPtr)storage_ptr)) { + rc = -1; + } + found = 1; + break; + + case DATATYPE_IPV6MASK: + storage_ptr = &item->u.u8; + if (!virNWIPv6AddressParser(prop, &ipaddr)) { + if (sscanf(prop, "%d", &int_val) == 1) { + if (int_val >= 0 && int_val <= 128) { + if (!validator) + *(uint8_t *)storage_ptr = + (uint8_t)int_val; + found = 1; + data_ptr = &int_val; + } else + rc = -1; + } else + rc = -1; + } else { + if (checkIPv6Mask(datatype, + ipaddr.addr.ipv6Addr, nwf)) + *(uint8_t *)storage_ptr = + getMaskNumBits(ipaddr.addr.ipv6Addr, + sizeof(ipaddr.addr.ipv6Addr)); + else + rc = -1; + found = 1; + } + break; + case DATATYPE_STRING: if (!validator) { // not supported @@ -1076,6 +1264,13 @@ virNWFilterRuleDefFixup(virNWFilterRuleD rule->p.ipHdrFilter.ipHdr.dataDstIPAddr); break; + case VIR_NWFILTER_RULE_PROTOCOL_IPV6: + COPY_NEG_SIGN(rule->p.ipv6HdrFilter.ipHdr.dataSrcIPMask, + rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr); + COPY_NEG_SIGN(rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask, + rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr); + break; + case VIR_NWFILTER_RULE_PROTOCOL_ARP: case VIR_NWFILTER_RULE_PROTOCOL_NONE: break; @@ -1952,7 +2147,36 @@ virNWIPAddressFormat(virBufferPtr buf, n ipaddr->addr.ipv4Addr[2], ipaddr->addr.ipv4Addr[3]); } else { - virBufferAddLit(buf, "MISSING IPv6 ADDRESS FORMATTER"); + int i; + int dcshown = 0, in_dc = 0; + unsigned short n; + while (i < 8) { + n = (ipaddr->addr.ipv6Addr[i * 2 + 0] << 8) | + ipaddr->addr.ipv6Addr[i * 2 + 1]; + if (n == 0) { + if (!dcshown) { + in_dc = 1; + if (i == 0) + virBufferAddLit(buf, ":"); + dcshown = 1; + } + if (in_dc) { + i++; + continue; + } + } + if (in_dc) { + dcshown = 1; + virBufferAddLit(buf, ":"); + in_dc = 0; + } + i++; + virBufferVSprintf(buf, "%x", n); + if (i < 8) + virBufferAddLit(buf, ":"); + } + if (in_dc) + virBufferAddLit(buf, ":"); } } @@ -2021,6 +2245,7 @@ virNWFilterRuleDefDetailsFormat(virConne switch (att[i].datatype) { case DATATYPE_IPMASK: + case DATATYPE_IPV6MASK: // display all masks in CIDR format case DATATYPE_UINT8: storage_ptr = &item->u.u8; @@ -2033,6 +2258,7 @@ virNWFilterRuleDefDetailsFormat(virConne break; case DATATYPE_IPADDR: + case DATATYPE_IPV6ADDR: storage_ptr = &item->u.ipaddr; virNWIPAddressFormat(buf, (nwIPAddressPtr)storage_ptr); Index: libvirt-acl/src/conf/nwfilter_conf.h =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_conf.h +++ libvirt-acl/src/conf/nwfilter_conf.h @@ -68,8 +68,10 @@ enum attrDatatype { DATATYPE_IPADDR = (1 << 4), DATATYPE_IPMASK = (1 << 5), DATATYPE_STRING = (1 << 6), + DATATYPE_IPV6ADDR = (1 << 7), + DATATYPE_IPV6MASK = (1 << 8), - DATATYPE_LAST = (1 << 7), + DATATYPE_LAST = (1 << 9), }; @@ -86,7 +88,7 @@ struct _nwIPAddress { int isIPv6; union { unsigned char ipv4Addr[4]; - /* unsigned char ipv6Addr[16]; future :-) */ + unsigned char ipv6Addr[16]; } addr; }; @@ -171,6 +173,15 @@ struct _ipHdrFilterDef { }; +typedef struct _ipv6HdrFilterDef ipv6HdrFilterDef; +typedef ipv6HdrFilterDef *ipv6HdrFilterDefPtr; +struct _ipv6HdrFilterDef { + ethHdrDataDef ethHdr; + ipHdrDataDef ipHdr; + portDataDef portData; +}; + + enum virNWFilterRuleActionType { VIR_NWFILTER_RULE_ACTION_DROP = 0, VIR_NWFILTER_RULE_ACTION_ACCEPT, @@ -198,6 +209,7 @@ enum virNWFilterRuleProtocolType { VIR_NWFILTER_RULE_PROTOCOL_MAC, VIR_NWFILTER_RULE_PROTOCOL_ARP, VIR_NWFILTER_RULE_PROTOCOL_IP, + VIR_NWFILTER_RULE_PROTOCOL_IPV6, }; enum virNWFilterEbtablesTableType { @@ -223,6 +235,7 @@ struct _virNWFilterRuleDef { ethHdrFilterDef ethHdrFilter; arpHdrFilterDef arpHdrFilter; ipHdrFilterDef ipHdrFilter; + ipv6HdrFilterDef ipv6HdrFilter; } p; int nvars; @@ -249,6 +262,7 @@ enum virNWFilterChainSuffixType { VIR_NWFILTER_CHAINSUFFIX_ROOT = 0, VIR_NWFILTER_CHAINSUFFIX_ARP, VIR_NWFILTER_CHAINSUFFIX_IPv4, + VIR_NWFILTER_CHAINSUFFIX_IPv6, VIR_NWFILTER_CHAINSUFFIX_LAST, }; Index: libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c =================================================================== --- libvirt-acl.orig/src/nwfilter/nwfilter_ebiptables_driver.c +++ libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c @@ -73,6 +73,7 @@ static const char *supported_protocols[] = { "ipv4", + "ipv6", "arp", NULL, }; @@ -117,6 +118,8 @@ printDataType(virConnectPtr conn, nwItemDescPtr item) { int done; + int i, pos, s; + if (printVar(conn, vars, buf, bufsize, item, &done)) return 1; @@ -136,6 +139,21 @@ printDataType(virConnectPtr conn, } break; + case DATATYPE_IPV6ADDR: + pos = 0; + for (i = 0; i < 16; i++) { + s = snprintf(&buf[pos], bufsize - pos, "%x%s", + (unsigned int)item->u.ipaddr.addr.ipv6Addr[i], + ((i & 1) && (i < 15)) ? ":" : "" ); + if (s >= bufsize - pos) { + virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER, + _("Buffer too small for IPv6 address")); + return 1; + } + pos += s; + } + break; + case DATATYPE_MACADDR: if (bufsize < VIR_MAC_STRING_BUFLEN) { virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER, @@ -155,6 +173,7 @@ printDataType(virConnectPtr conn, } break; + case DATATYPE_IPV6MASK: case DATATYPE_IPMASK: case DATATYPE_UINT8: if (snprintf(buf, bufsize, "%d", @@ -304,6 +323,7 @@ ebtablesCreateRuleInstance(virConnectPtr { char macaddr[VIR_MAC_STRING_BUFLEN], ipaddr[INET_ADDRSTRLEN], + ipv6addr[INET6_ADDRSTRLEN], number[20]; char chain[MAX_CHAINNAME_LENGTH]; virBuffer buf = VIR_BUFFER_INITIALIZER; @@ -587,6 +607,135 @@ ebtablesCreateRuleInstance(virConnectPtr } break; + case VIR_NWFILTER_RULE_PROTOCOL_IPV6: + virBufferVSprintf(&buf, + CMD_DEF_PRE EBTABLES_CMD " -t %s -%%c %s %%s", + EBTABLES_DEFAULT_TABLE, chain); + + if (ebtablesHandleEthHdr(conn, + &buf, + vars, + &rule->p.ipv6HdrFilter.ethHdr)) + goto err_exit; + + virBufferAddLit(&buf, + " -p ipv6"); + + if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr)) { + if (printDataType(conn, + vars, + ipv6addr, sizeof(ipv6addr), + &rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr)) + goto err_exit; + + virBufferVSprintf(&buf, + " --ip6-source %s %s", + ENTRY_GET_NEG_SIGN(&rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr), + ipv6addr); + + if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataSrcIPMask)) { + if (printDataType(conn, + vars, + number, sizeof(number), + &rule->p.ipv6HdrFilter.ipHdr.dataSrcIPMask)) + goto err_exit; + virBufferVSprintf(&buf, + "/%s", + number); + } + } + + if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr)) { + + if (printDataType(conn, + vars, + ipv6addr, sizeof(ipv6addr), + &rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr)) + goto err_exit; + + virBufferVSprintf(&buf, + " --ip6-destination %s %s", + ENTRY_GET_NEG_SIGN(&rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr), + ipv6addr); + + if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask)) { + if (printDataType(conn, + vars, + number, sizeof(number), + &rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask)) + goto err_exit; + virBufferVSprintf(&buf, + "/%s", + number); + } + } + + if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataProtocolID)) { + if (printDataType(conn, + vars, + number, sizeof(number), + &rule->p.ipv6HdrFilter.ipHdr.dataProtocolID)) + goto err_exit; + + virBufferVSprintf(&buf, + " --ip6-protocol %s %s", + ENTRY_GET_NEG_SIGN(&rule->p.ipv6HdrFilter.ipHdr.dataProtocolID), + number); + } + + if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.portData.dataSrcPortStart)) { + + if (printDataType(conn, + vars, + number, sizeof(number), + &rule->p.ipv6HdrFilter.portData.dataSrcPortStart)) + goto err_exit; + + virBufferVSprintf(&buf, + " --ip6-source-port %s %s", + ENTRY_GET_NEG_SIGN(&rule->p.ipv6HdrFilter.portData.dataSrcPortStart), + number); + + if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.portData.dataSrcPortEnd)) { + if (printDataType(conn, + vars, + number, sizeof(number), + &rule->p.ipv6HdrFilter.portData.dataSrcPortEnd)) + goto err_exit; + + virBufferVSprintf(&buf, + ":%s", + number); + } + } + + if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.portData.dataDstPortStart)) { + + if (printDataType(conn, + vars, + number, sizeof(number), + &rule->p.ipv6HdrFilter.portData.dataDstPortStart)) + goto err_exit; + + virBufferVSprintf(&buf, + " --ip6-destination-port %s %s", + ENTRY_GET_NEG_SIGN(&rule->p.ipv6HdrFilter.portData.dataDstPortStart), + number); + + if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.portData.dataDstPortEnd)) { + if (printDataType(conn, + vars, + number, sizeof(number), + &rule->p.ipv6HdrFilter.portData.dataDstPortEnd)) + goto err_exit; + + virBufferVSprintf(&buf, + ":%s", + number); + } + } + break; + case VIR_NWFILTER_RULE_PROTOCOL_NONE: virBufferVSprintf(&buf, CMD_DEF_PRE EBTABLES_CMD " -t %s -%%c %s %%s", @@ -650,6 +799,7 @@ ebiptablesCreateRuleInstance(virConnectP case VIR_NWFILTER_RULE_PROTOCOL_MAC: case VIR_NWFILTER_RULE_PROTOCOL_ARP: case VIR_NWFILTER_RULE_PROTOCOL_NONE: + case VIR_NWFILTER_RULE_PROTOCOL_IPV6: if (rule->tt == VIR_NWFILTER_RULE_DIRECTION_OUT || rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT) { @@ -1230,6 +1380,11 @@ ebiptablesApplyNewRules(virConnectPtr co if (chains_out & (1 << VIR_NWFILTER_CHAINSUFFIX_IPv4)) ebtablesCreateTmpSubChain(conn, &buf, 0, ifname, "ipv4", 1); + if (chains_in & (1 << VIR_NWFILTER_CHAINSUFFIX_IPv6)) + ebtablesCreateTmpSubChain(conn, &buf, 1, ifname, "ipv6", 1); + if (chains_out & (1 << VIR_NWFILTER_CHAINSUFFIX_IPv6)) + ebtablesCreateTmpSubChain(conn, &buf, 0, ifname, "ipv6", 1); + // keep arp as last if (chains_in & (1 << VIR_NWFILTER_CHAINSUFFIX_ARP)) ebtablesCreateTmpSubChain(conn, &buf, 1, ifname, "arp", 1);

On Thu, Mar 25, 2010 at 01:46:11PM -0400, Stefan Berger wrote:
This patch adds IPv6 support for the ebtables layer. Since the parser etc. are all parameterized, it was fairly easy to add this...
Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
[...]
+static bool +virNWIPv6AddressParser(const char *input, + nwIPAddressPtr output) +{ + int i, j, pos; + uint16_t n; + int shiftpos = -1; + char prevchar; + char base; + + memset(output, 0x0, sizeof(*output)); + + output->isIPv6 = 1; + + pos = 0; + i = 0; + + while (i < 8) { + j = 0; + n = 0; + while (1) { + prevchar = input[pos++]; + if (prevchar == ':' || prevchar == 0) { + if (j > 0) { + output->addr.ipv6Addr[i * 2 + 0] = n >> 8; + output->addr.ipv6Addr[i * 2 + 1] = n; + i++; + } + break; + } + + if (j >= 4) + return 0; + + if (prevchar >= '0' && prevchar <= '9') + base = '0'; + else if (prevchar >= 'a' && prevchar <= 'f') + base = 'a' - 10; + else if (prevchar >= 'A' && prevchar <= 'F') + base = 'A' - 10; + else + return 0; + n <<= 4; + n |= (prevchar - base); + j++; + } + + if (prevchar == 0) + break; + + if (input[pos] == ':') { + pos ++; + // sequence of zeros + if (prevchar != ':') + return 0; + + if (shiftpos != -1) + return 0; + + shiftpos = i; + } + } + + if (shiftpos != -1) { + if (i >= 7) + return 0; + i--; + j = 0; + while (i >= shiftpos) { + output->addr.ipv6Addr[15 - (j*2) - 1] = + output->addr.ipv6Addr[i * 2 + 0]; + output->addr.ipv6Addr[15 - (j*2) - 0] = + output->addr.ipv6Addr[i * 2 + 1]; + output->addr.ipv6Addr[i * 2 + 0] = 0; + output->addr.ipv6Addr[i * 2 + 1] = 0; + i--; + j++; + } + } + return 1; +} +
hum, we already have an IPv6 parser, virSocketParseIpv6Addr() can we reuse that instead of an hand made parser ?
+ if (checkIPv6Mask(datatype, + ipaddr.addr.ipv6Addr, nwf)) + *(uint8_t *)storage_ptr = + getMaskNumBits(ipaddr.addr.ipv6Addr, + sizeof(ipaddr.addr.ipv6Addr));
there are also already netmask checkers for IPv4 and IPv6, let's reuse them
@@ -1076,6 +1264,13 @@ virNWFilterRuleDefFixup(virNWFilterRuleD rule->p.ipHdrFilter.ipHdr.dataDstIPAddr); break;
+ case VIR_NWFILTER_RULE_PROTOCOL_IPV6: + COPY_NEG_SIGN(rule->p.ipv6HdrFilter.ipHdr.dataSrcIPMask, + rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr); + COPY_NEG_SIGN(rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask, + rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr); + break; + case VIR_NWFILTER_RULE_PROTOCOL_ARP: case VIR_NWFILTER_RULE_PROTOCOL_NONE: break; @@ -1952,7 +2147,36 @@ virNWIPAddressFormat(virBufferPtr buf, n ipaddr->addr.ipv4Addr[2], ipaddr->addr.ipv4Addr[3]); } else { - virBufferAddLit(buf, "MISSING IPv6 ADDRESS FORMATTER"); + int i; + int dcshown = 0, in_dc = 0; + unsigned short n; + while (i < 8) { + n = (ipaddr->addr.ipv6Addr[i * 2 + 0] << 8) | + ipaddr->addr.ipv6Addr[i * 2 + 1]; + if (n == 0) { + if (!dcshown) { + in_dc = 1; + if (i == 0) + virBufferAddLit(buf, ":"); + dcshown = 1; + } + if (in_dc) { + i++; + continue; + } + } + if (in_dc) { + dcshown = 1; + virBufferAddLit(buf, ":"); + in_dc = 0; + } + i++; + virBufferVSprintf(buf, "%x", n); + if (i < 8) + virBufferAddLit(buf, ":"); + } + if (in_dc) + virBufferAddLit(buf, ":"); }
and virSocketFormatAddr()
@@ -304,6 +323,7 @@ ebtablesCreateRuleInstance(virConnectPtr { char macaddr[VIR_MAC_STRING_BUFLEN], ipaddr[INET_ADDRSTRLEN], + ipv6addr[INET6_ADDRSTRLEN], number[20]; char chain[MAX_CHAINNAME_LENGTH]; virBuffer buf = VIR_BUFFER_INITIALIZER;
it seems that code was really coded without looking at util/network.h first. I don't think it's a blocker but I would like to see this reunified, unless network.h routines really prove unusable (but maybe we should fix them instead then). Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Daniel Veillard <veillard@redhat.com> wrote on 03/26/2010 01:10:14 PM:
it seems that code was really coded without looking at util/network.h first.
I don't think it's a blocker but I would like to see this reunified, unless network.h routines really prove unusable (but maybe we should fix them instead then).
I'll adapt the datastructures and code. Stefan
Daniel
-- Daniel Veillard | libxml Gnome XML XSLT toolkit
daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

This patch adds support for L3/L4 filtering using iptables. This adds support for 'tcp', 'udp', 'icmp', 'igmp', 'sctp' etc. filtering. As mentioned in the introduction, a .c file provided by this patch is #include'd into a .c file. This will need work, but should be alright for review. Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- configure.ac | 7 src/conf/nwfilter_conf.c | 278 ++++++ src/conf/nwfilter_conf.h | 73 + src/libvirt_private.syms | 1 src/nwfilter/nwfilter_ebiptables_driver.c | 1231 ++++++++++++++++++++++++++++-- src/nwfilter/nwfilter_ebiptables_driver.h | 7 6 files changed, 1529 insertions(+), 68 deletions(-) Index: libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c =================================================================== --- libvirt-acl.orig/src/nwfilter/nwfilter_ebiptables_driver.c +++ libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c @@ -63,7 +63,10 @@ #define EBTABLES_CMD EBTABLES_PATH +#define IPTABLES_CMD IPTABLES_PATH #define BASH_CMD BASH_PATH +#define GREP_CMD GREP_PATH +#define GAWK_CMD GAWK_PATH #define PRINT_ROOT_CHAIN(buf, prefix, ifname) \ snprintf(buf, sizeof(buf), "libvirt-%c-%s", prefix, ifname) @@ -71,6 +74,28 @@ snprintf(buf, sizeof(buf), "%c-%s-%s", prefix, ifname, suffix) +#define VIRT_IN_CHAIN "libvirt-in" +#define VIRT_OUT_CHAIN "libvirt-out" +#define VIRT_IN_POST_CHAIN "libvirt-in-post" +#define HOST_IN_CHAIN "libvirt-host-in" + +#define PRINT_IPT_ROOT_CHAIN(buf, prefix, ifname) \ + snprintf(buf, sizeof(buf), "%c%c-%s", prefix[0], prefix[1], ifname) + +#define PHYSDEV_IN "--physdev-in" +#define PHYSDEV_OUT "--physdev-out" + +static const char *m_state_out_str = "-m state --state NEW,ESTABLISHED"; +static const char *m_state_in_str = "-m state --state ESTABLISHED"; +static const char *m_physdev_in_str = "-m physdev " PHYSDEV_IN; +static const char *m_physdev_out_str = "-m physdev " PHYSDEV_OUT; + +#define MATCH_STATE_OUT m_state_out_str +#define MATCH_STATE_IN m_state_in_str +#define MATCH_PHYSDEV_IN m_physdev_in_str +#define MATCH_PHYSDEV_OUT m_physdev_out_str + + static const char *supported_protocols[] = { "ipv4", "ipv6", @@ -195,108 +220,1087 @@ printDataType(virConnectPtr conn, } -static void -ebiptablesRuleInstFree(ebiptablesRuleInstPtr inst) -{ - if (!inst) - return; +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, + enum RuleType ruleType) +{ + ebiptablesRuleInstPtr inst; + + if (VIR_ALLOC(inst) < 0) { + virReportOOMError(); + return 1; + } + + inst->commandTemplate = commandTemplate; + inst->neededProtocolChain = neededChain; + inst->chainprefix = chainprefix; + inst->priority = priority; + inst->ruleType = ruleType; + + 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; +} + + +/************************ iptables support ************************/ + +static int iptablesLinkIPTablesBaseChain(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + const char *udchain, + const char *syschain, + unsigned int pos, + int stopOnError) +{ + virBufferVSprintf(buf, + "res=$(" + IPTABLES_CMD " -L %s -n --line-number | " + GREP_CMD " \" %s \")\n" + "if [ $? -ne 0 ]; then\n" + " " IPTABLES_CMD " -I %s %d -j %s\n" + "else\n" + " r=$(echo $res | " GAWK_CMD " '{print $1}')\n" + " if [ \"${r}\" != \"%d\" ]; then\n" + " " CMD_DEF(IPTABLES_CMD " -I %s %d -j %s") CMD_SEPARATOR + " " CMD_EXEC + " %s" + " let r=r+1\n" + " " CMD_DEF(IPTABLES_CMD " -D %s ${r}") CMD_SEPARATOR + " " CMD_EXEC + " %s" + " fi\n" + "fi\n", + + syschain, udchain, + + syschain, pos, udchain, + + pos, + + syschain, pos, udchain, + CMD_STOPONERR(stopOnError), + + syschain, + CMD_STOPONERR(stopOnError)); + return 0; +} + + +static int iptablesCreateBaseChains(virConnectPtr conn, + virBufferPtr buf) +{ + virBufferAddLit(buf, IPTABLES_CMD " -N " VIRT_IN_CHAIN CMD_SEPARATOR + IPTABLES_CMD " -N " VIRT_OUT_CHAIN CMD_SEPARATOR + IPTABLES_CMD " -N " VIRT_IN_POST_CHAIN CMD_SEPARATOR + IPTABLES_CMD " -N " HOST_IN_CHAIN CMD_SEPARATOR); + iptablesLinkIPTablesBaseChain(conn, buf, + VIRT_IN_CHAIN , "FORWARD", 1, 1); + iptablesLinkIPTablesBaseChain(conn, buf, + VIRT_OUT_CHAIN , "FORWARD", 2, 1); + iptablesLinkIPTablesBaseChain(conn, buf, + VIRT_IN_POST_CHAIN, "FORWARD", 3, 1); + iptablesLinkIPTablesBaseChain(conn, buf, + HOST_IN_CHAIN , "INPUT" , 1, 1); + + return 0; +} + + +static int +iptablesCreateTmpRootChain(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + char prefix, + int incoming, const char *ifname, + int stopOnError) +{ + char chain[MAX_CHAINNAME_LENGTH]; + char chainPrefix[2] = { + prefix, + (incoming) ? CHAINPREFIX_HOST_IN_TEMP + : CHAINPREFIX_HOST_OUT_TEMP + }; + + PRINT_IPT_ROOT_CHAIN(chain, chainPrefix, ifname); + + virBufferVSprintf(buf, + CMD_DEF(IPTABLES_CMD " -N %s") CMD_SEPARATOR + CMD_EXEC + "%s", + chain, + CMD_STOPONERR(stopOnError)); + + return 0; +} + + +static int +iptablesCreateTmpRootChains(virConnectPtr conn, + virBufferPtr buf, + const char *ifname) +{ + iptablesCreateTmpRootChain(conn, buf, 'F', 0, ifname, 1); + iptablesCreateTmpRootChain(conn, buf, 'F', 1, ifname, 1); + iptablesCreateTmpRootChain(conn, buf, 'H', 1, ifname, 1); + return 0; +} + + +static int +_iptablesRemoveRootChain(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + char prefix, + int incoming, const char *ifname, + int isTempChain) +{ + char chain[MAX_CHAINNAME_LENGTH]; + char chainPrefix[2] = { + prefix, + }; + + if (isTempChain) + chainPrefix[1] = (incoming) ? CHAINPREFIX_HOST_IN_TEMP + : CHAINPREFIX_HOST_OUT_TEMP; + else + chainPrefix[1] = (incoming) ? CHAINPREFIX_HOST_IN + : CHAINPREFIX_HOST_OUT; + + PRINT_IPT_ROOT_CHAIN(chain, chainPrefix, ifname); + + virBufferVSprintf(buf, + IPTABLES_CMD " -F %s" CMD_SEPARATOR + IPTABLES_CMD " -X %s" CMD_SEPARATOR, + chain, + chain); + + return 0; +} + + +static int +iptablesRemoveRootChain(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + char prefix, + int incoming, + const char *ifname) +{ + return _iptablesRemoveRootChain(conn, buf, prefix, incoming, ifname, 0); +} + + +static int +iptablesRemoveTmpRootChain(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + char prefix, + int incoming, + const char *ifname) +{ + return _iptablesRemoveRootChain(conn, buf, prefix, incoming, ifname, 1); +} + + +static int +iptablesRemoveTmpRootChains(virConnectPtr conn, + virBufferPtr buf, + const char *ifname) +{ + iptablesRemoveTmpRootChain(conn, buf, 'F', 0, ifname); + iptablesRemoveTmpRootChain(conn, buf, 'F', 1, ifname); + iptablesRemoveTmpRootChain(conn, buf, 'H', 1, ifname); + return 0; +} + + +static int +iptablesRemoveRootChains(virConnectPtr conn, + virBufferPtr buf, + const char *ifname) +{ + iptablesRemoveRootChain(conn, buf, 'F', 0, ifname); + iptablesRemoveRootChain(conn, buf, 'F', 1, ifname); + iptablesRemoveRootChain(conn, buf, 'H', 1, ifname); + return 0; +} + + +static int +iptablesLinkTmpRootChain(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + const char *basechain, + char prefix, + int incoming, const char *ifname, + int stopOnError) +{ + char chain[MAX_CHAINNAME_LENGTH]; + char chainPrefix[2] = { + prefix, + (incoming) ? CHAINPREFIX_HOST_IN_TEMP + : CHAINPREFIX_HOST_OUT_TEMP + }; + const char *match = (incoming) ? MATCH_PHYSDEV_IN + : MATCH_PHYSDEV_OUT; + + PRINT_IPT_ROOT_CHAIN(chain, chainPrefix, ifname); + + virBufferVSprintf(buf, + CMD_DEF(IPTABLES_CMD " -A %s " + "%s %s -g %s") CMD_SEPARATOR + CMD_EXEC + "%s", + basechain, + match, ifname, chain, + + CMD_STOPONERR(stopOnError)); + + return 0; +} + + +static int +iptablesLinkTmpRootChains(virConnectPtr conn, + virBufferPtr buf, + const char *ifname) +{ + iptablesLinkTmpRootChain(conn, buf, VIRT_OUT_CHAIN, 'F', 0, ifname, 1); + iptablesLinkTmpRootChain(conn, buf, VIRT_IN_CHAIN , 'F', 1, ifname, 1); + iptablesLinkTmpRootChain(conn, buf, HOST_IN_CHAIN , 'H', 1, ifname, 1); + + return 0; +} + + +static int +iptablesSetupVirtInPost(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + const char *ifname) +{ + const char *match = MATCH_PHYSDEV_IN; + virBufferVSprintf(buf, + "res=$(" IPTABLES_CMD " -L " VIRT_IN_POST_CHAIN + " | grep \"\\%s %s\")\n" + "if [ \"${res}\" == \"\" ]; then " + CMD_DEF(IPTABLES_CMD + " -A " VIRT_IN_POST_CHAIN + " %s %s -j ACCEPT") CMD_SEPARATOR + CMD_EXEC + "%s" + "fi\n", + PHYSDEV_IN, ifname, + match, ifname, + CMD_STOPONERR(1)); + return 0; +} + + +static int +iptablesClearVirtInPost(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + const char *ifname) +{ + const char *match = MATCH_PHYSDEV_IN; + virBufferVSprintf(buf, + IPTABLES_CMD + " -D " VIRT_IN_POST_CHAIN + " %s %s -j ACCEPT" CMD_SEPARATOR, + match, ifname); + return 0; +} + +static int +_iptablesUnlinkRootChain(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + const char *basechain, + char prefix, + int incoming, const char *ifname, + int isTempChain) +{ + char chain[MAX_CHAINNAME_LENGTH]; + char chainPrefix[2] = { + prefix, + }; + if (isTempChain) + chainPrefix[1] = (incoming) ? CHAINPREFIX_HOST_IN_TEMP + : CHAINPREFIX_HOST_OUT_TEMP; + else + chainPrefix[1] = (incoming) ? CHAINPREFIX_HOST_IN + : CHAINPREFIX_HOST_OUT; + const char *match = (incoming) ? MATCH_PHYSDEV_IN + : MATCH_PHYSDEV_OUT; + + PRINT_IPT_ROOT_CHAIN(chain, chainPrefix, ifname); + + virBufferVSprintf(buf, + IPTABLES_CMD " -D %s " + "%s %s -g %s" CMD_SEPARATOR, + basechain, + match, ifname, chain); + + return 0; +} + + +static int +iptablesUnlinkRootChain(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + const char *basechain, + char prefix, + int incoming, const char *ifname) +{ + return _iptablesUnlinkRootChain(conn, buf, + basechain, prefix, incoming, ifname, 0); +} + + +static int +iptablesUnlinkTmpRootChain(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + const char *basechain, + char prefix, + int incoming, const char *ifname) +{ + return _iptablesUnlinkRootChain(conn, buf, + basechain, prefix, incoming, ifname, 1); +} + + +static int +iptablesUnlinkRootChains(virConnectPtr conn, + virBufferPtr buf, + const char *ifname) +{ + iptablesUnlinkRootChain(conn, buf, VIRT_OUT_CHAIN, 'F', 0, ifname); + iptablesUnlinkRootChain(conn, buf, VIRT_IN_CHAIN , 'F', 1, ifname); + iptablesUnlinkRootChain(conn, buf, HOST_IN_CHAIN , 'H', 1, ifname); + + return 0; +} + + +static int +iptablesUnlinkTmpRootChains(virConnectPtr conn, + virBufferPtr buf, + const char *ifname) +{ + iptablesUnlinkTmpRootChain(conn, buf, VIRT_OUT_CHAIN, 'F', 0, ifname); + iptablesUnlinkTmpRootChain(conn, buf, VIRT_IN_CHAIN , 'F', 1, ifname); + iptablesUnlinkTmpRootChain(conn, buf, HOST_IN_CHAIN , 'H', 1, ifname); + return 0; +} + + +static int +iptablesRenameTmpRootChain(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + char prefix, + int incoming, + const char *ifname) +{ + char tmpchain[MAX_CHAINNAME_LENGTH], chain[MAX_CHAINNAME_LENGTH]; + char tmpChainPrefix[2] = { + prefix, + (incoming) ? CHAINPREFIX_HOST_IN_TEMP + : CHAINPREFIX_HOST_OUT_TEMP + }; + char chainPrefix[2] = { + prefix, + (incoming) ? CHAINPREFIX_HOST_IN + : CHAINPREFIX_HOST_OUT + }; + + PRINT_IPT_ROOT_CHAIN(tmpchain, tmpChainPrefix, ifname); + PRINT_IPT_ROOT_CHAIN( chain, chainPrefix, ifname); + + virBufferVSprintf(buf, + IPTABLES_CMD " -E %s %s" CMD_SEPARATOR, + tmpchain, + chain); + return 0; +} + + +static int +iptablesRenameTmpRootChains(virConnectPtr conn, + virBufferPtr buf, + const char *ifname) +{ + iptablesRenameTmpRootChain(conn, buf, 'F', 0, ifname); + iptablesRenameTmpRootChain(conn, buf, 'F', 1, ifname); + iptablesRenameTmpRootChain(conn, buf, 'H', 1, ifname); + return 0; +} + + +static void +iptablesInstCommand(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 +iptablesHandleSrcMacAddr(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + virNWFilterHashTablePtr vars, + nwItemDescPtr srcMacAddr, + int directionIn ATTRIBUTE_UNUSED) +{ + char macaddr[VIR_MAC_STRING_BUFLEN]; + + if (HAS_ENTRY_ITEM(srcMacAddr)) { + if (printDataType(conn, + vars, + macaddr, sizeof(macaddr), + srcMacAddr)) + goto err_exit; + + virBufferVSprintf(buf, + " -m mac %s --mac-source %s", + ENTRY_GET_NEG_SIGN(srcMacAddr), + macaddr); + } + + return 0; + +err_exit: + virBufferFreeAndReset(buf); + + return 1; +} + + +static int +iptablesHandleIpHdr(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + virNWFilterHashTablePtr vars, + ipHdrDataDefPtr ipHdr, + int directionIn) +{ + char ipaddr[INET_ADDRSTRLEN], + number[20]; + const char *src = "--source"; + const char *dst = "--destination"; + const char *srcrange = "--src-range"; + const char *dstrange = "--dst-range"; + if (directionIn) { + src = "--destination"; + dst = "--source"; + srcrange = "--dst-range"; + dstrange = "--src-range"; + } + + if (HAS_ENTRY_ITEM(&ipHdr->dataSrcIPAddr)) { + + if (printDataType(conn, + vars, + ipaddr, sizeof(ipaddr), + &ipHdr->dataSrcIPAddr)) + goto err_exit; + + virBufferVSprintf(buf, + " %s %s %s", + ENTRY_GET_NEG_SIGN(&ipHdr->dataSrcIPAddr), + src, + ipaddr); + + if (HAS_ENTRY_ITEM(&ipHdr->dataSrcIPMask)) { + + if (printDataType(conn, + vars, + number, sizeof(number), + &ipHdr->dataSrcIPMask)) + goto err_exit; + + virBufferVSprintf(buf, + "/%s", + number); + } + } else if (HAS_ENTRY_ITEM(&ipHdr->dataSrcIPFrom)) { + + if (printDataType(conn, + vars, + ipaddr, sizeof(ipaddr), + &ipHdr->dataSrcIPFrom)) + goto err_exit; + + virBufferVSprintf(buf, + " -m iprange %s %s %s", + ENTRY_GET_NEG_SIGN(&ipHdr->dataSrcIPFrom), + srcrange, + ipaddr); + + if (HAS_ENTRY_ITEM(&ipHdr->dataSrcIPTo)) { + + if (printDataType(conn, + vars, + ipaddr, sizeof(ipaddr), + &ipHdr->dataSrcIPTo)) + goto err_exit; + + virBufferVSprintf(buf, + "-%s", + ipaddr); + } + } + + if (HAS_ENTRY_ITEM(&ipHdr->dataDstIPAddr)) { + + if (printDataType(conn, + vars, + ipaddr, sizeof(ipaddr), + &ipHdr->dataDstIPAddr)) + goto err_exit; + + virBufferVSprintf(buf, + " %s %s %s", + ENTRY_GET_NEG_SIGN(&ipHdr->dataDstIPAddr), + dst, + ipaddr); + + if (HAS_ENTRY_ITEM(&ipHdr->dataDstIPMask)) { + + if (printDataType(conn, + vars, + number, sizeof(number), + &ipHdr->dataDstIPMask)) + goto err_exit; + + virBufferVSprintf(buf, + "/%s", + number); + + } + } else if (HAS_ENTRY_ITEM(&ipHdr->dataDstIPFrom)) { + + if (printDataType(conn, + vars, + ipaddr, sizeof(ipaddr), + &ipHdr->dataDstIPFrom)) + goto err_exit; + + virBufferVSprintf(buf, + " -m iprange %s %s %s", + ENTRY_GET_NEG_SIGN(&ipHdr->dataDstIPFrom), + dstrange, + ipaddr); + + if (HAS_ENTRY_ITEM(&ipHdr->dataDstIPTo)) { + + if (printDataType(conn, + vars, + ipaddr, sizeof(ipaddr), + &ipHdr->dataDstIPTo)) + goto err_exit; - VIR_FREE(inst->commandTemplate); - VIR_FREE(inst); -} + virBufferVSprintf(buf, + "-%s", + ipaddr); + } + } + if (HAS_ENTRY_ITEM(&ipHdr->dataDSCP)) { -static int -ebiptablesAddRuleInst(virConnectPtr conn, - virNWFilterRuleInstPtr res, - char *commandTemplate, - enum virNWFilterChainSuffixType neededChain, - char chainprefix, - unsigned int priority) -{ - ebiptablesRuleInstPtr inst; + if (printDataType(conn, + vars, + number, sizeof(number), + &ipHdr->dataDSCP)) + goto err_exit; - if (VIR_ALLOC(inst) < 0) { - virReportOOMError(); - return 1; + virBufferVSprintf(buf, + " -m dscp %s --dscp %s", + ENTRY_GET_NEG_SIGN(&ipHdr->dataDSCP), + number); } - inst->commandTemplate = commandTemplate; - inst->neededProtocolChain = neededChain; - inst->chainprefix = chainprefix; - inst->priority = priority; + return 0; - return virNWFilterRuleInstAddData(conn, res, inst); +err_exit: + virBufferFreeAndReset(buf); + + return 1; } static int -ebtablesHandleEthHdr(virConnectPtr conn, - virBufferPtr buf, - virNWFilterHashTablePtr vars, - ethHdrDataDefPtr ethHdr) -{ - char macaddr[VIR_MAC_STRING_BUFLEN]; +iptablesHandlePortData(virConnectPtr conn ATTRIBUTE_UNUSED, + virBufferPtr buf, + virNWFilterHashTablePtr vars, + portDataDefPtr portData, + int directionIn) +{ + char portstr[20]; + const char *sport = "--sport"; + const char *dport = "--dport"; + if (directionIn) { + sport = "--dport"; + dport = "--sport"; + } - if (HAS_ENTRY_ITEM(ðHdr->dataSrcMACAddr)) { + if (HAS_ENTRY_ITEM(&portData->dataSrcPortStart)) { if (printDataType(conn, vars, - macaddr, sizeof(macaddr), - ðHdr->dataSrcMACAddr)) + portstr, sizeof(portstr), + &portData->dataSrcPortStart)) goto err_exit; virBufferVSprintf(buf, - " -s %s %s", - ENTRY_GET_NEG_SIGN(ðHdr->dataSrcMACAddr), - macaddr); + " %s %s %s", + ENTRY_GET_NEG_SIGN(&portData->dataSrcPortStart), + sport, + portstr); - if (HAS_ENTRY_ITEM(ðHdr->dataSrcMACMask)) { + if (HAS_ENTRY_ITEM(&portData->dataSrcPortEnd)) { if (printDataType(conn, vars, - macaddr, sizeof(macaddr), - ðHdr->dataSrcMACMask)) + portstr, sizeof(portstr), + &portData->dataSrcPortEnd)) goto err_exit; - virBufferVSprintf(buf, - "/%s", - macaddr); + virBufferVSprintf(buf, + ":%s", + portstr); } } - if (HAS_ENTRY_ITEM(ðHdr->dataDstMACAddr)) { + if (HAS_ENTRY_ITEM(&portData->dataDstPortStart)) { if (printDataType(conn, vars, - macaddr, sizeof(macaddr), - ðHdr->dataDstMACAddr)) + portstr, sizeof(portstr), + &portData->dataDstPortStart)) goto err_exit; virBufferVSprintf(buf, - " -d %s %s", - ENTRY_GET_NEG_SIGN(ðHdr->dataDstMACAddr), - macaddr); + " %s %s %s", + ENTRY_GET_NEG_SIGN(&portData->dataDstPortStart), + dport, + portstr); - if (HAS_ENTRY_ITEM(ðHdr->dataDstMACMask)) { + if (HAS_ENTRY_ITEM(&portData->dataDstPortEnd)) { if (printDataType(conn, vars, - macaddr, sizeof(macaddr), - ðHdr->dataDstMACMask)) + portstr, sizeof(portstr), + &portData->dataDstPortEnd)) goto err_exit; - virBufferVSprintf(buf, - "/%s", - macaddr); + virBufferVSprintf(buf, + ":%s", + portstr); } } return 0; - err_exit: - virBufferFreeAndReset(buf); - +err_exit: return 1; } /* + * _iptablesCreateRuleInstance: + * @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 +_iptablesCreateRuleInstance(virConnectPtr conn, + int directionIn, + const char *chainPrefix, + virNWFilterDefPtr nwfilter, + virNWFilterRuleDefPtr rule, + const char *ifname, + virNWFilterHashTablePtr vars, + virNWFilterRuleInstPtr res, + const char *match, + const char *accept_target) +{ + char chain[MAX_CHAINNAME_LENGTH]; + char number[20]; + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *target; + + PRINT_IPT_ROOT_CHAIN(chain, chainPrefix, ifname); + + switch (rule->prtclType) { + case VIR_NWFILTER_RULE_PROTOCOL_TCP: + virBufferVSprintf(&buf, + CMD_DEF_PRE IPTABLES_CMD " -%%c %s %%s", + chain); + + virBufferAddLit(&buf, " -p tcp"); + + if (iptablesHandleSrcMacAddr(conn, + &buf, + vars, + &rule->p.tcpHdrFilter.dataSrcMACAddr, + directionIn)) + goto err_exit; + + if (iptablesHandleIpHdr(conn, + &buf, + vars, + &rule->p.tcpHdrFilter.ipHdr, + directionIn)) + goto err_exit; + + if (iptablesHandlePortData(conn, + &buf, + vars, + &rule->p.tcpHdrFilter.portData, + directionIn)) + goto err_exit; + + if (HAS_ENTRY_ITEM(&rule->p.tcpHdrFilter.dataTCPOption)) { + if (printDataType(conn, + vars, + number, sizeof(number), + &rule->p.tcpHdrFilter.dataTCPOption)) + goto err_exit; + + virBufferVSprintf(&buf, + " %s --tcp-option %s", + ENTRY_GET_NEG_SIGN(&rule->p.tcpHdrFilter.dataTCPOption), + number); + } + + break; + + case VIR_NWFILTER_RULE_PROTOCOL_UDP: + virBufferVSprintf(&buf, + CMD_DEF_PRE IPTABLES_CMD " -%%c %s %%s", + chain); + + virBufferAddLit(&buf, " -p udp"); + + if (iptablesHandleSrcMacAddr(conn, + &buf, + vars, + &rule->p.udpHdrFilter.dataSrcMACAddr, + directionIn)) + goto err_exit; + + if (iptablesHandleIpHdr(conn, + &buf, + vars, + &rule->p.udpHdrFilter.ipHdr, + directionIn)) + goto err_exit; + + if (iptablesHandlePortData(conn, + &buf, + vars, + &rule->p.udpHdrFilter.portData, + directionIn)) + goto err_exit; + break; + + case VIR_NWFILTER_RULE_PROTOCOL_SCTP: + virBufferVSprintf(&buf, + CMD_DEF_PRE IPTABLES_CMD " -%%c %s %%s", + chain); + + virBufferAddLit(&buf, " -p sctp"); + + if (iptablesHandleSrcMacAddr(conn, + &buf, + vars, + &rule->p.sctpHdrFilter.dataSrcMACAddr, + directionIn)) + goto err_exit; + + if (iptablesHandleIpHdr(conn, + &buf, + vars, + &rule->p.sctpHdrFilter.ipHdr, + directionIn)) + goto err_exit; + + if (iptablesHandlePortData(conn, + &buf, + vars, + &rule->p.sctpHdrFilter.portData, + directionIn)) + goto err_exit; + break; + + case VIR_NWFILTER_RULE_PROTOCOL_ICMP: + virBufferVSprintf(&buf, + CMD_DEF_PRE IPTABLES_CMD " -%%c %s %%s", + chain); + + virBufferAddLit(&buf, " -p icmp"); + + if (iptablesHandleSrcMacAddr(conn, + &buf, + vars, + &rule->p.icmpHdrFilter.dataSrcMACAddr, + directionIn)) + goto err_exit; + + if (iptablesHandleIpHdr(conn, + &buf, + vars, + &rule->p.icmpHdrFilter.ipHdr, + directionIn)) + goto err_exit; + + if (HAS_ENTRY_ITEM(&rule->p.icmpHdrFilter.dataICMPType)) { + if (printDataType(conn, + vars, + number, sizeof(number), + &rule->p.icmpHdrFilter.dataICMPType)) + goto err_exit; + + virBufferVSprintf(&buf, + " %s --icmp-type %s", + ENTRY_GET_NEG_SIGN(&rule->p.icmpHdrFilter.dataICMPType), + number); + + if (HAS_ENTRY_ITEM(&rule->p.icmpHdrFilter.dataICMPCode)) { + if (printDataType(conn, + vars, + number, sizeof(number), + &rule->p.icmpHdrFilter.dataICMPCode)) + goto err_exit; + + virBufferVSprintf(&buf, + "/%s", + number); + } + } + break; + + case VIR_NWFILTER_RULE_PROTOCOL_ALL: + virBufferVSprintf(&buf, + CMD_DEF_PRE IPTABLES_CMD " -%%c %s %%s", + chain); + + virBufferAddLit(&buf, " -p all"); + + if (iptablesHandleSrcMacAddr(conn, + &buf, + vars, + &rule->p.allHdrFilter.dataSrcMACAddr, + directionIn)) + goto err_exit; + + if (iptablesHandleIpHdr(conn, + &buf, + vars, + &rule->p.allHdrFilter.ipHdr, + directionIn)) + goto err_exit; + + break; + + default: + return -1; + } + + if (match) + virBufferVSprintf(&buf, " %s", match); + + if (rule->action == VIR_NWFILTER_RULE_ACTION_ACCEPT) + target = accept_target; + else + target = "DROP"; + + virBufferVSprintf(&buf, + " -j %s" CMD_DEF_POST CMD_SEPARATOR + CMD_EXEC, + target); + + if (virBufferError(&buf)) { + virBufferFreeAndReset(&buf); + virReportOOMError(); + return -1; + } + + return ebiptablesAddRuleInst(conn, + res, + virBufferContentAndReset(&buf), + nwfilter->chainsuffix, + '\0', + rule->priority, + 1); + + +err_exit: + virBufferFreeAndReset(&buf); + + return -1; + +} + + +static int +iptablesCreateRuleInstance(virConnectPtr conn, + virNWFilterDefPtr nwfilter, + virNWFilterRuleDefPtr rule, + const char *ifname, + virNWFilterHashTablePtr vars, + virNWFilterRuleInstPtr res) +{ + int rc; + int directionIn = 0; + char chainPrefix[2]; + int needState = 1; + + if ((rule->tt == VIR_NWFILTER_RULE_DIRECTION_IN) || + (rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT)) { + directionIn = 1; + needState = 0; + } + + chainPrefix[0] = 'F'; + + chainPrefix[1] = CHAINPREFIX_HOST_IN_TEMP; + rc = _iptablesCreateRuleInstance(conn, + directionIn, + chainPrefix, + nwfilter, + rule, + ifname, + vars, + res, + needState ? MATCH_STATE_OUT + : NULL, + "RETURN"); + if (rc) + return rc; + + chainPrefix[1] = CHAINPREFIX_HOST_OUT_TEMP; + rc = _iptablesCreateRuleInstance(conn, + !directionIn, + chainPrefix, + nwfilter, + rule, + ifname, + vars, + res, + needState ? MATCH_STATE_IN + : NULL, + "ACCEPT"); + if (rc) + return rc; + + chainPrefix[0] = 'H'; + chainPrefix[1] = CHAINPREFIX_HOST_IN_TEMP; + rc = _iptablesCreateRuleInstance(conn, + directionIn, + chainPrefix, + nwfilter, + rule, + ifname, + vars, + res, + NULL, + "ACCEPT"); + if (rc) + return rc; + + return rc; +} + + + + +/* * ebtablesCreateRuleInstance: * @conn : Pointer to a virConnect object * @chainPrefix : The prefix to put in front of the name of the chain @@ -741,6 +1745,9 @@ ebtablesCreateRuleInstance(virConnectPtr CMD_DEF_PRE EBTABLES_CMD " -t %s -%%c %s %%s", EBTABLES_DEFAULT_TABLE, chain); break; + + default: + return -1; } virBufferVSprintf(&buf, @@ -759,7 +1766,8 @@ ebtablesCreateRuleInstance(virConnectPtr virBufferContentAndReset(&buf), nwfilter->chainsuffix, chainPrefix, - rule->priority); + rule->priority, + RT_EBTABLES); err_exit: virBufferFreeAndReset(&buf); @@ -785,7 +1793,7 @@ err_exit: */ static int ebiptablesCreateRuleInstance(virConnectPtr conn, - enum virDomainNetType nettype ATTRIBUTE_UNUSED, + enum virDomainNetType nettype, virNWFilterDefPtr nwfilter, virNWFilterRuleDefPtr rule, const char *ifname, @@ -825,6 +1833,33 @@ ebiptablesCreateRuleInstance(virConnectP res); } break; + + case VIR_NWFILTER_RULE_PROTOCOL_TCP: + case VIR_NWFILTER_RULE_PROTOCOL_UDP: + case VIR_NWFILTER_RULE_PROTOCOL_SCTP: + case VIR_NWFILTER_RULE_PROTOCOL_ICMP: + case VIR_NWFILTER_RULE_PROTOCOL_IGMP: + case VIR_NWFILTER_RULE_PROTOCOL_ALL: + if (nettype == VIR_DOMAIN_NET_TYPE_DIRECT) { + virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER, + _("'%s' protocol not support for net type '%s'"), + virNWFilterRuleProtocolTypeToString(rule->prtclType), + virDomainNetTypeToString(nettype)); + return 1; + } + rc = iptablesCreateRuleInstance(conn, + nwfilter, + rule, + ifname, + vars, + res); + break; + + case VIR_NWFILTER_RULE_PROTOCOL_LAST: + virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER, + "%s", _("illegal protocol type")); + rc = 1; + break; } return rc; @@ -1352,16 +2387,19 @@ ebiptablesApplyNewRules(virConnectPtr co ebiptablesRuleInstPtr *inst = (ebiptablesRuleInstPtr *)_inst; int chains_in = 0, chains_out = 0; virBuffer buf = VIR_BUFFER_INITIALIZER; + int haveIptables = 0; 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); + if (inst[i]->ruleType == RT_EBTABLES) { + 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); @@ -1369,6 +2407,7 @@ ebiptablesApplyNewRules(virConnectPtr co ebtablesRemoveTmpSubChains(conn, &buf, ifname); ebtablesRemoveTmpRootChain(conn, &buf, 1, ifname); ebtablesRemoveTmpRootChain(conn, &buf, 0, ifname); + ebiptablesExecCLI(conn, &buf, &cli_status); if (chains_in != 0) ebtablesCreateTmpRootChain(conn, &buf, 1, ifname, 1); @@ -1395,15 +2434,53 @@ ebiptablesApplyNewRules(virConnectPtr co goto tear_down_tmpebchains; for (i = 0; i < nruleInstances; i++) - ebiptablesInstCommand(conn, &buf, - inst[i]->commandTemplate, - 'A', -1, 1); + switch (inst[i]->ruleType) { + case RT_EBTABLES: + ebiptablesInstCommand(conn, &buf, + inst[i]->commandTemplate, + 'A', -1, 1); + break; + case RT_IPTABLES: + haveIptables = 1; + break; + } if (ebiptablesExecCLI(conn, &buf, &cli_status) || cli_status != 0) goto tear_down_tmpebchains; // FIXME: establishment of iptables user define table tree goes here + if (haveIptables) { + iptablesUnlinkTmpRootChains(conn, &buf, ifname); + iptablesRemoveTmpRootChains(conn, &buf, ifname); + + iptablesCreateBaseChains(conn, &buf); + + if (ebiptablesExecCLI(conn, &buf, &cli_status) || cli_status != 0) + goto tear_down_tmpebchains; + + iptablesCreateTmpRootChains(conn, &buf, ifname); + + if (ebiptablesExecCLI(conn, &buf, &cli_status) || cli_status != 0) + goto tear_down_tmpiptchains; + + iptablesLinkTmpRootChains(conn, &buf, ifname); + iptablesSetupVirtInPost(conn, &buf, ifname); + if (ebiptablesExecCLI(conn, &buf, &cli_status) || cli_status != 0) + goto tear_down_tmpiptchains; + + for (i = 0; i < nruleInstances; i++) { + if (inst[i]->ruleType == RT_IPTABLES) + iptablesInstCommand(conn, &buf, + inst[i]->commandTemplate, + 'A', -1, 1); + } + + if (ebiptablesExecCLI(conn, &buf, &cli_status) || cli_status != 0) + goto tear_down_tmpiptchains; + } + + // END IPTABLES stuff if (chains_in != 0) @@ -1420,6 +2497,12 @@ tear_down_ebsubchains_and_unlink: ebtablesUnlinkTmpRootChain(conn, &buf, 1, ifname); ebtablesUnlinkTmpRootChain(conn, &buf, 0, ifname); +tear_down_tmpiptchains: + if (haveIptables) { + iptablesUnlinkTmpRootChains(conn, &buf, ifname); + iptablesRemoveTmpRootChains(conn, &buf, ifname); + } + tear_down_tmpebchains: ebtablesRemoveTmpSubChains(conn, &buf, ifname); ebtablesRemoveTmpRootChain(conn, &buf, 1, ifname); @@ -1442,6 +2525,9 @@ ebiptablesTearNewRules(virConnectPtr con int cli_status; virBuffer buf = VIR_BUFFER_INITIALIZER; + iptablesUnlinkTmpRootChains(conn, &buf, ifname); + iptablesRemoveTmpRootChains(conn, &buf, ifname); + ebtablesUnlinkTmpRootChain(conn, &buf, 1, ifname); ebtablesUnlinkTmpRootChain(conn, &buf, 0, ifname); @@ -1462,6 +2548,13 @@ ebiptablesTearOldRules(virConnectPtr con int cli_status; virBuffer buf = VIR_BUFFER_INITIALIZER; + // switch to new iptables user defined chains + iptablesUnlinkRootChains(conn, &buf, ifname); + iptablesRemoveRootChains(conn, &buf, ifname); + + iptablesRenameTmpRootChains(conn, &buf, ifname); + ebiptablesExecCLI(conn, &buf, &cli_status); + ebtablesUnlinkRootChain(conn, &buf, 1, ifname); ebtablesUnlinkRootChain(conn, &buf, 0, ifname); @@ -1541,6 +2634,10 @@ ebiptablesAllTeardown(const char *ifname int cli_status; virConnectPtr conn = NULL; + iptablesUnlinkRootChains(conn, &buf, ifname); + iptablesClearVirtInPost(conn, &buf, ifname); + iptablesRemoveRootChains(conn, &buf, ifname); + ebtablesUnlinkRootChain(conn, &buf, 1, ifname); ebtablesUnlinkRootChain(conn, &buf, 0, ifname); Index: libvirt-acl/src/conf/nwfilter_conf.h =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_conf.h +++ libvirt-acl/src/conf/nwfilter_conf.h @@ -150,6 +150,10 @@ struct _ipHdrDataDef { nwItemDesc dataDstIPAddr; nwItemDesc dataDstIPMask; nwItemDesc dataProtocolID; + nwItemDesc dataSrcIPFrom; + nwItemDesc dataSrcIPTo; + nwItemDesc dataDstIPFrom; + nwItemDesc dataDstIPTo; nwItemDesc dataDSCP; }; @@ -182,6 +186,61 @@ struct _ipv6HdrFilterDef { }; +typedef struct _icmpHdrFilterDef icmpHdrFilterDef; +typedef icmpHdrFilterDef *icmpHdrFilterDefPtr; +struct _icmpHdrFilterDef { + nwItemDesc dataSrcMACAddr; + ipHdrDataDef ipHdr; + nwItemDesc dataICMPType; + nwItemDesc dataICMPCode; + nwItemDesc dataStateFlags; +}; + + +typedef struct _allHdrFilterDef allHdrFilterDef; +typedef allHdrFilterDef *allHdrFilterDefPtr; +struct _allHdrFilterDef { + nwItemDesc dataSrcMACAddr; + ipHdrDataDef ipHdr; +}; + + +typedef struct _igmpHdrFilterDef igmpHdrFilterDef; +typedef igmpHdrFilterDef *igmpHdrFilterDefPtr; +struct _igmpHdrFilterDef { + nwItemDesc dataSrcMACAddr; + ipHdrDataDef ipHdr; +}; + + +typedef struct _tcpHdrFilterDef tcpHdrFilterDef; +typedef tcpHdrFilterDef *tcpHdrFilterDefPtr; +struct _tcpHdrFilterDef { + nwItemDesc dataSrcMACAddr; + ipHdrDataDef ipHdr; + portDataDef portData; + nwItemDesc dataTCPOption; +}; + + +typedef struct _udpHdrFilterDef udpHdrFilterDef; +typedef udpHdrFilterDef *udpHdrFilterDefPtr; +struct _udpHdrFilterDef { + nwItemDesc dataSrcMACAddr; + ipHdrDataDef ipHdr; + portDataDef portData; +}; + + +typedef struct _sctpHdrFilterDef sctpHdrFilterDef; +typedef sctpHdrFilterDef *sctpHdrFilterDefPtr; +struct _sctpHdrFilterDef { + nwItemDesc dataSrcMACAddr; + ipHdrDataDef ipHdr; + portDataDef portData; +}; + + enum virNWFilterRuleActionType { VIR_NWFILTER_RULE_ACTION_DROP = 0, VIR_NWFILTER_RULE_ACTION_ACCEPT, @@ -210,6 +269,14 @@ enum virNWFilterRuleProtocolType { VIR_NWFILTER_RULE_PROTOCOL_ARP, VIR_NWFILTER_RULE_PROTOCOL_IP, VIR_NWFILTER_RULE_PROTOCOL_IPV6, + VIR_NWFILTER_RULE_PROTOCOL_TCP, + VIR_NWFILTER_RULE_PROTOCOL_ICMP, + VIR_NWFILTER_RULE_PROTOCOL_IGMP, + VIR_NWFILTER_RULE_PROTOCOL_UDP, + VIR_NWFILTER_RULE_PROTOCOL_SCTP, + VIR_NWFILTER_RULE_PROTOCOL_ALL, + + VIR_NWFILTER_RULE_PROTOCOL_LAST }; enum virNWFilterEbtablesTableType { @@ -236,6 +303,12 @@ struct _virNWFilterRuleDef { arpHdrFilterDef arpHdrFilter; ipHdrFilterDef ipHdrFilter; ipv6HdrFilterDef ipv6HdrFilter; + tcpHdrFilterDef tcpHdrFilter; + icmpHdrFilterDef icmpHdrFilter; + udpHdrFilterDef udpHdrFilter; + allHdrFilterDef allHdrFilter; + igmpHdrFilterDef igmpHdrFilter; + sctpHdrFilterDef sctpHdrFilter; } p; int nvars; Index: libvirt-acl/src/conf/nwfilter_conf.c =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_conf.c +++ libvirt-acl/src/conf/nwfilter_conf.c @@ -76,6 +76,19 @@ VIR_ENUM_IMPL(virNWFilterChainSuffix, VI "ipv4", "ipv6"); +VIR_ENUM_IMPL(virNWFilterRuleProtocol, VIR_NWFILTER_RULE_PROTOCOL_LAST, + "none", + "mac", + "arp", + "ip", + "ipv6", + "tcp", + "icmp", + "igmp", + "udp", + "sctp", + "all"); + /* * a map entry for a simple static int-to-string map @@ -117,6 +130,10 @@ static const char srcipaddr_str[] = " static const char srcipmask_str[] = "srcipmask"; static const char dstipaddr_str[] = "dstipaddr"; static const char dstipmask_str[] = "dstipmask"; +static const char srcipfrom_str[] = "srcipfrom"; +static const char srcipto_str[] = "srcipto"; +static const char dstipfrom_str[] = "dstipfrom"; +static const char dstipto_str[] = "dstipto"; static const char srcportstart_str[] = "srcportstart"; static const char srcportend_str[] = "srcportend"; static const char dstportstart_str[] = "dstportstart"; @@ -135,6 +152,10 @@ static const char dscp_str[] = " #define SRCIPMASK srcipmask_str #define DSTIPADDR dstipaddr_str #define DSTIPMASK dstipmask_str +#define SRCIPFROM srcipfrom_str +#define SRCIPTO srcipto_str +#define DSTIPFROM dstipfrom_str +#define DSTIPTO dstipto_str #define SRCPORTSTART srcportstart_str #define SRCPORTEND srcportend_str #define DSTPORTSTART dstportstart_str @@ -831,6 +852,149 @@ static const virXMLAttr2Struct ipv6Attri }; +#define COMMON_L3_MAC_PROPS(STRUCT) \ + {\ + .name = SRCMACADDR,\ + .datatype = DATATYPE_MACADDR,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.dataSrcMACAddr),\ + } + +#define COMMON_IP_PROPS(STRUCT) \ + COMMON_L3_MAC_PROPS(STRUCT),\ + {\ + .name = SRCIPADDR,\ + .datatype = DATATYPE_IPADDR,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPAddr),\ + },\ + {\ + .name = DSTIPADDR,\ + .datatype = DATATYPE_IPADDR,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPAddr),\ + },\ + {\ + .name = SRCIPMASK,\ + .datatype = DATATYPE_IPMASK,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPMask),\ + },\ + {\ + .name = DSTIPMASK,\ + .datatype = DATATYPE_IPMASK,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPMask),\ + },\ + {\ + .name = SRCIPFROM,\ + .datatype = DATATYPE_IPADDR,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPFrom),\ + },\ + {\ + .name = SRCIPTO,\ + .datatype = DATATYPE_IPADDR,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataSrcIPTo),\ + },\ + {\ + .name = DSTIPFROM,\ + .datatype = DATATYPE_IPADDR,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPFrom),\ + },\ + {\ + .name = DSTIPTO,\ + .datatype = DATATYPE_IPADDR,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDstIPTo),\ + },\ + {\ + .name = DSCP,\ + .datatype = DATATYPE_UINT8,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataDSCP),\ + .validator = dscpValidator,\ + } + +#define COMMON_PORT_PROPS(STRUCT) \ + {\ + .name = SRCPORTSTART,\ + .datatype = DATATYPE_UINT16,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.portData.dataSrcPortStart),\ + },\ + {\ + .name = SRCPORTEND,\ + .datatype = DATATYPE_UINT16,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.portData.dataSrcPortEnd),\ + },\ + {\ + .name = DSTPORTSTART,\ + .datatype = DATATYPE_UINT16,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.portData.dataDstPortStart),\ + },\ + {\ + .name = DSTPORTEND,\ + .datatype = DATATYPE_UINT16,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.portData.dataDstPortEnd),\ + } + +static const virXMLAttr2Struct tcpAttributes[] = { + COMMON_IP_PROPS(tcpHdrFilter), + COMMON_PORT_PROPS(tcpHdrFilter), + { + .name = "option", + .datatype = DATATYPE_UINT8, + .dataIdx = offsetof(virNWFilterRuleDef, p.tcpHdrFilter.dataTCPOption), + }, + { + .name = NULL, + } +}; + +static const virXMLAttr2Struct udpAttributes[] = { + COMMON_IP_PROPS(udpHdrFilter), + COMMON_PORT_PROPS(udpHdrFilter), + { + .name = NULL, + } +}; + + +static const virXMLAttr2Struct sctpAttributes[] = { + COMMON_IP_PROPS(sctpHdrFilter), + COMMON_PORT_PROPS(sctpHdrFilter), + { + .name = NULL, + } +}; + + +static const virXMLAttr2Struct icmpAttributes[] = { + COMMON_IP_PROPS(icmpHdrFilter), + { + .name = "type", + .datatype = DATATYPE_UINT8, + .dataIdx = offsetof(virNWFilterRuleDef, p.icmpHdrFilter.dataICMPType), + }, + { + .name = "code", + .datatype = DATATYPE_UINT8, + .dataIdx = offsetof(virNWFilterRuleDef, p.icmpHdrFilter.dataICMPCode), + }, + { + .name = NULL, + } +}; + + +static const virXMLAttr2Struct allAttributes[] = { + COMMON_IP_PROPS(allHdrFilter), + { + .name = NULL, + } +}; + + +static const virXMLAttr2Struct igmpAttributes[] = { + COMMON_IP_PROPS(igmpHdrFilter), + { + .name = NULL, + } +}; + + typedef struct _virAttributes virAttributes; struct _virAttributes { const char *id; @@ -857,6 +1021,30 @@ static const virAttributes virAttr[] = { .att = ipv6Attributes, .prtclType = VIR_NWFILTER_RULE_PROTOCOL_IPV6, }, { + .id = "tcp", + .att = tcpAttributes, + .prtclType = VIR_NWFILTER_RULE_PROTOCOL_TCP, + }, { + .id = "udp", + .att = udpAttributes, + .prtclType = VIR_NWFILTER_RULE_PROTOCOL_UDP, + }, { + .id = "sctp", + .att = sctpAttributes, + .prtclType = VIR_NWFILTER_RULE_PROTOCOL_SCTP, + }, { + .id = "icmp", + .att = icmpAttributes, + .prtclType = VIR_NWFILTER_RULE_PROTOCOL_ICMP, + }, { + .id = "all", // = 'any' + .att = allAttributes, + .prtclType = VIR_NWFILTER_RULE_PROTOCOL_ALL, + }, { + .id = "igmp", + .att = igmpAttributes, + .prtclType = VIR_NWFILTER_RULE_PROTOCOL_IGMP, + }, { .id = NULL, } }; @@ -1274,8 +1462,96 @@ virNWFilterRuleDefFixup(virNWFilterRuleD case VIR_NWFILTER_RULE_PROTOCOL_ARP: case VIR_NWFILTER_RULE_PROTOCOL_NONE: break; - } + case VIR_NWFILTER_RULE_PROTOCOL_TCP: + COPY_NEG_SIGN(rule->p.tcpHdrFilter.ipHdr.dataSrcIPMask, + rule->p.tcpHdrFilter.ipHdr.dataSrcIPAddr); + COPY_NEG_SIGN(rule->p.tcpHdrFilter.ipHdr.dataDstIPMask, + rule->p.tcpHdrFilter.ipHdr.dataDstIPAddr); + COPY_NEG_SIGN(rule->p.tcpHdrFilter.ipHdr.dataSrcIPTo, + rule->p.tcpHdrFilter.ipHdr.dataSrcIPFrom); + COPY_NEG_SIGN(rule->p.tcpHdrFilter.ipHdr.dataDstIPTo, + rule->p.tcpHdrFilter.ipHdr.dataDstIPFrom); + COPY_NEG_SIGN(rule->p.tcpHdrFilter.portData.dataSrcPortEnd, + rule->p.tcpHdrFilter.portData.dataSrcPortStart); + COPY_NEG_SIGN(rule->p.tcpHdrFilter.portData.dataDstPortStart, + rule->p.tcpHdrFilter.portData.dataSrcPortStart); + COPY_NEG_SIGN(rule->p.tcpHdrFilter.portData.dataDstPortEnd, + rule->p.tcpHdrFilter.portData.dataSrcPortStart); + break; + + case VIR_NWFILTER_RULE_PROTOCOL_UDP: + COPY_NEG_SIGN(rule->p.udpHdrFilter.ipHdr.dataSrcIPMask, + rule->p.udpHdrFilter.ipHdr.dataSrcIPAddr); + COPY_NEG_SIGN(rule->p.udpHdrFilter.ipHdr.dataDstIPMask, + rule->p.udpHdrFilter.ipHdr.dataDstIPAddr); + COPY_NEG_SIGN(rule->p.udpHdrFilter.ipHdr.dataSrcIPTo, + rule->p.udpHdrFilter.ipHdr.dataSrcIPFrom); + COPY_NEG_SIGN(rule->p.udpHdrFilter.ipHdr.dataDstIPTo, + rule->p.udpHdrFilter.ipHdr.dataDstIPFrom); + COPY_NEG_SIGN(rule->p.udpHdrFilter.portData.dataSrcPortEnd, + rule->p.udpHdrFilter.portData.dataSrcPortStart); + COPY_NEG_SIGN(rule->p.udpHdrFilter.portData.dataDstPortStart, + rule->p.udpHdrFilter.portData.dataSrcPortStart); + COPY_NEG_SIGN(rule->p.udpHdrFilter.portData.dataDstPortEnd, + rule->p.udpHdrFilter.portData.dataSrcPortStart); + break; + + case VIR_NWFILTER_RULE_PROTOCOL_SCTP: + COPY_NEG_SIGN(rule->p.sctpHdrFilter.ipHdr.dataSrcIPMask, + rule->p.sctpHdrFilter.ipHdr.dataSrcIPAddr); + COPY_NEG_SIGN(rule->p.sctpHdrFilter.ipHdr.dataDstIPMask, + rule->p.sctpHdrFilter.ipHdr.dataDstIPAddr); + COPY_NEG_SIGN(rule->p.sctpHdrFilter.ipHdr.dataSrcIPTo, + rule->p.sctpHdrFilter.ipHdr.dataSrcIPFrom); + COPY_NEG_SIGN(rule->p.sctpHdrFilter.ipHdr.dataDstIPTo, + rule->p.sctpHdrFilter.ipHdr.dataDstIPFrom); + COPY_NEG_SIGN(rule->p.sctpHdrFilter.portData.dataSrcPortEnd, + rule->p.sctpHdrFilter.portData.dataSrcPortStart); + COPY_NEG_SIGN(rule->p.sctpHdrFilter.portData.dataDstPortStart, + rule->p.sctpHdrFilter.portData.dataSrcPortStart); + COPY_NEG_SIGN(rule->p.sctpHdrFilter.portData.dataDstPortEnd, + rule->p.sctpHdrFilter.portData.dataSrcPortStart); + break; + + case VIR_NWFILTER_RULE_PROTOCOL_ICMP: + COPY_NEG_SIGN(rule->p.icmpHdrFilter.ipHdr.dataSrcIPMask, + rule->p.icmpHdrFilter.ipHdr.dataSrcIPAddr); + COPY_NEG_SIGN(rule->p.icmpHdrFilter.ipHdr.dataDstIPMask, + rule->p.icmpHdrFilter.ipHdr.dataDstIPAddr); + COPY_NEG_SIGN(rule->p.icmpHdrFilter.ipHdr.dataSrcIPTo, + rule->p.icmpHdrFilter.ipHdr.dataSrcIPFrom); + COPY_NEG_SIGN(rule->p.icmpHdrFilter.ipHdr.dataDstIPTo, + rule->p.icmpHdrFilter.ipHdr.dataDstIPFrom); + COPY_NEG_SIGN(rule->p.icmpHdrFilter.dataICMPCode, + rule->p.icmpHdrFilter.dataICMPType); + break; + + case VIR_NWFILTER_RULE_PROTOCOL_ALL: + COPY_NEG_SIGN(rule->p.allHdrFilter.ipHdr.dataSrcIPMask, + rule->p.allHdrFilter.ipHdr.dataSrcIPAddr); + COPY_NEG_SIGN(rule->p.allHdrFilter.ipHdr.dataDstIPMask, + rule->p.allHdrFilter.ipHdr.dataDstIPAddr); + COPY_NEG_SIGN(rule->p.allHdrFilter.ipHdr.dataSrcIPTo, + rule->p.allHdrFilter.ipHdr.dataSrcIPFrom); + COPY_NEG_SIGN(rule->p.allHdrFilter.ipHdr.dataDstIPTo, + rule->p.allHdrFilter.ipHdr.dataDstIPFrom); + break; + + case VIR_NWFILTER_RULE_PROTOCOL_IGMP: + COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataSrcIPMask, + rule->p.igmpHdrFilter.ipHdr.dataSrcIPAddr); + COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataDstIPMask, + rule->p.igmpHdrFilter.ipHdr.dataDstIPAddr); + COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataSrcIPTo, + rule->p.igmpHdrFilter.ipHdr.dataSrcIPFrom); + COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataDstIPTo, + rule->p.igmpHdrFilter.ipHdr.dataDstIPFrom); + break; + + case VIR_NWFILTER_RULE_PROTOCOL_LAST: + break; + } #undef COPY_NEG_SIGN } Index: libvirt-acl/src/libvirt_private.syms =================================================================== --- libvirt-acl.orig/src/libvirt_private.syms +++ libvirt-acl/src/libvirt_private.syms @@ -448,6 +448,7 @@ virNWFilterPoolObjListFree; virNWFilterDefFormat; virNWFilterChainSuffixTypeToString; virNWFilterRuleActionTypeToString; +virNWFilterRuleProtocolTypeToString; virNWFilterJumpTargetTypeToString; virNWFilterRegisterCallbackDriver; virNWFilterTestUnassignDef; Index: libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.h =================================================================== --- libvirt-acl.orig/src/nwfilter/nwfilter_ebiptables_driver.h +++ libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.h @@ -25,6 +25,12 @@ #define MAX_CHAINNAME_LENGTH 32 /* see linux/netfilter_bridge/ebtables.h */ +enum RuleType { + RT_EBTABLES, + RT_IPTABLES, +/* RT_IP6TABLES, for future use */ +}; + typedef struct _ebiptablesRuleInst ebiptablesRuleInst; typedef ebiptablesRuleInst *ebiptablesRuleInstPtr; struct _ebiptablesRuleInst { @@ -32,6 +38,7 @@ struct _ebiptablesRuleInst { enum virNWFilterChainSuffixType neededProtocolChain; char chainprefix; // I for incoming, O for outgoing unsigned int priority; + enum RuleType ruleType; }; extern virNWFilterTechDriver ebiptables_driver; Index: libvirt-acl/configure.ac =================================================================== --- libvirt-acl.orig/configure.ac +++ libvirt-acl/configure.ac @@ -303,6 +303,13 @@ AC_DEFINE_UNQUOTED([IPTABLES_PATH], "$IP AC_PATH_PROG([EBTABLES_PATH], [ebtables], /sbin/ebtables, [/usr/sbin:$PATH]) AC_DEFINE_UNQUOTED([EBTABLES_PATH], "$EBTABLES_PATH", [path to ebtables binary]) +AC_PATH_PROG([GREP_PATH], [grep], /bin/grep, [/bin:$PATH]) +AC_DEFINE_UNQUOTED([GREP_PATH], "$GREP_PATH", [path to grep binary]) + +AC_PATH_PROG([GAWK_PATH], [gawk], /bin/gawk, [/bin:$PATH]) +AC_DEFINE_UNQUOTED([GAWK_PATH], "$GAWK_PATH", [path to gawk binary]) + + if test "$with_openvz" = "yes"; then AC_DEFINE_UNQUOTED([WITH_OPENVZ], 1, [whether OpenVZ driver is enabled]) fi

This patch adds some example filters to libvirt. They are automatically installed into the proper directory for libvirt to pick them up. --- Makefile.am | 3 +- configure.ac | 3 +- examples/xml/nwfilter/Makefile.am | 29 ++++++++++++++++++++++++++ examples/xml/nwfilter/allow-arp.xml | 3 ++ examples/xml/nwfilter/allow-dhcp-server.xml | 24 +++++++++++++++++++++ examples/xml/nwfilter/allow-dhcp.xml | 21 ++++++++++++++++++ examples/xml/nwfilter/allow-ipv4.xml | 3 ++ examples/xml/nwfilter/clean-traffic.xml | 15 +++++++++++++ examples/xml/nwfilter/no-arp-spoofing.xml | 29 ++++++++++++++++++++++++++ examples/xml/nwfilter/no-ip-multicast.xml | 9 ++++++++ examples/xml/nwfilter/no-ip-spoofing.xml | 7 ++++++ examples/xml/nwfilter/no-mac-broadcast.xml | 8 +++++++ examples/xml/nwfilter/no-mac-spoofing.xml | 5 ++++ examples/xml/nwfilter/no-other-l2-traffic.xml | 7 ++++++ 14 files changed, 164 insertions(+), 2 deletions(-) Index: libvirt-acl/Makefile.am =================================================================== --- libvirt-acl.orig/Makefile.am +++ libvirt-acl/Makefile.am @@ -5,7 +5,8 @@ GENHTML = genhtml SUBDIRS = gnulib/lib include src daemon tools proxy docs gnulib/tests \ python tests po examples/domain-events/events-c examples/hellolibvirt \ - examples/dominfo examples/domsuspend examples/python examples/apparmor + examples/dominfo examples/domsuspend examples/python examples/apparmor \ + examples/xml/nwfilter ACLOCAL_AMFLAGS = -I m4 -I gnulib/m4 Index: libvirt-acl/examples/xml/nwfilter/no-mac-spoofing.xml =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/no-mac-spoofing.xml @@ -0,0 +1,5 @@ +<filter name='no-mac-spoofing' chain='ipv4'> + <rule action='drop' direction='out' priority='10'> + <mac match='no' srcmacaddr='$MAC' /> + </rule> +</filter> Index: libvirt-acl/configure.ac =================================================================== --- libvirt-acl.orig/configure.ac +++ libvirt-acl/configure.ac @@ -1987,7 +1987,8 @@ AC_OUTPUT(Makefile src/Makefile include/ examples/domsuspend/Makefile \ examples/dominfo/Makefile \ examples/python/Makefile \ - examples/hellolibvirt/Makefile) + examples/hellolibvirt/Makefile \ + examples/xml/nwfilter/Makefile) AC_MSG_NOTICE([]) AC_MSG_NOTICE([Configuration summary]) Index: libvirt-acl/examples/xml/nwfilter/Makefile.am =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/Makefile.am @@ -0,0 +1,30 @@ + +FILTERS = \ + allow-arp.xml \ + allow-dhcp-server.xml \ + allow-dhcp.xml \ + allow-incoming-ipv4.xml \ + allow-ipv4.xml \ + clean-traffic.xml \ + no-arp-spoofing.xml \ + no-ip-multicast.xml \ + no-ip-spoofing.xml \ + no-mac-broadcast.xml \ + no-mac-spoofing.xml \ + no-other-l2-traffic.xml + +confdir = $(sysconfdir)/libvirt + +NWFILTER_DIR = "$(DESTDIR)$(sysconfdir)/libvirt/nwfilter" + +install-data-local: + $(MKDIR_P) "$(NWFILTER_DIR)" + for f in $(FILTERS); do \ + $(INSTALL_DATA) $$f "$(NWFILTER_DIR)"; \ + done + +uninstall-local:: + for f in $(FILTERS); do \ + rm -f "$(NWFILTER_DIR)/$$f"; \ + done + -test -z $(shell ls $(NWFILTER_DIR)) || rmdir $(NWFILTER_DIR) Index: libvirt-acl/examples/xml/nwfilter/no-arp-spoofing.xml =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/no-arp-spoofing.xml @@ -0,0 +1,29 @@ +<filter name='no-arp-spoofing' chain='arp'> + <uuid>f88f1932-debf-4aa1-9fbe-f10d3aa4bc95</uuid> + + <!-- no arp spoofing --> + <!-- drop if ipaddr or macaddr does not belong to guest --> + <rule action='drop' direction='out' priority='400' > + <arp match='no' arpsrcmacaddr='$MAC'/> + </rule> + <rule action='drop' direction='out' priority='400' > + <arp match='no' arpsrcipaddr='$IP' /> + </rule> + <!-- drop if ipaddr or macaddr odes not belong to guest --> + <rule action='drop' direction='in' priority='400' > + <arp match='no' arpdstmacaddr='$MAC'/> + <arp opcode='reply'/> + </rule> + <rule action='drop' direction='in' priority='400' > + <arp match='no' arpdstipaddr='$IP' /> + </rule> + <!-- accept only request or reply packets --> + <rule action='accept' direction='inout' priority='500' > + <arp opcode='request'/> + </rule> + <rule action='accept' direction='inout' priority='500' > + <arp opcode='reply'/> + </rule> + <!-- drop everything else --> + <rule action='drop' direction='inout' priority='1000' /> +</filter> Index: libvirt-acl/examples/xml/nwfilter/no-mac-broadcast.xml =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/no-mac-broadcast.xml @@ -0,0 +1,8 @@ +<filter name='no-mac-broadcast' chain='ipv4'> + <!-- drop if destination mac is bcast mac addr. --> + <rule action='drop' direction='out'> + <mac dstmacaddr='ff:ff:ff:ff:ff:ff' /> + </rule> + + <!-- not doing anything with receiving side ... --> +</filter> Index: libvirt-acl/examples/xml/nwfilter/no-ip-multicast.xml =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/no-ip-multicast.xml @@ -0,0 +1,9 @@ +<filter name='no-ip-multicast' chain='ipv4'> + + <!-- drop if destination IP address is in the 224.0.0.0/4 subnet --> + <rule action='drop' direction='out'> + <ip dstipaddr='224.0.0.0' dstipmask='4' /> + </rule> + + <!-- not doing anything with receiving side ... --> +</filter> Index: libvirt-acl/examples/xml/nwfilter/no-ip-spoofing.xml =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/no-ip-spoofing.xml @@ -0,0 +1,7 @@ +<filter name='no-ip-spoofing' chain='ipv4'> + + <!-- drop if srcipaddr is not the IP address of the guest --> + <rule action='drop' direction='out'> + <ip match='no' srcipaddr='$IP' /> + </rule> +</filter> Index: libvirt-acl/examples/xml/nwfilter/allow-arp.xml =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/allow-arp.xml @@ -0,0 +1,3 @@ +<filter name='allow-arp' chain='arp'> + <rule direction='inout' action='accept'/> +</filter> Index: libvirt-acl/examples/xml/nwfilter/allow-ipv4.xml =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/allow-ipv4.xml @@ -0,0 +1,3 @@ +<filter name='allow-ipv4' chain='ipv4'> + <rule direction='inout' action='accept'/> +</filter> Index: libvirt-acl/examples/xml/nwfilter/allow-dhcp-server.xml =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/allow-dhcp-server.xml @@ -0,0 +1,24 @@ +<filter name='allow-dhcp-server' chain='ipv4'> + + <!-- accept outgoing DHCP requests --> + <!-- note, this rule must be evaluated before general MAC broadcast + traffic is discarded since DHCP requests use MAC broadcast --> + <rule action='accept' direction='out' priority='100'> + <ip srcipaddr='0.0.0.0' + dstipaddr='255.255.255.255' + protocol='udp' + srcportstart='68' + dstportstart='67' /> + </rule> + + <!-- accept incoming DHCP responses from a specific DHCP server + parameter DHPCSERVER needs to be passed from where this filter is + referenced --> + <rule action='accept' direction='in' priority='100' > + <ip srcipaddr='$DHCPSERVER' + protocol='udp' + srcportstart='67' + dstportstart='68'/> + </rule> + +</filter> Index: libvirt-acl/examples/xml/nwfilter/no-other-l2-traffic.xml =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/no-other-l2-traffic.xml @@ -0,0 +1,7 @@ +<filter name='no-other-l2-traffic'> + + <!-- drop all other l2 traffic than for which rules have been + written for; i.e., drop all other than arp and ipv4 traffic --> + <rule action='drop' direction='inout' priority='1000'/> + +</filter> Index: libvirt-acl/examples/xml/nwfilter/allow-dhcp.xml =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/allow-dhcp.xml @@ -0,0 +1,21 @@ +<filter name='allow-dhcp' chain='ipv4'> + + <!-- accept outgoing DHCP requests --> + <!-- not, this rule must be evaluated before general MAC broadcast + traffic is discarded since DHCP requests use MAC broadcast --> + <rule action='accept' direction='out' priority='100'> + <ip srcipaddr='0.0.0.0' + dstipaddr='255.255.255.255' + protocol='udp' + srcportstart='68' + dstportstart='67' /> + </rule> + + <!-- accept incoming DHCP responses from any DHCP server --> + <rule action='accept' direction='in' priority='100' > + <ip protocol='udp' + srcportstart='67' + dstportstart='68'/> + </rule> + +</filter> Index: libvirt-acl/examples/xml/nwfilter/clean-traffic.xml =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/clean-traffic.xml @@ -0,0 +1,17 @@ +<filter name='clean-traffic'> + <!-- An example of a traffic filter enforcing clean traffic + from a VM by + - preventing MAC spoofing --> + <filterref filter='no-mac-spoofing'/> + + <!-- preventing IP spoofing on outgoing, allow all IPv4 in incoming --> + <filterref filter='no-ip-spoofing'/> + <filterref filter='allow-incoming-ipv4'/> + + <!-- preventing ARP spoofing/poisoning --> + <filterref filter='no-arp-spoofing'/> + + <!-- preventing any other traffic than IPv4 and ARP --> + <filterref filter='no-other-l2-traffic'/> + +</filter> Index: libvirt-acl/examples/xml/nwfilter/allow-incoming-ipv4.xml =================================================================== --- /dev/null +++ libvirt-acl/examples/xml/nwfilter/allow-incoming-ipv4.xml @@ -0,0 +1,3 @@ +<filter name='allow-incoming-ipv4' chain='ipv4'> + <rule direction='in' action='accept'/> +</filter>

On Thu, Mar 25, 2010 at 01:45:58PM -0400, Stefan Berger wrote:
Hi!
This is a repost of this set of patches with some of the suggested fixes applied and ipv6 support on the ebtables layer added.
Between V3 and V4 of this patch series the following changes were made: - occurrences of typo 'scp' were changed to 'sctp' - the root ebtables chain for each interface now has the previx of 'libvirt-' - additional calls into tear-down functions in case something goes wrong while starting the qemu/kvm VM in 2nd level error paths - additional functions in the driver interface to split up the application of firewall rules into - creation of new firewall rules 'tree' - switch-over to new firewall rules 'tree', tear down of old one and renaming of new firewall 'tree' - tear down of new firewall rules 'tree' in case an error happend during update of several VMs. - additional patch with example filters
FYI, I have pushed this whole v4 series to libvirt GIT. I had to re-order the patches to make the series bisectable, and fix one or two minor syntax check problems, but no code changes. There is one problem I would like to see fixed asap though src/conf/nwfilter_conf.c has a dependancy on the driver implementation nwfilter/nwfilter_gentech_driver.h which is not good. The 'conf' directory is only allowed to depend on stuff in util/, or itself, never depend on driver code. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote on 03/26/2010 02:04:26 PM:
Please respond to "Daniel P. Berrange"
On Thu, Mar 25, 2010 at 01:45:58PM -0400, Stefan Berger wrote:
Hi!
This is a repost of this set of patches with some of the suggested
fixes
applied and ipv6 support on the ebtables layer added.
Between V3 and V4 of this patch series the following changes were made: - occurrences of typo 'scp' were changed to 'sctp' - the root ebtables chain for each interface now has the previx of'libvirt-' - additional calls into tear-down functions in case something goes wrong while starting the qemu/kvm VM in 2nd level error paths - additional functions in the driver interface to split up the application of firewall rules into - creation of new firewall rules 'tree' - switch-over to new firewall rules 'tree', tear down of old one and renaming of new firewall 'tree' - tear down of new firewall rules 'tree' in case an error happend during update of several VMs. - additional patch with example filters
FYI, I have pushed this whole v4 series to libvirt GIT.
I had to re-order the patches to make the series bisectable, and fix one or two minor syntax check problems, but no code changes.
There is one problem I would like to see fixed asap though
src/conf/nwfilter_conf.c
has a dependancy on the driver implementation nwfilter/ nwfilter_gentech_driver.h which is not good. The 'conf' directory is only allowed to depend on stuff in util/, or itself, never depend on driver code.
From nwfilter_conf.c I call several functions of the nwfilter_gentech_driver.c from within an iterator callback function. Is the general right solution for this to have nwfilter_gentech_driver.c register an interface with nwfilter_conf.c that provides the addresses of those functions call from within nwfilter_conf.c now? If so, I think I could pass the callback function to the nwfilter_conf.c and move the actual callback function in nwfilter_gentech_driver.c and pass its address via the initialization function I call in nwfilter_conf.c from nwfilter_gentech_driver.c.
Thanks and regards, Stefan
Regards, Daniel -- |: Red Hat, Engineering, London -o-
http://people.redhat.com/berrange/:|
|: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org:| |: http://autobuild.org -o- http://search.cpan.org/~danberr/:| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Fri, Mar 26, 2010 at 02:41:30PM -0400, Stefan Berger wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote on 03/26/2010 02:04:26 PM:
Please respond to "Daniel P. Berrange"
On Thu, Mar 25, 2010 at 01:45:58PM -0400, Stefan Berger wrote:
Hi!
This is a repost of this set of patches with some of the suggested
fixes
applied and ipv6 support on the ebtables layer added.
Between V3 and V4 of this patch series the following changes were made: - occurrences of typo 'scp' were changed to 'sctp' - the root ebtables chain for each interface now has the previx of'libvirt-' - additional calls into tear-down functions in case something goes wrong while starting the qemu/kvm VM in 2nd level error paths - additional functions in the driver interface to split up the application of firewall rules into - creation of new firewall rules 'tree' - switch-over to new firewall rules 'tree', tear down of old one and renaming of new firewall 'tree' - tear down of new firewall rules 'tree' in case an error happend during update of several VMs. - additional patch with example filters
FYI, I have pushed this whole v4 series to libvirt GIT.
I had to re-order the patches to make the series bisectable, and fix one or two minor syntax check problems, but no code changes.
There is one problem I would like to see fixed asap though
src/conf/nwfilter_conf.c
has a dependancy on the driver implementation nwfilter/ nwfilter_gentech_driver.h which is not good. The 'conf' directory is only allowed to depend on stuff in util/, or itself, never depend on driver code.
From nwfilter_conf.c I call several functions of the nwfilter_gentech_driver.c from within an iterator callback function. Is the general right solution for this to have nwfilter_gentech_driver.c register an interface with nwfilter_conf.c that provides the addresses of those functions call from within nwfilter_conf.c now? If so, I think I could pass the callback function to the nwfilter_conf.c and move the actual callback function in nwfilter_gentech_driver.c and pass its address via the initialization function I call in nwfilter_conf.c from nwfilter_gentech_driver.c.
Cool, that sounds like a good plan Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

"Daniel P. Berrange" <berrange@redhat.com> wrote on 03/26/2010 04:44:49 PM:
On Fri, Mar 26, 2010 at 02:41:30PM -0400, Stefan Berger wrote:
"Daniel P. Berrange" <berrange@redhat.com> wrote on 03/26/2010 02:04:26 PM:
[...]
has a dependancy on the driver implementation nwfilter/ nwfilter_gentech_driver.h which is not good. The 'conf' directory is only allowed to depend on
stuff
in util/, or itself, never depend on driver code.
From nwfilter_conf.c I call several functions of the nwfilter_gentech_driver.c from within an iterator callback function. Is the general right solution for this to have nwfilter_gentech_driver.c register an interface with nwfilter_conf.c that provides the addresses of those functions call from within nwfilter_conf.c now? If so, I think I
could pass the callback function to the nwfilter_conf.c and move the actual callback function in nwfilter_gentech_driver.c and pass its address via the initialization function I call in nwfilter_conf.c from nwfilter_gentech_driver.c.
Cool, that sounds like a good plan
Posted the patch that does that on Friday. Regards, Stefan
Regards, Daniel -- |: Red Hat, Engineering, London -o-
http://people.redhat.com/berrange/:|
|: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org:| |: http://autobuild.org -o- http://search.cpan.org/~danberr/:| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
participants (3)
-
Daniel P. Berrange
-
Daniel Veillard
-
Stefan Berger