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@xxxxxxxxxx> --- 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@xxxxxxxxxx> + */ +#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@xxxxxxxxxx> + */ +#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@xxxxxxxxxx> + * Stefan Berger <stefanb@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx> + * Stefan Berger <stefanb@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx> + */ +#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@xxxxxxxxxx> + */ +#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]) -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list