This patch adds support for filtering of VLAN (802.1Q) traffic to the parser and makes us of the ebtables support for VLAN filtering. This code now enables the filtering of traffic in chains with prefix 'vlan'. Signed-off-by: Stefan Berger --- docs/schemas/nwfilter.rng | 47 +++++++++++++ src/conf/nwfilter_conf.c | 102 ++++++++++++++++++++++++++++++ src/conf/nwfilter_conf.h | 17 +++++ src/nwfilter/nwfilter_ebiptables_driver.c | 35 ++++++++++ 4 files changed, 201 insertions(+) Index: libvirt-acl/src/conf/nwfilter_conf.c =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_conf.c +++ libvirt-acl/src/conf/nwfilter_conf.c @@ -82,6 +82,7 @@ VIR_ENUM_IMPL(virNWFilterEbtablesTable, VIR_ENUM_IMPL(virNWFilterChainSuffix, VIR_NWFILTER_CHAINSUFFIX_LAST, "root", + "vlan", "arp", "rarp", "ipv4", @@ -90,6 +91,7 @@ VIR_ENUM_IMPL(virNWFilterChainSuffix, VI VIR_ENUM_IMPL(virNWFilterRuleProtocol, VIR_NWFILTER_RULE_PROTOCOL_LAST, "none", "mac", + "vlan", "arp", "rarp", "ip", @@ -126,6 +128,7 @@ struct int_map { static const struct int_map chain_priorities[] = { INTMAP_ENTRY(NWFILTER_ROOT_FILTER_PRI, "root"), + INTMAP_ENTRY(NWFILTER_VLAN_FILTER_PRI, "vlan"), INTMAP_ENTRY(NWFILTER_IPV4_FILTER_PRI, "ipv4"), INTMAP_ENTRY(NWFILTER_IPV6_FILTER_PRI, "ipv6"), INTMAP_ENTRY(NWFILTER_ARP_FILTER_PRI , "arp" ), @@ -459,6 +462,7 @@ static const struct int_map macProtoMap[ INTMAP_ENTRY(ETHERTYPE_REVARP, "rarp"), INTMAP_ENTRY(ETHERTYPE_IP , "ipv4"), INTMAP_ENTRY(ETHERTYPE_IPV6 , "ipv6"), + INTMAP_ENTRY(ETHERTYPE_VLAN , "vlan"), INTMAP_ENTRY_LAST }; @@ -513,6 +517,75 @@ macProtocolIDFormatter(virBufferPtr buf, } +static bool +checkVlanVlanID(enum attrDatatype datatype, union data *value, + virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED, + nwItemDesc *item ATTRIBUTE_UNUSED) +{ + int32_t res; + + res = value->ui; + if (res < 0 || res > 4095) { + res = -1; + } + + if (res != -1) { + nwf->p.vlanHdrFilter.dataVlanID.u.u16 = res; + nwf->p.vlanHdrFilter.dataVlanID.datatype = datatype; + return true; + } + + return false; +} + +static bool +checkVlanProtocolID(enum attrDatatype datatype, union data *value, + virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED, + nwItemDesc *item ATTRIBUTE_UNUSED) +{ + int32_t res = -1; + + if (datatype == DATATYPE_STRING) { + if (intMapGetByString(macProtoMap, value->c, 1, &res) == 0) + res = -1; + datatype = DATATYPE_UINT16; + } else if (datatype == DATATYPE_UINT16 || + datatype == DATATYPE_UINT16_HEX) { + res = value->ui; + if (res < 0x3c) + res = -1; + } + + if (res != -1) { + nwf->p.vlanHdrFilter.dataVlanEncap.u.u16 = res; + nwf->p.vlanHdrFilter.dataVlanEncap.datatype = datatype; + return true; + } + + return false; +} + +static bool +vlanProtocolIDFormatter(virBufferPtr buf, + virNWFilterRuleDefPtr nwf, + nwItemDesc *item ATTRIBUTE_UNUSED) +{ + const char *str = NULL; + bool asHex = true; + + if (intMapGetByInt(macProtoMap, + nwf->p.vlanHdrFilter.dataVlanEncap.u.u16, + &str)) { + virBufferAdd(buf, str, -1); + } else { + if (nwf->p.vlanHdrFilter.dataVlanEncap.datatype == DATATYPE_UINT16) + asHex = false; + virBufferAsprintf(buf, asHex ? "0x%x" : "%d", + nwf->p.vlanHdrFilter.dataVlanEncap.u.u16); + } + return true; +} + /* 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 @@ -951,6 +1024,27 @@ static const virXMLAttr2Struct macAttrib } }; +static const virXMLAttr2Struct vlanAttributes[] = { + COMMON_MAC_PROPS(ethHdrFilter), + { + .name = "vlanid", + .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX, + .dataIdx = offsetof(virNWFilterRuleDef, p.vlanHdrFilter.dataVlanID), + .validator = checkVlanVlanID, + }, + { + .name = "encap-protocol", + .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX | DATATYPE_STRING, + .dataIdx = offsetof(virNWFilterRuleDef, p.vlanHdrFilter.dataVlanEncap), + .validator = checkVlanProtocolID, + .formatter = vlanProtocolIDFormatter, + }, + COMMENT_PROP(vlanHdrFilter), + { + .name = NULL, + } +}; + static const virXMLAttr2Struct arpAttributes[] = { COMMON_MAC_PROPS(arpHdrFilter), { @@ -1408,6 +1502,7 @@ static const virAttributes virAttr[] = { PROTOCOL_ENTRY("arp" , arpAttributes , VIR_NWFILTER_RULE_PROTOCOL_ARP), PROTOCOL_ENTRY("rarp" , arpAttributes , VIR_NWFILTER_RULE_PROTOCOL_RARP), PROTOCOL_ENTRY("mac" , macAttributes , VIR_NWFILTER_RULE_PROTOCOL_MAC), + PROTOCOL_ENTRY("vlan" , vlanAttributes , VIR_NWFILTER_RULE_PROTOCOL_VLAN), PROTOCOL_ENTRY("ip" , ipAttributes , VIR_NWFILTER_RULE_PROTOCOL_IP), PROTOCOL_ENTRY("ipv6" , ipv6Attributes , VIR_NWFILTER_RULE_PROTOCOL_IPV6), PROTOCOL_ENTRY("tcp" , tcpAttributes , VIR_NWFILTER_RULE_PROTOCOL_TCP), @@ -1737,6 +1832,13 @@ virNWFilterRuleDefFixup(virNWFilterRuleD rule->p.ethHdrFilter.ethHdr.dataDstMACAddr); break; + case VIR_NWFILTER_RULE_PROTOCOL_VLAN: + COPY_NEG_SIGN(rule->p.vlanHdrFilter.ethHdr.dataSrcMACMask, + rule->p.vlanHdrFilter.ethHdr.dataSrcMACAddr); + COPY_NEG_SIGN(rule->p.vlanHdrFilter.ethHdr.dataDstMACMask, + rule->p.vlanHdrFilter.ethHdr.dataDstMACAddr); + break; + case VIR_NWFILTER_RULE_PROTOCOL_IP: COPY_NEG_SIGN(rule->p.ipHdrFilter.ipHdr.dataSrcIPMask, rule->p.ipHdrFilter.ipHdr.dataSrcIPAddr); Index: libvirt-acl/src/conf/nwfilter_conf.h =================================================================== --- libvirt-acl.orig/src/conf/nwfilter_conf.h +++ libvirt-acl/src/conf/nwfilter_conf.h @@ -55,6 +55,9 @@ # ifndef ETHERTYPE_IPV6 # define ETHERTYPE_IPV6 0x86dd # endif +# ifndef ETHERTYPE_VLAN +# define ETHERTYPE_VLAN 0x8100 +# endif /** * Chain suffix size is: @@ -151,6 +154,16 @@ struct _ethHdrFilterDef { }; +typedef struct _vlanHdrFilterDef vlanHdrFilterDef; +typedef vlanHdrFilterDef *vlanHdrFilterDefPtr; +struct _vlanHdrFilterDef { + ethHdrDataDef ethHdr; + nwItemDesc dataVlanID; + nwItemDesc dataVlanEncap; + nwItemDesc dataComment; +}; + + typedef struct _arpHdrFilterDef arpHdrFilterDef; typedef arpHdrFilterDef *arpHdrFilterDefPtr; struct _arpHdrFilterDef { @@ -323,6 +336,7 @@ enum virNWFilterChainPolicyType { enum virNWFilterRuleProtocolType { VIR_NWFILTER_RULE_PROTOCOL_NONE = 0, VIR_NWFILTER_RULE_PROTOCOL_MAC, + VIR_NWFILTER_RULE_PROTOCOL_VLAN, VIR_NWFILTER_RULE_PROTOCOL_ARP, VIR_NWFILTER_RULE_PROTOCOL_RARP, VIR_NWFILTER_RULE_PROTOCOL_IP, @@ -364,6 +378,7 @@ enum virNWFilterEbtablesTableType { # define NWFILTER_MAX_FILTER_PRIORITY MAX_RULE_PRIORITY # define NWFILTER_ROOT_FILTER_PRI 0 +# define NWFILTER_VLAN_FILTER_PRI -750 # define NWFILTER_IPV4_FILTER_PRI -700 # define NWFILTER_IPV6_FILTER_PRI -600 # define NWFILTER_ARP_FILTER_PRI -500 @@ -401,6 +416,7 @@ struct _virNWFilterRuleDef { enum virNWFilterRuleProtocolType prtclType; union { ethHdrFilterDef ethHdrFilter; + vlanHdrFilterDef vlanHdrFilter; arpHdrFilterDef arpHdrFilter; /* also used for rarp */ ipHdrFilterDef ipHdrFilter; ipv6HdrFilterDef ipv6HdrFilter; @@ -440,6 +456,7 @@ struct _virNWFilterEntry { enum virNWFilterChainSuffixType { VIR_NWFILTER_CHAINSUFFIX_ROOT = 0, + VIR_NWFILTER_CHAINSUFFIX_VLAN, VIR_NWFILTER_CHAINSUFFIX_ARP, VIR_NWFILTER_CHAINSUFFIX_RARP, VIR_NWFILTER_CHAINSUFFIX_IPv4, 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 @@ -177,6 +177,7 @@ enum l3_proto_idx { L3_PROTO_IPV6_IDX, L3_PROTO_ARP_IDX, L3_PROTO_RARP_IDX, + L2_PROTO_VLAN_IDX, L3_PROTO_LAST_IDX }; @@ -187,6 +188,7 @@ static const struct ushort_map l3_protoc USHORTMAP_ENTRY_IDX(L3_PROTO_IPV6_IDX, ETHERTYPE_IPV6 , "ipv6"), USHORTMAP_ENTRY_IDX(L3_PROTO_ARP_IDX , ETHERTYPE_ARP , "arp"), USHORTMAP_ENTRY_IDX(L3_PROTO_RARP_IDX, ETHERTYPE_REVARP, "rarp"), + USHORTMAP_ENTRY_IDX(L2_PROTO_VLAN_IDX, ETHERTYPE_VLAN , "vlan"), USHORTMAP_ENTRY_IDX(L3_PROTO_LAST_IDX, 0 , NULL), }; @@ -1980,6 +1982,38 @@ ebtablesCreateRuleInstance(char chainPre } break; + case VIR_NWFILTER_RULE_PROTOCOL_VLAN: + + virBufferAsprintf(&buf, + CMD_DEF_PRE "%s -t %s -%%c %s %%s", + ebtables_cmd_path, EBTABLES_DEFAULT_TABLE, chain); + + + if (ebtablesHandleEthHdr(&buf, + vars, + &rule->p.vlanHdrFilter.ethHdr, + reverse)) + goto err_exit; + + virBufferAddLit(&buf, + " -p 0x8100"); + +#define INST_ITEM(STRUCT, ITEM, CLI) \ + if (HAS_ENTRY_ITEM(&rule->p.STRUCT.ITEM)) { \ + if (printDataType(vars, \ + number, sizeof(number), \ + &rule->p.STRUCT.ITEM)) \ + goto err_exit; \ + virBufferAsprintf(&buf, \ + " " CLI " %s %s", \ + ENTRY_GET_NEG_SIGN(&rule->p.STRUCT.ITEM), \ + number); \ + } + + INST_ITEM(vlanHdrFilter, dataVlanID, "--vlan-id") + INST_ITEM(vlanHdrFilter, dataVlanEncap, "--vlan-encap") + break; + case VIR_NWFILTER_RULE_PROTOCOL_ARP: case VIR_NWFILTER_RULE_PROTOCOL_RARP: @@ -2427,6 +2461,7 @@ ebiptablesCreateRuleInstance(virConnectP switch (rule->prtclType) { case VIR_NWFILTER_RULE_PROTOCOL_IP: case VIR_NWFILTER_RULE_PROTOCOL_MAC: + case VIR_NWFILTER_RULE_PROTOCOL_VLAN: case VIR_NWFILTER_RULE_PROTOCOL_ARP: case VIR_NWFILTER_RULE_PROTOCOL_RARP: case VIR_NWFILTER_RULE_PROTOCOL_NONE: Index: libvirt-acl/docs/schemas/nwfilter.rng =================================================================== --- libvirt-acl.orig/docs/schemas/nwfilter.rng +++ libvirt-acl/docs/schemas/nwfilter.rng @@ -30,6 +30,16 @@ + + + + + + + + + + @@ -287,6 +297,9 @@ root + vlan[a-zA-Z0-9_\.:-]* + + arp[a-zA-Z0-9_\.:-]* @@ -559,6 +572,21 @@ + + + + + + + + + + + + + + + @@ -764,10 +792,29 @@ rarp ipv4 ipv6 + vlan + + + + + $[a-zA-Z0-9_]+ + + + + 0x([0-9a-fA-F]{1,3}) + + + + 0 + 4095 + + + +