diff --git a/docs/formatnwfilter.html.in b/docs/formatnwfilter.html.in index 9cb7644925b2b4859e2d9a283eda8a1388007cc6..5eea5c6cae3cd5355ffc0a3e1d85e8f89c52e04c 100644 --- a/docs/formatnwfilter.html.in +++ b/docs/formatnwfilter.html.in @@ -528,6 +528,11 @@
  • IPV6_MASK: IPv6 mask in numbers format (FFFF:FFFF:FC00::) or CIDR mask (0-128)
  • STRING: A string
  • BOOLEAN: 'true', 'yes', '1' or 'false', 'no', '0'
  • +
  • IPSETFLAGS: The source and destination flags of the ipset described + by up to 6 'src' or 'dst' elements selecting features from either + the source or destination part of the packet header; example: + src,src,dst. The number of 'selectors' to provide here depends + on the type of ipset that is referenced.


  • @@ -1169,6 +1174,16 @@ STRING TCP-only: format of mask/flags with mask and flags each being a comma separated list of SYN,ACK,URG,PSH,FIN,RST or NONE or ALL + + ipset (Since 0.9.13) + STRING + The name of an IPSet managed outside of libvirt + + + ipsetflags (Since 0.9.13) + IPSETFLAGS + flags for the IPSet; requires ipset attribute +



    @@ -1269,6 +1284,16 @@ STRING comma separated list of NEW,ESTABLISHED,RELATED,INVALID or NONE + + ipset (Since 0.9.13) + STRING + The name of an IPSet managed outside of libvirt + + + ipsetflags (Since 0.9.13) + IPSETFLAGS + flags for the IPSet; requires ipset attribute +



    @@ -1358,6 +1383,16 @@ STRING comma separated list of NEW,ESTABLISHED,RELATED,INVALID or NONE + + ipset (Since 0.9.13) + STRING + The name of an IPSet managed outside of libvirt + + + ipsetflags (Since 0.9.13) + IPSETFLAGS + flags for the IPSet; requires ipset attribute +



    @@ -1459,6 +1494,16 @@ STRING TCP-only: format of mask/flags with mask and flags each being a comma separated list of SYN,ACK,URG,PSH,FIN,RST or NONE or ALL + + ipset (Since 0.9.13) + STRING + The name of an IPSet managed outside of libvirt + + + ipsetflags (Since 0.9.13) + IPSETFLAGS + flags for the IPSet; requires ipset attribute +



    @@ -1545,6 +1590,16 @@ STRING comma separated list of NEW,ESTABLISHED,RELATED,INVALID or NONE + + ipset (Since 0.9.13) + STRING + The name of an IPSet managed outside of libvirt + + + ipsetflags (Since 0.9.13) + IPSETFLAGS + flags for the IPSet; requires ipset attribute +



    @@ -1619,6 +1674,16 @@ STRING comma separated list of NEW,ESTABLISHED,RELATED,INVALID or NONE + + ipset (Since 0.9.13) + STRING + The name of an IPSet managed outside of libvirt + + + ipsetflags (Since 0.9.13) + IPSETFLAGS + flags for the IPSet; requires ipset attribute +



    diff --git a/docs/schemas/nwfilter.rng b/docs/schemas/nwfilter.rng index 13fd9b95f0e4c9afb3e303906a37d366d9f161c8..cfd9ba57c77cf10bd965f3266b9dc638f1d769c4 100644 --- a/docs/schemas/nwfilter.rng +++ b/docs/schemas/nwfilter.rng @@ -485,6 +485,14 @@ + + + + + + + + @@ -1060,4 +1068,19 @@ ((SYN|ACK|URG|PSH|FIN|RST)(,(SYN|ACK|URG|PSH|FIN|RST))*|ALL|NONE)/((SYN|ACK|URG|PSH|FIN|RST)(,(SYN|ACK|URG|PSH|FIN|RST))*|ALL|NONE) + + + + + + [a-zA-Z0-9_\.:\-\+ ]{1,31} + + + + + + + (src|dst)(,(src|dst)){0,5} + + diff --git a/src/conf/nwfilter_conf.c b/src/conf/nwfilter_conf.c index 58115926aaba44600968ec2aa50b9eda8f245ca2..0d6d0b697d072ec565fd89eb7c5a56900297baa7 100644 --- a/src/conf/nwfilter_conf.c +++ b/src/conf/nwfilter_conf.c @@ -183,6 +183,8 @@ static const char dstportstart_str[] = "dstportstart"; static const char dstportend_str[] = "dstportend"; static const char dscp_str[] = "dscp"; static const char state_str[] = "state"; +static const char ipset_str[] = "ipset"; +static const char ipsetflags_str[] = "ipsetflags"; #define SRCMACADDR srcmacaddr_str #define SRCMACMASK srcmacmask_str @@ -206,6 +208,8 @@ static const char state_str[] = "state"; #define DSTPORTEND dstportend_str #define DSCP dscp_str #define STATE state_str +#define IPSET ipset_str +#define IPSETFLAGS ipsetflags_str /** @@ -980,6 +984,97 @@ tcpFlagsFormatter(virBufferPtr buf, return true; } +static bool +ipsetValidator(enum attrDatatype datatype ATTRIBUTE_UNUSED, union data *val, + virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED, + nwItemDesc *item) +{ + const char *errmsg = NULL; + + if (virStrcpy(item->u.ipset.setname, val->c, + sizeof(item->u.ipset.setname)) == NULL) { + errmsg = _("ipset name is too long"); + goto arg_err_exit; + } + + if (item->u.ipset.setname[strspn(item->u.ipset.setname, + VALID_IPSETNAME)] != 0) { + errmsg = _("ipset name contains invalid characters"); + goto arg_err_exit; + } + + return true; + +arg_err_exit: + virNWFilterReportError(VIR_ERR_INVALID_ARG, + "%s", errmsg); + return false; +} + +static bool +ipsetFormatter(virBufferPtr buf, + virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED, + nwItemDesc *item) +{ + virBufferAdd(buf, item->u.ipset.setname, -1); + + return true; +} + +static bool +ipsetFlagsValidator(enum attrDatatype datatype ATTRIBUTE_UNUSED, union data *val, + virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED, nwItemDesc *item) +{ + const char *errmsg = NULL; + size_t idx = 0; + + item->u.ipset.numFlags = 0; + item->u.ipset.flags = 0; + + errmsg = _("malformed ipset flags"); + + while (item->u.ipset.numFlags < 6) { + if (STRCASEEQLEN(&val->c[idx], "src", 3)) { + item->u.ipset.flags |= (1 << item->u.ipset.numFlags); + } else if (!STRCASEEQLEN(&val->c[idx], "dst", 3)) { + goto arg_err_exit; + } + item->u.ipset.numFlags++; + idx += 3; + if (val->c[idx] != ',') + break; + idx++; + } + + if (val->c[idx] != '\0') + goto arg_err_exit; + + return true; + +arg_err_exit: + virNWFilterReportError(VIR_ERR_INVALID_ARG, + "%s", errmsg); + return false; +} + +static bool +ipsetFlagsFormatter(virBufferPtr buf, + virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED, + nwItemDesc *item) +{ + uint8_t ctr; + + for (ctr = 0; ctr < item->u.ipset.numFlags; ctr++) { + if (ctr != 0) + virBufferAddLit(buf, ","); + if ((item->u.ipset.flags & (1 << ctr))) + virBufferAddLit(buf, "src"); + else + virBufferAddLit(buf, "dst"); + } + + return true; +} #define COMMON_MAC_PROPS(STRUCT) \ {\ @@ -1411,6 +1506,20 @@ static const virXMLAttr2Struct ipv6Attributes[] = { .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataState),\ .validator = stateValidator,\ .formatter = stateFormatter,\ + },\ + {\ + .name = IPSET,\ + .datatype = DATATYPE_IPSETNAME,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataIPSet),\ + .validator = ipsetValidator,\ + .formatter = ipsetFormatter,\ + },\ + {\ + .name = IPSETFLAGS,\ + .datatype = DATATYPE_IPSETFLAGS,\ + .dataIdx = offsetof(virNWFilterRuleDef, p.STRUCT.ipHdr.dataIPSetFlags),\ + .validator = ipsetFlagsValidator,\ + .formatter = ipsetFlagsFormatter,\ } #define COMMON_PORT_PROPS(STRUCT) \ @@ -1853,6 +1962,8 @@ virNWFilterRuleDetailsParse(xmlNodePtr node, break; case DATATYPE_STRING: + case DATATYPE_IPSETFLAGS: + case DATATYPE_IPSETNAME: if (!validator) { /* not supported */ rc = -1; @@ -1964,6 +2075,19 @@ err_exit: goto cleanup; } +static void +virNWFilterRuleDefFixupIPSet(ipHdrDataDefPtr ipHdr) +{ + if (HAS_ENTRY_ITEM(&ipHdr->dataIPSet) && + !HAS_ENTRY_ITEM(&ipHdr->dataIPSetFlags)) { + ipHdr->dataIPSetFlags.flags = NWFILTER_ENTRY_ITEM_FLAG_EXISTS; + ipHdr->dataIPSetFlags.u.ipset.numFlags = 1; + ipHdr->dataIPSetFlags.u.ipset.flags = 1; + } else { + ipHdr->dataIPSet.flags = 0; + ipHdr->dataIPSetFlags.flags = 0; + } +} static void virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule) @@ -2017,6 +2141,7 @@ virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule) rule->p.ipHdrFilter.ipHdr.dataSrcIPAddr); COPY_NEG_SIGN(rule->p.ipHdrFilter.ipHdr.dataDstIPMask, rule->p.ipHdrFilter.ipHdr.dataDstIPAddr); + virNWFilterRuleDefFixupIPSet(&rule->p.ipHdrFilter.ipHdr); break; case VIR_NWFILTER_RULE_PROTOCOL_IPV6: @@ -2024,6 +2149,7 @@ virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule) rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr); COPY_NEG_SIGN(rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask, rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr); + virNWFilterRuleDefFixupIPSet(&rule->p.ipv6HdrFilter.ipHdr); break; case VIR_NWFILTER_RULE_PROTOCOL_ARP: @@ -2047,6 +2173,7 @@ virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule) rule->p.tcpHdrFilter.portData.dataSrcPortStart); COPY_NEG_SIGN(rule->p.tcpHdrFilter.portData.dataDstPortEnd, rule->p.tcpHdrFilter.portData.dataSrcPortStart); + virNWFilterRuleDefFixupIPSet(&rule->p.tcpHdrFilter.ipHdr); break; case VIR_NWFILTER_RULE_PROTOCOL_UDP: @@ -2065,6 +2192,7 @@ virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule) rule->p.udpHdrFilter.portData.dataSrcPortStart); COPY_NEG_SIGN(rule->p.udpHdrFilter.portData.dataDstPortEnd, rule->p.udpHdrFilter.portData.dataSrcPortStart); + virNWFilterRuleDefFixupIPSet(&rule->p.udpHdrFilter.ipHdr); break; case VIR_NWFILTER_RULE_PROTOCOL_UDPLITE: @@ -2077,6 +2205,7 @@ virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule) rule->p.udpliteHdrFilter.ipHdr.dataSrcIPFrom); COPY_NEG_SIGN(rule->p.udpliteHdrFilter.ipHdr.dataDstIPTo, rule->p.udpliteHdrFilter.ipHdr.dataDstIPFrom); + virNWFilterRuleDefFixupIPSet(&rule->p.udpliteHdrFilter.ipHdr); break; case VIR_NWFILTER_RULE_PROTOCOL_ESP: @@ -2089,6 +2218,7 @@ virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule) rule->p.espHdrFilter.ipHdr.dataSrcIPFrom); COPY_NEG_SIGN(rule->p.espHdrFilter.ipHdr.dataDstIPTo, rule->p.espHdrFilter.ipHdr.dataDstIPFrom); + virNWFilterRuleDefFixupIPSet(&rule->p.espHdrFilter.ipHdr); break; case VIR_NWFILTER_RULE_PROTOCOL_AH: @@ -2101,6 +2231,7 @@ virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule) rule->p.ahHdrFilter.ipHdr.dataSrcIPFrom); COPY_NEG_SIGN(rule->p.ahHdrFilter.ipHdr.dataDstIPTo, rule->p.ahHdrFilter.ipHdr.dataDstIPFrom); + virNWFilterRuleDefFixupIPSet(&rule->p.ahHdrFilter.ipHdr); break; case VIR_NWFILTER_RULE_PROTOCOL_SCTP: @@ -2119,6 +2250,7 @@ virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule) rule->p.sctpHdrFilter.portData.dataSrcPortStart); COPY_NEG_SIGN(rule->p.sctpHdrFilter.portData.dataDstPortEnd, rule->p.sctpHdrFilter.portData.dataSrcPortStart); + virNWFilterRuleDefFixupIPSet(&rule->p.sctpHdrFilter.ipHdr); break; case VIR_NWFILTER_RULE_PROTOCOL_ICMP: @@ -2133,6 +2265,7 @@ virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule) rule->p.icmpHdrFilter.ipHdr.dataDstIPFrom); COPY_NEG_SIGN(rule->p.icmpHdrFilter.dataICMPCode, rule->p.icmpHdrFilter.dataICMPType); + virNWFilterRuleDefFixupIPSet(&rule->p.icmpHdrFilter.ipHdr); break; case VIR_NWFILTER_RULE_PROTOCOL_ALL: @@ -2156,6 +2289,7 @@ virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule) rule->p.igmpHdrFilter.ipHdr.dataSrcIPFrom); COPY_NEG_SIGN(rule->p.igmpHdrFilter.ipHdr.dataDstIPTo, rule->p.igmpHdrFilter.ipHdr.dataDstIPFrom); + virNWFilterRuleDefFixupIPSet(&rule->p.igmpHdrFilter.ipHdr); break; case VIR_NWFILTER_RULE_PROTOCOL_LAST: @@ -3120,7 +3254,7 @@ virNWFilterRuleDefDetailsFormat(virBufferPtr buf, virBufferAsprintf(buf, " %s='", att[i].name); - if (att[i].formatter) { + if (att[i].formatter && !(flags & NWFILTER_ENTRY_ITEM_FLAG_HAS_VAR)) { if (!att[i].formatter(buf, def, item)) { virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, _("formatter for %s %s reported error"), diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h index a9a55c7e68ec19ef33b4283c96a57b6a249da1c0..a52826d2c3897038ba21b8cd02f14d372f515467 100644 --- a/src/conf/nwfilter_conf.h +++ b/src/conf/nwfilter_conf.h @@ -79,6 +79,7 @@ enum virNWFilterEntryItemFlags { # define MAX_COMMENT_LENGTH 256 +# define MAX_IPSET_NAME_LENGTH 32 /* incl. terminating '\0' */ # define HAS_ENTRY_ITEM(data) \ (((data)->flags) & NWFILTER_ENTRY_ITEM_FLAG_EXISTS) @@ -103,8 +104,10 @@ enum attrDatatype { DATATYPE_BOOLEAN = (1 << 12), DATATYPE_UINT32 = (1 << 13), DATATYPE_UINT32_HEX = (1 << 14), + DATATYPE_IPSETNAME = (1 << 15), + DATATYPE_IPSETFLAGS = (1 << 16), - DATATYPE_LAST = (1 << 15), + DATATYPE_LAST = (1 << 17), }; # define NWFILTER_MAC_BGA "01:80:c2:00:00:00" @@ -136,9 +139,16 @@ struct _nwItemDesc { uint8_t mask; uint8_t flags; } tcpFlags; + struct { + char setname[MAX_IPSET_NAME_LENGTH]; + uint8_t numFlags; + uint8_t flags; + } ipset; } u; }; +# define VALID_IPSETNAME \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.:-+ " typedef struct _ethHdrDataDef ethHdrDataDef; typedef ethHdrDataDef *ethHdrDataDefPtr; @@ -232,6 +242,8 @@ struct _ipHdrDataDef { nwItemDesc dataState; nwItemDesc dataConnlimitAbove; nwItemDesc dataComment; + nwItemDesc dataIPSet; + nwItemDesc dataIPSetFlags; }; diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfilter_ebiptables_driver.c index 28f48bd54c6e2426fcfaa24d2577d09b7dee8bd9..1b0bbf42da6c5dce272838fb712dfb80206c2618 100644 --- a/src/nwfilter/nwfilter_ebiptables_driver.c +++ b/src/nwfilter/nwfilter_ebiptables_driver.c @@ -256,10 +256,13 @@ static int _printDataType(virNWFilterVarCombIterPtr vars, char *buf, int bufsize, nwItemDescPtr item, - bool asHex) + bool asHex, bool directionIn) { int done; char *data; + uint8_t ctr; + virBuffer vb = VIR_BUFFER_INITIALIZER; + char *flags; if (printVar(vars, buf, bufsize, item, &done) < 0) return -1; @@ -346,6 +349,48 @@ _printDataType(virNWFilterVarCombIterPtr vars, } break; + case DATATYPE_IPSETNAME: + if (virStrcpy(buf, item->u.ipset.setname, bufsize) == NULL) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Buffer to small for ipset name")); + return -1; + } + break; + + case DATATYPE_IPSETFLAGS: + for (ctr = 0; ctr < item->u.ipset.numFlags; ctr++) { + if (ctr != 0) + virBufferAddLit(&vb, ","); + if ((item->u.ipset.flags & (1 << ctr))) { + if (directionIn) + virBufferAddLit(&vb, "dst"); + else + virBufferAddLit(&vb, "src"); + } else { + if (directionIn) + virBufferAddLit(&vb, "src"); + else + virBufferAddLit(&vb, "dst"); + } + } + + if (virBufferError(&vb)) { + virReportOOMError(); + virBufferFreeAndReset(&vb); + return -1; + } + + flags = virBufferContentAndReset(&vb); + + if (virStrcpy(buf, flags, bufsize) == NULL) { + virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Buffer too small for IPSETFLAGS type")); + VIR_FREE(flags); + return -1; + } + VIR_FREE(flags); + break; + default: virNWFilterReportError(VIR_ERR_INTERNAL_ERROR, _("Unhandled datatype %x"), item->datatype); @@ -362,16 +407,23 @@ printDataType(virNWFilterVarCombIterPtr vars, char *buf, int bufsize, nwItemDescPtr item) { - return _printDataType(vars, buf, bufsize, item, 0); + return _printDataType(vars, buf, bufsize, item, 0, 0); } +static int +printDataTypeDirection(virNWFilterVarCombIterPtr vars, + char *buf, int bufsize, + nwItemDescPtr item, bool directionIn) +{ + return _printDataType(vars, buf, bufsize, item, 0, directionIn); +} static int printDataTypeAsHex(virNWFilterVarCombIterPtr vars, char *buf, int bufsize, nwItemDescPtr item) { - return _printDataType(vars, buf, bufsize, item, 1); + return _printDataType(vars, buf, bufsize, item, 1, 0); } @@ -927,6 +979,7 @@ iptablesHandleIpHdr(virBufferPtr buf, char ipaddr[INET6_ADDRSTRLEN], number[MAX(INT_BUFSIZE_BOUND(uint32_t), INT_BUFSIZE_BOUND(int))]; + char str[MAX_IPSET_NAME_LENGTH]; const char *src = "--source"; const char *dst = "--destination"; const char *srcrange = "--src-range"; @@ -938,6 +991,26 @@ iptablesHandleIpHdr(virBufferPtr buf, dstrange = "--src-range"; } + if (HAS_ENTRY_ITEM(&ipHdr->dataIPSet) && + HAS_ENTRY_ITEM(&ipHdr->dataIPSetFlags)) { + + if (printDataType(vars, + str, sizeof(str), + &ipHdr->dataIPSet) < 0) + goto err_exit; + + virBufferAsprintf(afterStateMatch, + " -m set --match-set \"%s\" ", + str); + + if (printDataTypeDirection(vars, + str, sizeof(str), + &ipHdr->dataIPSetFlags, directionIn) < 0) + goto err_exit; + + virBufferAdd(afterStateMatch, str, -1); + } + if (HAS_ENTRY_ITEM(&ipHdr->dataSrcIPAddr)) { if (printDataType(vars, diff --git a/tests/nwfilterxml2xmlin/ipset-test.xml b/tests/nwfilterxml2xmlin/ipset-test.xml new file mode 100644 index 0000000000000000000000000000000000000000..2a46508f9ee4e54148a9cf8ca900d69aea35c078 --- /dev/null +++ b/tests/nwfilterxml2xmlin/ipset-test.xml @@ -0,0 +1,24 @@ + + 5c6d49af-b071-6127-b4ec-6f8ed4b55335 + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/nwfilterxml2xmlout/ipset-test.xml b/tests/nwfilterxml2xmlout/ipset-test.xml new file mode 100644 index 0000000000000000000000000000000000000000..03d9a0e68daf49dd5b02ad46d90caeb91bafa413 --- /dev/null +++ b/tests/nwfilterxml2xmlout/ipset-test.xml @@ -0,0 +1,24 @@ + + 5c6d49af-b071-6127-b4ec-6f8ed4b55335 + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/nwfilterxml2xmltest.c b/tests/nwfilterxml2xmltest.c index 5fd8df823304583bd72fedc84553eedec1e1f48e..224ca93b856c55589c1e12d1897b58b15ed134e4 100644 --- a/tests/nwfilterxml2xmltest.c +++ b/tests/nwfilterxml2xmltest.c @@ -157,6 +157,8 @@ mymain(void) DO_TEST("iter-test2", false); DO_TEST("iter-test3", false); + DO_TEST("ipset-test", false); + return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; }