<li>IPV6_MASK: IPv6 mask in numbers format (FFFF:FFFF:FC00::) or CIDR mask (0-128)</li>
<li>STRING: A string</li>
<li>BOOLEAN: 'true', 'yes', '1' or 'false', 'no', '0'</li>
+ <li>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.</li>
</ul>
<p>
<br/><br/>
<td>STRING</td>
<td>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</td>
</tr>
+ <tr>
+ <td>ipset <span class="since">(Since 0.9.13)</span></td>
+ <td>STRING</td>
+ <td>The name of an IPSet managed outside of libvirt</td>
+ </tr>
+ <tr>
+ <td>ipsetflags <span class="since">(Since 0.9.13)</span></td>
+ <td>IPSETFLAGS</td>
+ <td>flags for the IPSet; requires ipset attribute</td>
+ </tr>
</table>
<p>
<br/><br/>
<td>STRING</td>
<td>comma separated list of NEW,ESTABLISHED,RELATED,INVALID or NONE</td>
</tr>
+ <tr>
+ <td>ipset <span class="since">(Since 0.9.13)</span></td>
+ <td>STRING</td>
+ <td>The name of an IPSet managed outside of libvirt</td>
+ </tr>
+ <tr>
+ <td>ipsetflags <span class="since">(Since 0.9.13)</span></td>
+ <td>IPSETFLAGS</td>
+ <td>flags for the IPSet; requires ipset attribute</td>
+ </tr>
</table>
<p>
<br/><br/>
<td>STRING</td>
<td>comma separated list of NEW,ESTABLISHED,RELATED,INVALID or NONE</td>
</tr>
+ <tr>
+ <td>ipset <span class="since">(Since 0.9.13)</span></td>
+ <td>STRING</td>
+ <td>The name of an IPSet managed outside of libvirt</td>
+ </tr>
+ <tr>
+ <td>ipsetflags <span class="since">(Since 0.9.13)</span></td>
+ <td>IPSETFLAGS</td>
+ <td>flags for the IPSet; requires ipset attribute</td>
+ </tr>
</table>
<p>
<br/><br/>
<td>STRING</td>
<td>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</td>
</tr>
+ <tr>
+ <td>ipset <span class="since">(Since 0.9.13)</span></td>
+ <td>STRING</td>
+ <td>The name of an IPSet managed outside of libvirt</td>
+ </tr>
+ <tr>
+ <td>ipsetflags <span class="since">(Since 0.9.13)</span></td>
+ <td>IPSETFLAGS</td>
+ <td>flags for the IPSet; requires ipset attribute</td>
+ </tr>
</table>
<p>
<br/><br/>
<td>STRING</td>
<td>comma separated list of NEW,ESTABLISHED,RELATED,INVALID or NONE</td>
</tr>
+ <tr>
+ <td>ipset <span class="since">(Since 0.9.13)</span></td>
+ <td>STRING</td>
+ <td>The name of an IPSet managed outside of libvirt</td>
+ </tr>
+ <tr>
+ <td>ipsetflags <span class="since">(Since 0.9.13)</span></td>
+ <td>IPSETFLAGS</td>
+ <td>flags for the IPSet; requires ipset attribute</td>
+ </tr>
</table>
<p>
<br/><br/>
<td>STRING</td>
<td>comma separated list of NEW,ESTABLISHED,RELATED,INVALID or NONE</td>
</tr>
+ <tr>
+ <td>ipset <span class="since">(Since 0.9.13)</span></td>
+ <td>STRING</td>
+ <td>The name of an IPSet managed outside of libvirt</td>
+ </tr>
+ <tr>
+ <td>ipsetflags <span class="since">(Since 0.9.13)</span></td>
+ <td>IPSETFLAGS</td>
+ <td>flags for the IPSet; requires ipset attribute</td>
+ </tr>
</table>
<p>
<br/><br/>
<ref name="stateflags-type"/>
</attribute>
</optional>
+ <optional>
+ <attribute name="ipset">
+ <ref name="ipset-name-type"/>
+ </attribute>
+ <attribute name="ipsetflags">
+ <ref name="ipset-flags-type"/>
+ </attribute>
+ </optional>
</interleave>
</define>
<param name="pattern">((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)</param>
</data>
</define>
+
+ <define name='ipset-name-type'>
+ <choice>
+ <ref name="variable-name-type"/>
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_\.:\-\+ ]{1,31}</param>
+ </data>
+ </choice>
+ </define>
+
+ <define name='ipset-flags-type'>
+ <data type="string">
+ <param name="pattern">(src|dst)(,(src|dst)){0,5}</param>
+ </data>
+ </define>
</grammar>
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
#define DSTPORTEND dstportend_str
#define DSCP dscp_str
#define STATE state_str
+#define IPSET ipset_str
+#define IPSETFLAGS ipsetflags_str
/**
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) \
{\
.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) \
break;
case DATATYPE_STRING:
+ case DATATYPE_IPSETFLAGS:
+ case DATATYPE_IPSETNAME:
if (!validator) {
/* not supported */
rc = -1;
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)
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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"),
# 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)
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"
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;
nwItemDesc dataState;
nwItemDesc dataConnlimitAbove;
nwItemDesc dataComment;
+ nwItemDesc dataIPSet;
+ nwItemDesc dataIPSetFlags;
};
_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;
}
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);
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);
}
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";
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,
--- /dev/null
+<filter name='testcase' chain='root'>
+ <uuid>5c6d49af-b071-6127-b4ec-6f8ed4b55335</uuid>
+ <rule action='accept' direction='out'>
+ <all ipset='test' ipsetflags='src,dst' />
+ </rule>
+ <rule action='accept' direction='in'>
+ <all state='NONE' ipset='test' ipsetflags='src,dst' comment='in+NONE'/>
+ </rule>
+ <rule action='accept' direction='out'>
+ <all state='NONE' ipset='test' ipsetflags='src,dst' comment='out+NONE'/>
+ </rule>
+ <rule action='accept' direction='in'>
+ <all ipset='test' ipsetflags='SRC,DST,SRC' />
+ </rule>
+ <rule action='accept' direction='in'>
+ <all ipset='test:_.-+' ipsetflags='SRC,dSt,SRC' />
+ </rule>
+ <rule action='accept' direction='in'>
+ <all ipset='$IPSETNAME' ipsetflags='src,dst' />
+ </rule>
+ <rule action='accept' direction='inout'>
+ <all ipset='$IPSETNAME' ipsetflags='src,dst' comment='inout'/>
+ </rule>
+</filter>
--- /dev/null
+<filter name='testcase' chain='root'>
+ <uuid>5c6d49af-b071-6127-b4ec-6f8ed4b55335</uuid>
+ <rule action='accept' direction='out' priority='500'>
+ <all ipset='test' ipsetflags='src,dst'/>
+ </rule>
+ <rule action='accept' direction='in' priority='500'>
+ <all state='NONE' ipset='test' ipsetflags='src,dst' comment='in+NONE'/>
+ </rule>
+ <rule action='accept' direction='out' priority='500'>
+ <all state='NONE' ipset='test' ipsetflags='src,dst' comment='out+NONE'/>
+ </rule>
+ <rule action='accept' direction='in' priority='500'>
+ <all ipset='test' ipsetflags='src,dst,src'/>
+ </rule>
+ <rule action='accept' direction='in' priority='500'>
+ <all ipset='test:_.-+' ipsetflags='src,dst,src'/>
+ </rule>
+ <rule action='accept' direction='in' priority='500'>
+ <all ipset='$IPSETNAME' ipsetflags='src,dst'/>
+ </rule>
+ <rule action='accept' direction='inout' priority='500'>
+ <all ipset='$IPSETNAME' ipsetflags='src,dst' comment='inout'/>
+ </rule>
+</filter>
DO_TEST("iter-test2", false);
DO_TEST("iter-test3", false);
+ DO_TEST("ipset-test", false);
+
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
}