[libvirt] [PATCH 0/6] handle NIC_RX_FILTER_CHANGED events from qemu

These patches set up an event handler for qemu's NIC_RX_FILTER_CHANGED event, which is sent whenever a guest makes a change to a network device's unicast/multicast filter, vlan table, or MAC address. The handler checks if it is appropriate to respond to the NIC_RX_FILTER_CHANGED event (based on device type and configuration) and takes appropriate action. Currently it checks if the guest interface has been configured with trustGuestRxFilters='yes' (defaults to 'no' for security reasons), and if the host side device is macvtap. If so, and the MAC address on the guest has changed, the MAC address of the macvtap device is changed to match. The result of this is that networking from the guest will continue to work if the mac address of a macvtap-connected network device is changed from within the guest, as long as trustGuestRxFilters='yes' (previously changing the MAC address in the guest would break networking). I still need to add code to compare the old and new unicast and multicast lists and program the filters in the macvtap to match the guest, and to check for a non-empty vlan table and handle that (currently that means just setting promiscuous mode on the macvtap), but that can come in a followup series. Laine Stump (6): conf: add trustGuestRxFilters attribute to network and domain interface network: set interface actual trustGuestRxFilters from network/portgroup util: define virNetDevRxFilter and basic utility functions qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter qemu: setup infrastructure to handle NIC_RX_FILTER_CHANGED event qemu: change macvtap device MAC address in response to NIC_RX_FILTER_CHANGED docs/formatdomain.html.in | 38 +++- docs/formatnetwork.html.in | 28 ++- docs/schemas/domaincommon.rng | 5 + docs/schemas/network.rng | 10 + src/conf/domain_conf.c | 42 ++++ src/conf/domain_conf.h | 3 + src/conf/network_conf.c | 35 ++++ src/conf/network_conf.h | 2 + src/libvirt_private.syms | 9 + src/network/bridge_driver.c | 11 + src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 105 ++++++++++ src/qemu/qemu_monitor.c | 39 ++++ src/qemu/qemu_monitor.h | 11 + src/qemu/qemu_monitor_json.c | 232 +++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + src/qemu/qemu_process.c | 42 ++++ src/util/virnetdev.c | 40 ++++ src/util/virnetdev.h | 57 ++++- tests/Makefile.am | 3 + tests/networkxml2xmlin/vepa-net.xml | 4 +- tests/networkxml2xmlout/vepa-net.xml | 4 +- .../qemuxml2argv-net-virtio-network-portgroup.xml | 4 +- 23 files changed, 711 insertions(+), 17 deletions(-) -- 1.9.3

This new attribute will control whether or not libvirt will pay attention to guest notifications about changes to network device mac addresses and receive filters. The default for this is 'no' (for security reasons). If it is set to 'yes' *and* the specified device model and connection support it (currently only macvtap+virtio) then libvirt will watch for NIC_RX_FILTER_CHANGED events, and when it receives one, it will issue a query-rx-filter command, retrieve the result, and modify the host-side macvtap interface's mac address and unicast/multicast filters accordingly. The functionality behind this attribute will be in a later patch. This patch merely adds the attribute to the top-level of a domain's <interface> as well as to <network> and <portgroup>, and adds documentation and schema/xml2xml tests. Rather than adding even more test files, I've just added the net attribute in various applicable places of existing test files. --- docs/formatdomain.html.in | 38 ++++++++++++++++---- docs/formatnetwork.html.in | 28 +++++++++++++-- docs/schemas/domaincommon.rng | 5 +++ docs/schemas/network.rng | 10 ++++++ src/conf/domain_conf.c | 42 ++++++++++++++++++++++ src/conf/domain_conf.h | 3 ++ src/conf/network_conf.c | 35 ++++++++++++++++++ src/conf/network_conf.h | 2 ++ src/libvirt_private.syms | 1 + tests/networkxml2xmlin/vepa-net.xml | 4 +-- tests/networkxml2xmlout/vepa-net.xml | 4 +-- .../qemuxml2argv-net-virtio-network-portgroup.xml | 4 +-- 12 files changed, 160 insertions(+), 16 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index eefdd5e..17e180d 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -3343,10 +3343,9 @@ <pre> ... <devices> - <interface type='bridge'> - <source bridge='xenbr0'/> - <mac address='00:16:3e:5d:c7:9e'/> - <script path='vif-bridge'/> + <interface type='direct' trustGuestRxFilters='yes'> + <source dev='eth0'/> + <mac address='52:54:00:5d:c7:9e'/> <boot order='1'/> <rom bar='off'/> </interface> @@ -3356,8 +3355,21 @@ <p> There are several possibilities for specifying a network interface visible to the guest. Each subsection below provides - more details about common setup options. Additionally, - each <code><interface></code> element has an + more details about common setup options. + </p> + <p> + libvirt allows specifying when the host should trust reports + from the guest of changes to the interface mac address and + receive filters by setting the <code>trustGuestRxFilters</code> + attribute to <code>yes</code><span class="since">Since + 1.2.9</span>. <code>trustGuestRxFilters</code> defaults + to <code>no</code> for security reasons, and support depends on + the guest network device driver as well as the type of + connection on the host - currently it is only supported for the + virtio driver, and for macvtap connections on the host. + </p> + <p> + Each <code><interface></code> element has an optional <code><address></code> sub-element that can tie the interface to a particular pci slot, with attribute <code>type='pci'</code> @@ -3589,6 +3601,18 @@ being the default mode. The individual modes cause the delivery of packets to behave as follows: </p> + <p> + If the model type is set to <code>virtio</code> and + interface's <code>trustGuestRxFilters</code> attribute is set + to <code>yes</code>, changes made to the interface mac address, + unicast/multicast receive filters, and vlan settings in the + guest will be monitored and propogated to the associated macvtap + device on the host. <span class="since">Since + 1.2.9</span>. If <code>trustGuestRxFilters</code> is not set, or + is not supported for the device model in use, an attempted + change to the mac address originating from the guest side will + result in a non-working network connection. + </p> <dl> <dt><code>vepa</code></dt> @@ -3621,7 +3645,7 @@ ... <devices> ... - <interface type='direct'> + <interface type='direct' trustGuestRxFilters='no'> <source dev='eth0' mode='vepa'/> </interface> </devices> diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 1a8ad8e..ff73952 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -35,7 +35,7 @@ </p> <pre> - <network ipv6='yes'> + <network ipv6='yes' trustGuestRxFilters='no'> <name>default</name> <uuid>3e3fce45-4f53-4fa7-bb32-11f34168b82b</uuid> ...</pre> @@ -60,6 +60,16 @@ to have guest-to-guest communications. For further information, see the example below for the example with no gateway addresses. <span class="since">Since 1.0.1</span></dd> + <dt><code>trustGuestRxFilters='yes'</code></dt> + <dd>The optional parameter <code>trustGuestRxFilters</code> can + be used to set that attribute of the same name for each domain + interface connected to this network <span class="since">Since + 1.2.9</span> See + the <a href="formatdomain.html#elementSNICS">Network + interfaces</a> section of the domain XML documentation for + more details. Note that an explicit setting of this attribute + in a portgroup or the individual domain interface will + override the setting in the network.</dd> </dl> <h3><a name="elementsConnect">Connectivity</a></h3> @@ -606,7 +616,7 @@ <outbound average='1000' peak='5000' burst='5120'/> </bandwidth> </portgroup></b> - <b><portgroup name='sales'> + <b><portgroup name='sales' trustGuestRxFilters='no'> <virtualport type='802.1Qbh'> <parameters profileid='salestest'/> </virtualport> @@ -626,7 +636,7 @@ network can have multiple portgroup elements (and one of those can optionally be designated as the 'default' portgroup for the network), and each portgroup has a name, as well as various - subelements associated with it. The currently supported + attributes and subelements associated with it. The currently supported subelements are <code><bandwidth></code> (described <a href="formatnetwork.html#elementQoS">here</a>) and <code><virtualport></code> @@ -650,6 +660,18 @@ considered an error, and will prevent the interface from starting. </p> + <p> + portgroups also support the optional + parameter <code>trustGuestRxFilters</code> which can be used to + set that attribute of the same name for each domain interface + using this portgroup <span class="since">Since 1.2.9</span> See + the <a href="formatdomain.html#elementSNICS">Network + interfaces</a> section of the domain XML documentation for more + details. Note that an explicit setting of this attribute in the + portgroup overrides the network-wide setting, and an explicit + setting in the individual domain interface will override the + setting in the portgroup. + </p> <h5><a name="elementsStaticroute">Static Routes</a></h5> <p> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index b6b309d..74a6d8c 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2007,6 +2007,11 @@ --> <define name="interface"> <element name="interface"> + <optional> + <attribute name="trustGuestRxFilters"> + <ref name="virYesNo"/> + </attribute> + </optional> <choice> <group> <attribute name="type"> diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index dc732b4..4546f80 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -24,6 +24,11 @@ <ref name="virYesNo"/> </attribute> </optional> + <optional> + <attribute name="trustGuestRxFilters"> + <ref name="virYesNo"/> + </attribute> + </optional> <interleave> <!-- The name of the network, used to refer to it through the API @@ -197,6 +202,11 @@ <ref name="virYesNo"/> </attribute> </optional> + <optional> + <attribute name="trustGuestRxFilters"> + <ref name="virYesNo"/> + </attribute> + </optional> <interleave> <optional> <ref name="virtualPortProfile"/> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 9cc118c..7e0d147 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -6756,6 +6756,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, char *type = NULL; char *mode = NULL; char *addrtype = NULL; + char *trustGuestRxFilters = NULL; if (VIR_ALLOC(actual) < 0) return -1; @@ -6783,6 +6784,16 @@ virDomainActualNetDefParseXML(xmlNodePtr node, goto error; } + trustGuestRxFilters = virXMLPropString(node, "trustGuestRxFilters"); + if (trustGuestRxFilters && + ((int)(actual->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown trustGuestRxFilters value '%s'"), + trustGuestRxFilters); + goto error; + } + virtPortNode = virXPathNode("./virtualport", ctxt); if (virtPortNode) { if (actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE || @@ -6869,6 +6880,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, VIR_FREE(type); VIR_FREE(mode); VIR_FREE(addrtype); + VIR_FREE(trustGuestRxFilters); virDomainActualNetDefFree(actual); ctxt->node = save_ctxt; @@ -6919,6 +6931,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, char *vhostuser_mode = NULL; char *vhostuser_path = NULL; char *vhostuser_type = NULL; + char *trustGuestRxFilters = NULL; virNWFilterHashTablePtr filterparams = NULL; virDomainActualNetDefPtr actual = NULL; xmlNodePtr oldnode = ctxt->node; @@ -6940,6 +6953,16 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, def->type = VIR_DOMAIN_NET_TYPE_USER; } + trustGuestRxFilters = virXMLPropString(node, "trustGuestRxFilters"); + if (trustGuestRxFilters && + ((int)(def->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown trustGuestRxFilters value '%s'"), + trustGuestRxFilters); + goto error; + } + cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { @@ -7469,6 +7492,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_FREE(mode); VIR_FREE(linkstate); VIR_FREE(addrtype); + VIR_FREE(trustGuestRxFilters); virNWFilterHashTableFree(filterparams); return def; @@ -16491,6 +16515,9 @@ virDomainActualNetDefFormat(virBufferPtr buf, if (hostdef && hostdef->managed) virBufferAddLit(buf, " managed='yes'"); } + if (def->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " trustGuestRxFilters='%s'", + virTristateBoolTypeToString(def->trustGuestRxFilters)); virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); @@ -16575,6 +16602,9 @@ virDomainNetDefFormat(virBufferPtr buf, virBufferAsprintf(buf, "<interface type='%s'", typeStr); if (hostdef && hostdef->managed) virBufferAddLit(buf, " managed='yes'"); + if (def->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " trustGuestRxFilters='%s'", + virTristateBoolTypeToString(def->trustGuestRxFilters)); virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); @@ -19976,6 +20006,18 @@ virDomainNetGetActualVlan(virDomainNetDefPtr iface) return NULL; } + +bool +virDomainNetGetActualTrustGuestRxFilters(virDomainNetDefPtr iface) +{ + if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK && + iface->data.network.actual) + return (iface->data.network.actual->trustGuestRxFilters + == VIR_TRISTATE_BOOL_YES); + return iface->trustGuestRxFilters == VIR_TRISTATE_BOOL_YES; +} + + /* Return listens[i] from the appropriate union for the graphics * type, or NULL if this is an unsuitable type, or the index is out of * bounds. If force0 is TRUE, i == 0, and there is no listen array, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index d97b1f8..2d0cb06 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -880,6 +880,7 @@ struct _virDomainActualNetDef { virNetDevVPortProfilePtr virtPortProfile; virNetDevBandwidthPtr bandwidth; virNetDevVlan vlan; + int trustGuestRxFilters; /* enum virTristateBool */ unsigned int class_id; /* class ID for bandwidth 'floor' */ }; @@ -954,6 +955,7 @@ struct _virDomainNetDef { virNWFilterHashTablePtr filterparams; virNetDevBandwidthPtr bandwidth; virNetDevVlan vlan; + int trustGuestRxFilters; /* enum virTristateBool */ int linkstate; }; @@ -2471,6 +2473,7 @@ virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface); virNetDevBandwidthPtr virDomainNetGetActualBandwidth(virDomainNetDefPtr iface); virNetDevVlanPtr virDomainNetGetActualVlan(virDomainNetDefPtr iface); +bool virDomainNetGetActualTrustGuestRxFilters(virDomainNetDefPtr iface); int virDomainControllerInsert(virDomainDefPtr def, virDomainControllerDefPtr controller) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 892bd8a..63b5917 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -1615,6 +1615,7 @@ virNetworkPortGroupParseXML(virPortGroupDefPtr def, xmlNodePtr vlanNode; xmlNodePtr bandwidth_node; char *isDefault = NULL; + char *trustGuestRxFilters = NULL; int result = -1; @@ -1632,6 +1633,18 @@ virNetworkPortGroupParseXML(virPortGroupDefPtr def, isDefault = virXPathString("string(./@default)", ctxt); def->isDefault = isDefault && STRCASEEQ(isDefault, "yes"); + trustGuestRxFilters + = virXPathString("string(./@trustGuestRxFilters)", ctxt); + if (trustGuestRxFilters) { + if ((def->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid trustGuestRxFilters setting '%s' " + "in portgroup"), trustGuestRxFilters); + goto cleanup; + } + } + virtPortNode = virXPathNode("./virtualport", ctxt); if (virtPortNode && (!(def->virtPortProfile = virNetDevVPortProfileParse(virtPortNode, 0)))) { @@ -1654,6 +1667,7 @@ virNetworkPortGroupParseXML(virPortGroupDefPtr def, virPortGroupDefClear(def); } VIR_FREE(isDefault); + VIR_FREE(trustGuestRxFilters); ctxt->node = save; return result; @@ -2013,6 +2027,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) xmlNodePtr virtPortNode = NULL; xmlNodePtr forwardNode = NULL; char *ipv6nogwStr = NULL; + char *trustGuestRxFilters = NULL; xmlNodePtr save = ctxt->node; xmlNodePtr bandwidthNode = NULL; xmlNodePtr vlanNode; @@ -2061,6 +2076,20 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) VIR_FREE(ipv6nogwStr); } + trustGuestRxFilters + = virXPathString("string(./@trustGuestRxFilters)", ctxt); + if (trustGuestRxFilters) { + if ((def->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid trustGuestRxFilters setting '%s' " + "in network '%s'"), + trustGuestRxFilters, def->name); + goto error; + } + VIR_FREE(trustGuestRxFilters); + } + /* Parse network domain information */ def->domain = virXPathString("string(./domain[1]/@name)", ctxt); @@ -2597,6 +2626,9 @@ virPortGroupDefFormat(virBufferPtr buf, if (def->isDefault) { virBufferAddLit(buf, " default='yes'"); } + if (def->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " trustGuestRxFilters='%s'", + virTristateBoolTypeToString(def->trustGuestRxFilters)); virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); if (virNetDevVlanFormat(&def->vlan, buf) < 0) @@ -2675,6 +2707,9 @@ virNetworkDefFormatBuf(virBufferPtr buf, } if (def->ipv6nogw) virBufferAddLit(buf, " ipv6='yes'"); + if (def->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " trustGuestRxFilters='%s'", + virTristateBoolTypeToString(def->trustGuestRxFilters)); virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); virBufferEscapeString(buf, "<name>%s</name>\n", def->name); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 7ed58cd..660cd2d 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -219,6 +219,7 @@ struct _virPortGroupDef { virNetDevVPortProfilePtr virtPortProfile; virNetDevBandwidthPtr bandwidth; virNetDevVlan vlan; + int trustGuestRxFilters; /* enum virTristateBool */ }; typedef struct _virNetworkDef virNetworkDef; @@ -256,6 +257,7 @@ struct _virNetworkDef { virPortGroupDefPtr portGroups; virNetDevBandwidthPtr bandwidth; virNetDevVlan vlan; + int trustGuestRxFilters; /* enum virTristateBool */ }; typedef struct _virNetworkObj virNetworkObj; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a339ced..bb2b9a3 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -331,6 +331,7 @@ virDomainNetGetActualBridgeName; virDomainNetGetActualDirectDev; virDomainNetGetActualDirectMode; virDomainNetGetActualHostdev; +virDomainNetGetActualTrustGuestRxFilters; virDomainNetGetActualType; virDomainNetGetActualVirtPortProfile; virDomainNetGetActualVlan; diff --git a/tests/networkxml2xmlin/vepa-net.xml b/tests/networkxml2xmlin/vepa-net.xml index 030c1d1..07c59c5 100644 --- a/tests/networkxml2xmlin/vepa-net.xml +++ b/tests/networkxml2xmlin/vepa-net.xml @@ -1,4 +1,4 @@ -<network> +<network trustGuestRxFilters="no"> <name>vepa-net</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8b</uuid> <forward mode="vepa"> @@ -14,7 +14,7 @@ <parameters typeid="2193047" typeidversion="3"/> </virtualport> </portgroup> - <portgroup name="alice"> + <portgroup name="alice" trustGuestRxFilters="yes"> <virtualport type="802.1Qbg"> <parameters managerid="13"/> </virtualport> diff --git a/tests/networkxml2xmlout/vepa-net.xml b/tests/networkxml2xmlout/vepa-net.xml index 4d35a8a..b266620 100644 --- a/tests/networkxml2xmlout/vepa-net.xml +++ b/tests/networkxml2xmlout/vepa-net.xml @@ -1,4 +1,4 @@ -<network> +<network trustGuestRxFilters='no'> <name>vepa-net</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8b</uuid> <forward dev='eth1' mode='vepa'> @@ -14,7 +14,7 @@ <parameters typeid='2193047' typeidversion='3'/> </virtualport> </portgroup> - <portgroup name='alice'> + <portgroup name='alice' trustGuestRxFilters='yes'> <virtualport type='802.1Qbg'> <parameters managerid='13'/> </virtualport> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-virtio-network-portgroup.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-virtio-network-portgroup.xml index 950a9db..6cba439 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-net-virtio-network-portgroup.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-virtio-network-portgroup.xml @@ -22,7 +22,7 @@ <controller type='usb' index='0'/> <controller type='ide' index='0'/> <controller type='pci' index='0' model='pci-root'/> - <interface type='network'> + <interface type='network' trustGuestRxFilters='yes'> <mac address='00:11:22:33:44:55'/> <source network='rednet' portgroup='bob'/> <vlan> @@ -33,7 +33,7 @@ </virtualport> <model type='virtio'/> </interface> - <interface type='network'> + <interface type='network' trustGuestRxFilters='no'> <mac address='10:11:22:33:44:55'/> <source network='blue' portgroup='sam'/> <virtualport> -- 1.9.3

On 09/24/2014 05:50 AM, Laine Stump wrote:
This new attribute will control whether or not libvirt will pay attention to guest notifications about changes to network device mac addresses and receive filters. The default for this is 'no' (for security reasons). If it is set to 'yes' *and* the specified device model and connection support it (currently only macvtap+virtio) then libvirt will watch for NIC_RX_FILTER_CHANGED events, and when it receives one, it will issue a query-rx-filter command, retrieve the result, and modify the host-side macvtap interface's mac address and unicast/multicast filters accordingly.
The functionality behind this attribute will be in a later patch. This patch merely adds the attribute to the top-level of a domain's <interface> as well as to <network> and <portgroup>, and adds documentation and schema/xml2xml tests. Rather than adding even more test files, I've just added the net attribute in various applicable places of existing test files. --- docs/formatdomain.html.in | 38 ++++++++++++++++---- docs/formatnetwork.html.in | 28 +++++++++++++-- docs/schemas/domaincommon.rng | 5 +++ docs/schemas/network.rng | 10 ++++++ src/conf/domain_conf.c | 42 ++++++++++++++++++++++ src/conf/domain_conf.h | 3 ++ src/conf/network_conf.c | 35 ++++++++++++++++++ src/conf/network_conf.h | 2 ++ src/libvirt_private.syms | 1 + tests/networkxml2xmlin/vepa-net.xml | 4 +-- tests/networkxml2xmlout/vepa-net.xml | 4 +-- .../qemuxml2argv-net-virtio-network-portgroup.xml | 4 +-- 12 files changed, 160 insertions(+), 16 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index eefdd5e..17e180d 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -3343,10 +3343,9 @@ <pre> ... <devices> - <interface type='bridge'> - <source bridge='xenbr0'/> - <mac address='00:16:3e:5d:c7:9e'/> - <script path='vif-bridge'/> + <interface type='direct' trustGuestRxFilters='yes'> + <source dev='eth0'/> + <mac address='52:54:00:5d:c7:9e'/> <boot order='1'/> <rom bar='off'/> </interface> @@ -3356,8 +3355,21 @@ <p> There are several possibilities for specifying a network interface visible to the guest. Each subsection below provides - more details about common setup options. Additionally, - each <code><interface></code> element has an + more details about common setup options. + </p> + <p> + libvirt allows specifying when the host should trust reports + from the guest of changes to the interface mac address and + receive filters by setting the <code>trustGuestRxFilters</code> + attribute to <code>yes</code><span class="since">Since + 1.2.9</span>. <code>trustGuestRxFilters</code> defaults + to <code>no</code> for security reasons, and support depends on + the guest network device driver as well as the type of + connection on the host - currently it is only supported for the + virtio driver, and for macvtap connections on the host.
<span class="since">Since 1.2.9</span>), the <code>interface</code> element property <code>trustGuestRxFilters</code> provides the capability for the host to detect and trust reports from the guest regarding changes to the interface mac address and receive filters by setting the attribute to <code>yes</yes>. The default setting for the attribute is <code>no</code> for security reasons and support depends on the guest network device driver as well as the type of connection on the host - currently it is only supported for the virtio driver and for macvtap connections on the host. [just looks wierd starting with libvirt allows... I also changed the "virtio driver, and for" to not have the "," since it's just joining two phrases not a list of things...]
+ </p> + <p> + Each <code><interface></code> element has an optional <code><address></code> sub-element that can tie the interface to a particular pci slot, with attribute <code>type='pci'</code> @@ -3589,6 +3601,18 @@ being the default mode. The individual modes cause the delivery of packets to behave as follows: </p> + <p> + If the model type is set to <code>virtio</code> and + interface's <code>trustGuestRxFilters</code> attribute is set + to <code>yes</code>, changes made to the interface mac address, + unicast/multicast receive filters, and vlan settings in the + guest will be monitored and propogated to the associated macvtap
s/propogated/propagated/
+ device on the host. <span class="since">Since + 1.2.9</span>. If <code>trustGuestRxFilters</code> is not set, or
Instead of "...on the host. Since 1.2.9. If..." - consider "...on the host (since 1.2.9). If..."
+ is not supported for the device model in use, an attempted + change to the mac address originating from the guest side will + result in a non-working network connection. + </p>
<dl> <dt><code>vepa</code></dt> @@ -3621,7 +3645,7 @@ ... <devices> ... - <interface type='direct'> + <interface type='direct' trustGuestRxFilters='no'> <source dev='eth0' mode='vepa'/> </interface> </devices> diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 1a8ad8e..ff73952 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -35,7 +35,7 @@ </p>
<pre> - <network ipv6='yes'> + <network ipv6='yes' trustGuestRxFilters='no'> <name>default</name> <uuid>3e3fce45-4f53-4fa7-bb32-11f34168b82b</uuid> ...</pre> @@ -60,6 +60,16 @@ to have guest-to-guest communications. For further information, see the example below for the example with no gateway addresses. <span class="since">Since 1.0.1</span></dd> + <dt><code>trustGuestRxFilters='yes'</code></dt> + <dd>The optional parameter <code>trustGuestRxFilters</code> can + be used to set that attribute of the same name for each domain + interface connected to this network <span class="since">Since + 1.2.9</span> See
I think the "Since" needs some parentheses and period before the "See", eg (<span class="since">since 1.2.9</span>). See
+ the <a href="formatdomain.html#elementSNICS">Network + interfaces</a> section of the domain XML documentation for + more details. Note that an explicit setting of this attribute + in a portgroup or the individual domain interface will + override the setting in the network.</dd> </dl>
<h3><a name="elementsConnect">Connectivity</a></h3> @@ -606,7 +616,7 @@ <outbound average='1000' peak='5000' burst='5120'/> </bandwidth> </portgroup></b> - <b><portgroup name='sales'> + <b><portgroup name='sales' trustGuestRxFilters='no'> <virtualport type='802.1Qbh'> <parameters profileid='salestest'/> </virtualport> @@ -626,7 +636,7 @@ network can have multiple portgroup elements (and one of those can optionally be designated as the 'default' portgroup for the network), and each portgroup has a name, as well as various - subelements associated with it. The currently supported + attributes and subelements associated with it. The currently supported subelements are <code><bandwidth></code> (described <a href="formatnetwork.html#elementQoS">here</a>) and <code><virtualport></code> @@ -650,6 +660,18 @@ considered an error, and will prevent the interface from starting. </p> + <p> + portgroups also support the optional + parameter <code>trustGuestRxFilters</code> which can be used to + set that attribute of the same name for each domain interface + using this portgroup <span class="since">Since 1.2.9</span> See
Similar issue here w/r/t to Since & See.
+ the <a href="formatdomain.html#elementSNICS">Network + interfaces</a> section of the domain XML documentation for more + details. Note that an explicit setting of this attribute in the + portgroup overrides the network-wide setting, and an explicit + setting in the individual domain interface will override the + setting in the portgroup. + </p>
<h5><a name="elementsStaticroute">Static Routes</a></h5> <p> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index b6b309d..74a6d8c 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2007,6 +2007,11 @@ --> <define name="interface"> <element name="interface"> + <optional> + <attribute name="trustGuestRxFilters"> + <ref name="virYesNo"/> + </attribute> + </optional>
Shouldn't this go after the end of the <choice>? Since that's how it's written out in the xml.
<choice> <group> <attribute name="type"> diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index dc732b4..4546f80 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -24,6 +24,11 @@ <ref name="virYesNo"/> </attribute> </optional> + <optional> + <attribute name="trustGuestRxFilters"> + <ref name="virYesNo"/> + </attribute> + </optional> <interleave>
<!-- The name of the network, used to refer to it through the API @@ -197,6 +202,11 @@ <ref name="virYesNo"/> </attribute> </optional> + <optional> + <attribute name="trustGuestRxFilters"> + <ref name="virYesNo"/> + </attribute> + </optional> <interleave> <optional> <ref name="virtualPortProfile"/> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 9cc118c..7e0d147 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -6756,6 +6756,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, char *type = NULL; char *mode = NULL; char *addrtype = NULL; + char *trustGuestRxFilters = NULL;
if (VIR_ALLOC(actual) < 0) return -1; @@ -6783,6 +6784,16 @@ virDomainActualNetDefParseXML(xmlNodePtr node, goto error; }
+ trustGuestRxFilters = virXMLPropString(node, "trustGuestRxFilters"); + if (trustGuestRxFilters && + ((int)(actual->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0)) {
No need for the (int) cast.
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown trustGuestRxFilters value '%s'"), + trustGuestRxFilters); + goto error; + } + virtPortNode = virXPathNode("./virtualport", ctxt); if (virtPortNode) { if (actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE || @@ -6869,6 +6880,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, VIR_FREE(type); VIR_FREE(mode); VIR_FREE(addrtype); + VIR_FREE(trustGuestRxFilters); virDomainActualNetDefFree(actual);
ctxt->node = save_ctxt; @@ -6919,6 +6931,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, char *vhostuser_mode = NULL; char *vhostuser_path = NULL; char *vhostuser_type = NULL; + char *trustGuestRxFilters = NULL; virNWFilterHashTablePtr filterparams = NULL; virDomainActualNetDefPtr actual = NULL; xmlNodePtr oldnode = ctxt->node; @@ -6940,6 +6953,16 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, def->type = VIR_DOMAIN_NET_TYPE_USER; }
+ trustGuestRxFilters = virXMLPropString(node, "trustGuestRxFilters"); + if (trustGuestRxFilters && + ((int)(def->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0)) {
No need for the (int) cast.
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown trustGuestRxFilters value '%s'"), + trustGuestRxFilters); + goto error; + } + cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { @@ -7469,6 +7492,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_FREE(mode); VIR_FREE(linkstate); VIR_FREE(addrtype); + VIR_FREE(trustGuestRxFilters); virNWFilterHashTableFree(filterparams);
return def; @@ -16491,6 +16515,9 @@ virDomainActualNetDefFormat(virBufferPtr buf, if (hostdef && hostdef->managed) virBufferAddLit(buf, " managed='yes'"); } + if (def->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " trustGuestRxFilters='%s'", + virTristateBoolTypeToString(def->trustGuestRxFilters));
Showoff - although other places that use the tristate logic just use "if (def->trustGuestRxFilters)" (since BOOL_ABSENT == 0)
virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2); @@ -16575,6 +16602,9 @@ virDomainNetDefFormat(virBufferPtr buf, virBufferAsprintf(buf, "<interface type='%s'", typeStr); if (hostdef && hostdef->managed) virBufferAddLit(buf, " managed='yes'"); + if (def->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " trustGuestRxFilters='%s'", + virTristateBoolTypeToString(def->trustGuestRxFilters));
Similarly here... Also I noted that the domaincommon.rng file has the "trustGuestRxFilters" defined first followed by type, managed, etc., while this code has it the other way (eg, last). I thought that caused issues for some test, but I guess it didn't. Maybe we just got lucky.
virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2); @@ -19976,6 +20006,18 @@ virDomainNetGetActualVlan(virDomainNetDefPtr iface) return NULL; }
+ +bool +virDomainNetGetActualTrustGuestRxFilters(virDomainNetDefPtr iface) +{ + if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK && + iface->data.network.actual) + return (iface->data.network.actual->trustGuestRxFilters + == VIR_TRISTATE_BOOL_YES); + return iface->trustGuestRxFilters == VIR_TRISTATE_BOOL_YES; +} + + /* Return listens[i] from the appropriate union for the graphics * type, or NULL if this is an unsuitable type, or the index is out of * bounds. If force0 is TRUE, i == 0, and there is no listen array, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index d97b1f8..2d0cb06 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -880,6 +880,7 @@ struct _virDomainActualNetDef { virNetDevVPortProfilePtr virtPortProfile; virNetDevBandwidthPtr bandwidth; virNetDevVlan vlan; + int trustGuestRxFilters; /* enum virTristateBool */ unsigned int class_id; /* class ID for bandwidth 'floor' */ };
@@ -954,6 +955,7 @@ struct _virDomainNetDef { virNWFilterHashTablePtr filterparams; virNetDevBandwidthPtr bandwidth; virNetDevVlan vlan; + int trustGuestRxFilters; /* enum virTristateBool */ int linkstate; };
@@ -2471,6 +2473,7 @@ virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface); virNetDevBandwidthPtr virDomainNetGetActualBandwidth(virDomainNetDefPtr iface); virNetDevVlanPtr virDomainNetGetActualVlan(virDomainNetDefPtr iface); +bool virDomainNetGetActualTrustGuestRxFilters(virDomainNetDefPtr iface);
int virDomainControllerInsert(virDomainDefPtr def, virDomainControllerDefPtr controller) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 892bd8a..63b5917 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -1615,6 +1615,7 @@ virNetworkPortGroupParseXML(virPortGroupDefPtr def, xmlNodePtr vlanNode; xmlNodePtr bandwidth_node; char *isDefault = NULL; + char *trustGuestRxFilters = NULL;
int result = -1;
@@ -1632,6 +1633,18 @@ virNetworkPortGroupParseXML(virPortGroupDefPtr def, isDefault = virXPathString("string(./@default)", ctxt); def->isDefault = isDefault && STRCASEEQ(isDefault, "yes");
+ trustGuestRxFilters + = virXPathString("string(./@trustGuestRxFilters)", ctxt); + if (trustGuestRxFilters) { + if ((def->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0) {
You could combine into one if like was done in domain_conf.
+ virReportError(VIR_ERR_XML_ERROR, + _("Invalid trustGuestRxFilters setting '%s' " + "in portgroup"), trustGuestRxFilters); + goto cleanup; + } + } + virtPortNode = virXPathNode("./virtualport", ctxt); if (virtPortNode && (!(def->virtPortProfile = virNetDevVPortProfileParse(virtPortNode, 0)))) { @@ -1654,6 +1667,7 @@ virNetworkPortGroupParseXML(virPortGroupDefPtr def, virPortGroupDefClear(def); } VIR_FREE(isDefault); + VIR_FREE(trustGuestRxFilters);
ctxt->node = save; return result; @@ -2013,6 +2027,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) xmlNodePtr virtPortNode = NULL; xmlNodePtr forwardNode = NULL; char *ipv6nogwStr = NULL; + char *trustGuestRxFilters = NULL; xmlNodePtr save = ctxt->node; xmlNodePtr bandwidthNode = NULL; xmlNodePtr vlanNode; @@ -2061,6 +2076,20 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) VIR_FREE(ipv6nogwStr); }
+ trustGuestRxFilters + = virXPathString("string(./@trustGuestRxFilters)", ctxt); + if (trustGuestRxFilters) { + if ((def->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0) {
Ditto
+ virReportError(VIR_ERR_XML_ERROR, + _("Invalid trustGuestRxFilters setting '%s' " + "in network '%s'"), + trustGuestRxFilters, def->name); + goto error;
^^^ Memleak ...
+ } + VIR_FREE(trustGuestRxFilters);
Memory leak if the goto error occurs. Move VIR_FREE() to error:
+ } + /* Parse network domain information */ def->domain = virXPathString("string(./domain[1]/@name)", ctxt);
@@ -2597,6 +2626,9 @@ virPortGroupDefFormat(virBufferPtr buf, if (def->isDefault) { virBufferAddLit(buf, " default='yes'"); } + if (def->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " trustGuestRxFilters='%s'", + virTristateBoolTypeToString(def->trustGuestRxFilters));
Similar to domain_conf - the BOOL_ABSENT isn't necessary
virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); if (virNetDevVlanFormat(&def->vlan, buf) < 0) @@ -2675,6 +2707,9 @@ virNetworkDefFormatBuf(virBufferPtr buf, } if (def->ipv6nogw) virBufferAddLit(buf, " ipv6='yes'"); + if (def->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " trustGuestRxFilters='%s'", + virTristateBoolTypeToString(def->trustGuestRxFilters));
ditto Nothing major - just some minor cleanups and it's an ACK John
virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); virBufferEscapeString(buf, "<name>%s</name>\n", def->name); diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 7ed58cd..660cd2d 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -219,6 +219,7 @@ struct _virPortGroupDef { virNetDevVPortProfilePtr virtPortProfile; virNetDevBandwidthPtr bandwidth; virNetDevVlan vlan; + int trustGuestRxFilters; /* enum virTristateBool */ };
typedef struct _virNetworkDef virNetworkDef; @@ -256,6 +257,7 @@ struct _virNetworkDef { virPortGroupDefPtr portGroups; virNetDevBandwidthPtr bandwidth; virNetDevVlan vlan; + int trustGuestRxFilters; /* enum virTristateBool */ };
typedef struct _virNetworkObj virNetworkObj; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a339ced..bb2b9a3 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -331,6 +331,7 @@ virDomainNetGetActualBridgeName; virDomainNetGetActualDirectDev; virDomainNetGetActualDirectMode; virDomainNetGetActualHostdev; +virDomainNetGetActualTrustGuestRxFilters; virDomainNetGetActualType; virDomainNetGetActualVirtPortProfile; virDomainNetGetActualVlan; diff --git a/tests/networkxml2xmlin/vepa-net.xml b/tests/networkxml2xmlin/vepa-net.xml index 030c1d1..07c59c5 100644 --- a/tests/networkxml2xmlin/vepa-net.xml +++ b/tests/networkxml2xmlin/vepa-net.xml @@ -1,4 +1,4 @@ -<network> +<network trustGuestRxFilters="no"> <name>vepa-net</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8b</uuid> <forward mode="vepa"> @@ -14,7 +14,7 @@ <parameters typeid="2193047" typeidversion="3"/> </virtualport> </portgroup> - <portgroup name="alice"> + <portgroup name="alice" trustGuestRxFilters="yes"> <virtualport type="802.1Qbg"> <parameters managerid="13"/> </virtualport> diff --git a/tests/networkxml2xmlout/vepa-net.xml b/tests/networkxml2xmlout/vepa-net.xml index 4d35a8a..b266620 100644 --- a/tests/networkxml2xmlout/vepa-net.xml +++ b/tests/networkxml2xmlout/vepa-net.xml @@ -1,4 +1,4 @@ -<network> +<network trustGuestRxFilters='no'> <name>vepa-net</name> <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8b</uuid> <forward dev='eth1' mode='vepa'> @@ -14,7 +14,7 @@ <parameters typeid='2193047' typeidversion='3'/> </virtualport> </portgroup> - <portgroup name='alice'> + <portgroup name='alice' trustGuestRxFilters='yes'> <virtualport type='802.1Qbg'> <parameters managerid='13'/> </virtualport> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-virtio-network-portgroup.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-virtio-network-portgroup.xml index 950a9db..6cba439 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-net-virtio-network-portgroup.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-virtio-network-portgroup.xml @@ -22,7 +22,7 @@ <controller type='usb' index='0'/> <controller type='ide' index='0'/> <controller type='pci' index='0' model='pci-root'/> - <interface type='network'> + <interface type='network' trustGuestRxFilters='yes'> <mac address='00:11:22:33:44:55'/> <source network='rednet' portgroup='bob'/> <vlan> @@ -33,7 +33,7 @@ </virtualport> <model type='virtio'/> </interface> - <interface type='network'> + <interface type='network' trustGuestRxFilters='no'> <mac address='10:11:22:33:44:55'/> <source network='blue' portgroup='sam'/> <virtualport>

On 09/24/2014 07:17 PM, John Ferlan wrote:
This new attribute will control whether or not libvirt will pay attention to guest notifications about changes to network device mac addresses and receive filters. The default for this is 'no' (for security reasons). If it is set to 'yes' *and* the specified device model and connection support it (currently only macvtap+virtio) then libvirt will watch for NIC_RX_FILTER_CHANGED events, and when it receives one, it will issue a query-rx-filter command, retrieve the result, and modify the host-side macvtap interface's mac address and unicast/multicast filters accordingly.
The functionality behind this attribute will be in a later patch. This patch merely adds the attribute to the top-level of a domain's <interface> as well as to <network> and <portgroup>, and adds documentation and schema/xml2xml tests. Rather than adding even more test files, I've just added the net attribute in various applicable places of existing test files. --- docs/formatdomain.html.in | 38 ++++++++++++++++---- docs/formatnetwork.html.in | 28 +++++++++++++-- docs/schemas/domaincommon.rng | 5 +++ docs/schemas/network.rng | 10 ++++++ src/conf/domain_conf.c | 42 ++++++++++++++++++++++ src/conf/domain_conf.h | 3 ++ src/conf/network_conf.c | 35 ++++++++++++++++++ src/conf/network_conf.h | 2 ++ src/libvirt_private.syms | 1 + tests/networkxml2xmlin/vepa-net.xml | 4 +-- tests/networkxml2xmlout/vepa-net.xml | 4 +-- .../qemuxml2argv-net-virtio-network-portgroup.xml | 4 +-- 12 files changed, 160 insertions(+), 16 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index eefdd5e..17e180d 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -3343,10 +3343,9 @@ <pre> ... <devices> - <interface type='bridge'> - <source bridge='xenbr0'/> - <mac address='00:16:3e:5d:c7:9e'/> - <script path='vif-bridge'/> + <interface type='direct' trustGuestRxFilters='yes'> + <source dev='eth0'/> + <mac address='52:54:00:5d:c7:9e'/> <boot order='1'/> <rom bar='off'/> </interface> @@ -3356,8 +3355,21 @@ <p> There are several possibilities for specifying a network interface visible to the guest. Each subsection below provides - more details about common setup options. Additionally, - each <code><interface></code> element has an + more details about common setup options. + </p> + <p> + libvirt allows specifying when the host should trust reports + from the guest of changes to the interface mac address and + receive filters by setting the <code>trustGuestRxFilters</code> + attribute to <code>yes</code><span class="since">Since + 1.2.9</span>. <code>trustGuestRxFilters</code> defaults + to <code>no</code> for security reasons, and support depends on + the guest network device driver as well as the type of + connection on the host - currently it is only supported for the + virtio driver, and for macvtap connections on the host. <span class="since">Since 1.2.9</span>), the <code>interface</code> element property <code>trustGuestRxFilters</code> provides the capability for the host to detect and trust reports from the guest regarding changes to the interface mac address and receive filters by setting the attribute to <code>yes</yes>. The default setting for the attribute is <code>no</code> for security reasons and support depends on
On 09/24/2014 05:50 AM, Laine Stump wrote: the guest network device driver as well as the type of connection on the host - currently it is only supported for the virtio driver and for macvtap connections on the host.
*applause* Seriously. Can you write all my documentation from now on?
[just looks wierd starting with libvirt allows... I also changed the "virtio driver, and for" to not have the "," since it's just joining two phrases not a list of things...]
+ </p> + <p> + Each <code><interface></code> element has an optional <code><address></code> sub-element that can tie the interface to a particular pci slot, with attribute <code>type='pci'</code> @@ -3589,6 +3601,18 @@ being the default mode. The individual modes cause the delivery of packets to behave as follows: </p> + <p> + If the model type is set to <code>virtio</code> and + interface's <code>trustGuestRxFilters</code> attribute is set + to <code>yes</code>, changes made to the interface mac address, + unicast/multicast receive filters, and vlan settings in the + guest will be monitored and propogated to the associated macvtap s/propogated/propagated/
+ device on the host. <span class="since">Since + 1.2.9</span>. If <code>trustGuestRxFilters</code> is not set, or Instead of "...on the host. Since 1.2.9. If..." - consider "...on the host (since 1.2.9). If..."
+ is not supported for the device model in use, an attempted + change to the mac address originating from the guest side will + result in a non-working network connection. + </p>
<dl> <dt><code>vepa</code></dt> @@ -3621,7 +3645,7 @@ ... <devices> ... - <interface type='direct'> + <interface type='direct' trustGuestRxFilters='no'> <source dev='eth0' mode='vepa'/> </interface> </devices> diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in index 1a8ad8e..ff73952 100644 --- a/docs/formatnetwork.html.in +++ b/docs/formatnetwork.html.in @@ -35,7 +35,7 @@ </p>
<pre> - <network ipv6='yes'> + <network ipv6='yes' trustGuestRxFilters='no'> <name>default</name> <uuid>3e3fce45-4f53-4fa7-bb32-11f34168b82b</uuid> ...</pre> @@ -60,6 +60,16 @@ to have guest-to-guest communications. For further information, see the example below for the example with no gateway addresses. <span class="since">Since 1.0.1</span></dd> + <dt><code>trustGuestRxFilters='yes'</code></dt> + <dd>The optional parameter <code>trustGuestRxFilters</code> can + be used to set that attribute of the same name for each domain + interface connected to this network <span class="since">Since + 1.2.9</span> See I think the "Since" needs some parentheses and period before the "See", eg (<span class="since">since 1.2.9</span>). See
+ the <a href="formatdomain.html#elementSNICS">Network + interfaces</a> section of the domain XML documentation for + more details. Note that an explicit setting of this attribute + in a portgroup or the individual domain interface will + override the setting in the network.</dd> </dl>
<h3><a name="elementsConnect">Connectivity</a></h3> @@ -606,7 +616,7 @@ <outbound average='1000' peak='5000' burst='5120'/> </bandwidth> </portgroup></b> - <b><portgroup name='sales'> + <b><portgroup name='sales' trustGuestRxFilters='no'> <virtualport type='802.1Qbh'> <parameters profileid='salestest'/> </virtualport> @@ -626,7 +636,7 @@ network can have multiple portgroup elements (and one of those can optionally be designated as the 'default' portgroup for the network), and each portgroup has a name, as well as various - subelements associated with it. The currently supported + attributes and subelements associated with it. The currently supported subelements are <code><bandwidth></code> (described <a href="formatnetwork.html#elementQoS">here</a>) and <code><virtualport></code> @@ -650,6 +660,18 @@ considered an error, and will prevent the interface from starting. </p> + <p> + portgroups also support the optional + parameter <code>trustGuestRxFilters</code> which can be used to + set that attribute of the same name for each domain interface + using this portgroup <span class="since">Since 1.2.9</span> See Similar issue here w/r/t to Since & See.
+ the <a href="formatdomain.html#elementSNICS">Network + interfaces</a> section of the domain XML documentation for more + details. Note that an explicit setting of this attribute in the + portgroup overrides the network-wide setting, and an explicit + setting in the individual domain interface will override the + setting in the portgroup. + </p>
<h5><a name="elementsStaticroute">Static Routes</a></h5> <p> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index b6b309d..74a6d8c 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2007,6 +2007,11 @@ --> <define name="interface"> <element name="interface"> + <optional> + <attribute name="trustGuestRxFilters"> + <ref name="virYesNo"/> + </attribute> + </optional> Shouldn't this go after the end of the <choice>? Since that's how it's written out in the xml.
Well, attributes are position-indifferent, so it doesn't really matter. I had put it up at the top so that it wasn't lost down at the bottom of all the <choice> sections. But there is something to be said for consistent positioning, so I'll change it.
<choice> <group> <attribute name="type"> diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng index dc732b4..4546f80 100644 --- a/docs/schemas/network.rng +++ b/docs/schemas/network.rng @@ -24,6 +24,11 @@ <ref name="virYesNo"/> </attribute> </optional> + <optional> + <attribute name="trustGuestRxFilters"> + <ref name="virYesNo"/> + </attribute> + </optional> <interleave>
<!-- The name of the network, used to refer to it through the API @@ -197,6 +202,11 @@ <ref name="virYesNo"/> </attribute> </optional> + <optional> + <attribute name="trustGuestRxFilters"> + <ref name="virYesNo"/> + </attribute> + </optional> <interleave> <optional> <ref name="virtualPortProfile"/> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 9cc118c..7e0d147 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -6756,6 +6756,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, char *type = NULL; char *mode = NULL; char *addrtype = NULL; + char *trustGuestRxFilters = NULL;
if (VIR_ALLOC(actual) < 0) return -1; @@ -6783,6 +6784,16 @@ virDomainActualNetDefParseXML(xmlNodePtr node, goto error; }
+ trustGuestRxFilters = virXMLPropString(node, "trustGuestRxFilters"); + if (trustGuestRxFilters && + ((int)(actual->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0)) {
No need for the (int) cast.
Yeah, I think I was following the pattern of the setting of def->type for virDomainNetDef (which is in the struct as "virDomainNetType type;" rather than "int type; /* enum virDomainNetType */).
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown trustGuestRxFilters value '%s'"), + trustGuestRxFilters); + goto error; + } + virtPortNode = virXPathNode("./virtualport", ctxt); if (virtPortNode) { if (actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE || @@ -6869,6 +6880,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node, VIR_FREE(type); VIR_FREE(mode); VIR_FREE(addrtype); + VIR_FREE(trustGuestRxFilters); virDomainActualNetDefFree(actual);
ctxt->node = save_ctxt; @@ -6919,6 +6931,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, char *vhostuser_mode = NULL; char *vhostuser_path = NULL; char *vhostuser_type = NULL; + char *trustGuestRxFilters = NULL; virNWFilterHashTablePtr filterparams = NULL; virDomainActualNetDefPtr actual = NULL; xmlNodePtr oldnode = ctxt->node; @@ -6940,6 +6953,16 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, def->type = VIR_DOMAIN_NET_TYPE_USER; }
+ trustGuestRxFilters = virXMLPropString(node, "trustGuestRxFilters"); + if (trustGuestRxFilters && + ((int)(def->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0)) { No need for the (int) cast.
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown trustGuestRxFilters value '%s'"), + trustGuestRxFilters); + goto error; + } + cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { @@ -7469,6 +7492,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_FREE(mode); VIR_FREE(linkstate); VIR_FREE(addrtype); + VIR_FREE(trustGuestRxFilters); virNWFilterHashTableFree(filterparams);
return def; @@ -16491,6 +16515,9 @@ virDomainActualNetDefFormat(virBufferPtr buf, if (hostdef && hostdef->managed) virBufferAddLit(buf, " managed='yes'"); } + if (def->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " trustGuestRxFilters='%s'", + virTristateBoolTypeToString(def->trustGuestRxFilters)); Showoff - although other places that use the tristate logic just use "if (def->trustGuestRxFilters)" (since BOOL_ABSENT == 0)
If that's already commonly done and expected, then I can change my code to do the same.
virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2); @@ -16575,6 +16602,9 @@ virDomainNetDefFormat(virBufferPtr buf, virBufferAsprintf(buf, "<interface type='%s'", typeStr); if (hostdef && hostdef->managed) virBufferAddLit(buf, " managed='yes'"); + if (def->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " trustGuestRxFilters='%s'", + virTristateBoolTypeToString(def->trustGuestRxFilters));
Similarly here...
Also I noted that the domaincommon.rng file has the "trustGuestRxFilters" defined first followed by type, managed, etc., while this code has it the other way (eg, last). I thought that caused issues for some test, but I guess it didn't. Maybe we just got lucky.
That's only problematic for *elements* that are in different order in RNG vs. Format, and then can be solved by putting <interleave> ... </interleave> around all of the elements involved. (there is another potential issue, if the test case datafile has elements or attributes in a different order than they are emitted in the Format, the test case will fail unless there is a separate "out" file that has everything in the correct order.)
virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2); @@ -19976,6 +20006,18 @@ virDomainNetGetActualVlan(virDomainNetDefPtr iface) return NULL; }
+ +bool +virDomainNetGetActualTrustGuestRxFilters(virDomainNetDefPtr iface) +{ + if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK && + iface->data.network.actual) + return (iface->data.network.actual->trustGuestRxFilters + == VIR_TRISTATE_BOOL_YES); + return iface->trustGuestRxFilters == VIR_TRISTATE_BOOL_YES; +} + + /* Return listens[i] from the appropriate union for the graphics * type, or NULL if this is an unsuitable type, or the index is out of * bounds. If force0 is TRUE, i == 0, and there is no listen array, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index d97b1f8..2d0cb06 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -880,6 +880,7 @@ struct _virDomainActualNetDef { virNetDevVPortProfilePtr virtPortProfile; virNetDevBandwidthPtr bandwidth; virNetDevVlan vlan; + int trustGuestRxFilters; /* enum virTristateBool */ unsigned int class_id; /* class ID for bandwidth 'floor' */ };
@@ -954,6 +955,7 @@ struct _virDomainNetDef { virNWFilterHashTablePtr filterparams; virNetDevBandwidthPtr bandwidth; virNetDevVlan vlan; + int trustGuestRxFilters; /* enum virTristateBool */ int linkstate; };
@@ -2471,6 +2473,7 @@ virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface); virNetDevBandwidthPtr virDomainNetGetActualBandwidth(virDomainNetDefPtr iface); virNetDevVlanPtr virDomainNetGetActualVlan(virDomainNetDefPtr iface); +bool virDomainNetGetActualTrustGuestRxFilters(virDomainNetDefPtr iface);
int virDomainControllerInsert(virDomainDefPtr def, virDomainControllerDefPtr controller) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 892bd8a..63b5917 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -1615,6 +1615,7 @@ virNetworkPortGroupParseXML(virPortGroupDefPtr def, xmlNodePtr vlanNode; xmlNodePtr bandwidth_node; char *isDefault = NULL; + char *trustGuestRxFilters = NULL;
int result = -1;
@@ -1632,6 +1633,18 @@ virNetworkPortGroupParseXML(virPortGroupDefPtr def, isDefault = virXPathString("string(./@default)", ctxt); def->isDefault = isDefault && STRCASEEQ(isDefault, "yes");
+ trustGuestRxFilters + = virXPathString("string(./@trustGuestRxFilters)", ctxt); + if (trustGuestRxFilters) { + if ((def->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0) {
You could combine into one if like was done in domain_conf.
You mean have a single utility function used in all three places? The problem with that is that the error message is slightly different to point out the location of the problematic attribute in the XML.
+ virReportError(VIR_ERR_XML_ERROR, + _("Invalid trustGuestRxFilters setting '%s' " + "in portgroup"), trustGuestRxFilters); + goto cleanup; + } + } + virtPortNode = virXPathNode("./virtualport", ctxt); if (virtPortNode && (!(def->virtPortProfile = virNetDevVPortProfileParse(virtPortNode, 0)))) { @@ -1654,6 +1667,7 @@ virNetworkPortGroupParseXML(virPortGroupDefPtr def, virPortGroupDefClear(def); } VIR_FREE(isDefault); + VIR_FREE(trustGuestRxFilters);
ctxt->node = save; return result; @@ -2013,6 +2027,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) xmlNodePtr virtPortNode = NULL; xmlNodePtr forwardNode = NULL; char *ipv6nogwStr = NULL; + char *trustGuestRxFilters = NULL; xmlNodePtr save = ctxt->node; xmlNodePtr bandwidthNode = NULL; xmlNodePtr vlanNode; @@ -2061,6 +2076,20 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt) VIR_FREE(ipv6nogwStr); }
+ trustGuestRxFilters + = virXPathString("string(./@trustGuestRxFilters)", ctxt); + if (trustGuestRxFilters) { + if ((def->trustGuestRxFilters + = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0) { Ditto
+ virReportError(VIR_ERR_XML_ERROR, + _("Invalid trustGuestRxFilters setting '%s' " + "in network '%s'"), + trustGuestRxFilters, def->name); + goto error; ^^^ Memleak ...
Oops.
+ } + VIR_FREE(trustGuestRxFilters); Memory leak if the goto error occurs. Move VIR_FREE() to error:
Actually you can't *move* it, because in this function, the stuff at error: isn't executed in the case of success. It could benefit from being updated to use the currently accepted "do *all* cleanup at "cleanup:" and make sure all exits from the function go through there" form. That can/should be a separate patch though, so I'll just duplicate the VIR_FREE() at error: for now.
+ } + /* Parse network domain information */ def->domain = virXPathString("string(./domain[1]/@name)", ctxt);
@@ -2597,6 +2626,9 @@ virPortGroupDefFormat(virBufferPtr buf, if (def->isDefault) { virBufferAddLit(buf, " default='yes'"); } + if (def->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " trustGuestRxFilters='%s'", + virTristateBoolTypeToString(def->trustGuestRxFilters)); Similar to domain_conf - the BOOL_ABSENT isn't necessary
virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); if (virNetDevVlanFormat(&def->vlan, buf) < 0) @@ -2675,6 +2707,9 @@ virNetworkDefFormatBuf(virBufferPtr buf, } if (def->ipv6nogw) virBufferAddLit(buf, " ipv6='yes'"); + if (def->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + virBufferAsprintf(buf, " trustGuestRxFilters='%s'", + virTristateBoolTypeToString(def->trustGuestRxFilters));
ditto
Nothing major - just some minor cleanups and it's an ACK
I've made all the suggested changes. Since the freeze has started, I won't be pushing until after 1.2.9 release anyway, so I'll repost the entire series later.

As is done with other items such as vlan, virtualport, and bandwidth, set the actual trustGuestRxFilters value to be used by a domain interface according to a merge of the same attribute in the interface, portgroup, and network in use. the interface setting always takes precedence (if specified), followed by portgroup, and finally the setting in the network is used if it's not specified in the interface or portgroup. --- src/network/bridge_driver.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 979fb13..548e354 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -3794,6 +3794,17 @@ networkAllocateActualDevice(virDomainDefPtr dom, if (vlan && virNetDevVlanCopy(&iface->data.network.actual->vlan, vlan) < 0) goto error; + if (iface->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + iface->data.network.actual->trustGuestRxFilters + = iface->trustGuestRxFilters; + else if (portgroup && + portgroup->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + iface->data.network.actual->trustGuestRxFilters + = portgroup->trustGuestRxFilters; + else if (netdef->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + iface->data.network.actual->trustGuestRxFilters + = netdef->trustGuestRxFilters; + if ((netdef->forward.type == VIR_NETWORK_FORWARD_NONE) || (netdef->forward.type == VIR_NETWORK_FORWARD_NAT) || (netdef->forward.type == VIR_NETWORK_FORWARD_ROUTE)) { -- 1.9.3

On 09/24/2014 05:50 AM, Laine Stump wrote:
As is done with other items such as vlan, virtualport, and bandwidth, set the actual trustGuestRxFilters value to be used by a domain interface according to a merge of the same attribute in the interface, portgroup, and network in use. the interface setting always takes precedence (if specified), followed by portgroup, and finally the setting in the network is used if it's not specified in the interface or portgroup. --- src/network/bridge_driver.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 979fb13..548e354 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -3794,6 +3794,17 @@ networkAllocateActualDevice(virDomainDefPtr dom, if (vlan && virNetDevVlanCopy(&iface->data.network.actual->vlan, vlan) < 0) goto error;
+ if (iface->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + iface->data.network.actual->trustGuestRxFilters + = iface->trustGuestRxFilters; + else if (portgroup && + portgroup->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + iface->data.network.actual->trustGuestRxFilters + = portgroup->trustGuestRxFilters; + else if (netdef->trustGuestRxFilters != VIR_TRISTATE_BOOL_ABSENT) + iface->data.network.actual->trustGuestRxFilters + = netdef->trustGuestRxFilters; +
Since BOOL_ABSENT is 0 - you don't "need" the comparison - it may look cleaner too ACK either way John
if ((netdef->forward.type == VIR_NETWORK_FORWARD_NONE) || (netdef->forward.type == VIR_NETWORK_FORWARD_NAT) || (netdef->forward.type == VIR_NETWORK_FORWARD_ROUTE)) {

This same structure will be used to retrieve RX filter info for interfaces on the host via netlink messages, and RX filter info for interfaces on the guest via the qemu "query-rx-filter" command. --- src/libvirt_private.syms | 8 +++++++ src/util/virnetdev.c | 40 +++++++++++++++++++++++++++++++++ src/util/virnetdev.h | 57 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bb2b9a3..e5723d2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1595,6 +1595,14 @@ virNetDevReplaceMacAddress; virNetDevReplaceNetConfig; virNetDevRestoreMacAddress; virNetDevRestoreNetConfig; +virNetDevRxFilterFree; +virNetDevRxFilterMulticastModeTypeFromString; +virNetDevRxFilterMulticastModeTypeToString; +virNetDevRxFilterNew; +virNetDevRxFilterUnicastModeTypeFromString; +virNetDevRxFilterUnicastModeTypeToString; +virNetDevRxFilterVlanModeTypeFromString; +virNetDevRxFilterVlanModeTypeToString; virNetDevSetIPv4Address; virNetDevSetMAC; virNetDevSetMTU; diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 8815e18..dd1f530 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -1932,3 +1932,43 @@ virNetDevGetLinkInfo(const char *ifname, return 0; } #endif /* defined(__linux__) */ + + +VIR_ENUM_IMPL(virNetDevRxFilterUnicastMode, + VIR_NETDEV_RX_FILTER_UNICAST_MODE_LAST, + "none", + "normal"); + +VIR_ENUM_IMPL(virNetDevRxFilterMulticastMode, + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_LAST, + "none", + "normal"); + +VIR_ENUM_IMPL(virNetDevRxFilterVlanMode, + VIR_NETDEV_RX_FILTER_VLAN_MODE_LAST, + "none", + "normal"); + + +virNetDevRxFilterPtr +virNetDevRxFilterNew(void) +{ + virNetDevRxFilterPtr filter; + + if (VIR_ALLOC(filter) < 0) + return NULL; + return filter; +} + + +void +virNetDevRxFilterFree(virNetDevRxFilterPtr filter) +{ + if (filter) { + VIR_FREE(filter->name); + VIR_FREE(filter->unicast.table); + VIR_FREE(filter->multicast.table); + VIR_FREE(filter->vlan.table); + VIR_FREE(filter); + } +} diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index 69e365e..307871c 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2013 Red Hat, Inc. + * Copyright (C) 2007-2014 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -37,6 +37,57 @@ typedef struct ifreq virIfreq; typedef void virIfreq; # endif +typedef enum { + VIR_NETDEV_RX_FILTER_UNICAST_MODE_NONE = 0, + VIR_NETDEV_RX_FILTER_UNICAST_MODE_NORMAL, + + VIR_NETDEV_RX_FILTER_UNICAST_MODE_LAST +} virNetDevRxFilterUnicastMode; +VIR_ENUM_DECL(virNetDevRxFilterUnicastMode) + +typedef enum { + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_NONE = 0, + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_NORMAL, + + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_LAST +} virNetDevRxFilterMulticastMode; +VIR_ENUM_DECL(virNetDevRxFilterMulticastMode) + +typedef enum { + VIR_NETDEV_RX_FILTER_VLAN_MODE_NONE = 0, + VIR_NETDEV_RX_FILTER_VLAN_MODE_NORMAL, + + VIR_NETDEV_RX_FILTER_VLAN_MODE_LAST +} virNetDevRxFilterVlanMode; +VIR_ENUM_DECL(virNetDevRxFilterVlanMode) + +typedef struct _virNetDevRxFilter virNetDevRxFilter; +typedef virNetDevRxFilter *virNetDevRxFilterPtr; +struct _virNetDevRxFilter { + char *name; /* the alias used by qemu, *not* name used by guest */ + virMacAddr mac; + bool promiscuous; + bool broadcastAllowed; + + struct { + int mode; /* enum virNetDevRxFilterUnicastMode */ + bool overflow; + virMacAddrPtr table; + size_t nTable; + } unicast; + struct { + int mode; /* enum virNetDevRxFilterMulticastMode */ + bool overflow; + virMacAddrPtr table; + size_t nTable; + } multicast; + struct { + int mode; /* enum virNetDevRxFilterVlanMode */ + unsigned int *table; + size_t nTable; + } vlan; +}; + int virNetDevSetupControl(const char *ifname, virIfreq *ifr) ATTRIBUTE_RETURN_CHECK; @@ -150,4 +201,8 @@ int virNetDevGetLinkInfo(const char *ifname, virInterfaceLinkPtr lnk) ATTRIBUTE_NONNULL(1); +virNetDevRxFilterPtr virNetDevRxFilterNew(void) + ATTRIBUTE_RETURN_CHECK; +void virNetDevRxFilterFree(virNetDevRxFilterPtr filter); + #endif /* __VIR_NETDEV_H__ */ -- 1.9.3

On 09/24/2014 05:50 AM, Laine Stump wrote:
This same structure will be used to retrieve RX filter info for interfaces on the host via netlink messages, and RX filter info for interfaces on the guest via the qemu "query-rx-filter" command. --- src/libvirt_private.syms | 8 +++++++ src/util/virnetdev.c | 40 +++++++++++++++++++++++++++++++++ src/util/virnetdev.h | 57 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 1 deletion(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bb2b9a3..e5723d2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1595,6 +1595,14 @@ virNetDevReplaceMacAddress; virNetDevReplaceNetConfig; virNetDevRestoreMacAddress; virNetDevRestoreNetConfig; +virNetDevRxFilterFree; +virNetDevRxFilterMulticastModeTypeFromString; +virNetDevRxFilterMulticastModeTypeToString; +virNetDevRxFilterNew; +virNetDevRxFilterUnicastModeTypeFromString; +virNetDevRxFilterUnicastModeTypeToString; +virNetDevRxFilterVlanModeTypeFromString; +virNetDevRxFilterVlanModeTypeToString; virNetDevSetIPv4Address; virNetDevSetMAC; virNetDevSetMTU; diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 8815e18..dd1f530 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -1932,3 +1932,43 @@ virNetDevGetLinkInfo(const char *ifname, return 0; } #endif /* defined(__linux__) */ + + +VIR_ENUM_IMPL(virNetDevRxFilterUnicastMode, + VIR_NETDEV_RX_FILTER_UNICAST_MODE_LAST, + "none", + "normal"); + +VIR_ENUM_IMPL(virNetDevRxFilterMulticastMode, + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_LAST, + "none", + "normal"); + +VIR_ENUM_IMPL(virNetDevRxFilterVlanMode, + VIR_NETDEV_RX_FILTER_VLAN_MODE_LAST, + "none", + "normal");
Is there ever a chance these could be different for one of these types? Seems "excessive" to make 3 specific definitions when 1 generic one could suffice (drop the "Unicast, Multicast, Vlan")
+ + +virNetDevRxFilterPtr +virNetDevRxFilterNew(void) +{ + virNetDevRxFilterPtr filter; + + if (VIR_ALLOC(filter) < 0) + return NULL; + return filter; +} + + +void +virNetDevRxFilterFree(virNetDevRxFilterPtr filter) +{ + if (filter) { + VIR_FREE(filter->name); + VIR_FREE(filter->unicast.table); + VIR_FREE(filter->multicast.table); + VIR_FREE(filter->vlan.table);
I haven't peeked ahead yet, but are these tables where the allocation is allocate space for 10 table entries, allocate entries in each array entry... If so, then the free will need to run through the tables. Reason why I ask is I see size_t's for each structure indicating to me it's a array of entries.
+ VIR_FREE(filter); + } +} diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index 69e365e..307871c 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2013 Red Hat, Inc. + * Copyright (C) 2007-2014 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -37,6 +37,57 @@ typedef struct ifreq virIfreq; typedef void virIfreq; # endif
+typedef enum { + VIR_NETDEV_RX_FILTER_UNICAST_MODE_NONE = 0, + VIR_NETDEV_RX_FILTER_UNICAST_MODE_NORMAL, + + VIR_NETDEV_RX_FILTER_UNICAST_MODE_LAST +} virNetDevRxFilterUnicastMode; +VIR_ENUM_DECL(virNetDevRxFilterUnicastMode) + +typedef enum { + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_NONE = 0, + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_NORMAL, + + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_LAST +} virNetDevRxFilterMulticastMode; +VIR_ENUM_DECL(virNetDevRxFilterMulticastMode) + +typedef enum { + VIR_NETDEV_RX_FILTER_VLAN_MODE_NONE = 0, + VIR_NETDEV_RX_FILTER_VLAN_MODE_NORMAL, + + VIR_NETDEV_RX_FILTER_VLAN_MODE_LAST +} virNetDevRxFilterVlanMode; +VIR_ENUM_DECL(virNetDevRxFilterVlanMode)
Same here with respect to generic rather than specific
+ +typedef struct _virNetDevRxFilter virNetDevRxFilter; +typedef virNetDevRxFilter *virNetDevRxFilterPtr; +struct _virNetDevRxFilter { + char *name; /* the alias used by qemu, *not* name used by guest */ + virMacAddr mac; + bool promiscuous; + bool broadcastAllowed; + + struct { + int mode; /* enum virNetDevRxFilterUnicastMode */ + bool overflow; + virMacAddrPtr table; + size_t nTable; + } unicast; + struct { + int mode; /* enum virNetDevRxFilterMulticastMode */ + bool overflow; + virMacAddrPtr table; + size_t nTable; + } multicast; + struct { + int mode; /* enum virNetDevRxFilterVlanMode */ + unsigned int *table; + size_t nTable; + } vlan; +}; +
Each of the mode's would just use the generic FilterMode and the comments adjusted accordingly... soft ACK with changes depending on future patches which I haven't peeked at yet. John
int virNetDevSetupControl(const char *ifname, virIfreq *ifr) ATTRIBUTE_RETURN_CHECK; @@ -150,4 +201,8 @@ int virNetDevGetLinkInfo(const char *ifname, virInterfaceLinkPtr lnk) ATTRIBUTE_NONNULL(1);
+virNetDevRxFilterPtr virNetDevRxFilterNew(void) + ATTRIBUTE_RETURN_CHECK; +void virNetDevRxFilterFree(virNetDevRxFilterPtr filter); + #endif /* __VIR_NETDEV_H__ */

On 09/24/2014 07:45 PM, John Ferlan wrote:
On 09/24/2014 05:50 AM, Laine Stump wrote:
This same structure will be used to retrieve RX filter info for interfaces on the host via netlink messages, and RX filter info for interfaces on the guest via the qemu "query-rx-filter" command. --- src/libvirt_private.syms | 8 +++++++ src/util/virnetdev.c | 40 +++++++++++++++++++++++++++++++++ src/util/virnetdev.h | 57 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 1 deletion(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bb2b9a3..e5723d2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1595,6 +1595,14 @@ virNetDevReplaceMacAddress; virNetDevReplaceNetConfig; virNetDevRestoreMacAddress; virNetDevRestoreNetConfig; +virNetDevRxFilterFree; +virNetDevRxFilterMulticastModeTypeFromString; +virNetDevRxFilterMulticastModeTypeToString; +virNetDevRxFilterNew; +virNetDevRxFilterUnicastModeTypeFromString; +virNetDevRxFilterUnicastModeTypeToString; +virNetDevRxFilterVlanModeTypeFromString; +virNetDevRxFilterVlanModeTypeToString; virNetDevSetIPv4Address; virNetDevSetMAC; virNetDevSetMTU; diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 8815e18..dd1f530 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -1932,3 +1932,43 @@ virNetDevGetLinkInfo(const char *ifname, return 0; } #endif /* defined(__linux__) */ + + +VIR_ENUM_IMPL(virNetDevRxFilterUnicastMode, + VIR_NETDEV_RX_FILTER_UNICAST_MODE_LAST, + "none", + "normal"); + +VIR_ENUM_IMPL(virNetDevRxFilterMulticastMode, + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_LAST, + "none", + "normal"); + +VIR_ENUM_IMPL(virNetDevRxFilterVlanMode, + VIR_NETDEV_RX_FILTER_VLAN_MODE_LAST, + "none", + "normal"); Is there ever a chance these could be different for one of these types?
I looked back through my IRC discussions with Vlad (who worked on the qemu/kernel parts of this) and found that, while initially he thought they might have different values, that after looking it up in the qemu source he found that they have the same potential values. But I had already forgotten that by the time I got around to the implementation, and had put in three different enums simply because it's easier to combine them later than to split them out into separate enums when I find a difference. I've just now verified that all three have the same possible values according to qemu's qmp-commands.hx, so I will combine them into a single enum. BTW, I also see that in qemu, the associated enum is called RxState, but I think it is really more of a mode, and it's only related to the RX filters, so I'm still going to name it virNetDevRxFilterMode rather than the less descriptive virNetDevRxState.
Seems "excessive" to make 3 specific definitions when 1 generic one could suffice (drop the "Unicast, Multicast, Vlan")
+ + +virNetDevRxFilterPtr +virNetDevRxFilterNew(void) +{ + virNetDevRxFilterPtr filter; + + if (VIR_ALLOC(filter) < 0) + return NULL; + return filter; +} + + +void +virNetDevRxFilterFree(virNetDevRxFilterPtr filter) +{ + if (filter) { + VIR_FREE(filter->name); + VIR_FREE(filter->unicast.table); + VIR_FREE(filter->multicast.table); + VIR_FREE(filter->vlan.table); I haven't peeked ahead yet, but are these tables where the allocation is allocate space for 10 table entries, allocate entries in each array entry... If so, then the free will need to run through the tables.
They are tables, but each entry is a virMacAddr, *not* a virMacAddrPtr (if that was the case, the definition would say "virMacAddrPtr *table"), so there is no extra allocation.
Reason why I ask is I see size_t's for each structure indicating to me it's a array of entries.
+ VIR_FREE(filter); + } +} diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index 69e365e..307871c 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2013 Red Hat, Inc. + * Copyright (C) 2007-2014 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -37,6 +37,57 @@ typedef struct ifreq virIfreq; typedef void virIfreq; # endif
+typedef enum { + VIR_NETDEV_RX_FILTER_UNICAST_MODE_NONE = 0, + VIR_NETDEV_RX_FILTER_UNICAST_MODE_NORMAL, + + VIR_NETDEV_RX_FILTER_UNICAST_MODE_LAST +} virNetDevRxFilterUnicastMode; +VIR_ENUM_DECL(virNetDevRxFilterUnicastMode) + +typedef enum { + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_NONE = 0, + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_NORMAL, + + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_LAST +} virNetDevRxFilterMulticastMode; +VIR_ENUM_DECL(virNetDevRxFilterMulticastMode) + +typedef enum { + VIR_NETDEV_RX_FILTER_VLAN_MODE_NONE = 0, + VIR_NETDEV_RX_FILTER_VLAN_MODE_NORMAL, + + VIR_NETDEV_RX_FILTER_VLAN_MODE_LAST +} virNetDevRxFilterVlanMode; +VIR_ENUM_DECL(virNetDevRxFilterVlanMode) Same here with respect to generic rather than specific
+ +typedef struct _virNetDevRxFilter virNetDevRxFilter; +typedef virNetDevRxFilter *virNetDevRxFilterPtr; +struct _virNetDevRxFilter { + char *name; /* the alias used by qemu, *not* name used by guest */ + virMacAddr mac; + bool promiscuous; + bool broadcastAllowed; + + struct { + int mode; /* enum virNetDevRxFilterUnicastMode */ + bool overflow; + virMacAddrPtr table; + size_t nTable; + } unicast; + struct { + int mode; /* enum virNetDevRxFilterMulticastMode */ + bool overflow; + virMacAddrPtr table; + size_t nTable; + } multicast; + struct { + int mode; /* enum virNetDevRxFilterVlanMode */ + unsigned int *table; + size_t nTable; + } vlan; +}; + Each of the mode's would just use the generic FilterMode and the comments adjusted accordingly...
soft ACK with changes depending on future patches which I haven't peeked at yet.
John
int virNetDevSetupControl(const char *ifname, virIfreq *ifr) ATTRIBUTE_RETURN_CHECK; @@ -150,4 +201,8 @@ int virNetDevGetLinkInfo(const char *ifname, virInterfaceLinkPtr lnk) ATTRIBUTE_NONNULL(1);
+virNetDevRxFilterPtr virNetDevRxFilterNew(void) + ATTRIBUTE_RETURN_CHECK; +void virNetDevRxFilterFree(virNetDevRxFilterPtr filter); + #endif /* __VIR_NETDEV_H__ */

On Wed, Sep 24, 2014 at 05:50:53AM -0400, Laine Stump wrote:
This same structure will be used to retrieve RX filter info for interfaces on the host via netlink messages, and RX filter info for interfaces on the guest via the qemu "query-rx-filter" command. --- src/libvirt_private.syms | 8 +++++++ src/util/virnetdev.c | 40 +++++++++++++++++++++++++++++++++ src/util/virnetdev.h | 57 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 1 deletion(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bb2b9a3..e5723d2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1595,6 +1595,14 @@ virNetDevReplaceMacAddress; virNetDevReplaceNetConfig; virNetDevRestoreMacAddress; virNetDevRestoreNetConfig; +virNetDevRxFilterFree;
+virNetDevRxFilterMulticastModeTypeFromString; +virNetDevRxFilterMulticastModeTypeToString; +virNetDevRxFilterNew; +virNetDevRxFilterUnicastModeTypeFromString; +virNetDevRxFilterUnicastModeTypeToString; +virNetDevRxFilterVlanModeTypeFromString; +virNetDevRxFilterVlanModeTypeToString;
The RxFilter ModeTypes of unicast, multicast, vlan are same 'normal', 'none', 'all' Can we just use some general functions to process the string? virNetDevRxFilterModeTypeFromString; virNetDevRxFilterModeTypeToString;
virNetDevSetIPv4Address; virNetDevSetMAC; virNetDevSetMTU; diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 8815e18..dd1f530 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -1932,3 +1932,43 @@ virNetDevGetLinkInfo(const char *ifname, return 0; } #endif /* defined(__linux__) */ + + +VIR_ENUM_IMPL(virNetDevRxFilterUnicastMode, + VIR_NETDEV_RX_FILTER_UNICAST_MODE_LAST, + "none", + "normal"); + +VIR_ENUM_IMPL(virNetDevRxFilterMulticastMode, + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_LAST, + "none", + "normal"); + +VIR_ENUM_IMPL(virNetDevRxFilterVlanMode, + VIR_NETDEV_RX_FILTER_VLAN_MODE_LAST, + "none", + "normal"); + + +virNetDevRxFilterPtr +virNetDevRxFilterNew(void) +{ + virNetDevRxFilterPtr filter; + + if (VIR_ALLOC(filter) < 0) + return NULL; + return filter; +} + + +void +virNetDevRxFilterFree(virNetDevRxFilterPtr filter) +{ + if (filter) { + VIR_FREE(filter->name); + VIR_FREE(filter->unicast.table); + VIR_FREE(filter->multicast.table); + VIR_FREE(filter->vlan.table); + VIR_FREE(filter); + } +} diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index 69e365e..307871c 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2013 Red Hat, Inc. + * Copyright (C) 2007-2014 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -37,6 +37,57 @@ typedef struct ifreq virIfreq; typedef void virIfreq; # endif
+typedef enum { + VIR_NETDEV_RX_FILTER_UNICAST_MODE_NONE = 0, + VIR_NETDEV_RX_FILTER_UNICAST_MODE_NORMAL, + + VIR_NETDEV_RX_FILTER_UNICAST_MODE_LAST +} virNetDevRxFilterUnicastMode; +VIR_ENUM_DECL(virNetDevRxFilterUnicastMode) + +typedef enum { + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_NONE = 0, + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_NORMAL, + + VIR_NETDEV_RX_FILTER_MULTICAST_MODE_LAST +} virNetDevRxFilterMulticastMode; +VIR_ENUM_DECL(virNetDevRxFilterMulticastMode) + +typedef enum { + VIR_NETDEV_RX_FILTER_VLAN_MODE_NONE = 0, + VIR_NETDEV_RX_FILTER_VLAN_MODE_NORMAL, + + VIR_NETDEV_RX_FILTER_VLAN_MODE_LAST +} virNetDevRxFilterVlanMode; +VIR_ENUM_DECL(virNetDevRxFilterVlanMode) + +typedef struct _virNetDevRxFilter virNetDevRxFilter; +typedef virNetDevRxFilter *virNetDevRxFilterPtr; +struct _virNetDevRxFilter { + char *name; /* the alias used by qemu, *not* name used by guest */ + virMacAddr mac; + bool promiscuous; + bool broadcastAllowed; + + struct { + int mode; /* enum virNetDevRxFilterUnicastMode */ + bool overflow; + virMacAddrPtr table; + size_t nTable; + } unicast; + struct { + int mode; /* enum virNetDevRxFilterMulticastMode */ + bool overflow; + virMacAddrPtr table; + size_t nTable; + } multicast; + struct { + int mode; /* enum virNetDevRxFilterVlanMode */ + unsigned int *table; + size_t nTable; + } vlan; +}; + int virNetDevSetupControl(const char *ifname, virIfreq *ifr) ATTRIBUTE_RETURN_CHECK; @@ -150,4 +201,8 @@ int virNetDevGetLinkInfo(const char *ifname, virInterfaceLinkPtr lnk) ATTRIBUTE_NONNULL(1);
+virNetDevRxFilterPtr virNetDevRxFilterNew(void) + ATTRIBUTE_RETURN_CHECK; +void virNetDevRxFilterFree(virNetDevRxFilterPtr filter); + #endif /* __VIR_NETDEV_H__ */ -- 1.9.3
-- Amos.

On 09/27/2014 12:40 AM, Amos Kong wrote:
On Wed, Sep 24, 2014 at 05:50:53AM -0400, Laine Stump wrote:
This same structure will be used to retrieve RX filter info for interfaces on the host via netlink messages, and RX filter info for interfaces on the guest via the qemu "query-rx-filter" command. --- src/libvirt_private.syms | 8 +++++++ src/util/virnetdev.c | 40 +++++++++++++++++++++++++++++++++ src/util/virnetdev.h | 57 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 1 deletion(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bb2b9a3..e5723d2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1595,6 +1595,14 @@ virNetDevReplaceMacAddress; virNetDevReplaceNetConfig; virNetDevRestoreMacAddress; virNetDevRestoreNetConfig; +virNetDevRxFilterFree; +virNetDevRxFilterMulticastModeTypeFromString; +virNetDevRxFilterMulticastModeTypeToString; +virNetDevRxFilterNew; +virNetDevRxFilterUnicastModeTypeFromString; +virNetDevRxFilterUnicastModeTypeToString; +virNetDevRxFilterVlanModeTypeFromString; +virNetDevRxFilterVlanModeTypeToString; The RxFilter ModeTypes of unicast, multicast, vlan are same
'normal', 'none', 'all'
Can we just use some general functions to process the string?
virNetDevRxFilterModeTypeFromString; virNetDevRxFilterModeTypeToString;
Yep, I've already made that change for V2. I had madde separe enums in the beginning because it wasn't yet clear to me that they would have identical possible values.

This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The commend sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).) --- src/qemu/qemu_monitor.c | 26 ++++++ src/qemu/qemu_monitor.h | 4 + src/qemu/qemu_monitor_json.c | 215 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + tests/Makefile.am | 3 + 5 files changed, 251 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index c25f002..48cbe3e 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2918,6 +2918,32 @@ int qemuMonitorRemoveNetdev(qemuMonitorPtr mon, } +int +qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + VIR_DEBUG("mon=%p alias=%s filter=%p", + mon, alias, filter); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + + VIR_DEBUG("mon=%p, alias=%s", mon, alias); + + if (mon->json) + ret = qemuMonitorJSONQueryRxFilter(mon, alias, filter); + else + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("query-rx-filter requires JSON monitor")); + return ret; +} + + int qemuMonitorGetPtyPaths(qemuMonitorPtr mon, virHashTablePtr paths) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 6b91e29..c37e36f 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -31,6 +31,7 @@ # include "virbitmap.h" # include "virhash.h" # include "virjson.h" +# include "virnetdev.h" # include "device_conf.h" # include "cpu/cpu.h" @@ -622,6 +623,9 @@ int qemuMonitorAddNetdev(qemuMonitorPtr mon, int qemuMonitorRemoveNetdev(qemuMonitorPtr mon, const char *alias); +int qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter); + int qemuMonitorGetPtyPaths(qemuMonitorPtr mon, virHashTablePtr paths); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index a3d7c2c..58007e6 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3194,6 +3194,221 @@ int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon, } +static int +qemuMonitorJSONQueryRxFilterParse(virJSONValuePtr msg, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + const char *tmp; + virJSONValuePtr returnArray, entry, table, element; + int nTable; + size_t i; + virNetDevRxFilterPtr fil = virNetDevRxFilterNew(); + + if (!(returnArray = virJSONValueObjectGet(msg, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-rx-filter reply was missing return data")); + goto cleanup; + } + if (returnArray->type != VIR_JSON_TYPE_ARRAY) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-rx-filter return data was not an array")); + goto cleanup; + } + if (!(entry = virJSONValueArrayGet(returnArray, 0))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query -rx-filter returne data missing array element")); + goto cleanup; + } + + if (!fil) + goto cleanup; + + if (!(tmp = virJSONValueObjectGetString(entry, "name"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid name " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_STRDUP(fil->name, tmp) < 0) + goto cleanup; + if ((!(tmp = virJSONValueObjectGetString(entry, "main-mac"))) || + virMacAddrParse(tmp, &fil->mac) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'main-mac' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "promiscuous", + &fil->promiscuous) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'promiscuous' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "broadcast-allowed", + &fil->broadcastAllowed) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'broadcast-allowed' " + "in query-rx-filter response")); + goto cleanup; + } + + if ((!(tmp = virJSONValueObjectGetString(entry, "unicast"))) || + ((fil->unicast.mode + = virNetDevRxFilterUnicastModeTypeFromString(tmp)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'unicast' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "unicast-overflow", + &fil->unicast.overflow) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'unicast-overflow' " + "in query-rx-filter response")); + goto cleanup; + } + if ((!(table = virJSONValueObjectGet(entry, "unicast-table"))) || + ((nTable = virJSONValueArraySize(table)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'unicast-table' array " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_ALLOC_N(fil->unicast.table, nTable)) + goto cleanup; + for (i = 0; i < nTable; i++) { + if (!(element = virJSONValueArrayGet(table, i)) || + !(tmp = virJSONValueGetString(element))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing or invalid element %zu of 'unicast' " + "list in query-rx-filter response"), i); + goto cleanup; + } + if (virMacAddrParse(tmp, &fil->unicast.table[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid mac address '%s' in 'unicast-table' " + "array in query-rx-filter response"), tmp); + goto cleanup; + } + } + fil->unicast.nTable = nTable; + + if ((!(tmp = virJSONValueObjectGetString(entry, "multicast"))) || + ((fil->multicast.mode + = virNetDevRxFilterMulticastModeTypeFromString(tmp)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'multicast' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "multicast-overflow", + &fil->multicast.overflow) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'multicast-overflow' " + "in query-rx-filter response")); + goto cleanup; + } + if ((!(table = virJSONValueObjectGet(entry, "multicast-table"))) || + ((nTable = virJSONValueArraySize(table)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'multicast-table' array " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_ALLOC_N(fil->multicast.table, nTable)) + goto cleanup; + for (i = 0; i < nTable; i++) { + if (!(element = virJSONValueArrayGet(table, i)) || + !(tmp = virJSONValueGetString(element))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing or invalid element %zu of 'multicast' " + "list in query-rx-filter response"), i); + goto cleanup; + } + if (virMacAddrParse(tmp, &fil->multicast.table[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid mac address '%s' in 'multicast-table' " + "array in query-rx-filter response"), tmp); + goto cleanup; + } + } + fil->multicast.nTable = nTable; + + if ((!(tmp = virJSONValueObjectGetString(entry, "vlan"))) || + ((fil->vlan.mode + = virNetDevRxFilterVlanModeTypeFromString(tmp)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'vlan' " + "in query-rx-filter response")); + goto cleanup; + } + if ((!(table = virJSONValueObjectGet(entry, "vlan-table"))) || + ((nTable = virJSONValueArraySize(table)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'vlan-table' array " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_ALLOC_N(fil->vlan.table, nTable)) + goto cleanup; + for (i = 0; i < nTable; i++) { + if (!(element = virJSONValueArrayGet(table, i)) || + virJSONValueGetNumberUint(element, &fil->vlan.table[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing or invalid element %zu of 'vlan-table' " + "array in query-rx-filter response"), i); + goto cleanup; + } + } + fil->vlan.nTable = nTable; + + ret = 0; + cleanup: + if (ret < 0) { + virNetDevRxFilterFree(fil); + fil = NULL; + } + *filter = fil; + return ret; +} + + +int +qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-rx-filter", + "s:name", alias, + NULL); + virJSONValuePtr reply = NULL; + + if (!cmd) + goto cleanup; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONQueryRxFilterParse(reply, filter) < 0) + goto cleanup; + + ret = 0; + cleanup: + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + if (ret < 0) { + virNetDevRxFilterFree(*filter); + *filter = NULL; + } + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + /* * Example return data * diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index d366179..a41281b 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -210,6 +210,9 @@ int qemuMonitorJSONAddNetdev(qemuMonitorPtr mon, int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon, const char *alias); +int qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter); + int qemuMonitorJSONGetPtyPaths(qemuMonitorPtr mon, virHashTablePtr paths); diff --git a/tests/Makefile.am b/tests/Makefile.am index d6c3cfb..bd4371b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -35,6 +35,7 @@ AM_CFLAGS = \ -Dabs_builddir="\"$(abs_builddir)\"" \ -Dabs_srcdir="\"$(abs_srcdir)\"" \ $(LIBXML_CFLAGS) \ + $(LIBNL_CFLAGS) \ $(GNUTLS_CFLAGS) \ $(SASL_CFLAGS) \ $(SELINUX_CFLAGS) \ @@ -43,6 +44,8 @@ AM_CFLAGS = \ $(COVERAGE_CFLAGS) \ $(WARN_CFLAGS) + + AM_LDFLAGS = \ -export-dynamic -- 1.9.3

On 09/24/2014 05:50 AM, Laine Stump wrote:
This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The commend sent to the qemu monitor looks like this:
{"execute":"query-rx-filter", "arguments": {"name":"net2"} }'
and the results will look something like this:
{ "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [
], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" }
This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table).
(NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).) --- src/qemu/qemu_monitor.c | 26 ++++++ src/qemu/qemu_monitor.h | 4 + src/qemu/qemu_monitor_json.c | 215 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + tests/Makefile.am | 3 + 5 files changed, 251 insertions(+)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index c25f002..48cbe3e 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2918,6 +2918,32 @@ int qemuMonitorRemoveNetdev(qemuMonitorPtr mon, }
+int +qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + VIR_DEBUG("mon=%p alias=%s filter=%p", + mon, alias, filter); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + }
The "if (!mon->json)" usually goes here rather than later as an else. Just trying to be consistent with other functions I'm assuming at some point whomever calls this will check if the query-rx-filter command exits (eg, the qemu capability exists)... Again I haven't peeked ahead.
+ + + VIR_DEBUG("mon=%p, alias=%s", mon, alias); + + if (mon->json) + ret = qemuMonitorJSONQueryRxFilter(mon, alias, filter); + else + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("query-rx-filter requires JSON monitor")); + return ret; +} + + int qemuMonitorGetPtyPaths(qemuMonitorPtr mon, virHashTablePtr paths) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 6b91e29..c37e36f 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -31,6 +31,7 @@ # include "virbitmap.h" # include "virhash.h" # include "virjson.h" +# include "virnetdev.h" # include "device_conf.h" # include "cpu/cpu.h"
@@ -622,6 +623,9 @@ int qemuMonitorAddNetdev(qemuMonitorPtr mon, int qemuMonitorRemoveNetdev(qemuMonitorPtr mon, const char *alias);
+int qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter); + int qemuMonitorGetPtyPaths(qemuMonitorPtr mon, virHashTablePtr paths);
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index a3d7c2c..58007e6 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3194,6 +3194,221 @@ int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon, }
+static int +qemuMonitorJSONQueryRxFilterParse(virJSONValuePtr msg, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + const char *tmp; + virJSONValuePtr returnArray, entry, table, element; + int nTable; + size_t i; + virNetDevRxFilterPtr fil = virNetDevRxFilterNew(); + + if (!(returnArray = virJSONValueObjectGet(msg, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-rx-filter reply was missing return data")); + goto cleanup; + } + if (returnArray->type != VIR_JSON_TYPE_ARRAY) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-rx-filter return data was not an array")); + goto cleanup; + } + if (!(entry = virJSONValueArrayGet(returnArray, 0))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query -rx-filter returne data missing array element")); + goto cleanup; + } + + if (!fil) + goto cleanup; + + if (!(tmp = virJSONValueObjectGetString(entry, "name"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid name " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_STRDUP(fil->name, tmp) < 0) + goto cleanup; + if ((!(tmp = virJSONValueObjectGetString(entry, "main-mac"))) || + virMacAddrParse(tmp, &fil->mac) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'main-mac' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "promiscuous", + &fil->promiscuous) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'promiscuous' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "broadcast-allowed", + &fil->broadcastAllowed) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'broadcast-allowed' " + "in query-rx-filter response")); + goto cleanup; + } + + if ((!(tmp = virJSONValueObjectGetString(entry, "unicast"))) || + ((fil->unicast.mode + = virNetDevRxFilterUnicastModeTypeFromString(tmp)) < 0)) {
Unless different values for each could be returned - I think the common function would work - requires change here...
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'unicast' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "unicast-overflow", + &fil->unicast.overflow) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'unicast-overflow' " + "in query-rx-filter response")); + goto cleanup; + } + if ((!(table = virJSONValueObjectGet(entry, "unicast-table"))) || + ((nTable = virJSONValueArraySize(table)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'unicast-table' array " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_ALLOC_N(fil->unicast.table, nTable)) + goto cleanup; + for (i = 0; i < nTable; i++) { + if (!(element = virJSONValueArrayGet(table, i)) || + !(tmp = virJSONValueGetString(element))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing or invalid element %zu of 'unicast' " + "list in query-rx-filter response"), i); + goto cleanup; + } + if (virMacAddrParse(tmp, &fil->unicast.table[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid mac address '%s' in 'unicast-table' " + "array in query-rx-filter response"), tmp); + goto cleanup; + } + } + fil->unicast.nTable = nTable;
hmm.. so this is what relates to patch 3's query... Looks like a [n] entry table of virMacAddr where each entry just gets copied in place - so the single VIR_FREE should be fine. No issue - just thinking outloud to help me make a better ACK for patch3
+ + if ((!(tmp = virJSONValueObjectGetString(entry, "multicast"))) || + ((fil->multicast.mode + = virNetDevRxFilterMulticastModeTypeFromString(tmp)) < 0)) {
Unless different values for each could be returned - I think the common function would work - requires change here...
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'multicast' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "multicast-overflow", + &fil->multicast.overflow) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'multicast-overflow' " + "in query-rx-filter response")); + goto cleanup; + } + if ((!(table = virJSONValueObjectGet(entry, "multicast-table"))) || + ((nTable = virJSONValueArraySize(table)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'multicast-table' array " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_ALLOC_N(fil->multicast.table, nTable)) + goto cleanup; + for (i = 0; i < nTable; i++) { + if (!(element = virJSONValueArrayGet(table, i)) || + !(tmp = virJSONValueGetString(element))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing or invalid element %zu of 'multicast' " + "list in query-rx-filter response"), i); + goto cleanup; + } + if (virMacAddrParse(tmp, &fil->multicast.table[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid mac address '%s' in 'multicast-table' " + "array in query-rx-filter response"), tmp); + goto cleanup; + } + } + fil->multicast.nTable = nTable; + + if ((!(tmp = virJSONValueObjectGetString(entry, "vlan"))) || + ((fil->vlan.mode + = virNetDevRxFilterVlanModeTypeFromString(tmp)) < 0)) {
Unless different values for each could be returned - I think the common function would work - requires change here...
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'vlan' " + "in query-rx-filter response")); + goto cleanup; + } + if ((!(table = virJSONValueObjectGet(entry, "vlan-table"))) || + ((nTable = virJSONValueArraySize(table)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'vlan-table' array " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_ALLOC_N(fil->vlan.table, nTable)) + goto cleanup; + for (i = 0; i < nTable; i++) { + if (!(element = virJSONValueArrayGet(table, i)) || + virJSONValueGetNumberUint(element, &fil->vlan.table[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing or invalid element %zu of 'vlan-table' " + "array in query-rx-filter response"), i); + goto cleanup; + } + } + fil->vlan.nTable = nTable; + + ret = 0; + cleanup: + if (ret < 0) { + virNetDevRxFilterFree(fil); + fil = NULL; + } + *filter = fil; + return ret; +} + + +int +qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-rx-filter", + "s:name", alias, + NULL); + virJSONValuePtr reply = NULL; + + if (!cmd) + goto cleanup; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONQueryRxFilterParse(reply, filter) < 0) + goto cleanup; + + ret = 0; + cleanup: + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + if (ret < 0) { + virNetDevRxFilterFree(*filter); + *filter = NULL; + } + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + /* * Example return data * diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index d366179..a41281b 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -210,6 +210,9 @@ int qemuMonitorJSONAddNetdev(qemuMonitorPtr mon, int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon, const char *alias);
+int qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter); + int qemuMonitorJSONGetPtyPaths(qemuMonitorPtr mon, virHashTablePtr paths);
diff --git a/tests/Makefile.am b/tests/Makefile.am index d6c3cfb..bd4371b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -35,6 +35,7 @@ AM_CFLAGS = \ -Dabs_builddir="\"$(abs_builddir)\"" \ -Dabs_srcdir="\"$(abs_srcdir)\"" \ $(LIBXML_CFLAGS) \ + $(LIBNL_CFLAGS) \ $(GNUTLS_CFLAGS) \ $(SASL_CFLAGS) \ $(SELINUX_CFLAGS) \ @@ -43,6 +44,8 @@ AM_CFLAGS = \ $(COVERAGE_CFLAGS) \ $(WARN_CFLAGS)
+ +
Not sure why there's extra lines here? Should be removed
AM_LDFLAGS = \ -export-dynamic
Seeing libnl copied into the Makefile just sends up the warning flag in general about libnl ACK (maybe Eric has a different thought about the Makefile) John

On 09/24/2014 08:11 PM, John Ferlan wrote:
On 09/24/2014 05:50 AM, Laine Stump wrote:
This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The commend sent to the qemu monitor looks like this:
{"execute":"query-rx-filter", "arguments": {"name":"net2"} }'
and the results will look something like this:
{ "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [
], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" }
This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table).
(NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).) --- src/qemu/qemu_monitor.c | 26 ++++++ src/qemu/qemu_monitor.h | 4 + src/qemu/qemu_monitor_json.c | 215 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + tests/Makefile.am | 3 + 5 files changed, 251 insertions(+)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index c25f002..48cbe3e 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2918,6 +2918,32 @@ int qemuMonitorRemoveNetdev(qemuMonitorPtr mon, }
+int +qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + VIR_DEBUG("mon=%p alias=%s filter=%p", + mon, alias, filter); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } The "if (!mon->json)" usually goes here rather than later as an else. Just trying to be consistent with other functions
I'm not seeing that - every other function I checked is patterned just like this one (which is a direct copy of qemuMonitorRemoveNetdev()).
I'm assuming at some point whomever calls this will check if the query-rx-filter command exits (eg, the qemu capability exists)...
At one point I had added a capability bit for it, but since the command is only issued in response to a NIC_RX_FILTER_CHANGED event, and the two were implemented in qemu at the same time, we would never attempt to call this function unless it was actually available in qemu, and I didn't want to clutter up the capabilities with an extra item unless it was really necessary.
Again I haven't peeked ahead.
+ + + VIR_DEBUG("mon=%p, alias=%s", mon, alias); + + if (mon->json) + ret = qemuMonitorJSONQueryRxFilter(mon, alias, filter); + else + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("query-rx-filter requires JSON monitor")); + return ret; +} + + int qemuMonitorGetPtyPaths(qemuMonitorPtr mon, virHashTablePtr paths) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 6b91e29..c37e36f 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -31,6 +31,7 @@ # include "virbitmap.h" # include "virhash.h" # include "virjson.h" +# include "virnetdev.h" # include "device_conf.h" # include "cpu/cpu.h"
@@ -622,6 +623,9 @@ int qemuMonitorAddNetdev(qemuMonitorPtr mon, int qemuMonitorRemoveNetdev(qemuMonitorPtr mon, const char *alias);
+int qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter); + int qemuMonitorGetPtyPaths(qemuMonitorPtr mon, virHashTablePtr paths);
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index a3d7c2c..58007e6 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3194,6 +3194,221 @@ int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon, }
+static int +qemuMonitorJSONQueryRxFilterParse(virJSONValuePtr msg, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + const char *tmp; + virJSONValuePtr returnArray, entry, table, element; + int nTable; + size_t i; + virNetDevRxFilterPtr fil = virNetDevRxFilterNew(); + + if (!(returnArray = virJSONValueObjectGet(msg, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-rx-filter reply was missing return data")); + goto cleanup; + } + if (returnArray->type != VIR_JSON_TYPE_ARRAY) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-rx-filter return data was not an array")); + goto cleanup; + } + if (!(entry = virJSONValueArrayGet(returnArray, 0))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query -rx-filter returne data missing array element")); + goto cleanup; + } + + if (!fil) + goto cleanup; + + if (!(tmp = virJSONValueObjectGetString(entry, "name"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid name " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_STRDUP(fil->name, tmp) < 0) + goto cleanup; + if ((!(tmp = virJSONValueObjectGetString(entry, "main-mac"))) || + virMacAddrParse(tmp, &fil->mac) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'main-mac' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "promiscuous", + &fil->promiscuous) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'promiscuous' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "broadcast-allowed", + &fil->broadcastAllowed) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'broadcast-allowed' " + "in query-rx-filter response")); + goto cleanup; + } + + if ((!(tmp = virJSONValueObjectGetString(entry, "unicast"))) || + ((fil->unicast.mode + = virNetDevRxFilterUnicastModeTypeFromString(tmp)) < 0)) { Unless different values for each could be returned - I think the common function would work - requires change here...
Yep. Changed it as discussed in 3/6.
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'unicast' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "unicast-overflow", + &fil->unicast.overflow) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'unicast-overflow' " + "in query-rx-filter response")); + goto cleanup; + } + if ((!(table = virJSONValueObjectGet(entry, "unicast-table"))) || + ((nTable = virJSONValueArraySize(table)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'unicast-table' array " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_ALLOC_N(fil->unicast.table, nTable)) + goto cleanup; + for (i = 0; i < nTable; i++) { + if (!(element = virJSONValueArrayGet(table, i)) || + !(tmp = virJSONValueGetString(element))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing or invalid element %zu of 'unicast' " + "list in query-rx-filter response"), i); + goto cleanup; + } + if (virMacAddrParse(tmp, &fil->unicast.table[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid mac address '%s' in 'unicast-table' " + "array in query-rx-filter response"), tmp); + goto cleanup; + } + } + fil->unicast.nTable = nTable; hmm.. so this is what relates to patch 3's query... Looks like a [n] entry table of virMacAddr where each entry just gets copied in place - so the single VIR_FREE should be fine.
No issue - just thinking outloud to help me make a better ACK for patch3
+ + if ((!(tmp = virJSONValueObjectGetString(entry, "multicast"))) || + ((fil->multicast.mode + = virNetDevRxFilterMulticastModeTypeFromString(tmp)) < 0)) { Unless different values for each could be returned - I think the common function would work - requires change here...
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'multicast' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "multicast-overflow", + &fil->multicast.overflow) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'multicast-overflow' " + "in query-rx-filter response")); + goto cleanup; + } + if ((!(table = virJSONValueObjectGet(entry, "multicast-table"))) || + ((nTable = virJSONValueArraySize(table)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'multicast-table' array " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_ALLOC_N(fil->multicast.table, nTable)) + goto cleanup; + for (i = 0; i < nTable; i++) { + if (!(element = virJSONValueArrayGet(table, i)) || + !(tmp = virJSONValueGetString(element))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing or invalid element %zu of 'multicast' " + "list in query-rx-filter response"), i); + goto cleanup; + } + if (virMacAddrParse(tmp, &fil->multicast.table[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid mac address '%s' in 'multicast-table' " + "array in query-rx-filter response"), tmp); + goto cleanup; + } + } + fil->multicast.nTable = nTable; + + if ((!(tmp = virJSONValueObjectGetString(entry, "vlan"))) || + ((fil->vlan.mode + = virNetDevRxFilterVlanModeTypeFromString(tmp)) < 0)) { Unless different values for each could be returned - I think the common function would work - requires change here...
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'vlan' " + "in query-rx-filter response")); + goto cleanup; + } + if ((!(table = virJSONValueObjectGet(entry, "vlan-table"))) || + ((nTable = virJSONValueArraySize(table)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'vlan-table' array " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_ALLOC_N(fil->vlan.table, nTable)) + goto cleanup; + for (i = 0; i < nTable; i++) { + if (!(element = virJSONValueArrayGet(table, i)) || + virJSONValueGetNumberUint(element, &fil->vlan.table[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing or invalid element %zu of 'vlan-table' " + "array in query-rx-filter response"), i); + goto cleanup; + } + } + fil->vlan.nTable = nTable; + + ret = 0; + cleanup: + if (ret < 0) { + virNetDevRxFilterFree(fil); + fil = NULL; + } + *filter = fil; + return ret; +} + + +int +qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-rx-filter", + "s:name", alias, + NULL); + virJSONValuePtr reply = NULL; + + if (!cmd) + goto cleanup; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONQueryRxFilterParse(reply, filter) < 0) + goto cleanup; + + ret = 0; + cleanup: + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + if (ret < 0) { + virNetDevRxFilterFree(*filter); + *filter = NULL; + } + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + /* * Example return data * diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index d366179..a41281b 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -210,6 +210,9 @@ int qemuMonitorJSONAddNetdev(qemuMonitorPtr mon, int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon, const char *alias);
+int qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter); + int qemuMonitorJSONGetPtyPaths(qemuMonitorPtr mon, virHashTablePtr paths);
diff --git a/tests/Makefile.am b/tests/Makefile.am index d6c3cfb..bd4371b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -35,6 +35,7 @@ AM_CFLAGS = \ -Dabs_builddir="\"$(abs_builddir)\"" \ -Dabs_srcdir="\"$(abs_srcdir)\"" \ $(LIBXML_CFLAGS) \ + $(LIBNL_CFLAGS) \ $(GNUTLS_CFLAGS) \ $(SASL_CFLAGS) \ $(SELINUX_CFLAGS) \ @@ -43,6 +44,8 @@ AM_CFLAGS = \ $(COVERAGE_CFLAGS) \ $(WARN_CFLAGS)
+ + Not sure why there's extra lines here? Should be removed
Fell asleep with my thumb on the space bar??
AM_LDFLAGS = \ -export-dynamic
Seeing libnl copied into the Makefile just sends up the warning flag in general about libnl
I was initially concerned as well, but keep in mind that it only affects whether an include directory is added to the compile line, and that doesn't happen for platforms without libnl.
ACK (maybe Eric has a different thought about the Makefile)
So far I just removed the extra lines in the Makefile and changed the names of the enum conversion functions.

On Wed, Sep 24, 2014 at 05:50:54AM -0400, Laine Stump wrote:
This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The commend sent to the qemu monitor looks like this:
%s/commend/command/
{"execute":"query-rx-filter", "arguments": {"name":"net2"} }'
and the results will look something like this:
{ "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [
], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" }
This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table).
(NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).) --- src/qemu/qemu_monitor.c | 26 ++++++ src/qemu/qemu_monitor.h | 4 + src/qemu/qemu_monitor_json.c | 215 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + tests/Makefile.am | 3 + 5 files changed, 251 insertions(+)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index c25f002..48cbe3e 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2918,6 +2918,32 @@ int qemuMonitorRemoveNetdev(qemuMonitorPtr mon, }
+int +qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + VIR_DEBUG("mon=%p alias=%s filter=%p", + mon, alias, filter); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + + VIR_DEBUG("mon=%p, alias=%s", mon, alias); + + if (mon->json) + ret = qemuMonitorJSONQueryRxFilter(mon, alias, filter); + else + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("query-rx-filter requires JSON monitor")); + return ret; +} + + int qemuMonitorGetPtyPaths(qemuMonitorPtr mon, virHashTablePtr paths) { diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 6b91e29..c37e36f 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -31,6 +31,7 @@ # include "virbitmap.h" # include "virhash.h" # include "virjson.h" +# include "virnetdev.h" # include "device_conf.h" # include "cpu/cpu.h"
@@ -622,6 +623,9 @@ int qemuMonitorAddNetdev(qemuMonitorPtr mon, int qemuMonitorRemoveNetdev(qemuMonitorPtr mon, const char *alias);
+int qemuMonitorQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter); + int qemuMonitorGetPtyPaths(qemuMonitorPtr mon, virHashTablePtr paths);
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index a3d7c2c..58007e6 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3194,6 +3194,221 @@ int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon, }
+static int +qemuMonitorJSONQueryRxFilterParse(virJSONValuePtr msg, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + const char *tmp; + virJSONValuePtr returnArray, entry, table, element; + int nTable; + size_t i; + virNetDevRxFilterPtr fil = virNetDevRxFilterNew(); + + if (!(returnArray = virJSONValueObjectGet(msg, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-rx-filter reply was missing return data")); + goto cleanup; + } + if (returnArray->type != VIR_JSON_TYPE_ARRAY) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-rx-filter return data was not an array")); + goto cleanup; + } + if (!(entry = virJSONValueArrayGet(returnArray, 0))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query -rx-filter returne data missing array element")); + goto cleanup; + } + + if (!fil) + goto cleanup;
How about checking fil after virNetDevRxFilterNew()?
+ + if (!(tmp = virJSONValueObjectGetString(entry, "name"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid name " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_STRDUP(fil->name, tmp) < 0) + goto cleanup; + if ((!(tmp = virJSONValueObjectGetString(entry, "main-mac"))) || + virMacAddrParse(tmp, &fil->mac) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'main-mac' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "promiscuous", + &fil->promiscuous) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'promiscuous' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "broadcast-allowed", + &fil->broadcastAllowed) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'broadcast-allowed' " + "in query-rx-filter response")); + goto cleanup; + } + + if ((!(tmp = virJSONValueObjectGetString(entry, "unicast"))) || + ((fil->unicast.mode + = virNetDevRxFilterUnicastModeTypeFromString(tmp)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'unicast' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "unicast-overflow", + &fil->unicast.overflow) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'unicast-overflow' " + "in query-rx-filter response")); + goto cleanup; + } + if ((!(table = virJSONValueObjectGet(entry, "unicast-table"))) || + ((nTable = virJSONValueArraySize(table)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'unicast-table' array " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_ALLOC_N(fil->unicast.table, nTable)) + goto cleanup; + for (i = 0; i < nTable; i++) { + if (!(element = virJSONValueArrayGet(table, i)) || + !(tmp = virJSONValueGetString(element))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing or invalid element %zu of 'unicast' " + "list in query-rx-filter response"), i); + goto cleanup; + } + if (virMacAddrParse(tmp, &fil->unicast.table[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid mac address '%s' in 'unicast-table' " + "array in query-rx-filter response"), tmp); + goto cleanup; + } + } + fil->unicast.nTable = nTable; + + if ((!(tmp = virJSONValueObjectGetString(entry, "multicast"))) || + ((fil->multicast.mode + = virNetDevRxFilterMulticastModeTypeFromString(tmp)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'multicast' " + "in query-rx-filter response")); + goto cleanup; + } + if (virJSONValueObjectGetBoolean(entry, "multicast-overflow", + &fil->multicast.overflow) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'multicast-overflow' " + "in query-rx-filter response")); + goto cleanup; + } + if ((!(table = virJSONValueObjectGet(entry, "multicast-table"))) || + ((nTable = virJSONValueArraySize(table)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'multicast-table' array " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_ALLOC_N(fil->multicast.table, nTable)) + goto cleanup; + for (i = 0; i < nTable; i++) { + if (!(element = virJSONValueArrayGet(table, i)) || + !(tmp = virJSONValueGetString(element))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing or invalid element %zu of 'multicast' " + "list in query-rx-filter response"), i); + goto cleanup; + } + if (virMacAddrParse(tmp, &fil->multicast.table[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid mac address '%s' in 'multicast-table' " + "array in query-rx-filter response"), tmp); + goto cleanup; + } + } + fil->multicast.nTable = nTable; + + if ((!(tmp = virJSONValueObjectGetString(entry, "vlan"))) || + ((fil->vlan.mode + = virNetDevRxFilterVlanModeTypeFromString(tmp)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'vlan' " + "in query-rx-filter response")); + goto cleanup; + } + if ((!(table = virJSONValueObjectGet(entry, "vlan-table"))) || + ((nTable = virJSONValueArraySize(table)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid 'vlan-table' array " + "in query-rx-filter response")); + goto cleanup; + } + if (VIR_ALLOC_N(fil->vlan.table, nTable)) + goto cleanup; + for (i = 0; i < nTable; i++) { + if (!(element = virJSONValueArrayGet(table, i)) || + virJSONValueGetNumberUint(element, &fil->vlan.table[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing or invalid element %zu of 'vlan-table' " + "array in query-rx-filter response"), i); + goto cleanup; + } + } + fil->vlan.nTable = nTable; + + ret = 0; + cleanup: + if (ret < 0) { + virNetDevRxFilterFree(fil); + fil = NULL; + } + *filter = fil; + return ret; +} + + +int +qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-rx-filter", + "s:name", alias, + NULL); + virJSONValuePtr reply = NULL; + + if (!cmd) + goto cleanup; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONQueryRxFilterParse(reply, filter) < 0) + goto cleanup; + + ret = 0; + cleanup: + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + if (ret < 0) { + virNetDevRxFilterFree(*filter); + *filter = NULL; + } + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + /* * Example return data * diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index d366179..a41281b 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -210,6 +210,9 @@ int qemuMonitorJSONAddNetdev(qemuMonitorPtr mon, int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon, const char *alias);
+int qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias, + virNetDevRxFilterPtr *filter); + int qemuMonitorJSONGetPtyPaths(qemuMonitorPtr mon, virHashTablePtr paths);
diff --git a/tests/Makefile.am b/tests/Makefile.am index d6c3cfb..bd4371b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -35,6 +35,7 @@ AM_CFLAGS = \ -Dabs_builddir="\"$(abs_builddir)\"" \ -Dabs_srcdir="\"$(abs_srcdir)\"" \ $(LIBXML_CFLAGS) \ + $(LIBNL_CFLAGS) \ $(GNUTLS_CFLAGS) \ $(SASL_CFLAGS) \ $(SELINUX_CFLAGS) \ @@ -43,6 +44,8 @@ AM_CFLAGS = \ $(COVERAGE_CFLAGS) \ $(WARN_CFLAGS)
+ + AM_LDFLAGS = \ -export-dynamic
-- 1.9.3
-- Amos.

On 09/27/2014 12:41 AM, Amos Kong wrote:
On Wed, Sep 24, 2014 at 05:50:54AM -0400, Laine Stump wrote:
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index a3d7c2c..58007e6 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3194,6 +3194,221 @@ int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon, }
+static int +qemuMonitorJSONQueryRxFilterParse(virJSONValuePtr msg, + virNetDevRxFilterPtr *filter) +{ + int ret = -1; + const char *tmp; + virJSONValuePtr returnArray, entry, table, element; + int nTable; + size_t i; + virNetDevRxFilterPtr fil = virNetDevRxFilterNew(); + + if (!(returnArray = virJSONValueObjectGet(msg, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-rx-filter reply was missing return data")); + goto cleanup; + } + if (returnArray->type != VIR_JSON_TYPE_ARRAY) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-rx-filter return data was not an array")); + goto cleanup; + } + if (!(entry = virJSONValueArrayGet(returnArray, 0))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query -rx-filter returne data missing array element")); + goto cleanup; + } + + if (!fil) + goto cleanup; How about checking fil after virNetDevRxFilterNew()?
Derp. I added in the extra stuff to drill into the JSON structure after the initial writing of the function, and wasn't paying attention. Thanks for catching that!

NIC_RX_FILTER_CHANGED is sent by qemu any time a NIC driver in the guest modified the NIC's RX Filter (for example, if the MAC address of the NIC is changed by the guest). This patch doesn't do anything useful with that event; it just sets up all the plumbing to get news of the event into a worker thread with all proper locking/reference counting, and provide an easy place to add in desired functionality. For easy reference the next time a handler for a qemu event is written, here is the sequence: The handler in qemu_json_monitor.c calls qemu_json_monitor.c:qemuMonitorJSONHandleNicRxFilterChanged() which calls qemu_monitor.c:qemuMonitorEmitNicRxFilterChanged() which uses QEMU_MONITOR_CALLBACK() to call mon->cb->domainNicRxFilterChanged(), ie: qemuProcessHandleNicRxFilterChanged() which will queue an event QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED for a worker thread to handle. When the worker thread gets the event, it calls qemuProcessEventHandler() which calls processNicRxFilterChangedEvent() and *that* is where the actual work will be done (and any event-specific memory allocated during qemuProcessHandleXXX() will be freed) - it is the middle of this function where functionality behind the event will be added in the next patch; for now there is just a VIR_DEBUG() to log the event. --- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 13 +++++++++++ src/qemu/qemu_monitor.h | 7 ++++++ src/qemu/qemu_monitor_json.c | 17 ++++++++++++++ src/qemu/qemu_process.c | 42 +++++++++++++++++++++++++++++++++ 6 files changed, 135 insertions(+) diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 845d3c7..ad45a66 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -195,6 +195,7 @@ typedef enum { QEMU_PROCESS_EVENT_WATCHDOG = 0, QEMU_PROCESS_EVENT_GUESTPANIC, QEMU_PROCESS_EVENT_DEVICE_DELETED, + QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED, QEMU_PROCESS_EVENT_LAST } qemuProcessEventType; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 543de79..64f1d45 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4140,6 +4140,58 @@ processDeviceDeletedEvent(virQEMUDriverPtr driver, } +static void +processNicRxFilterChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virDomainDeviceDef dev; + virDomainNetDefPtr def; + + VIR_DEBUG("Received NIC_RX_FILTER_CHANGED event for device %s " + "from domain %p %s", + devAlias, vm, vm->def->name); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) { + VIR_WARN("NIC_RX_FILTER_CHANGED event received for " + "non-existent device %s in domain %s", + devAlias, vm->def->name); + goto endjob; + } + if (dev.type != VIR_DOMAIN_DEVICE_NET) { + VIR_WARN("NIC_RX_FILTER_CHANGED event received for " + "non-network device %s in domain %s", + devAlias, vm->def->name); + goto endjob; + } + def = dev.data.net; + + /* handle the event - send query-rx-filter and respond to it. */ + + VIR_DEBUG("process NIC_RX_FILTER_CHANGED event for network " + "device %s in domain %s", def->info.alias, vm->def->name); + + endjob: + /* We had an extra reference to vm before starting a new job so ending the + * job is guaranteed not to remove the last reference. + */ + ignore_value(qemuDomainObjEndJob(driver, vm)); + + cleanup: + VIR_FREE(devAlias); + virObjectUnref(cfg); +} + + static void qemuProcessEventHandler(void *data, void *opaque) { struct qemuProcessEvent *processEvent = data; @@ -4160,6 +4212,9 @@ static void qemuProcessEventHandler(void *data, void *opaque) case QEMU_PROCESS_EVENT_DEVICE_DELETED: processDeviceDeletedEvent(driver, vm, processEvent->data); break; + case QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED: + processNicRxFilterChangedEvent(driver, vm, processEvent->data); + break; case QEMU_PROCESS_EVENT_LAST: break; } diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 48cbe3e..a4661f8 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1387,6 +1387,19 @@ qemuMonitorEmitDeviceDeleted(qemuMonitorPtr mon, } +int +qemuMonitorEmitNicRxFilterChanged(qemuMonitorPtr mon, + const char *devAlias) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + QEMU_MONITOR_CALLBACK(mon, ret, domainNicRxFilterChanged, mon->vm, devAlias); + + return ret; +} + + int qemuMonitorSetCapabilities(qemuMonitorPtr mon) { int ret; diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index c37e36f..e841505 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -171,6 +171,10 @@ typedef int (*qemuMonitorDomainDeviceDeletedCallback)(qemuMonitorPtr mon, virDomainObjPtr vm, const char *devAlias, void *opaque); +typedef int (*qemuMonitorDomainNicRxFilterChangedCallback)(qemuMonitorPtr mon, + virDomainObjPtr vm, + const char *devAlias, + void *opaque); typedef struct _qemuMonitorCallbacks qemuMonitorCallbacks; typedef qemuMonitorCallbacks *qemuMonitorCallbacksPtr; @@ -197,6 +201,7 @@ struct _qemuMonitorCallbacks { qemuMonitorDomainPMSuspendDiskCallback domainPMSuspendDisk; qemuMonitorDomainGuestPanicCallback domainGuestPanic; qemuMonitorDomainDeviceDeletedCallback domainDeviceDeleted; + qemuMonitorDomainNicRxFilterChangedCallback domainNicRxFilterChanged; }; char *qemuMonitorEscapeArg(const char *in); @@ -285,6 +290,8 @@ int qemuMonitorEmitPMSuspendDisk(qemuMonitorPtr mon); int qemuMonitorEmitGuestPanic(qemuMonitorPtr mon); int qemuMonitorEmitDeviceDeleted(qemuMonitorPtr mon, const char *devAlias); +int qemuMonitorEmitNicRxFilterChanged(qemuMonitorPtr mon, + const char *devAlias); int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 58007e6..640d96e 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -81,6 +81,7 @@ static void qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, virJSONValueP static void qemuMonitorJSONHandlePMSuspendDisk(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleGuestPanic(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleDeviceDeleted(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitorPtr mon, virJSONValuePtr data); typedef struct { const char *type; @@ -96,6 +97,7 @@ static qemuEventHandler eventHandlers[] = { { "DEVICE_DELETED", qemuMonitorJSONHandleDeviceDeleted, }, { "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, }, { "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, }, + { "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged, }, { "POWERDOWN", qemuMonitorJSONHandlePowerdown, }, { "RESET", qemuMonitorJSONHandleReset, }, { "RESUME", qemuMonitorJSONHandleResume, }, @@ -1041,6 +1043,21 @@ qemuMonitorJSONHandleDeviceDeleted(qemuMonitorPtr mon, virJSONValuePtr data) qemuMonitorEmitDeviceDeleted(mon, device); } + +static void +qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitorPtr mon, virJSONValuePtr data) +{ + const char *name; + + if (!(name = virJSONValueObjectGetString(data, "name"))) { + VIR_WARN("missing device in NIC_RX_FILTER_CHANGED event"); + return; + } + + qemuMonitorEmitNicRxFilterChanged(mon, name); +} + + int qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon, const char *cmd_str, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index dddca35..07f1f38 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1489,6 +1489,47 @@ qemuProcessHandleDeviceDeleted(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } +static int +qemuProcessHandleNicRxFilterChanged(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + const char *devAlias, + void *opaque) +{ + virQEMUDriverPtr driver = opaque; + struct qemuProcessEvent *processEvent = NULL; + char *data; + + virObjectLock(vm); + + VIR_DEBUG("Device %s RX Filter changed in domain %p %s", + devAlias, vm, vm->def->name); + + if (VIR_ALLOC(processEvent) < 0) + goto error; + + processEvent->eventType = QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED; + if (VIR_STRDUP(data, devAlias) < 0) + goto error; + processEvent->data = data; + processEvent->vm = vm; + + virObjectRef(vm); + if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) { + ignore_value(virObjectUnref(vm)); + goto error; + } + + cleanup: + virObjectUnlock(vm); + return 0; + error: + if (processEvent) + VIR_FREE(processEvent->data); + VIR_FREE(processEvent); + goto cleanup; +} + + static qemuMonitorCallbacks monitorCallbacks = { .eofNotify = qemuProcessHandleMonitorEOF, .errorNotify = qemuProcessHandleMonitorError, @@ -1510,6 +1551,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainPMSuspendDisk = qemuProcessHandlePMSuspendDisk, .domainGuestPanic = qemuProcessHandleGuestPanic, .domainDeviceDeleted = qemuProcessHandleDeviceDeleted, + .domainNicRxFilterChanged = qemuProcessHandleNicRxFilterChanged, }; static int -- 1.9.3

On 09/24/2014 05:50 AM, Laine Stump wrote:
NIC_RX_FILTER_CHANGED is sent by qemu any time a NIC driver in the guest modified the NIC's RX Filter (for example, if the MAC address of the NIC is changed by the guest).
This patch doesn't do anything useful with that event; it just sets up all the plumbing to get news of the event into a worker thread with all proper locking/reference counting, and provide an easy place to add in desired functionality.
For easy reference the next time a handler for a qemu event is written, here is the sequence:
The handler in qemu_json_monitor.c calls
qemu_json_monitor.c:qemuMonitorJSONHandleNicRxFilterChanged()
which calls
qemu_monitor.c:qemuMonitorEmitNicRxFilterChanged()
which uses QEMU_MONITOR_CALLBACK() to call mon->cb->domainNicRxFilterChanged(), ie:
qemuProcessHandleNicRxFilterChanged()
which will queue an event QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED for a worker thread to handle.
When the worker thread gets the event, it calls
qemuProcessEventHandler()
which calls
processNicRxFilterChangedEvent()
and *that* is where the actual work will be done (and any event-specific memory allocated during qemuProcessHandleXXX() will be freed) - it is the middle of this function where functionality behind the event will be added in the next patch; for now there is just a VIR_DEBUG() to log the event.
This is the kind of stuff while great in a commit message - is perhaps even better in the comments of the code somewhere. Not sure of which is the "best place", but perhaps before the typedef enum {} qemuProcessEventType; At least someone will get a head start on the various places they have to change. Anyone adding an event has to start there.
--- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 13 +++++++++++ src/qemu/qemu_monitor.h | 7 ++++++ src/qemu/qemu_monitor_json.c | 17 ++++++++++++++ src/qemu/qemu_process.c | 42 +++++++++++++++++++++++++++++++++ 6 files changed, 135 insertions(+)
Although not my area of expertise, things look good. Looks like all the changes were very similar to other events added. Not the definitive ACK you probably want, but hopefully someone else who's made changes in this space recently can take a quick look to make sure you've covered everything. My one question would be - is there any sort of issue if when registering the event that the underlying qemu doesn't support/have the NIC_RX_FILTER_CHANGED event defined? Or does that just not matter and all this does is take events sent to libvirt after registering at some higher level to receive "any" event. It seems to be the latter through qemuMonitorIO, but I figured I'd ask... John

On 09/24/2014 05:50 AM, Laine Stump wrote:
NIC_RX_FILTER_CHANGED is sent by qemu any time a NIC driver in the guest modified the NIC's RX Filter (for example, if the MAC address of the NIC is changed by the guest).
This patch doesn't do anything useful with that event; it just sets up all the plumbing to get news of the event into a worker thread with all proper locking/reference counting, and provide an easy place to add in desired functionality.
For easy reference the next time a handler for a qemu event is written, here is the sequence: I assume you mean qemu_monitor_json.c The handler in qemu_json_monitor.c calls
qemu_json_monitor.c:qemuMonitorJSONHandleNicRxFilterChanged()
which calls
qemu_monitor.c:qemuMonitorEmitNicRxFilterChanged()
which uses QEMU_MONITOR_CALLBACK() to call mon->cb->domainNicRxFilterChanged(), ie:
qemuProcessHandleNicRxFilterChanged()
which will queue an event QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED for a worker thread to handle.
When the worker thread gets the event, it calls
qemuProcessEventHandler()
which calls
processNicRxFilterChangedEvent()
and *that* is where the actual work will be done (and any event-specific memory allocated during qemuProcessHandleXXX() will be freed) - it is the middle of this function where functionality behind the event will be added in the next patch; for now there is just a VIR_DEBUG() to log the event. --- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 13 +++++++++++ src/qemu/qemu_monitor.h | 7 ++++++ src/qemu/qemu_monitor_json.c | 17 ++++++++++++++ src/qemu/qemu_process.c | 42 +++++++++++++++++++++++++++++++++ 6 files changed, 135 insertions(+)
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 845d3c7..ad45a66 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -195,6 +195,7 @@ typedef enum { QEMU_PROCESS_EVENT_WATCHDOG = 0, QEMU_PROCESS_EVENT_GUESTPANIC, QEMU_PROCESS_EVENT_DEVICE_DELETED, + QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED,
QEMU_PROCESS_EVENT_LAST } qemuProcessEventType; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 543de79..64f1d45 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4140,6 +4140,58 @@ processDeviceDeletedEvent(virQEMUDriverPtr driver, }
+static void +processNicRxFilterChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virDomainDeviceDef dev; + virDomainNetDefPtr def; + + VIR_DEBUG("Received NIC_RX_FILTER_CHANGED event for device %s " + "from domain %p %s", + devAlias, vm, vm->def->name); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) { + VIR_WARN("NIC_RX_FILTER_CHANGED event received for " + "non-existent device %s in domain %s", + devAlias, vm->def->name); + goto endjob; + } + if (dev.type != VIR_DOMAIN_DEVICE_NET) { + VIR_WARN("NIC_RX_FILTER_CHANGED event received for " + "non-network device %s in domain %s", + devAlias, vm->def->name); + goto endjob; + } I understand the need to check the device type, but is it necessary to log a warning message? I wonder if it may not cause unnecessary concern for someone viewing the logs. Is it possible to receive a NIC_RX_FILTER_CHANGED event for something other than a network device? + def = dev.data.net; + + /* handle the event - send query-rx-filter and respond to it. */ + + VIR_DEBUG("process NIC_RX_FILTER_CHANGED event for network " + "device %s in domain %s", def->info.alias, vm->def->name); + + endjob: + /* We had an extra reference to vm before starting a new job so ending the + * job is guaranteed not to remove the last reference. + */ + ignore_value(qemuDomainObjEndJob(driver, vm)); + + cleanup: + VIR_FREE(devAlias); + virObjectUnref(cfg); +} + + static void qemuProcessEventHandler(void *data, void *opaque) { struct qemuProcessEvent *processEvent = data; @@ -4160,6 +4212,9 @@ static void qemuProcessEventHandler(void *data, void *opaque) case QEMU_PROCESS_EVENT_DEVICE_DELETED: processDeviceDeletedEvent(driver, vm, processEvent->data); break; + case QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED: + processNicRxFilterChangedEvent(driver, vm, processEvent->data); + break; case QEMU_PROCESS_EVENT_LAST: break; } diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 48cbe3e..a4661f8 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1387,6 +1387,19 @@ qemuMonitorEmitDeviceDeleted(qemuMonitorPtr mon, }
+int +qemuMonitorEmitNicRxFilterChanged(qemuMonitorPtr mon, + const char *devAlias) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + QEMU_MONITOR_CALLBACK(mon, ret, domainNicRxFilterChanged, mon->vm, devAlias); + + return ret; +} + + int qemuMonitorSetCapabilities(qemuMonitorPtr mon) { int ret; diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index c37e36f..e841505 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -171,6 +171,10 @@ typedef int (*qemuMonitorDomainDeviceDeletedCallback)(qemuMonitorPtr mon, virDomainObjPtr vm, const char *devAlias, void *opaque); +typedef int (*qemuMonitorDomainNicRxFilterChangedCallback)(qemuMonitorPtr mon, + virDomainObjPtr vm, + const char *devAlias, + void *opaque);
typedef struct _qemuMonitorCallbacks qemuMonitorCallbacks; typedef qemuMonitorCallbacks *qemuMonitorCallbacksPtr; @@ -197,6 +201,7 @@ struct _qemuMonitorCallbacks { qemuMonitorDomainPMSuspendDiskCallback domainPMSuspendDisk; qemuMonitorDomainGuestPanicCallback domainGuestPanic; qemuMonitorDomainDeviceDeletedCallback domainDeviceDeleted; + qemuMonitorDomainNicRxFilterChangedCallback domainNicRxFilterChanged; };
char *qemuMonitorEscapeArg(const char *in); @@ -285,6 +290,8 @@ int qemuMonitorEmitPMSuspendDisk(qemuMonitorPtr mon); int qemuMonitorEmitGuestPanic(qemuMonitorPtr mon); int qemuMonitorEmitDeviceDeleted(qemuMonitorPtr mon, const char *devAlias); +int qemuMonitorEmitNicRxFilterChanged(qemuMonitorPtr mon, + const char *devAlias);
int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 58007e6..640d96e 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -81,6 +81,7 @@ static void qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, virJSONValueP static void qemuMonitorJSONHandlePMSuspendDisk(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleGuestPanic(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleDeviceDeleted(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitorPtr mon, virJSONValuePtr data);
typedef struct { const char *type; @@ -96,6 +97,7 @@ static qemuEventHandler eventHandlers[] = { { "DEVICE_DELETED", qemuMonitorJSONHandleDeviceDeleted, }, { "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, }, { "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, }, + { "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged, }, { "POWERDOWN", qemuMonitorJSONHandlePowerdown, }, { "RESET", qemuMonitorJSONHandleReset, }, { "RESUME", qemuMonitorJSONHandleResume, }, @@ -1041,6 +1043,21 @@ qemuMonitorJSONHandleDeviceDeleted(qemuMonitorPtr mon, virJSONValuePtr data) qemuMonitorEmitDeviceDeleted(mon, device); }
+ +static void +qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitorPtr mon, virJSONValuePtr data) +{ + const char *name; + + if (!(name = virJSONValueObjectGetString(data, "name"))) { + VIR_WARN("missing device in NIC_RX_FILTER_CHANGED event"); + return; + } The device path is also sent with the event. It may be that data element is useless with regard to subsequent NIC_RX_FILTER_CHANGED event handling but, for my edification, I am curious as to why you ignored it. + + qemuMonitorEmitNicRxFilterChanged(mon, name); +} + + int qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon, const char *cmd_str, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index dddca35..07f1f38 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1489,6 +1489,47 @@ qemuProcessHandleDeviceDeleted(qemuMonitorPtr mon ATTRIBUTE_UNUSED, }
+static int +qemuProcessHandleNicRxFilterChanged(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + const char *devAlias, + void *opaque) +{ + virQEMUDriverPtr driver = opaque; + struct qemuProcessEvent *processEvent = NULL; + char *data; + + virObjectLock(vm); + + VIR_DEBUG("Device %s RX Filter changed in domain %p %s", + devAlias, vm, vm->def->name); + + if (VIR_ALLOC(processEvent) < 0) + goto error; + + processEvent->eventType = QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED; + if (VIR_STRDUP(data, devAlias) < 0) + goto error; + processEvent->data = data; + processEvent->vm = vm; + + virObjectRef(vm); + if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) { + ignore_value(virObjectUnref(vm)); + goto error; + } + + cleanup: + virObjectUnlock(vm); + return 0; + error: + if (processEvent) + VIR_FREE(processEvent->data); + VIR_FREE(processEvent); + goto cleanup; +} + + static qemuMonitorCallbacks monitorCallbacks = { .eofNotify = qemuProcessHandleMonitorEOF, .errorNotify = qemuProcessHandleMonitorError, @@ -1510,6 +1551,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainPMSuspendDisk = qemuProcessHandlePMSuspendDisk, .domainGuestPanic = qemuProcessHandleGuestPanic, .domainDeviceDeleted = qemuProcessHandleDeviceDeleted, + .domainNicRxFilterChanged = qemuProcessHandleNicRxFilterChanged, };
static int

On 09/30/2014 11:54 AM, Tony Krowiak wrote:
On 09/24/2014 05:50 AM, Laine Stump wrote:
NIC_RX_FILTER_CHANGED is sent by qemu any time a NIC driver in the guest modified the NIC's RX Filter (for example, if the MAC address of the NIC is changed by the guest).
This patch doesn't do anything useful with that event; it just sets up all the plumbing to get news of the event into a worker thread with all proper locking/reference counting, and provide an easy place to add in desired functionality.
For easy reference the next time a handler for a qemu event is written, here is the sequence: I assume you mean qemu_monitor_json.c The handler in qemu_json_monitor.c calls
Yes.
+ if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) { + VIR_WARN("NIC_RX_FILTER_CHANGED event received for " + "non-existent device %s in domain %s", + devAlias, vm->def->name); + goto endjob; + } + if (dev.type != VIR_DOMAIN_DEVICE_NET) { + VIR_WARN("NIC_RX_FILTER_CHANGED event received for " + "non-network device %s in domain %s", + devAlias, vm->def->name); + goto endjob; + }
I understand the need to check the device type, but is it necessary to log a warning message? I wonder if it may not cause unnecessary concern for someone viewing the logs. Is it possible to receive a NIC_RX_FILTER_CHANGED event for something other than a network device?
As far as I know this event should never be issued for any device that isn't a net device. If this was happening, it would mean that either qemu or libvirt had messed up somewhere, and could be an indicator of larger problems.
+ + +static void +qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitorPtr mon, virJSONValuePtr data) +{ + const char *name; + + if (!(name = virJSONValueObjectGetString(data, "name"))) { + VIR_WARN("missing device in NIC_RX_FILTER_CHANGED event"); + return; + } The device path is also sent with the event. It may be that data element is useless with regard to subsequent NIC_RX_FILTER_CHANGED event handling but, for my edification, I am curious as to why you ignored it.
Because we don't use it in libvirt. We only use the alias (aka "name")

This patch fills in the functionality of processNicRxFilterChangedEvent(). It now checks if it is appropriate to respond to the NIC_RX_FILTER_CHANGED event (based on device type and configuration) and takes appropriate action. Currently it checks if the guest interface has been configured with trustGuestRxFilters='yes', and if the host side device is macvtap. If so, and the MAC address on the guest has changed, the MAC address of the macvtap device is changed to match. The result of this is that networking from the guest will continue to work if the mac address of a macvtap-connected network device is changed from within the guest, as long as trustGuestRxFilters='yes' (previously changing the MAC address in the guest would break networking). --- src/qemu/qemu_driver.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 64f1d45..7801d91 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4146,8 +4146,13 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, char *devAlias) { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + qemuDomainObjPrivatePtr priv = vm->privateData; virDomainDeviceDef dev; virDomainNetDefPtr def; + virNetDevRxFilterPtr filter = NULL; + virMacAddr oldMAC; + char newMacStr[VIR_MAC_STRING_BUFLEN]; + int ret; VIR_DEBUG("Received NIC_RX_FILTER_CHANGED event for device %s " "from domain %p %s", @@ -4175,11 +4180,55 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, } def = dev.data.net; + if (!virDomainNetGetActualTrustGuestRxFilters(def)) { + VIR_WARN("ignore NIC_RX_FILTER_CHANGED event for network " + "device %s in domain %s", + def->info.alias, vm->def->name); + /* not sending "query-rx-filter" will also suppress any + * further NIC_RX_FILTER_CHANGED events for this device + */ + goto endjob; + } + /* handle the event - send query-rx-filter and respond to it. */ VIR_DEBUG("process NIC_RX_FILTER_CHANGED event for network " "device %s in domain %s", def->info.alias, vm->def->name); + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorQueryRxFilter(priv->mon, devAlias, &filter); + qemuDomainObjExitMonitor(driver, vm); + if (ret < 0) + goto endjob; + + virMacAddrFormat(&filter->mac, newMacStr); + + if (virDomainNetGetActualType(def) == VIR_DOMAIN_NET_TYPE_DIRECT) { + + /* For macvtap connections, set the macvtap device's MAC + * address to match that of the guest device. + */ + + if (virNetDevGetMAC(def->ifname, &oldMAC) < 0) { + VIR_WARN("Couldn't get current MAC address of device %s " + "while responding to NIC_RX_FILTER_CHANGED", + def->ifname); + goto endjob; + } + + if (virMacAddrCmp(&oldMAC, &filter->mac)) { + /* set new MAC address from guest to associated macvtap device */ + if (virNetDevSetMAC(def->ifname, &filter->mac) < 0) { + VIR_WARN("Couldn't set new MAC address %s to device %s " + "while responding to NIC_RX_FILTER_CHANGED", + newMacStr, def->ifname); + } else { + VIR_DEBUG("device %s MAC address set to %s", + def->ifname, newMacStr); + } + } + } + endjob: /* We had an extra reference to vm before starting a new job so ending the * job is guaranteed not to remove the last reference. @@ -4187,6 +4236,7 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, ignore_value(qemuDomainObjEndJob(driver, vm)); cleanup: + virNetDevRxFilterFree(filter); VIR_FREE(devAlias); virObjectUnref(cfg); } -- 1.9.3

On 09/24/2014 05:50 AM, Laine Stump wrote:
This patch fills in the functionality of processNicRxFilterChangedEvent(). It now checks if it is appropriate to respond to the NIC_RX_FILTER_CHANGED event (based on device type and configuration) and takes appropriate action. Currently it checks if the guest interface has been configured with trustGuestRxFilters='yes', and if the host side device is macvtap. If so, and the MAC address on the guest has changed, the MAC address of the macvtap device is changed to match.
The result of this is that networking from the guest will continue to work if the mac address of a macvtap-connected network device is changed from within the guest, as long as trustGuestRxFilters='yes' (previously changing the MAC address in the guest would break networking). --- src/qemu/qemu_driver.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 64f1d45..7801d91 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4146,8 +4146,13 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, char *devAlias) { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + qemuDomainObjPrivatePtr priv = vm->privateData; virDomainDeviceDef dev; virDomainNetDefPtr def; + virNetDevRxFilterPtr filter = NULL; + virMacAddr oldMAC; + char newMacStr[VIR_MAC_STRING_BUFLEN]; + int ret;
VIR_DEBUG("Received NIC_RX_FILTER_CHANGED event for device %s " "from domain %p %s", @@ -4175,11 +4180,55 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, } def = dev.data.net;
+ if (!virDomainNetGetActualTrustGuestRxFilters(def)) { + VIR_WARN("ignore NIC_RX_FILTER_CHANGED event for network " + "device %s in domain %s", + def->info.alias, vm->def->name);
So how often could this be emitted? Do we need some sort of "filter" to only message once per life of the domain?
+ /* not sending "query-rx-filter" will also suppress any + * further NIC_RX_FILTER_CHANGED events for this device + */ + goto endjob; + } + /* handle the event - send query-rx-filter and respond to it. */
VIR_DEBUG("process NIC_RX_FILTER_CHANGED event for network " "device %s in domain %s", def->info.alias, vm->def->name);
There's no capabilities check necessary? What if the command doesn't exist in the underlying qemu?
+ qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorQueryRxFilter(priv->mon, devAlias, &filter); + qemuDomainObjExitMonitor(driver, vm); + if (ret < 0) + goto endjob;
You filled in a lot of data from the qemuMonitorQueryRxFilter(), but you're only using filter->mac (e.g. main-mac). Or am I missing something? John
+ + virMacAddrFormat(&filter->mac, newMacStr); + + if (virDomainNetGetActualType(def) == VIR_DOMAIN_NET_TYPE_DIRECT) { + + /* For macvtap connections, set the macvtap device's MAC + * address to match that of the guest device. + */ + + if (virNetDevGetMAC(def->ifname, &oldMAC) < 0) { + VIR_WARN("Couldn't get current MAC address of device %s " + "while responding to NIC_RX_FILTER_CHANGED", + def->ifname); + goto endjob; + } + + if (virMacAddrCmp(&oldMAC, &filter->mac)) { + /* set new MAC address from guest to associated macvtap device */ + if (virNetDevSetMAC(def->ifname, &filter->mac) < 0) { + VIR_WARN("Couldn't set new MAC address %s to device %s " + "while responding to NIC_RX_FILTER_CHANGED", + newMacStr, def->ifname); + } else { + VIR_DEBUG("device %s MAC address set to %s", + def->ifname, newMacStr); + } + } + } + endjob: /* We had an extra reference to vm before starting a new job so ending the * job is guaranteed not to remove the last reference. @@ -4187,6 +4236,7 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, ignore_value(qemuDomainObjEndJob(driver, vm));
cleanup: + virNetDevRxFilterFree(filter); VIR_FREE(devAlias); virObjectUnref(cfg); }

On 09/24/2014 10:19 PM, John Ferlan wrote:
On 09/24/2014 05:50 AM, Laine Stump wrote:
This patch fills in the functionality of processNicRxFilterChangedEvent(). It now checks if it is appropriate to respond to the NIC_RX_FILTER_CHANGED event (based on device type and configuration) and takes appropriate action. Currently it checks if the guest interface has been configured with trustGuestRxFilters='yes', and if the host side device is macvtap. If so, and the MAC address on the guest has changed, the MAC address of the macvtap device is changed to match.
The result of this is that networking from the guest will continue to work if the mac address of a macvtap-connected network device is changed from within the guest, as long as trustGuestRxFilters='yes' (previously changing the MAC address in the guest would break networking). --- src/qemu/qemu_driver.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 64f1d45..7801d91 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4146,8 +4146,13 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, char *devAlias) { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + qemuDomainObjPrivatePtr priv = vm->privateData; virDomainDeviceDef dev; virDomainNetDefPtr def; + virNetDevRxFilterPtr filter = NULL; + virMacAddr oldMAC; + char newMacStr[VIR_MAC_STRING_BUFLEN]; + int ret;
VIR_DEBUG("Received NIC_RX_FILTER_CHANGED event for device %s " "from domain %p %s", @@ -4175,11 +4180,55 @@ processNicRxFilterChangedEvent(virQEMUDriverPtr driver, } def = dev.data.net;
+ if (!virDomainNetGetActualTrustGuestRxFilters(def)) { + VIR_WARN("ignore NIC_RX_FILTER_CHANGED event for network " + "device %s in domain %s", + def->info.alias, vm->def->name); So how often could this be emitted? Do we need some sort of "filter" to only message once per life of the domain?
As often as we respond to the previous NIC_RX_FILTER_CHANGED event - qemu won't send another event until we've issued a new query-rx-filter command for that same interface. So for domains where we've set trustGuestRxFilters='no', we will get exactly one event per interface. On domains where we trust the guest enough to use its filter info, we will never get a backlog of more than one per interface.
+ /* not sending "query-rx-filter" will also suppress any + * further NIC_RX_FILTER_CHANGED events for this device + */ + goto endjob; + } + /* handle the event - send query-rx-filter and respond to it. */
VIR_DEBUG("process NIC_RX_FILTER_CHANGED event for network " "device %s in domain %s", def->info.alias, vm->def->name);
There's no capabilities check necessary? What if the command doesn't exist in the underlying qemu?
If NIC_RX_FILTER_CHANGED exists, then query-rx-filter exists. If NIC_RX_FILTER_CHANGED doesn't exist, we will never get to this point.
+ qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorQueryRxFilter(priv->mon, devAlias, &filter); + qemuDomainObjExitMonitor(driver, vm); + if (ret < 0) + goto endjob; You filled in a lot of data from the qemuMonitorQueryRxFilter(), but you're only using filter->mac (e.g. main-mac).
Or am I missing something?
No, you're not missing anything. All the other information will be used in the future in a similar manner. This single patch was all that was necessary to get regular unicast traffic working after a mac address change, so it is useful on its own, but I didn't want to add in the utility functions half-finished.

On Wed, Sep 24, 2014 at 05:50:50AM -0400, Laine Stump wrote:
These patches set up an event handler for qemu's NIC_RX_FILTER_CHANGED event, which is sent whenever a guest makes a change to a network device's unicast/multicast filter, vlan table, or MAC address.
Not 'whenever', if first event isn't processed by querying rx-filter of that interface, then qemu won't emit NIC_RX_FILTER_CHANGED event for upcoming change of that interface. First NIC_RX_FILTER_CHANGED event of each interfaces can be emitted unconditionally.
The handler checks if it is appropriate to respond to the NIC_RX_FILTER_CHANGED event (based on device type and configuration) and takes appropriate action. Currently it checks if the guest interface has been configured with trustGuestRxFilters='yes' (defaults to 'no' for security reasons), and if the host side device is macvtap. If so, and the MAC address on the guest has changed, the MAC address of the macvtap device is changed to match.
The result of this is that networking from the guest will continue to work if the mac address of a macvtap-connected network device is changed from within the guest, as long as trustGuestRxFilters='yes' (previously changing the MAC address in the guest would break networking).
I still need to add code to compare the old and new unicast and multicast lists and program the filters in the macvtap to match the guest, and to check for a non-empty vlan table and handle that (currently that means just setting promiscuous mode on the macvtap), but that can come in a followup series.
Laine Stump (6): conf: add trustGuestRxFilters attribute to network and domain interface network: set interface actual trustGuestRxFilters from network/portgroup util: define virNetDevRxFilter and basic utility functions qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter qemu: setup infrastructure to handle NIC_RX_FILTER_CHANGED event qemu: change macvtap device MAC address in response to NIC_RX_FILTER_CHANGED
docs/formatdomain.html.in | 38 +++- docs/formatnetwork.html.in | 28 ++- docs/schemas/domaincommon.rng | 5 + docs/schemas/network.rng | 10 + src/conf/domain_conf.c | 42 ++++ src/conf/domain_conf.h | 3 + src/conf/network_conf.c | 35 ++++ src/conf/network_conf.h | 2 + src/libvirt_private.syms | 9 + src/network/bridge_driver.c | 11 + src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 105 ++++++++++ src/qemu/qemu_monitor.c | 39 ++++ src/qemu/qemu_monitor.h | 11 + src/qemu/qemu_monitor_json.c | 232 +++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + src/qemu/qemu_process.c | 42 ++++ src/util/virnetdev.c | 40 ++++ src/util/virnetdev.h | 57 ++++- tests/Makefile.am | 3 + tests/networkxml2xmlin/vepa-net.xml | 4 +- tests/networkxml2xmlout/vepa-net.xml | 4 +- .../qemuxml2argv-net-virtio-network-portgroup.xml | 4 +- 23 files changed, 711 insertions(+), 17 deletions(-)
-- 1.9.3
-- Amos.

These patches set up an event handler for qemu's NIC_RX_FILTER_CHANGED event, which is sent whenever a guest makes a change to a network device's unicast/multicast filter, vlan table, or MAC address.
The handler checks if it is appropriate to respond to the NIC_RX_FILTER_CHANGED event (based on device type and configuration) and takes appropriate action. Currently it checks if the guest interface has been configured with trustGuestRxFilters='yes' (defaults to 'no' for security reasons), and if the host side device is macvtap. If so, and the MAC address on the guest has changed, the MAC address of the macvtap device is changed to match.
The result of this is that networking from the guest will continue to work if the mac address of a macvtap-connected network device is changed from within the guest, as long as trustGuestRxFilters='yes' (previously changing the MAC address in the guest would break networking).
I still need to add code to compare the old and new unicast and multicast lists and program the filters in the macvtap to match the guest, and to check for a non-empty vlan table and handle that (currently that means just setting promiscuous mode on the macvtap), but that can come in a followup series. I was very interested in this patch set because I developed a set of
On 09/24/2014 05:50 AM, Laine Stump wrote: patches to respond to the NIC_RX_FILTER_CHANGED event. I completed the patch set several weeks ago and have been awaiting completion of our internal review before submitting them to this mailing list. Apparently you beat me to the punch. I have code that compares the old and new multicast lists and synchronizes the macvtap filters with the guest's. I can modify my patches to integrate this function into what you have provided with this patch set. Would that be agreeable?
Laine Stump (6): conf: add trustGuestRxFilters attribute to network and domain interface network: set interface actual trustGuestRxFilters from network/portgroup util: define virNetDevRxFilter and basic utility functions qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter qemu: setup infrastructure to handle NIC_RX_FILTER_CHANGED event qemu: change macvtap device MAC address in response to NIC_RX_FILTER_CHANGED
docs/formatdomain.html.in | 38 +++- docs/formatnetwork.html.in | 28 ++- docs/schemas/domaincommon.rng | 5 + docs/schemas/network.rng | 10 + src/conf/domain_conf.c | 42 ++++ src/conf/domain_conf.h | 3 + src/conf/network_conf.c | 35 ++++ src/conf/network_conf.h | 2 + src/libvirt_private.syms | 9 + src/network/bridge_driver.c | 11 + src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 105 ++++++++++ src/qemu/qemu_monitor.c | 39 ++++ src/qemu/qemu_monitor.h | 11 + src/qemu/qemu_monitor_json.c | 232 +++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + src/qemu/qemu_process.c | 42 ++++ src/util/virnetdev.c | 40 ++++ src/util/virnetdev.h | 57 ++++- tests/Makefile.am | 3 + tests/networkxml2xmlin/vepa-net.xml | 4 +- tests/networkxml2xmlout/vepa-net.xml | 4 +- .../qemuxml2argv-net-virtio-network-portgroup.xml | 4 +- 23 files changed, 711 insertions(+), 17 deletions(-)

On 09/30/2014 02:28 PM, Tony Krowiak wrote:
These patches set up an event handler for qemu's NIC_RX_FILTER_CHANGED event, which is sent whenever a guest makes a change to a network device's unicast/multicast filter, vlan table, or MAC address.
The handler checks if it is appropriate to respond to the NIC_RX_FILTER_CHANGED event (based on device type and configuration) and takes appropriate action. Currently it checks if the guest interface has been configured with trustGuestRxFilters='yes' (defaults to 'no' for security reasons), and if the host side device is macvtap. If so, and the MAC address on the guest has changed, the MAC address of the macvtap device is changed to match.
The result of this is that networking from the guest will continue to work if the mac address of a macvtap-connected network device is changed from within the guest, as long as trustGuestRxFilters='yes' (previously changing the MAC address in the guest would break networking).
I still need to add code to compare the old and new unicast and multicast lists and program the filters in the macvtap to match the guest, and to check for a non-empty vlan table and handle that (currently that means just setting promiscuous mode on the macvtap), but that can come in a followup series. I was very interested in this patch set because I developed a set of
On 09/24/2014 05:50 AM, Laine Stump wrote: patches to respond to the NIC_RX_FILTER_CHANGED event. I completed the patch set several weeks ago and have been awaiting completion of our internal review before submitting them to this mailing list. Apparently you beat me to the punch. I have code that compares the old and new multicast lists and synchronizes the macvtap filters with the guest's. I can modify my patches to integrate this function into what you have provided with this patch set. Would that be agreeable?
Since I've just started working on exactly that, yes :-) What I'd started was a function virNetDevGetRxFilter(ifname, filter) in virnetdev.c which would do for the host-side tap/macvtap device what qemuMonitorQueryRxFilter() does for the guest's interface - retrieve the current unicast/multicast/vlan tables & modes (using code from iproute2's "bridge" command as a guide to write equivalent libnl-based code) and return them in a fully-populated virNetDevRxFilter object (that's why I defined that struct in virnetdev.h even though I've so far used it only in the qemu driver), which would be called from the event handler; the event handler would compare the two virNetDevRxFilter objects (the one from the host-side device and the one from the guest-side device) and issue the necessary commands to make everything match (well, actually what I've been told is that in the case of vlans, if the guest has a non-empty vlan table we currently have to just set the macvtap to promiscuous mode). It sounds like you're only interested in the multicast list, so if you wanted to fill in enough to make it do that, your code could be used as a model to do the unicast list (which seems to be empty most of the time anyway; as I usually operate above that layer, I'm truthfully not exactly sure when it's even used). In the future, this same infrastructure will be used to monitor MAC address and filter changes for interfaces connected to host bridges via normal tap devices, so that the bridge can be taken out of "learning" mode (we then need to manually update the bridge's MAC tables based on the info from query-rx-filter); this can apparently yield a significant performance increase.

On 09/30/2014 02:28 PM, Tony Krowiak wrote:
These patches set up an event handler for qemu's NIC_RX_FILTER_CHANGED event, which is sent whenever a guest makes a change to a network device's unicast/multicast filter, vlan table, or MAC address.
The handler checks if it is appropriate to respond to the NIC_RX_FILTER_CHANGED event (based on device type and configuration) and takes appropriate action. Currently it checks if the guest interface has been configured with trustGuestRxFilters='yes' (defaults to 'no' for security reasons), and if the host side device is macvtap. If so, and the MAC address on the guest has changed, the MAC address of the macvtap device is changed to match.
The result of this is that networking from the guest will continue to work if the mac address of a macvtap-connected network device is changed from within the guest, as long as trustGuestRxFilters='yes' (previously changing the MAC address in the guest would break networking).
I still need to add code to compare the old and new unicast and multicast lists and program the filters in the macvtap to match the guest, and to check for a non-empty vlan table and handle that (currently that means just setting promiscuous mode on the macvtap), but that can come in a followup series. I was very interested in this patch set because I developed a set of
On 09/24/2014 05:50 AM, Laine Stump wrote: patches to respond to the NIC_RX_FILTER_CHANGED event. I completed the patch set several weeks ago and have been awaiting completion of our internal review before submitting them to this mailing list. Apparently you beat me to the punch. I have code that compares the old and new multicast lists and synchronizes the macvtap filters with the guest's. I can modify my patches to integrate this function into what you have provided with this patch set. Would that be agreeable? Since I've just started working on exactly that, yes :-)
What I'd started was a function virNetDevGetRxFilter(ifname, filter) in virnetdev.c which would do for the host-side tap/macvtap device what qemuMonitorQueryRxFilter() does for the guest's interface - retrieve the current unicast/multicast/vlan tables & modes (using code from iproute2's "bridge" command as a guide to write equivalent libnl-based code) and return them in a fully-populated virNetDevRxFilter object (that's why I defined that struct in virnetdev.h even though I've so far used it only in the qemu driver), which would be called from the event handler; the event handler would compare the two virNetDevRxFilter objects (the one from the host-side device and the one from the guest-side device) and issue the necessary commands to make everything match (well, actually what I've been told is that in the case of vlans, if the guest has a non-empty vlan table we currently have to just set the macvtap to promiscuous mode).
It sounds like you're only interested in the multicast list, so if you wanted to fill in enough to make it do that, your code could be used as a model to do the unicast list (which seems to be empty most of the time anyway; as I usually operate above that layer, I'm truthfully not exactly sure when it's even used). I am interested in a complete solution, however; I chose to ignore the unicast
On 09/30/2014 03:47 PM, Laine Stump wrote: list for the time being for reasons similar to yours. I wrote code to synchronize the VLAN table, but subsequently learned that there were problems with VLAN on macvtap, so I took that code out of the patch set. I used the "ip maddr show" command in iproute2 as a model for acquiring the multicast list. This command reads the /proc/net/dev_mcast file on the host to get the current multicast MAC address list for the macvtap device. To synchronize the macvtap device's multicast list, I compared the list from /proc/net/dev_mcast with that returned from the query_rx_filter command and used the SIOCADDMULTI and SIOCDELMULTI ioctl's add and delete multicast MAC addresses as needed. I did not do anything with the mode (state) values as I have yet to figure out how to do that. I also wrote code to synchronize the following interface flags: * promiscuous * multicast * allmulti * broadcast I can integrate what I have with your infrastructure including: * Create a virNetDevGetRxFilter(ifname, filter) function in virnetdev.c that will populate the following fields in the filter: * name * mac * promiscuous * broadcastAllowed * multicast.table * multicast.nTable * vlan.table * vlan.nTable * Implement the event handler code to: * compare the two virNetDevRxFilter objects and issue the necessary commands to synchronize the multicast MAC address lists * set the promiscuous mode if the guest has a non-empty vlan table * compare the device flags and synchronize their values If you think this would be a worthwhile endeavor, I've got your patches installed and can proceed.
In the future, this same infrastructure will be used to monitor MAC address and filter changes for interfaces connected to host bridges via normal tap devices, so that the bridge can be taken out of "learning" mode (we then need to manually update the bridge's MAC tables based on the info from query-rx-filter); this can apparently yield a significant performance increase.
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On 10/01/2014 03:36 PM, Tony Krowiak wrote:
On 09/30/2014 02:28 PM, Tony Krowiak wrote:
These patches set up an event handler for qemu's NIC_RX_FILTER_CHANGED event, which is sent whenever a guest makes a change to a network device's unicast/multicast filter, vlan table, or MAC address.
The handler checks if it is appropriate to respond to the NIC_RX_FILTER_CHANGED event (based on device type and configuration) and takes appropriate action. Currently it checks if the guest interface has been configured with trustGuestRxFilters='yes' (defaults to 'no' for security reasons), and if the host side device is macvtap. If so, and the MAC address on the guest has changed, the MAC address of the macvtap device is changed to match.
The result of this is that networking from the guest will continue to work if the mac address of a macvtap-connected network device is changed from within the guest, as long as trustGuestRxFilters='yes' (previously changing the MAC address in the guest would break networking).
I still need to add code to compare the old and new unicast and multicast lists and program the filters in the macvtap to match the guest, and to check for a non-empty vlan table and handle that (currently that means just setting promiscuous mode on the macvtap), but that can come in a followup series. I was very interested in this patch set because I developed a set of
On 09/24/2014 05:50 AM, Laine Stump wrote: patches to respond to the NIC_RX_FILTER_CHANGED event. I completed the patch set several weeks ago and have been awaiting completion of our internal review before submitting them to this mailing list. Apparently you beat me to the punch. I have code that compares the old and new multicast lists and synchronizes the macvtap filters with the guest's. I can modify my patches to integrate this function into what you have provided with this patch set. Would that be agreeable? Since I've just started working on exactly that, yes :-)
What I'd started was a function virNetDevGetRxFilter(ifname, filter) in virnetdev.c which would do for the host-side tap/macvtap device what qemuMonitorQueryRxFilter() does for the guest's interface - retrieve the current unicast/multicast/vlan tables & modes (using code from iproute2's "bridge" command as a guide to write equivalent libnl-based code) and return them in a fully-populated virNetDevRxFilter object (that's why I defined that struct in virnetdev.h even though I've so far used it only in the qemu driver), which would be called from the event handler; the event handler would compare the two virNetDevRxFilter objects (the one from the host-side device and the one from the guest-side device) and issue the necessary commands to make everything match (well, actually what I've been told is that in the case of vlans, if the guest has a non-empty vlan table we currently have to just set the macvtap to promiscuous mode).
It sounds like you're only interested in the multicast list, so if you wanted to fill in enough to make it do that, your code could be used as a model to do the unicast list (which seems to be empty most of the time anyway; as I usually operate above that layer, I'm truthfully not exactly sure when it's even used). I am interested in a complete solution, however; I chose to ignore the unicast
On 09/30/2014 03:47 PM, Laine Stump wrote: list for the time being for reasons similar to yours. I wrote code to synchronize the VLAN table, but subsequently learned that there were problems with VLAN on macvtap, so I took that code out of the patch set.
I used the "ip maddr show" command in iproute2 as a model for acquiring the multicast list. This command reads the /proc/net/dev_mcast file on the host to get the current multicast MAC address list for the macvtap device. To synchronize the macvtap device's multicast list, I compared the list from /proc/net/dev_mcast with that returned from the query_rx_filter command and used the SIOCADDMULTI and SIOCDELMULTI ioctl's add and delete multicast MAC addresses as needed. I did not do anything with the mode (state) values as I have yet to figure out how to do that.
I also wrote code to synchronize the following interface flags: * promiscuous * multicast * allmulti * broadcast
I can integrate what I have with your infrastructure including: * Create a virNetDevGetRxFilter(ifname, filter) function in virnetdev.c that will populate the following fields in the filter: * name * mac * promiscuous * broadcastAllowed * multicast.table * multicast.nTable * vlan.table * vlan.nTable * Implement the event handler code to: * compare the two virNetDevRxFilter objects and issue the necessary commands to synchronize the multicast MAC address lists * set the promiscuous mode if the guest has a non-empty vlan table * compare the device flags and synchronize their values
If you think this would be a worthwhile endeavor, I've got your patches installed and can proceed.
Sure. I would prefer using netlink over /proc, but I have yet to figure out the code to retrieve the multicast list in that manner (see my above mention of iproute's "bridge" command - interesting that two different parts of the same package retrieve the info in different ways). It would be very useful if virNetDevGetRxFilter() called a function for each one of the components (e.g. virNetDevGetMcastList(), virNetDevGetPromiscuous(), virNetDevAddMcast(), virNetDevDelMCast(), etc) so that the implementation of each of these functions could be easily changed to use netlink (or whatever was appropriate for any given platform) based on some configure-time conditionals. I'm about to post V2 of my patches, which have minor changes, so you may want to use those as a base instead of the originals.

On 10/02/2014 10:19 AM, Laine Stump wrote:
On 09/30/2014 02:28 PM, Tony Krowiak wrote:
These patches set up an event handler for qemu's NIC_RX_FILTER_CHANGED event, which is sent whenever a guest makes a change to a network device's unicast/multicast filter, vlan table, or MAC address.
The handler checks if it is appropriate to respond to the NIC_RX_FILTER_CHANGED event (based on device type and configuration) and takes appropriate action. Currently it checks if the guest interface has been configured with trustGuestRxFilters='yes' (defaults to 'no' for security reasons), and if the host side device is macvtap. If so, and the MAC address on the guest has changed, the MAC address of the macvtap device is changed to match.
The result of this is that networking from the guest will continue to work if the mac address of a macvtap-connected network device is changed from within the guest, as long as trustGuestRxFilters='yes' (previously changing the MAC address in the guest would break networking).
I still need to add code to compare the old and new unicast and multicast lists and program the filters in the macvtap to match the guest, and to check for a non-empty vlan table and handle that (currently that means just setting promiscuous mode on the macvtap), but that can come in a followup series. I was very interested in this patch set because I developed a set of
On 09/24/2014 05:50 AM, Laine Stump wrote: patches to respond to the NIC_RX_FILTER_CHANGED event. I completed the patch set several weeks ago and have been awaiting completion of our internal review before submitting them to this mailing list. Apparently you beat me to the punch. I have code that compares the old and new multicast lists and synchronizes the macvtap filters with the guest's. I can modify my patches to integrate this function into what you have provided with this patch set. Would that be agreeable? Since I've just started working on exactly that, yes :-)
What I'd started was a function virNetDevGetRxFilter(ifname, filter) in virnetdev.c which would do for the host-side tap/macvtap device what qemuMonitorQueryRxFilter() does for the guest's interface - retrieve the current unicast/multicast/vlan tables & modes (using code from iproute2's "bridge" command as a guide to write equivalent libnl-based code) and return them in a fully-populated virNetDevRxFilter object (that's why I defined that struct in virnetdev.h even though I've so far used it only in the qemu driver), which would be called from the event handler; the event handler would compare the two virNetDevRxFilter objects (the one from the host-side device and the one from the guest-side device) and issue the necessary commands to make everything match (well, actually what I've been told is that in the case of vlans, if the guest has a non-empty vlan table we currently have to just set the macvtap to promiscuous mode).
It sounds like you're only interested in the multicast list, so if you wanted to fill in enough to make it do that, your code could be used as a model to do the unicast list (which seems to be empty most of the time anyway; as I usually operate above that layer, I'm truthfully not exactly sure when it's even used). I am interested in a complete solution, however; I chose to ignore the unicast
On 09/30/2014 03:47 PM, Laine Stump wrote: list for the time being for reasons similar to yours. I wrote code to synchronize the VLAN table, but subsequently learned that there were problems with VLAN on macvtap, so I took that code out of the patch set.
I used the "ip maddr show" command in iproute2 as a model for acquiring the multicast list. This command reads the /proc/net/dev_mcast file on the host to get the current multicast MAC address list for the macvtap device. To synchronize the macvtap device's multicast list, I compared the list from /proc/net/dev_mcast with that returned from the query_rx_filter command and used the SIOCADDMULTI and SIOCDELMULTI ioctl's add and delete multicast MAC addresses as needed. I did not do anything with the mode (state) values as I have yet to figure out how to do that.
I also wrote code to synchronize the following interface flags: * promiscuous * multicast * allmulti * broadcast
I can integrate what I have with your infrastructure including: * Create a virNetDevGetRxFilter(ifname, filter) function in virnetdev.c that will populate the following fields in the filter: * name * mac * promiscuous * broadcastAllowed * multicast.table * multicast.nTable * vlan.table * vlan.nTable * Implement the event handler code to: * compare the two virNetDevRxFilter objects and issue the necessary commands to synchronize the multicast MAC address lists * set the promiscuous mode if the guest has a non-empty vlan table * compare the device flags and synchronize their values
If you think this would be a worthwhile endeavor, I've got your patches installed and can proceed. Sure. I would prefer using netlink over /proc, but I have yet to figure out the code to retrieve the multicast list in that manner (see my above mention of iproute's "bridge" command - interesting that two different
On 10/01/2014 03:36 PM, Tony Krowiak wrote: parts of the same package retrieve the info in different ways). I spent some time back when I first started looking at this trying to figure out how to use netlink to retrieve the data, but was unable to find much doc on how to do it, so I ultimately settled on /proc. I think the code can be structured in such a way that the /proc code can eventually be replaced with netlink.
It would be very useful if virNetDevGetRxFilter() called a function for each one of the components (e.g. virNetDevGetMcastList(), virNetDevGetPromiscuous(), virNetDevAddMcast(), virNetDevDelMCast(), etc) so that the implementation of each of these functions could be easily changed to use netlink (or whatever was appropriate for any given platform) based on some configure-time conditionals. That is how my code is currently structured. I just need to change it to use your structure.
I'm about to post V2 of my patches, which have minor changes, so you may want to use those as a base instead of the originals. Will do.
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
participants (4)
-
Amos Kong
-
John Ferlan
-
Laine Stump
-
Tony Krowiak