VIR_ENUM_IMPL(virNWFilterChainSuffix, VIR_NWFILTER_CHAINSUFFIX_LAST,
"root",
"arp",
- "ipv4");
+ "ipv4",
+ "ipv6");
/*
}, {
.attr = ETHERTYPE_IP,
.val = "ipv4",
+ }, {
+ .attr = ETHERTYPE_IPV6,
+ .val = "ipv6",
}, {
.val = NULL,
}
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,
};
+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;
.id = "ip",
.att = ipAttributes,
.prtclType = VIR_NWFILTER_RULE_PROTOCOL_IP,
+ }, {
+ .id = "ipv6",
+ .att = ipv6Attributes,
+ .prtclType = VIR_NWFILTER_RULE_PROTOCOL_IPV6,
}, {
.id = NULL,
}
}
+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,
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
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;
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, ":");
}
}
switch (att[i].datatype) {
case DATATYPE_IPMASK:
+ case DATATYPE_IPV6MASK:
// display all masks in CIDR format
case DATATYPE_UINT8:
storage_ptr = &item->u.u8;
break;
case DATATYPE_IPADDR:
+ case DATATYPE_IPV6ADDR:
storage_ptr = &item->u.ipaddr;
virNWIPAddressFormat(buf,
(nwIPAddressPtr)storage_ptr);
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),
};
int isIPv6;
union {
unsigned char ipv4Addr[4];
- /* unsigned char ipv6Addr[16]; future :-) */
+ unsigned char ipv6Addr[16];
} addr;
};
};
+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,
VIR_NWFILTER_RULE_PROTOCOL_MAC,
VIR_NWFILTER_RULE_PROTOCOL_ARP,
VIR_NWFILTER_RULE_PROTOCOL_IP,
+ VIR_NWFILTER_RULE_PROTOCOL_IPV6,
};
enum virNWFilterEbtablesTableType {
ethHdrFilterDef ethHdrFilter;
arpHdrFilterDef arpHdrFilter;
ipHdrFilterDef ipHdrFilter;
+ ipv6HdrFilterDef ipv6HdrFilter;
} p;
int nvars;
VIR_NWFILTER_CHAINSUFFIX_ROOT = 0,
VIR_NWFILTER_CHAINSUFFIX_ARP,
VIR_NWFILTER_CHAINSUFFIX_IPv4,
+ VIR_NWFILTER_CHAINSUFFIX_IPv6,
VIR_NWFILTER_CHAINSUFFIX_LAST,
};
static const char *supported_protocols[] = {
"ipv4",
+ "ipv6",
"arp",
NULL,
};
nwItemDescPtr item)
{
int done;
+ int i, pos, s;
+
if (printVar(conn, vars, buf, bufsize, item, &done))
return 1;
}
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,
}
break;
+ case DATATYPE_IPV6MASK:
case DATATYPE_IPMASK:
case DATATYPE_UINT8:
if (snprintf(buf, bufsize, "%d",
{
char macaddr[VIR_MAC_STRING_BUFLEN],
ipaddr[INET_ADDRSTRLEN],
+ ipv6addr[INET6_ADDRSTRLEN],
number[20];
char chain[MAX_CHAINNAME_LENGTH];
virBuffer buf = VIR_BUFFER_INITIALIZER;
}
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",
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) {
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);