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