Attachment may have been too big for mailserver -- sending patch inline.
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(a)us.ibm.com>
---
configure.ac | 3
daemon/libvirtd.c | 7
include/libvirt/virterror.h | 5
python/generator.py | 2
src/conf/nwfilter_conf.c | 2503
++++++++++++++++++++++++++++++
src/conf/nwfilter_conf.h | 472 +++++
src/datatypes.c | 142 +
src/datatypes.h | 32
src/nwfilter/nwfilter_driver.c | 429 +++++
src/nwfilter/nwfilter_driver.h | 35
src/nwfilter/nwfilter_ebiptables_driver.c | 1313 +++++++++++++++
src/nwfilter/nwfilter_ebiptables_driver.h | 41
src/nwfilter/nwfilter_gentech_driver.c | 656 +++++++
src/nwfilter/nwfilter_gentech_driver.h | 52
src/util/virterror.c | 27
15 files changed, 5719 insertions(+)
Index: libvirt-acl/src/conf/nwfilter_conf.c
===================================================================
--- /dev/null
+++ libvirt-acl/src/conf/nwfilter_conf.c
@@ -0,0 +1,2503 @@
+/*
+ * 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(a)us.ibm.com>
+ */
+#include <stdio.h>
+#include <config.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <dirent.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/ethernet.h>
+
+#include "config.h"
+
+#include "virterror_internal.h"
+#include "datatypes.h"
+#include "nwfilter_conf.h"
+#include "domain_conf.h"
+
+#include "xml.h"
+#include "uuid.h"
+#include "buf.h"
+#include "util.h"
+#include "memory.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);
+}
+
+
+/*
+ * regular expressions for parameter names and values
+ */
+static regex_t regex_nam;
+static regex_t regex_val;
+
+
+/*
+ * 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 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";
+
+#define SRCMACADDR srcmacaddr_str
+#define SRCMACMASK srcmacmask_str
+#define DSTMACADDR dstmacaddr_str
+#define DSTMACMASK dstmacmask_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
+
+
+/**
+ * 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
+hashDealloc(void *payload, const char *name ATTRIBUTE_UNUSED)
+{
+ VIR_FREE(payload);
+}
+
+
+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 bool
+checkPriority(enum attrDatatype datatype ATTRIBUTE_UNUSED, void *val,
+ virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED)
+{
+ unsigned char prio = *(unsigned char *)val;
+ if (prio > 7)
+ return 0;
+ return 1;
+}
+
+
+static bool
+checkVLAN(enum attrDatatype datatype ATTRIBUTE_UNUSED, void *val,
+ virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED)
+{
+ uint16_t vid = *(uint16_t *)val;
+ if (vid >= 0x1000)
+ return 0;
+ return 1;
+}
+
+
+
+
+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_UINT8) {
+ if (intMapGetByInt(arpOpcodeMap,
+ (uint32_t)*(uint16_t *)value, &str) == 0)
+ res = -1;
+ }
+
+ if (res != -1) {
+ nwf->p.arpHdrFilter.dataOpcode.u.u16 = res;
+ 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.u16 = res;
+ return 1;
+ }
+ return 0;
+}
+
+
+static bool
+formatIPProtocolID(virBufferPtr buf,
+ virNWFilterRuleDefPtr nwf)
+{
+ const char *str = NULL;
+
+ if (intMapGetByInt(ipProtoMap,
+ nwf->p.ipHdrFilter.ipHdr.dataProtocolID.u.u16,
+ &str)) {
+ virBufferVSprintf(buf, "%s", str);
+ return 1;
+ }
+ return 0;
+}
+
+
+static const virXMLAttr2Struct macAttributes[] = {
+ {
+ .name = SRCMACADDR,
+ .datatype = DATATYPE_MACADDR,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.ethHdrFilter.dataSrcMACAddr),
+ },
+ {
+ .name = SRCMACMASK,
+ .datatype = DATATYPE_MACMASK,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.ethHdrFilter.dataSrcMACMask),
+ },
+ {
+ .name = DSTMACADDR,
+ .datatype = DATATYPE_MACADDR,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.ethHdrFilter.dataDstMACAddr),
+ },
+ {
+ .name = DSTMACMASK,
+ .datatype = DATATYPE_MACMASK,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.ethHdrFilter.dataDstMACMask),
+ },
+ {
+ .name = "protocolid",
+ .datatype = DATATYPE_UINT16 | DATATYPE_STRING,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.ethHdrFilter.dataProtocolID),
+ .validator= checkMacProtocolID,
+ .formatter= macProtocolIDFormatter,
+ },
+ {
+ .name = "priority",
+ .datatype = DATATYPE_UINT8,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.ethHdrFilter.dataPriority),
+ .validator= checkPriority, // enforce only 3 valid bits
+ },
+ {
+ .name = "vlanid",
+ .datatype = DATATYPE_UINT16,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.ethHdrFilter.dataVLANID),
+ .validator= checkVLAN, // enforce only 12 valid bits
+ },
+ {
+ .name = NULL,
+ }
+};
+
+static const virXMLAttr2Struct arpAttributes[] = {
+ {
+ .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_UINT8 | DATATYPE_STRING,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.arpHdrFilter.dataOpcode),
+ .validator= arpOpcodeValidator,
+ .formatter= arpOpcodeFormatter,
+ }, {
+ .name = SRCMACADDR,
+ .datatype = DATATYPE_MACADDR,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.arpHdrFilter.dataSrcMACAddr),
+ }, {
+ .name = DSTMACADDR,
+ .datatype = DATATYPE_MACADDR,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.arpHdrFilter.dataDstMACAddr),
+ }, {
+ .name = SRCIPADDR,
+ .datatype = DATATYPE_IPADDR,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.arpHdrFilter.dataSrcIPAddr),
+ }, {
+ .name = DSTIPADDR,
+ .datatype = DATATYPE_IPADDR,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.arpHdrFilter.dataDstIPAddr),
+ },
+ {
+ .name = NULL,
+ }
+};
+
+static const virXMLAttr2Struct ipAttributes[] = {
+ {
+ .name = "version",
+ .datatype = DATATYPE_UINT8,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.ipHdrFilter.ipHdr.dataIPVersion),
+ },
+ {
+ .name = SRCIPADDR,
+ .datatype = DATATYPE_IPADDR,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.ipHdrFilter.ipHdr.dataSrcAddr),
+ },
+ {
+ .name = DSTIPADDR,
+ .datatype = DATATYPE_IPADDR,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.ipHdrFilter.ipHdr.dataDstAddr),
+ },
+ {
+ .name = SRCIPMASK,
+ .datatype = DATATYPE_IPMASK,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.ipHdrFilter.ipHdr.dataSrcMask),
+ },
+ {
+ .name = DSTIPMASK,
+ .datatype = DATATYPE_IPMASK,
+ .dataIdx = offsetof(virNWFilterRuleDef,
p.ipHdrFilter.ipHdr.dataDstMask),
+ },
+ {
+ .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 = 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:
+ // parse as dotted IPv4
+ // search for existing parser in libvirt
+ // later: also parse as IPv6
+ storage_ptr = &item->u.ipaddr;
+ if (!virNWIPv4AddressParser(prop,
+ (nwIPAddressPtr)storage_ptr)) {
+ rc = -1;
+ }
+ found = 1;
+ break;
+
+ case DATATYPE_IPMASK:
+ // parse as dotted IPv4
+ // parse as CIDR mask
+ // later: also parse as IPv6
+ 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;
+}
+
+
+/**
+ * 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);
+}
+
+
+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.dataSrcMACMask,
+ rule->p.ethHdrFilter.dataSrcMACAddr);
+ COPY_NEG_SIGN(rule->p.ethHdrFilter.dataDstMACMask,
+ rule->p.ethHdrFilter.dataDstMACAddr);
+ break;
+
+ case VIR_NWFILTER_RULE_PROTOCOL_IP:
+ COPY_NEG_SIGN(rule->p.ipHdrFilter.ipHdr.dataSrcMask,
+ rule->p.ipHdrFilter.ipHdr.dataSrcAddr);
+ COPY_NEG_SIGN(rule->p.ipHdrFilter.ipHdr.dataDstMask,
+ rule->p.ipHdrFilter.ipHdr.dataDstAddr);
+ 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;
+ }
+}
+
+
+struct cbStruct {
+ virConnectPtr conn;
+ int doUpdate;
+ 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)) {
+ if (cb->doUpdate)
+ cb->err =
virNWFilterUpdateInstantiateFilter(cb->conn,
+ net);
+ else
+ cb->err = virNWFilterRollbackUpdateFilter(cb->conn,
net);
+ if (cb->err)
+ break;
+ }
+ }
+ }
+
+ virDomainObjUnlock(obj);
+}
+
+
+static int
+virNWFilterTriggerVMFilterRebuild(virConnectPtr conn)
+{
+ int i;
+ int err;
+ struct cbStruct cb = {
+ .conn = conn,
+ .err = 0,
+ .doUpdate = 1,
+ };
+
+ for (i = 0; i < nCallbackDriver; i++) {
+ callbackDrvArray[i]->vmFilterRebuild(conn,
+
virNWFilterDomainFWUpdateCB,
+ &cb);
+ }
+
+ err = cb.err;
+
+ if (err) {
+ cb.doUpdate = 0; // rollback
+ cb.err = 0;
+
+ 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 (virRecursiveMutexInit(&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 (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 virNWFilterConfLayerShutdown(void) {
+ regfree(®ex_nam);
+ regfree(®ex_val);
+}
+
+
+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,472 @@
+/*
+ * 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(a)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 _ethHdrFilterDef ethHdrFilterDef;
+typedef ethHdrFilterDef *ethHdrFilterDefPtr;
+struct _ethHdrFilterDef {
+ nwItemDesc dataSrcMACAddr;
+ nwItemDesc dataSrcMACMask;
+ nwItemDesc dataDstMACAddr;
+ nwItemDesc dataDstMACMask;
+ nwItemDesc dataProtocolID;
+ nwItemDesc dataPriority;
+ nwItemDesc dataVLANID;
+};
+
+
+typedef struct _arpHdrFilterDef arpHdrFilterDef;
+typedef arpHdrFilterDef *arpHdrFilterDefPtr;
+struct _arpHdrFilterDef {
+ nwItemDesc dataHWType;
+ nwItemDesc dataProtocolType;
+ nwItemDesc dataOpcode;
+ nwItemDesc dataSrcMACAddr;
+ nwItemDesc dataSrcIPAddr;
+ nwItemDesc dataDstMACAddr;
+ nwItemDesc dataDstIPAddr;
+};
+
+
+typedef struct _ipHdrDataDef ipHdrDataDef;
+typedef ipHdrDataDef *ipHdrDataDefPtr;
+struct _ipHdrDataDef {
+ nwItemDesc dataIPVersion;
+ nwItemDesc dataSrcAddr;
+ nwItemDesc dataSrcMask;
+ nwItemDesc dataDstAddr;
+ nwItemDesc dataDstMask;
+ nwItemDesc dataProtocolID;
+};
+
+
+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 {
+ ipHdrDataDef ipHdr;
+ portDataDef portData;
+ nwItemDesc dataDSCP;
+};
+
+
+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 _virNWFilterHashTable virNWFilterHashTable;
+typedef virNWFilterHashTable *virNWFilterHashTablePtr;
+struct _virNWFilterHashTable {
+ virHashTablePtr hashTable;
+
+ int nNames;
+ char **names;
+};
+
+
+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 (*virNWFilterRuleApplyRules)(virConnectPtr conn,
+ const char *ifname,
+ int nruleInstances,
+ void **_inst);
+
+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;
+ virNWFilterRuleApplyRules applyRules;
+ virNWFilterRuleRemoveRules removeRules;
+ virNWFilterRuleAllTeardown allTeardown;
+ virNWFilterRuleFreeInstanceData freeRuleInstance;
+ virNWFilterRuleDisplayInstanceData displayRuleInstance;
+};
+
+
+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);
+
+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);
+
+#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
Index: libvirt-acl/include/libvirt/virterror.h
===================================================================
--- libvirt-acl.orig/include/libvirt/virterror.h
+++ libvirt-acl/include/libvirt/virterror.h
@@ -69,6 +69,7 @@ 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_NWFILTER, /* Error from network filter driver */
} virErrorDomain;
@@ -168,6 +169,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
@@ -175,6 +175,9 @@ static const char *virErrorDomainName(vi
case VIR_FROM_CPU:
dom = "CPU ";
break;
+ case VIR_FROM_NWFILTER:
+ dom = "Network Filter";
+ break;
}
return(dom);
}
@@ -1097,6 +1100,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,429 @@
+/*
+ * 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(a)redhat.com>
+ * Stefan Berger <stefanb(a)us.ibm.com>
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif
+#include <errno.h>
+#include <string.h>
+
+#include "virterror_internal.h"
+#include "datatypes.h"
+#include "driver.h"
+#include "util.h"
+#include "nwfilter_driver.h"
+#include "nwfilter_conf.h"
+#include "memory.h"
+
+#include "domain_conf.h"
+#include "nwfilter_conf.h"
+#include "nwfilter_gentech_driver.h"
+#include "nwfilter_ebiptables_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,35 @@
+/*
+ * 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(a)redhat.com>
+ * Stefan Berger <stefanb(a)us.ibm.com>
+ */
+
+#ifndef __VIR_NWFILTER_DRIVER_H__
+#define __VIR_NWFILTER_DRIVER_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,1313 @@
+/*
+ * 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(a)us.ibm.com>
+ */
+
+#include <config.h>
+
+#include <stdint.h>
+#include <sys/stat.h>
+
+#include "internal.h"
+
+#include "buf.h"
+#include "memory.h"
+#include "logging.h"
+#include "datatypes.h"
+#include "virterror_internal.h"
+#include "domain_conf.h"
+#include "nwfilter_conf.h"
+#include "nwfilter_driver.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), "%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 port
number"));
+ return 1;
+ }
+ break;
+ default:
+ virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER,
+ _("Unhandled 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);
+}
+
+/*
+ * 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],
+ portstr[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 (HAS_ENTRY_ITEM(&rule->p.ethHdrFilter.dataProtocolID)) {
+ virBufferVSprintf(&buf,
+ " -p %s %d",
+
ENTRY_GET_NEG_SIGN(&rule->p.ethHdrFilter.dataProtocolID),
+ rule->p.ethHdrFilter.dataProtocolID.u.u16);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.ethHdrFilter.dataSrcMACAddr)) {
+ if (printDataType(conn,
+ vars,
+ macaddr, sizeof(macaddr),
+ &rule->p.ethHdrFilter.dataSrcMACAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " -s %s %s",
+
ENTRY_GET_NEG_SIGN(&rule->p.ethHdrFilter.dataSrcMACAddr),
+ macaddr);
+
+ if (HAS_ENTRY_ITEM(&rule->p.ethHdrFilter.dataSrcMACMask)) {
+ if (printDataType(conn,
+ vars,
+ macaddr, sizeof(macaddr),
+
&rule->p.ethHdrFilter.dataSrcMACMask))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ "/%s",
+ macaddr);
+ }
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.ethHdrFilter.dataDstMACAddr)) {
+ if (printDataType(conn,
+ vars,
+ macaddr, sizeof(macaddr),
+ &rule->p.ethHdrFilter.dataDstMACAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " -d %s %s",
+
ENTRY_GET_NEG_SIGN(&rule->p.ethHdrFilter.dataDstMACAddr),
+ macaddr);
+
+ if (HAS_ENTRY_ITEM(&rule->p.ethHdrFilter.dataDstMACMask)) {
+ if (printDataType(conn,
+ vars,
+ macaddr, sizeof(macaddr),
+
&rule->p.ethHdrFilter.dataDstMACMask))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ "/%s",
+ macaddr);
+ }
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.ethHdrFilter.dataPriority)) {
+ virBufferVSprintf(&buf,
+ " --vlan-prio %s %d",
+
ENTRY_GET_NEG_SIGN(&rule->p.ethHdrFilter.dataPriority),
+ rule->p.ethHdrFilter.dataPriority.u.u8);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.ethHdrFilter.dataVLANID)) {
+ virBufferVSprintf(&buf,
+ " --vlan-id %s %d",
+
ENTRY_GET_NEG_SIGN(&rule->p.ethHdrFilter.dataVLANID),
+ rule->p.ethHdrFilter.dataVLANID.u.u16);
+ }
+ break;
+
+ case VIR_NWFILTER_RULE_PROTOCOL_ARP:
+
+ virBufferVSprintf(&buf,
+ CMD_DEF_PRE EBTABLES_CMD " -t %s -%%c %s %%
s",
+ EBTABLES_DEFAULT_TABLE, chain);
+
+ virBufferAddLit(&buf, " -p arp");
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataHWType)) {
+ virBufferVSprintf(&buf,
+ " --arp-htype %s %d",
+
ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataHWType),
+ rule->p.arpHdrFilter.dataHWType.u.u16);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataOpcode)) {
+ virBufferVSprintf(&buf,
+ " --arp-opcode %s %d",
+
ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataOpcode),
+ rule->p.arpHdrFilter.dataOpcode.u.u16);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataProtocolType)) {
+ virBufferVSprintf(&buf,
+ " --arp-ptype %s %d",
+
ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataProtocolType),
+ rule->p.arpHdrFilter.dataProtocolType.u.u16);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataSrcIPAddr)) {
+ if (printDataType(conn,
+ vars,
+ ipaddr, sizeof(ipaddr),
+ &rule->p.arpHdrFilter.dataSrcIPAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --arp-ip-src %s %s",
+
ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataSrcIPAddr),
+ ipaddr);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataDstIPAddr)) {
+ if (printDataType(conn,
+ vars,
+ ipaddr, sizeof(ipaddr),
+ &rule->p.arpHdrFilter.dataDstIPAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --arp-ip-dst %s %s",
+
ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataDstIPAddr),
+ ipaddr);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataSrcMACAddr)) {
+ if (printDataType(conn,
+ vars,
+ macaddr, sizeof(macaddr),
+ &rule->p.arpHdrFilter.dataSrcMACAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --arp-mac-src %s %s",
+
ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataSrcMACAddr),
+ macaddr);
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.arpHdrFilter.dataDstMACAddr)) {
+ if (printDataType(conn,
+ vars,
+ macaddr, sizeof(macaddr),
+ &rule->p.arpHdrFilter.dataDstMACAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --arp-mac-dst %s %s",
+
ENTRY_GET_NEG_SIGN(&rule->p.arpHdrFilter.dataDstMACAddr),
+ macaddr);
+ }
+ break;
+
+ case VIR_NWFILTER_RULE_PROTOCOL_IP:
+ virBufferVSprintf(&buf,
+ CMD_DEF_PRE EBTABLES_CMD " -t %s -%%c %s %%
s",
+ EBTABLES_DEFAULT_TABLE, chain);
+
+ // FIXME -- may not be necessary if rule is in IPv4 user
defined
+ // table...
+ virBufferAddLit(&buf,
+ " -p ipv4");
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataSrcAddr)) {
+ if (printDataType(conn,
+ vars,
+ ipaddr, sizeof(ipaddr),
+ &rule->p.ipHdrFilter.ipHdr.dataSrcAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --ip-source %s %s",
+
ENTRY_GET_NEG_SIGN(&rule->p.ipHdrFilter.ipHdr.dataSrcAddr),
+ ipaddr);
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataSrcMask))
{
+ virBufferVSprintf(&buf,
+ "/%d",
+
rule->p.ipHdrFilter.ipHdr.dataSrcMask.u.u8);
+ }
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataDstAddr)) {
+
+ if (printDataType(conn,
+ vars,
+ ipaddr, sizeof(ipaddr),
+ &rule->p.ipHdrFilter.ipHdr.dataDstAddr))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --ip-destination %s %s",
+
ENTRY_GET_NEG_SIGN(&rule->p.ipHdrFilter.ipHdr.dataDstAddr),
+ ipaddr);
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataDstMask))
{
+ virBufferVSprintf(&buf,
+ "/%d",
+
rule->p.ipHdrFilter.ipHdr.dataDstMask.u.u8);
+ }
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.ipHdr.dataProtocolID))
{
+ virBufferVSprintf(&buf,
+ " --ip-protocol %s %d",
+
ENTRY_GET_NEG_SIGN(&rule->p.ipHdrFilter.ipHdr.dataProtocolID),
+
rule->p.ipHdrFilter.ipHdr.dataProtocolID.u.u16);
+ }
+
+ if
(HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.portData.dataSrcPortStart)) {
+
+ if (printDataType(conn,
+ vars,
+ portstr, sizeof(portstr),
+
&rule->p.ipHdrFilter.portData.dataSrcPortStart))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --ip-source-port %s %s",
+
ENTRY_GET_NEG_SIGN(&rule->p.ipHdrFilter.portData.dataSrcPortStart),
+ portstr);
+
+ if
(HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.portData.dataSrcPortEnd)) {
+ if (printDataType(conn,
+ vars,
+ portstr, sizeof(portstr),
+
&rule->p.ipHdrFilter.portData.dataSrcPortEnd))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ ":%s",
+ portstr);
+ }
+ }
+
+ if
(HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.portData.dataDstPortStart)) {
+
+ if (printDataType(conn,
+ vars,
+ portstr, sizeof(portstr),
+
&rule->p.ipHdrFilter.portData.dataDstPortStart))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ " --ip-destination-port %s %s",
+
ENTRY_GET_NEG_SIGN(&rule->p.ipHdrFilter.portData.dataDstPortStart),
+ portstr);
+
+ if
(HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.portData.dataDstPortEnd)) {
+ if (printDataType(conn,
+ vars,
+ portstr, sizeof(portstr),
+
&rule->p.ipHdrFilter.portData.dataDstPortEnd))
+ goto err_exit;
+
+ virBufferVSprintf(&buf,
+ ":%s",
+ portstr);
+ }
+ }
+
+ if (HAS_ENTRY_ITEM(&rule->p.ipHdrFilter.dataDSCP)) {
+ virBufferVSprintf(&buf,
+ " --ip-tos %s %d",
+
ENTRY_GET_NEG_SIGN(&rule->p.ipHdrFilter.dataDSCP),
+ rule->p.ipHdrFilter.dataDSCP.u.u8);
+ }
+ 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
+ebiptablesApplyRules(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;
+
+ 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;
+
+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;
+}
+
+
+/**
+ * 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,
+ .applyRules = ebiptablesApplyRules,
+ .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,656 @@
+/*
+ * 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(a)us.ibm.com>
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "config.h"
+
+#include "memory.h"
+#include "logging.h"
+#include "datatypes.h"
+#include "domain_conf.h"
+#include "nwfilter_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_ROLLBACK_NEWFILTER:
+ if (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)
+{
+ 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_ROLLBACK_NEWFILTER:
+ 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->applyRules(conn, ifname, nptrs, ptrs);
+
+ 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,
+ 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_ROLLBACK_NEWFILTER:
+ if (obj->newDef)
+ foundNewFilter = 1;
+ break;
+
+ case INSTANTIATE_ALWAYS:
+ break;
+ }
+
+ rc = virNWFilterInstantiate(conn,
+ techdriver,
+ net->type,
+ filter,
+ net->ifname,
+ vars,
+ useNewFilter, &foundNewFilter);
+
+ 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,
+ INSTANTIATE_ALWAYS);
+}
+
+
+int
+virNWFilterUpdateInstantiateFilter(virConnectPtr conn,
+ const virDomainNetDefPtr net)
+{
+ return _virNWFilterInstantiateFilter(conn, net,
+ INSTANTIATE_FOLLOW_NEWFILTER);
+}
+
+int virNWFilterRollbackUpdateFilter(virConnectPtr conn,
+ const virDomainNetDefPtr net)
+{
+ return _virNWFilterInstantiateFilter(conn, net,
+
INSTANTIATE_ROLLBACK_NEWFILTER);
+}
+
+
+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,52 @@
+/*
+ * 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(a)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,
+ INSTANTIATE_ROLLBACK_NEWFILTER,
+};
+
+
+int virNWFilterInstantiateFilter(virConnectPtr conn,
+ const virDomainNetDefPtr net);
+int virNWFilterUpdateInstantiateFilter(virConnectPtr conn,
+ const virDomainNetDefPtr net);
+int virNWFilterRollbackUpdateFilter(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(a)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",
+ 'virNWFilterPoolPtr': "No function types in python",
}
#######################################################################
@@ -268,6 +269,7 @@ skip_impl = (
'virConnectListStorageVols',
'virConnectListDefinedStorageVols',
'virConnectListDefinedInterfaces',
+ 'virConnectListNWFilters',
'virConnGetLastError',
'virGetLastError',
'virDomainGetInfo',
Index: libvirt-acl/configure.ac
===================================================================
--- libvirt-acl.orig/configure.ac
+++ libvirt-acl/configure.ac
@@ -291,6 +291,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])