Add IPv6 support for the ebtables layer

This patch adds IPv6 support for the ebtables layer. Since the parser
etc. are all parameterized, it was fairly easy to add this...

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
This commit is contained in:
Stefan Berger 2010-03-25 13:46:11 -04:00 committed by Daniel P. Berrange
parent d498175aad
commit f85208eec6
3 changed files with 399 additions and 4 deletions

View File

@ -73,7 +73,8 @@ VIR_ENUM_IMPL(virNWFilterEbtablesTable, VIR_NWFILTER_EBTABLES_TABLE_LAST,
VIR_ENUM_IMPL(virNWFilterChainSuffix, VIR_NWFILTER_CHAINSUFFIX_LAST,
"root",
"arp",
"ipv4");
"ipv4",
"ipv6");
/*
@ -365,6 +366,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 ATTRIBUTE_UNUSED, void *maskptr,
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 ipAttributes[] = {
};
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;
@ -786,6 +852,10 @@ static const virAttributes virAttr[] = {
.id = "ip",
.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(virConnectPtr conn ATTRIBUTE_UNUSED,
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(virNWFilterRuleDefPtr rule)
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, nwIPAddressPtr ipaddr)
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(virConnectPtr conn,
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(virConnectPtr conn,
break;
case DATATYPE_IPADDR:
case DATATYPE_IPV6ADDR:
storage_ptr = &item->u.ipaddr;
virNWIPAddressFormat(buf,
(nwIPAddressPtr)storage_ptr);

View File

@ -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,
};

View File

@ -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 conn,
{
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 conn,
}
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(virConnectPtr conn,
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 conn,
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);