[libvirt] [PATCH] [DOCS] nwfilter: documentation
by Stefan Berger
This patch adds documentation of the nwfilter subsystem of libvirt to
the existing (web) docs. I am attaching a PDF in case you don't want to
read the plain html sources.
Signed-off-by: Stefan Berger <stefanb(a)linux.vnet.ibm.com>
---
docs/formatnwfilter.html.in | 1407 ++++++++++++++++++++++++++++++++++++++++++++
docs/sitemap.html.in | 6
2 files changed, 1413 insertions(+)
Index: libvirt-acl/docs/sitemap.html.in
===================================================================
--- libvirt-acl.orig/docs/sitemap.html.in
+++ libvirt-acl/docs/sitemap.html.in
@@ -97,6 +97,12 @@
<li>
<a href="formatnetwork.html">Networks</a>
<span>The virtual network XML format</span>
+ <ul>
+ <li>
+ <a href="formatnwfilter.html">Network Filtering</a>
+ <span>Network filter XML format</span>
+ </li>
+ </ul>
</li>
<li>
<a href="formatstorage.html">Storage</a>
Index: libvirt-acl/docs/formatnwfilter.html.in
===================================================================
--- /dev/null
+++ libvirt-acl/docs/formatnwfilter.html.in
@@ -0,0 +1,1407 @@
+<html>
+ <body>
+ <h1>Network Filters</h1>
+
+ <ul id="toc">
+ </ul>
+
+ <p>
+ This page provides an introduction to libvirt's network filters,
+ their goals, concepts and XML format.
+ </p>
+
+ <h2><a name="goals">Goals and background</a></h2>
+
+ <p>
+ The goal of the network filtering XML is to enable administrators
+ of virtualized system to configure and enforce network traffic
+ filtering rules on virtual
+ machines and manage the parameters of network traffic that
+ virtual machines
+ are allowed to send or receive.
+ The network traffic filtering rules are
+ applied on the host when a virtual machine is started. Since the
+ filtering rules
+ cannot be circumvented from within
+ the virtual machine, it makes them mandatory from the point of
+ view of a virtual machine user.
+ <br><br>
+ The network filter subsystem allows each virtual machine's network
+ traffic filtering rules to be configured individually on a per
+ interface basis. The rules are
+ applied on the host when the virtual machine is started and can be modified
+ while the virtual machine is running. The latter can be achieved by
+ modifying the XML description of a network filter.
+ <br><br>
+ Multiple virtual machines can make use of the same generic network filter.
+ When such a filter is modified, the network traffic filtering rules
+ of all running virtual machines that reference this filter are updated.
+ <br><br>
+ Network filtering support is available <span class="since">since 0.8.1
+ (Qemu, KVM)</span>
+ </p>
+
+ <h2><a name="nwfconcpts">Concepts</a></h2>
+ <p>
+ The network traffic filtering subsystem enables configuration
+ of network traffic filtering rules on individual network
+ interfaces that are configured for certain types of
+ network configurations. Supported network types are
+ </p>
+ <ul>
+ <li><code>network</code></li>
+ <li><code>ethernet</code> -- must be used in bridging mode</li>
+ <li><code>bridge</code></li>
+ <li><code>direct</code> -- only protocols mac, arp, ip and ipv6
+ can be filtered</li>
+ </ul>
+ <p>
+ The interface XML is used to reference a top-level filter. In the
+ following example, the interface description references
+ the filter <code>clean-traffic</code>.
+ </p>
+<pre>
+ ...
+ <devices>
+ <interface type='bridge'>
+ <mac address='00:16:3e:5d:c7:9e'/>
+ <filterref filter='clean-traffic'/>
+ </interface>
+ </devices>
+ ...</pre>
+
+ <p>
+ Network filters are written in XML and may either contain references
+ to other filters, contain rules for traffic filtering or can
+ hold a combination of both. The above referenced filter
+ <code>clean-traffic </code> is a filter that for example only
+ contains references to
+ other filters and no actual filtering rules. Since references to
+ other filters can be used, a <i>tree</i> of filters can be built.
+ The <code>clean-traffic</code> filter can be viewed using the
+ command <code>virsh nwfilter-dumpxml clean-traffic</code>.
+ <br><br>
+ As previously mentioned, a single network filter can be referenced
+ by multiple virtual machines. Since interfaces will typically
+ have individual parameters associated with their respective traffic
+ filtering rules, the rules described in a filter XML can
+ be parameterized with variables. In this case, the variable name
+ is used in the filter XML and the name and value are provided at the
+ place where the filter is referenced. In the
+ following example, the interface description has been extended with
+ the parameter <code>IP</code> and a dotted IP address as value.
+ </p>
+<pre>
+ ...
+ <devices>
+ <interface type='bridge'>
+ <mac address='00:16:3e:5d:c7:9e'/>
+ <filterref filter='clean-traffic'>
+ <parameter name='IP' value='10.0.0.1'/>
+ </filterref>
+ </interface>
+ </devices>
+ ...</pre>
+
+ <p>
+ In this particular example, the <code>clean-traffic</code> network
+ traffic filter will be instantiated with the IP address parameter
+ 10.0.0.1 and enforce that the traffic from this interface will
+ always be using 10.0.0.1 as the source IP address, which is
+ one of the purposes of this particular filter.
+ <br><br>
+ </p>
+
+ <h3><a name="nwfconcptsvars">Usage of variables in filters</a></h3>
+ <p>
+
+ Two variables names have so far been reserved for usage by the
+ network traffic filtering subsystem: <code>MAC</code> and
+ <code>IP</code>.
+ <br><br>
+ <code>MAC</code> is the MAC address of the
+ network interface. A filtering rule that references this variable
+ will automatically be instantiated with the MAC address of the
+ interface. This works without the user having to explicitly provide
+ the MAC parameter. Even though it is possible to specify the MAC
+ parameter similar to the IP parameter above, it is discouraged
+ since libvirt knows what MAC address an interface will be using.
+ <br><br>
+ The parameter <code>IP</code> represents the IP address
+ that the operating system inside the virtual machine is expected
+ to use on the given interface. The <code>IP</code> parameter
+ is special in so far as the libvirt daemon will try to determine
+ the IP address (and thus the IP parameter's value) that is being
+ used on an interface if the parameter
+ is not explicitly provided but referenced.
+ For current limitations on IP address detection, consult the
+ <a href="#nwflimits">section on limitations</a> on how to use this
+ feature and what to expect when using it.
+ <br><br>
+ The following is the XML description of the network filer
+ <code>no-arp-spoofing</code>. It serves as an example for
+ a network filter XML referencing the <code>MAC</code> and
+ <code>IP</code> parameters. This particular filter is referenced by the
+ <code>clean-traffic</code> filter.
+ </p>
+<pre>
+<filter name='no-arp-spoofing' chain='arp'>
+ <uuid>f88f1932-debf-4aa1-9fbe-f10d3aa4bc95</uuid>
+ <rule action='drop' direction='out' priority='300'>
+ <mac match='no' srcmacaddr='$MAC'/>
+ </rule>
+ <rule action='drop' direction='out' priority='350'>
+ <arp match='no' arpsrcmacaddr='$MAC'/>
+ </rule>
+ <rule action='drop' direction='out' priority='400'>
+ <arp match='no' arpsrcipaddr='$IP'/>
+ </rule>
+ <rule action='drop' direction='in' priority='450'>
+ <arp opcode='Reply'/>
+ <arp match='no' arpdstmacaddr='$MAC'/>
+ </rule>
+ <rule action='drop' direction='in' priority='500'>
+ <arp match='no' arpdstipaddr='$IP'/>
+ </rule>
+ <rule action='accept' direction='inout' priority='600'>
+ <arp opcode='Request'/>
+ </rule>
+ <rule action='accept' direction='inout' priority='650'>
+ <arp opcode='Reply'/>
+ </rule>
+ <rule action='drop' direction='inout' priority='1000'/>
+</filter>
+</pre>
+
+ <p>
+ Note that referenced variables are always prefixed with the
+ $ (dollar) sign. The format of the value of a variable
+ must be of the type expected by the filter attribute in the
+ XML. In the above example, the <code>IP</code> parameter
+ must hold a dotted IP address in decimal numbers format.
+ Failure to provide the correct
+ value type will result in the filter not being instantiatable
+ and will prevent a virtual machine from starting or the
+ interface from attaching when hotplugging is used. The types
+ that are expected for each XML attribute are shown
+ below.
+ </p>
+
+ <h2><a name="nwfelems">Element and attribute overview</a></h2>
+
+ <p>
+ The root element required for all network filters is
+ named <code>filter</code> with two possible attributes. The
+ <code>name</code> attribute provides a unique name of the
+ given filter. The <code>chain</code> attribute is optional but
+ allows certain filters to be better organized for more efficient
+ processing by the firewall subsystem of the underlying host.
+ Currently the system only supports the chains <code>root,
+ ipv4, ipv6, arp and rarp</code>.
+ </p>
+
+ <h3><a name="nwfelemsRefs">References to other filers</a></h3>
+ <p>
+ Any filter may hold references to other filters. Individual
+ filters may be referenced multiple times in a filter tree but
+ references between filters must not introduce loops (directed
+ acyclic graph).
+ <br><br>
+ The following shows the XML of the <code>clean-traffic</code>
+ network filter referencing several other filters.
+ </p>
+<pre>
+<filter name='clean-traffic'>
+ <uuid>6ef53069-ba34-94a0-d33d-17751b9b8cb1</uuid>
+ <filterref filter='no-mac-spoofing'/>
+ <filterref filter='no-ip-spoofing'/>
+ <filterref filter='allow-incoming-ipv4'/>
+ <filterref filter='no-arp-spoofing'/>
+ <filterref filter='no-other-l2-traffic'/>
+ <filterref filter='qemu-announce-self'/>
+</filter>
+</pre>
+
+ <p>
+ To reference another filter, the XML node <code>filterref</code>
+ needs to be provided inside a <code>filter</code> node. This
+ node must have the attribute <code>filter</code> whose value contains
+ the name of the filter to be referenced.
+ <br><br>
+ New network filters can be defined at any time and
+ may contain references to network filters that are
+ not known to libvirt, yet. However, once a virtual machine
+ is started or a network interface
+ referencing a filter is to be hotplugged, all network filters
+ in the filter tree must be available. Otherwise the virtual
+ machine will not start or the network interface cannot be
+ attached.
+ </p>
+
+ <h3><a name="nwfelemsRules">Filter rules</a></h3>
+ <p>
+ The following XML shows a simple example of a network
+ traffic filter implementing a rule to drop traffic if
+ the IP address (provided through the value of the
+ variable IP) in an outgoing IP packet is not the expected
+ one, thus preventing IP address spoofing by the VM.
+ </p>
+<pre>
+<filter name='no-ip-spoofing' chain='ipv4'>
+ <uuid>fce8ae33-e69e-83bf-262e-30786c1f8072</uuid>
+ <rule action='drop' direction='out' priority='500'>
+ <ip match='no' srcipaddr='$IP'/>
+ </rule>
+</filter>
+</pre>
+
+ <p>
+ A traffic filtering rule starts with the <code>rule</code>
+ node. This node may contain up to three attributes
+ </p>
+ <ul>
+ <li>
+ action -- mandatory; must either be <code>drop</code> or <code>accept</code> if
+ the evaluation of the filtering rule is supposed to drop or accept
+ a packet
+ </li>
+ <li>
+ direction -- mandatory; must either be <code>in</code>, <code>out</code> or
+ <code>inout</code> if the rule is for incoming,
+ outgoing or incoming-and-outgoing traffic
+ </li>
+ <li>
+ priority -- optional; the priority of the rule controls the order in
+ which the rule will be instantiated relative to other rules.
+ Rules with lower value will be instantiated and therefore evaluated
+ before rules with higher value.
+ Valid values are in the range of 0 to 1000. If this attribute is not
+ provided, the value 500 will automatically be assigned.
+ </li>
+ </ul>
+ <p>
+ The above example indicates that the traffic of type <code>ip</code>
+ will be asscociated with the chain 'ipv4' and the rule will have
+ priority 500. If for example another filter is referenced whose
+ traffic of type <code>ip</code> is also associated with the chain
+ 'ipv4' then that filter's rules will be ordered relative to the priority
+ 500 of the shown rule.
+ <br><br>
+ A rule may contain a single rule for filtering of traffic. The
+ above example shows that traffic of type <code>ip</code> is to be
+ filtered.
+ </p>
+
+ <h4><a name="nwfelemsRulesProto">Supported protocols</a></h4>
+ <p>
+ The following sections enumerate the list of protocols that
+ are supported by the network filtering subsystem. The
+ type of traffic a rule is supposed to filter on is provided
+ in the <code>rule</code> node as a nested node. Depending
+ on the traffic type a rule is filtering, the attributes are
+ different. The above example showed the single
+ attribute <code>srcipaddr</code> that is valid inside the
+ <code>ip</code> traffic filtering node. The following sections
+ show what attributes are valid and what type of data they are
+ expecting. The following datatypes are available:
+ </p>
+ <ul>
+ <li>UINT8 : 8 bit integer; range 0-255</li>
+ <li>UINT16: 16 bit integer; range 0-65535</li>
+ <li>MAC_ADDR: MAC adrress in dotted decimal format, i.e., 00:11:22:33:44:55</li>
+ <li>MAC_MASK: MAC address mask in MAC address format, i.e., FF:FF:FF:FC:00:00</li>
+ <li>IP_ADDR: IP address in dotted decimal format, i.e., 10.1.2.3</li>
+ <li>IP_MASK: IP address mask in either dotted decimal format (255.255.248.0) or CIDR mask (0-32)</li>
+ <li>IPV6_ADDR: IPv6 address in numbers format, i.e., FFFF::1</li>
+ <li>IPV6_MASK: IPv6 mask in numbers format (FFFF:FFFF:FC00::) or CIDR mask (0-128)</li>
+ <li>STRING: A string</li>
+ </ul>
+ <p>
+ <br><br>
+ Every attribute except for those of type IP_MASK or IPV6_MASK can
+ be negated using the <code>match</code>
+ attribute with value <code>no</code>. Multiple negated attributes
+ may be grouped together. The following
+ XML fragment shows such an example using abstract attributes.
+ </p>
+<pre>
+[...]
+ <rule action='drop' direction='in'>
+ <protocol match='no' attribute1='value1' attribute2='value2'/>
+ <protocol attribute3='value3'/>
+ </rule>
+[...]
+</pre>
+ <p>
+ Rules perform a logical AND evaluation on all values of the given
+ protocol attributes. Thus, if a single attribute's value does not match
+ the one given in the rule, the whole rule will be skipped during
+ evaluation. Therefore, in the above example incoming traffic
+ will only be dropped if
+ the protocol property attribute1 does not match value1 AND
+ the protocol property attribute2 does not match value2 AND
+ the protocol property attribute3 matches value3.
+ <br><br>
+ </p>
+
+
+ <h5><a name="nwfelemsRulesProtoMAC">MAC (Ethernet)</a></h5>
+ <p>
+ Protocol ID: <code>mac</code>
+ <br>
+ Note: Rules of this type should go into the <code>root</code> chain.
+ </p>
+ <table class="top_table">
+ <tr>
+ <th> Attribute </th>
+ <th> Datatype </th>
+ <th> Semantics </th>
+ </tr>
+ <tr>
+ <td>srcmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>srcmacmask</td>
+ <td>MAC_MASK</td>
+ <td>Mask applied to MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>dstmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of destination</td>
+ </tr>
+ <tr>
+ <td>dstmacmask</td>
+ <td>MAC_MASK</td>
+ <td>Mask applied to MAC address of destination</td>
+ </tr>
+ <tr>
+ <td>protocolid</td>
+ <td>UINT16 (0x600-0xffff), STRING</td>
+ <td>Layer 3 protocol ID</td>
+ </tr>
+ </table>
+ <p>
+ Valid Strings for <code>protocolid</code> are: arp, rarp, ipv4, ipv6
+ <br><br>
+ Example: <pre><mac match='no' srcmacaddr='$MAC'/></pre>
+ <br><br>
+ </p>
+
+ <h5><a name="nwfelemsRulesProtoARP">ARP/RARP</a></h5>
+ <p>
+ Protocol ID: <code>arp</code> or <code>rarp</code>
+ <br>
+ Note: Rules of this type should either go into the
+ <code>root</code> or <code>arp/rarp</code> chain.
+ </p>
+ <table class="top_table">
+ <tr>
+ <th> Attribute </th>
+ <th> Datatype </th>
+ <th> Semantics </th>
+ </tr>
+ <tr>
+ <td>srcmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>srcmacmask</td>
+ <td>MAC_MASK</td>
+ <td>Mask applied to MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>dstmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of destination</td>
+ </tr>
+ <tr>
+ <td>dstmacmask</td>
+ <td>MAC_MASK</td>
+ <td>Mask applied to MAC address of destination</td>
+ </tr>
+ <tr>
+ <td>hwtype</td>
+ <td>UINT16</td>
+ <td>Hardware type</td>
+ </tr>
+ <tr>
+ <td>protocoltype</td>
+ <td>UINT16</td>
+ <td>Protocol type</td>
+ </tr>
+ <tr>
+ <td>opcode</td>
+ <td>UINT16, STRING</td>
+ <td>Opcode</td>
+ </tr>
+ <tr>
+ <td>arpsrcmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>Source MAC address in ARP/RARP packet</td>
+ </tr>
+ <tr>
+ <td>arpdstmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>Destination MAC address in ARP/RARP packet</td>
+ </tr>
+ <tr>
+ <td>arpsrcipaddr</td>
+ <td>IP_ADDR</td>
+ <td>Source IP address in ARP/RARP packet</td>
+ </tr>
+ <tr>
+ <td>arpdstipaddr</td>
+ <td>IP_ADDR</td>
+ <td>Destination IP address in ARP/RARP packet</td>
+ </tr>
+ </table>
+ <p>
+ Valid strings for the <code>Opcode</code> field are:
+ Request, Reply, Request_Reverse, Reply_Reverse, DRARP_Request,
+ DRARP_Reply, DRARP_Error, InARP_Request, ARP_NAK
+ <br><br>
+ </p>
+
+ <h5><a name="nwfelemsRulesProtoIP">IPv4</a></h5>
+ <p>
+ Protocol ID: <code>ip</code>
+ Note: Rules of this type should either go into the
+ <code>root</code> or <code>ipv4</code> chain.
+ </p>
+ <table class="top_table">
+ <tr>
+ <th> Attribute </th>
+ <th> Datatype </th>
+ <th> Semantics </th>
+ </tr>
+ <tr>
+ <td>srcmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>srcmacmask</td>
+ <td>MAC_MASK</td>
+ <td>Mask applied to MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>dstmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of destination</td>
+ </tr>
+ <tr>
+ <td>dstmacmask</td>
+ <td>MAC_MASK</td>
+ <td>Mask applied to MAC address of destination</td>
+ </tr>
+ <tr>
+ <td>srcipaddr</td>
+ <td>IP_ADDR</td>
+ <td>Source IP address</td>
+ </tr>
+ <tr>
+ <td>srcipmask</td>
+ <td>IP_MASK</td>
+ <td>Mask applied to source IP address</td>
+ </tr>
+ <tr>
+ <td>dstipaddr</td>
+ <td>IP_ADDR</td>
+ <td>Destination IP address</td>
+ </tr>
+ <tr>
+ <td>dstipmask</td>
+ <td>IP_MASK</td>
+ <td>Mask applied to destination IP address</td>
+ </tr>
+ <tr>
+ <td>protocol</td>
+ <td>UINT8, STRING</td>
+ <td>Layer 4 protocol identifier</td>
+ </tr>
+ <tr>
+ <td>srcportstart</td>
+ <td>UINT16</td>
+ <td>Start of range of valid source ports; requires <code>protocol</code></td>
+ </tr>
+ <tr>
+ <td>srcportend</td>
+ <td>UINT16</td>
+ <td>End of range of valid source ports; requires <code>protocol</code></td>
+ </tr>
+ <tr>
+ <td>dstportstart</td>
+ <td>UINT16</td>
+ <td>Start of range of valid destination ports; requires <code>protocol</code></td>
+ </tr>
+ <tr>
+ <td>dstportend</td>
+ <td>UINT16</td>
+ <td>End of range of valid destination ports; requires <code>protocol</code></td>
+ </tr>
+ </table>
+ <p>
+ Valid strings for <code>protocol</code> are:
+ tcp, udp, udplite, esp, ah, icmp, igmp, sctp
+ <br><br>
+ </p>
+
+
+ <h5><a name="nwfelemsRulesProtoIPv6">IPv6</a></h5>
+ <p>
+ Protocol ID: <code>ipv6</code>
+ Note: Rules of this type should either go into the
+ <code>root</code> or <code>ipv6</code> chain.
+ </p>
+ <table class="top_table">
+ <tr>
+ <th> Attribute </th>
+ <th> Datatype </th>
+ <th> Semantics </th>
+ </tr>
+ <tr>
+ <td>srcmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>srcmacmask</td>
+ <td>MAC_MASK</td>
+ <td>Mask applied to MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>dstmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of destination</td>
+ </tr>
+ <tr>
+ <td>dstmacmask</td>
+ <td>MAC_MASK</td>
+ <td>Mask applied to MAC address of destination</td>
+ </tr>
+ <tr>
+ <td>srcipaddr</td>
+ <td>IPV6_ADDR</td>
+ <td>Source IPv6 address</td>
+ </tr>
+ <tr>
+ <td>srcipmask</td>
+ <td>IPV6_MASK</td>
+ <td>Mask applied to source IPv6 address</td>
+ </tr>
+ <tr>
+ <td>dstipaddr</td>
+ <td>IPV6_ADDR</td>
+ <td>Destination IPv6 address</td>
+ </tr>
+ <tr>
+ <td>dstipmask</td>
+ <td>IPV6_MASK</td>
+ <td>Mask applied to destination IPv6 address</td>
+ </tr>
+ <tr>
+ <td>protocol</td>
+ <td>UINT8</td>
+ <td>Layer 4 protocol identifier</td>
+ </tr>
+ <tr>
+ <td>srcportstart</td>
+ <td>UINT16</td>
+ <td>Start of range of valid source ports; requires <code>protocol</code></td>
+ </tr>
+ <tr>
+ <td>srcportend</td>
+ <td>UINT16</td>
+ <td>End of range of valid source ports; requires <code>protocol</code></td>
+ </tr>
+ <tr>
+ <td>dstportstart</td>
+ <td>UINT16</td>
+ <td>Start of range of valid destination ports; requires <code>protocol</code></td>
+ </tr>
+ <tr>
+ <td>dstportend</td>
+ <td>UINT16</td>
+ <td>End of range of valid destination ports; requires <code>protocol</code></td>
+ </tr>
+ </table>
+ <p>
+ Valid strings for <code>protocol</code> are:
+ tcp, udp, udplite, esp, ah, icmpv6, sctp
+ <br><br>
+ </p>
+
+ <h5><a name="nwfelemsRulesProtoTCP-ipv4">TCP/UDP/SCTP</a></h5>
+ <p>
+ Protocol ID: <code>tcp</code>, <code>udp</code>, <code>sctp</code>
+ <br>
+ Note: The chain parameter is ignored for this type of traffic
+ and should either be omitted or set to <code>root</code>.
+ </p>
+ <table class="top_table">
+ <tr>
+ <th> Attribute </th>
+ <th> Datatype </th>
+ <th> Semantics </th>
+ </tr>
+ <tr>
+ <td>srcmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>srcipaddr</td>
+ <td>IP_ADDR</td>
+ <td>Source IP address</td>
+ </tr>
+ <tr>
+ <td>srcipmask</td>
+ <td>IP_MASK</td>
+ <td>Mask applied to source IP address</td>
+ </tr>
+ <tr>
+ <td>dstipaddr</td>
+ <td>IP_ADDR</td>
+ <td>Destination IP address</td>
+ </tr>
+ <tr>
+ <td>dstipmask</td>
+ <td>IP_MASK</td>
+ <td>Mask applied to destination IP address</td>
+ </tr>
+
+ <tr>
+ <td>srcipfrom</td>
+ <td>IP_ADDR</td>
+ <td>Start of range of source IP address</td>
+ </tr>
+ <tr>
+ <td>srcipto</td>
+ <td>IP_ADDR</td>
+ <td>End of range of source IP address</td>
+ </tr>
+ <tr>
+ <td>dstipfrom</td>
+ <td>IP_ADDR</td>
+ <td>Start of range of destination IP address</td>
+ </tr>
+ <tr>
+ <td>dstipto</td>
+ <td>IP_ADDR</td>
+ <td>End of range of destination IP address</td>
+ </tr>
+
+ <tr>
+ <td>srcportstart</td>
+ <td>UINT16</td>
+ <td>Start of range of valid source ports</td>
+ </tr>
+ <tr>
+ <td>srcportend</td>
+ <td>UINT16</td>
+ <td>End of range of valid source ports</code></td>
+ </tr>
+ <tr>
+ <td>dstportstart</td>
+ <td>UINT16</td>
+ <td>Start of range of valid destination ports</code></td>
+ </tr>
+ <tr>
+ <td>dstportend</td>
+ <td>UINT16</td>
+ <td>End of range of valid destination ports</td>
+ </tr>
+ </table>
+ <p>
+ <br><br>
+ </p>
+
+
+ <h5><a name="nwfelemsRulesProtoICMP">ICMP</a></h5>
+ <p>
+ Protocol ID: <code>icmp</code>
+ <br>
+ Note: The chain parameter is ignored for this type of traffic
+ and should either be omitted or set to <code>root</code>.
+ </p>
+ <table class="top_table">
+ <tr>
+ <th> Attribute </th>
+ <th> Datatype </th>
+ <th> Semantics </th>
+ </tr>
+ <tr>
+ <td>srcmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>srcmacmask</td>
+ <td>MAC_MASK</td>
+ <td>Mask applied to MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>dstmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of destination</td>
+ </tr>
+ <tr>
+ <td>dstmacmask</td>
+ <td>MAC_MASK</td>
+ <td>Mask applied to MAC address of destination</td>
+ </tr>
+ <tr>
+ <td>srcipaddr</td>
+ <td>IP_ADDR</td>
+ <td>Source IP address</td>
+ </tr>
+ <tr>
+ <td>srcipmask</td>
+ <td>IP_MASK</td>
+ <td>Mask applied to source IP address</td>
+ </tr>
+ <tr>
+ <td>dstipaddr</td>
+ <td>IP_ADDR</td>
+ <td>Destination IP address</td>
+ </tr>
+ <tr>
+ <td>dstipmask</td>
+ <td>IP_MASK</td>
+ <td>Mask applied to destination IP address</td>
+ </tr>
+
+ <tr>
+ <td>srcipfrom</td>
+ <td>IP_ADDR</td>
+ <td>Start of range of source IP address</td>
+ </tr>
+ <tr>
+ <td>srcipto</td>
+ <td>IP_ADDR</td>
+ <td>End of range of source IP address</td>
+ </tr>
+ <tr>
+ <td>dstipfrom</td>
+ <td>IP_ADDR</td>
+ <td>Start of range of destination IP address</td>
+ </tr>
+ <tr>
+ <td>dstipto</td>
+ <td>IP_ADDR</td>
+ <td>End of range of destination IP address</td>
+ </tr>
+ <tr>
+ <td>type</td>
+ <td>UINT16</td>
+ <td>ICMP type</td>
+ </tr>
+ <tr>
+ <td>code</td>
+ <td>UINT16</td>
+ <td>ICMP code</td>
+ </tr>
+ </table>
+ <p>
+ <br><br>
+ </p>
+
+ <h5><a name="nwfelemsRulesProtoMisc">IGMP, ESP, AH, UDPLITE, 'ALL'</a></h5>
+ <p>
+ Protocol ID: <code>igmp</code>, <code>esp</code>, <code>ah</code>, <code>udplite</code>, <code>all</code>
+ <br>
+ Note: The chain parameter is ignored for this type of traffic
+ and should either be omitted or set to <code>root</code>.
+ </p>
+ <table class="top_table">
+ <tr>
+ <th> Attribute </th>
+ <th> Datatype </th>
+ <th> Semantics </th>
+ </tr>
+ <tr>
+ <td>srcmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>srcmacmask</td>
+ <td>MAC_MASK</td>
+ <td>Mask applied to MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>dstmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of destination</td>
+ </tr>
+ <tr>
+ <td>dstmacmask</td>
+ <td>MAC_MASK</td>
+ <td>Mask applied to MAC address of destination</td>
+ </tr>
+ <tr>
+ <td>srcipaddr</td>
+ <td>IP_ADDR</td>
+ <td>Source IP address</td>
+ </tr>
+ <tr>
+ <td>srcipmask</td>
+ <td>IP_MASK</td>
+ <td>Mask applied to source IP address</td>
+ </tr>
+ <tr>
+ <td>dstipaddr</td>
+ <td>IP_ADDR</td>
+ <td>Destination IP address</td>
+ </tr>
+ <tr>
+ <td>dstipmask</td>
+ <td>IP_MASK</td>
+ <td>Mask applied to destination IP address</td>
+ </tr>
+
+ <tr>
+ <td>srcipfrom</td>
+ <td>IP_ADDR</td>
+ <td>Start of range of source IP address</td>
+ </tr>
+ <tr>
+ <td>srcipto</td>
+ <td>IP_ADDR</td>
+ <td>End of range of source IP address</td>
+ </tr>
+ <tr>
+ <td>dstipfrom</td>
+ <td>IP_ADDR</td>
+ <td>Start of range of destination IP address</td>
+ </tr>
+ <tr>
+ <td>dstipto</td>
+ <td>IP_ADDR</td>
+ <td>End of range of destination IP address</td>
+ </tr>
+ </table>
+ <p>
+ <br><br>
+ </p>
+
+
+ <h5><a name="nwfelemsRulesProtoTCP-ipv6">TCP/UDP/SCTP over IPV6</a></h5>
+ <p>
+ Protocol ID: <code>tcp-ipv6</code>, <code>udp-ipv6</code>, <code>sctp-ipv6</code>
+ <br>
+ Note: The chain parameter is ignored for this type of traffic
+ and should either be omitted or set to <code>root</code>.
+ </p>
+ <table class="top_table">
+ <tr>
+ <th> Attribute </th>
+ <th> Datatype </th>
+ <th> Semantics </th>
+ </tr>
+ <tr>
+ <td>srcmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>srcipaddr</td>
+ <td>IPV6_ADDR</td>
+ <td>Source IP address</td>
+ </tr>
+ <tr>
+ <td>srcipmask</td>
+ <td>IPV6_MASK</td>
+ <td>Mask applied to source IP address</td>
+ </tr>
+ <tr>
+ <td>dstipaddr</td>
+ <td>IPV6_ADDR</td>
+ <td>Destination IP address</td>
+ </tr>
+ <tr>
+ <td>dstipmask</td>
+ <td>IPV6_MASK</td>
+ <td>Mask applied to destination IP address</td>
+ </tr>
+
+ <tr>
+ <td>srcipfrom</td>
+ <td>IPV6_ADDR</td>
+ <td>Start of range of source IP address</td>
+ </tr>
+ <tr>
+ <td>srcipto</td>
+ <td>IPV6_ADDR</td>
+ <td>End of range of source IP address</td>
+ </tr>
+ <tr>
+ <td>dstipfrom</td>
+ <td>IPV6_ADDR</td>
+ <td>Start of range of destination IP address</td>
+ </tr>
+ <tr>
+ <td>dstipto</td>
+ <td>IPV6_ADDR</td>
+ <td>End of range of destination IP address</td>
+ </tr>
+
+ <tr>
+ <td>srcportstart</td>
+ <td>UINT16</td>
+ <td>Start of range of valid source ports</td>
+ </tr>
+ <tr>
+ <td>srcportend</td>
+ <td>UINT16</td>
+ <td>End of range of valid source ports</td>
+ </tr>
+ <tr>
+ <td>dstportstart</td>
+ <td>UINT16</td>
+ <td>Start of range of valid destination ports</td>
+ </tr>
+ <tr>
+ <td>dstportend</td>
+ <td>UINT16</td>
+ <td>End of range of valid destination ports</td>
+ </tr>
+ </table>
+ <p>
+ <br><br>
+ </p>
+
+
+ <h5><a name="nwfelemsRulesProtoICMPv6">ICMPv6</a></h5>
+ <p>
+ Protocol ID: <code>icmpv6</code>
+ <br>
+ Note: The chain parameter is ignored for this type of traffic
+ and should either be omitted or set to <code>root</code>.
+ </p>
+ <table class="top_table">
+ <tr>
+ <th> Attribute </th>
+ <th> Datatype </th>
+ <th> Semantics </th>
+ </tr>
+ <tr>
+ <td>srcmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>srcipaddr</td>
+ <td>IPV6_ADDR</td>
+ <td>Source IPv6 address</td>
+ </tr>
+ <tr>
+ <td>srcipmask</td>
+ <td>IPV6_MASK</td>
+ <td>Mask applied to source IPv6 address</td>
+ </tr>
+ <tr>
+ <td>dstipaddr</td>
+ <td>IPV6_ADDR</td>
+ <td>Destination IPv6 address</td>
+ </tr>
+ <tr>
+ <td>dstipmask</td>
+ <td>IPV6_MASK</td>
+ <td>Mask applied to destination IPv6 address</td>
+ </tr>
+
+ <tr>
+ <td>srcipfrom</td>
+ <td>IPV6_ADDR</td>
+ <td>Start of range of source IP address</td>
+ </tr>
+ <tr>
+ <td>srcipto</td>
+ <td>IPV6_ADDR</td>
+ <td>End of range of source IP address</td>
+ </tr>
+ <tr>
+ <td>dstipfrom</td>
+ <td>IPV6_ADDR</td>
+ <td>Start of range of destination IP address</td>
+ </tr>
+ <tr>
+ <td>dstipto</td>
+ <td>IPV6_ADDR</td>
+ <td>End of range of destination IP address</td>
+ </tr>
+
+ <tr>
+ <td>type</td>
+ <td>UINT16</td>
+ <td>ICMPv6 type</td>
+ </tr>
+ <tr>
+ <td>code</td>
+ <td>UINT16</td>
+ <td>ICMPv6 code</td>
+ </tr>
+ </table>
+ <p>
+ <br><br>
+ </p>
+
+ <h5><a name="nwfelemsRulesProtoMiscv6">IGMP, ESP, AH, UDPLITE, 'ALL' over IPv6</a></h5>
+ <p>
+ Protocol ID: <code>igmp-ipv6</code>, <code>esp-ipv6</code>, <code>ah-ipv6</code>, <code>udplite-ipv6</code>, <code>all-ipv6</code>
+ <br>
+ Note: The chain parameter is ignored for this type of traffic
+ and should either be omitted or set to <code>root</code>.
+ </p>
+ <table class="top_table">
+ <tr>
+ <th> Attribute </th>
+ <th> Datatype </th>
+ <th> Semantics </th>
+ </tr>
+ <tr>
+ <td>srcmacaddr</td>
+ <td>MAC_ADDR</td>
+ <td>MAC address of sender</td>
+ </tr>
+ <tr>
+ <td>srcipaddr</td>
+ <td>IPV6_ADDR</td>
+ <td>Source IPv6 address</td>
+ </tr>
+ <tr>
+ <td>srcipmask</td>
+ <td>IPV6_MASK</td>
+ <td>Mask applied to source IPv6 address</td>
+ </tr>
+ <tr>
+ <td>dstipaddr</td>
+ <td>IPV6_ADDR</td>
+ <td>Destination IPv6 address</td>
+ </tr>
+ <tr>
+ <td>dstipmask</td>
+ <td>IPV6_MASK</td>
+ <td>Mask applied to destination IPv6 address</td>
+ </tr>
+
+ <tr>
+ <td>srcipfrom</td>
+ <td>IPV6_ADDR</td>
+ <td>Start of range of source IP address</td>
+ </tr>
+ <tr>
+ <td>srcipto</td>
+ <td>IPV6_ADDR</td>
+ <td>End of range of source IP address</td>
+ </tr>
+ <tr>
+ <td>dstipfrom</td>
+ <td>IPV6_ADDR</td>
+ <td>Start of range of destination IP address</td>
+ </tr>
+ <tr>
+ <td>dstipto</td>
+ <td>IPV6_ADDR</td>
+ <td>End of range of destination IP address</td>
+ </tr>
+
+ </table>
+ <p>
+ <br><br>
+ </p>
+
+ <h2><a name="nwfcli">Command line tools</a></h2>
+ <p>
+ The libvirt command line tool <code>virsh</code> has been extended
+ with life-cycle support for network filters. All commands related
+ to the network filtering subsystem start with the prefix
+ <code>nwfilter</code>. The following commands are available:
+ <p>
+ <ul>
+ <li>nwfilter-list : list UUIDs and names of all network filters</li>
+ <li>nwfilter-define : define a new network filter or update an existing one</li>
+ <li>nwfilter-undefine : delete a network filter given its name; it must not be currently in use</li>
+ <li>nwfilter-dumpxml : display a network filter given its name</li>
+ <li>nwfilter-edit : edit a network filter given its name</li>
+ </ul>
+
+ <h2><a name="nwfexamples">Example network filters</a></h2>
+ <p>
+ The following is a list of example network filters that are
+ automatically installed with libvirt. </p>
+ <table class="top_table">
+ <tr>
+ <th> Name </th>
+ <th> Description </th>
+ </tr>
+ <tr>
+ <td> no-arp-spoofing </td>
+ <td> Prevent a VM from spoofing ARP traffic; this filter
+ only allows ARP request and reply messages and enforces
+ that those packets contain the MAC and IP addresses
+ of the VM.</td>
+ </tr>
+ <tr>
+ <td> allow-dhcp </td>
+ <td> Allow a VM to request an IP address via DHCP (from any
+ DHCP server)</td>
+ </tr>
+ <tr>
+ <td> allow-dhcp-server </td>
+ <td> Allow a VM to request an IP address from a specified
+ DHCP server. The dotted decimal IP address of the DHCP
+ server must be provided in a reference to this filter.
+ The name of the variable must be <i>DHCPSERVER</i>.</td>
+ </tr>
+ <tr>
+ <td> no-ip-spoofing </td>
+ <td> Prevent a VM from sending of IP packets with
+ a source IP address different from the one
+ in the packet. </td>
+ </tr>
+ <tr>
+ <td> no-ip-multicast </td>
+ <td> Prevent a VM from sending IP multicast packets. </td>
+ </tr>
+ <tr>
+ <td> clean-traffic </td>
+ <td> Prevent MAC, IP and ARP spoofing. This filter references
+ several other filters as building blocks. </td>
+ </tr>
+ </table>
+ <p>
+ Note that most of the above filters are only building blocks and
+ require a combination with other filters to provide useful network
+ traffic filtering.
+ The most useful one in the above list is the <i>clean-traffic</i>
+ filter. This filter itself can for example be combined with the
+ <i>no-ip-multicast</i>
+ filter to prevent virtual machines from sending IP multicast traffic
+ on top of the prevention of packet spoofing.
+ </p>
+
+ <h2><a name="nwfwrite">Writing your own filters</a></h2>
+
+ <p>
+ Since libvirt only provides a couple of example networking filters, you
+ may consider writing your own. When planning on doing so
+ there are a couple of things
+ you may need to know regarding the network filtering subsystem and how
+ it works internally. Certainly you also have to know and understand
+ the protocols very well that you want to be filtering on so that
+ no further traffic than what you want can pass and that in fact the
+ traffic you want to allow does pass.
+ <br><br>
+ The network filtering subsystem is currently only available on
+ Linux hosts and only works for Qemu and KVM type of virtual machines.
+ On Linux
+ it builds upon the support for <code>ebtables</code>, <code>iptables
+ </code> and <code>ip6tables</code> and makes use of their features.
+ From the above list of supported protocols the following ones are
+ implemented using <code>ebtables</code>:
+ </p>
+ <ul>
+ <li>mac</li>
+ <li>arp, rarp</li>
+ <li>ip</li>
+ <li>ipv6</li>
+ </uL>
+
+ <p>
+ All other protocols over IPv4 are supported using iptables, those over
+ IPv6 are implemented using ip6tables.
+ <br><br>
+ On a Linux host, all traffic filtering instantiated by libvirt's network
+ filter subsystem first passes through the filtering support implemented
+ by ebtables and only then through iptables or ip6tables filters. If
+ a filter tree has rules with the protocols <code>mac</code>,
+ <code>arp</code>, <code>rarp</code>, <code>ip</code>, or <code>ipv6</code>
+ ebtables rules will automatically be instantiated.
+ <br>
+ The role of the <code>chain</code> attribute in the network filter
+ XML is that internally a new user-defined ebtables table is created
+ that then for example receives all <code>arp</code> traffic coming
+ from or going to a virtual machine, if the chain <code>arp</code>
+ has been specified. Further, a rule is generated in an interface's
+ <code>root</code> chain that directs all ipv4 traffic into the
+ user-defined chain. Therefore, all ARP traffic rules should then be
+ placed into filters specifying this chain. This type of branching
+ into user-define tables is only supported with filtering on the ebtables
+ layer.
+ <br>
+ As an example, it is
+ possible to filter on UDP traffic by source and destination ports using
+ the <code>ip</code> protocol filter and specifying attributes for the
+ protocol, source and destination IP addresses and ports of UDP packets
+ that are to be accepted. This allows
+ early filtering of UDP traffic with ebtables. However, once an IP or IPv6
+ packet, such as a UDP packet,
+ has passed the ebtables layer and there is at least one rule in a filter
+ tree that instantiates iptables or ip6tables rules, a rule to let
+ the UDP packet pass will also be necessary to be provided for those
+ filtering layers. This can be
+ achieved with a rule containing an approriate <code>udp</code> or
+ <code>udp-ipv6</code> traffic filtering node.
+ </p>
+
+ <h3><a name="nwfwriteexample">Example custom filter</a></h3>
+ <p>
+ As an example we want to now build a filter that fulfills the following
+ list of requirements:
+ </p>
+ <ul>
+ <li>prevents a VM's interface from MAC, IP and ARP spoofing</li>
+ <li>opens only TCP ports 22 and 80 of a VM's interface</li>
+ <li>allows the VM to send ping traffic from an interface
+ but no let the VM be pinged on the interface</li>
+ </ul>
+ <p>
+ The requirement to prevent spoofing is fulfilled by the existing
+ <code>clean-traffic</code> network filter, thus we will reference this
+ filter from our custom filter.
+ <br>
+ To enable traffic for TCP ports 22 and 80 we will add 2 rules to
+ enable this type of traffic. To allow the VM to send ping traffic
+ we will add a rule for ICMP traffic. For simplicity reasons
+ we allow general ICMP traffic to be initated from the VM, not
+ just ICMP echo request and response messages. To then
+ disallow all other traffic to reach or be initated by the
+ VM we will then need to add a rule that drops all other traffic.
+ Assuming our VM is called <i>test</i> and
+ the interface we want to associate our filter with is called <i>eth0</i>,
+ we name our filter <i>test-eth0</i>.
+ The result of these considerations is the following network filter XML:
+ </p>
+<pre>
+<filter name='test-eth0'>
+ <!-- reference the clean traffic filter preventing
+ MAC, IP and ARP spoofing. By not providing
+ and IP address parameter libvirt will detect the
+ IP address the VM is using. -->
+ <filterref filter='clean-traffic'/>
+
+ <!-- enable TCP ports 22 (ssh) and 80 (http) to be reachable -->
+ <rule action='accept' direction='in'>
+ <tcp dstportstart='22'/>
+ </rule>
+
+ <rule action='accept' direction='in'>
+ <tcp dstportstart='80'/>
+ </rule>
+
+ <!-- enable general ICMP traffic to be initiated by the VM;
+ this includes ping traffic -->
+ <rule action='accept' direction='out'>
+ <icmp/>
+ </rule>
+
+ <!-- drop all other traffic -->
+ <rule action='drop' direction='inout'>
+ <all/>
+ </rule>
+
+</filter>
+</pre>
+ <p>
+ Note that none of the rules in the above XML contain the
+ IP address of the VM as either source or destination address, yet
+ the filtering of the traffic works correctly. The reason is that
+ the evaluation of the rules internally happens on a
+ per-interface basis and the rules are evaluated based on the knowledge
+ about which (tap) interface has sent or will receive the packet rather
+ than what their source or destination IP address may be.
+ <br><br>
+ An XML fragment for a possible network interface description inside
+ the domain XML of the <code>test</code> VM could then look like this:
+ </p>
+<pre>
+ [...]
+ <interface type='bridge'>
+ <source bridge='mybridge'/>
+ <filterref filter='test-eth0'/>
+ </interface>
+ [...]
+</pre>
+
+ <p>
+ To more strictly control the ICMP traffic and enforce that only
+ ICMP echo requests can be sent from the VM
+ and only ICMP echo responses be received by the VM, the above
+ <code>ICMP</code> rule can be replaced with the following two rules:
+ </p>
+<pre>
+ <!-- enable outgoing ICMP echo requests-->
+ <rule action='accept' direction='out'>
+ <icmp type='8'/>
+ </rule>
+
+ <!-- enable incoming ICMP echo replies-->
+ <rule action='accept' direction='in'>
+ <icmp type='0'/>
+ </rule>
+</pre>
+
+
+ <h2><a name="nwflimits">Limitations</a></h2>
+ <p>
+ The following sections list (current) limitations of the network
+ filtering subsystem.
+ </p>
+
+ <h3><a name="nwflimitsIP">IP Address Detection</a></h3>
+ <p>
+ In case a network filter references the variable
+ <i>IP</i> and no variable was defined in any higher layer
+ references to the filter, IP address detection will automatically
+ be started when the filter is to be instantiated (VM start, interface
+ hotplug event). Only IPv4
+ addresses can be detected and only a single IP address
+ legitimately in use by a VM on a single interface will be detected.
+ In case a VM was to use multiple IP address on a single interface
+ (IP aliasing),
+ the IP addresses would have to be provided explicitly either
+ in the network filter itself or as variables used in attributes'
+ values. These
+ variables must then be defined in a higher level reference to the filter
+ and each assigned the value of the IP address that the VM is expected
+ to be using.
+ Different IP addresses in use by multiple interfaces of a VM
+ (one IP address each) will be independently detected.
+ <br><br>
+ Once a VM's IP address has been detected, its IP network traffic
+ may be locked to that address, if for example IP address spoofing
+ is prevented by one of its filters. In that case the user of the VM
+ will not be able to change the IP address on the interface inside
+ the VM, which would be considered IP address spoofing.
+ <br><br>
+ In case a VM is resumed after suspension or migrated, IP address
+ detection will be restarted.
+ </p>
+
+ <h3><a name="nwflimitsmigr">VM Migration</a></h3>
+ <p>
+ VM migration is only supported if the whole filter tree
+ that is referenced by a virtual machine's top level filter
+ is also available on the target host. The network filter
+ <i>clean-traffic</i>
+ for example should be available on all libvirt installations
+ of version 0.8.1 or later and thus enable migration of VMs that
+ for example reference this filter. All other
+ custom filters must be migrated using higher layer software. It is
+ outside the scope of libvirt to ensure that referenced filters
+ on the source system are equivalent to those on the target system
+ and vice versa.
+ <br><br>
+ Migration must occurr between libvirt insallations of version
+ 0.8.1 or later in order not to loose the network traffic filters
+ associated with an interface.
+ </p>
+
+ </body>
+</html>
14 years, 11 months
[libvirt] [PATCH v2] storage: Check for invalid storage mode before opening
by Cole Robinson
If a directory pool contains pipes or sockets, a pool start can fail or hang:
https://bugzilla.redhat.com/show_bug.cgi?id=589577
We already try to avoid these special files, but only attempt after
opening the path, which is where the problems lie. Unify volume opening
into a single function which runs stat() before any open() call. Directory
pools can then proceed along, ignoring the invalid files.
v2: Use ATTRIBUTE_NONNULL. Drop stat check, just open with
O_NONBLOCK|O_NOCTTY.
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/storage/storage_backend.c | 23 +++++++++++++++++------
src/storage/storage_backend.h | 2 ++
src/storage/storage_backend_fs.c | 7 ++-----
src/storage/storage_backend_logical.c | 9 +++------
src/storage/storage_backend_mpath.c | 9 +++------
src/storage/storage_backend_scsi.c | 17 ++++++++---------
6 files changed, 35 insertions(+), 32 deletions(-)
diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c
index f4124df..bbd3375 100644
--- a/src/storage/storage_backend.c
+++ b/src/storage/storage_backend.c
@@ -872,6 +872,20 @@ virStorageBackendForType(int type) {
return NULL;
}
+int
+virStorageBackendVolOpen(const char *path)
+{
+ int fd;
+
+ if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_NOCTTY)) < 0) {
+ virReportSystemError(errno,
+ _("cannot open volume '%s'"),
+ path);
+ return -1;
+ }
+
+ return fd;
+}
int
virStorageBackendUpdateVolTargetInfo(virStorageVolTargetPtr target,
@@ -880,13 +894,10 @@ virStorageBackendUpdateVolTargetInfo(virStorageVolTargetPtr target,
{
int ret, fd;
- if ((fd = open(target->path, O_RDONLY)) < 0) {
- virReportSystemError(errno,
- _("cannot open volume '%s'"),
- target->path);
- return -1;
- }
+ if ((ret = virStorageBackendVolOpen(target->path)) < 0)
+ return ret;
+ fd = ret;
ret = virStorageBackendUpdateVolTargetInfoFD(target,
fd,
allocation,
diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h
index 907c4bc..45683ec 100644
--- a/src/storage/storage_backend.h
+++ b/src/storage/storage_backend.h
@@ -80,6 +80,8 @@ struct _virStorageBackend {
virStorageBackendPtr virStorageBackendForType(int type);
+int virStorageBackendVolOpen(const char *path)
+ATTRIBUTE_NONNULL(1);
int virStorageBackendUpdateVolInfo(virStorageVolDefPtr vol,
int withCapacity);
diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
index c96c4f3..1650177 100644
--- a/src/storage/storage_backend_fs.c
+++ b/src/storage/storage_backend_fs.c
@@ -61,12 +61,9 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target,
if (encryption)
*encryption = NULL;
- if ((fd = open(target->path, O_RDONLY)) < 0) {
- virReportSystemError(errno,
- _("cannot open volume '%s'"),
- target->path);
+ if ((ret = virStorageBackendVolOpen(target->path)) < 0)
return -1;
- }
+ fd = ret;
if ((ret = virStorageBackendUpdateVolTargetInfoFD(target, fd,
allocation,
diff --git a/src/storage/storage_backend_logical.c b/src/storage/storage_backend_logical.c
index 06238d1..616ca1a 100644
--- a/src/storage/storage_backend_logical.c
+++ b/src/storage/storage_backend_logical.c
@@ -559,7 +559,7 @@ virStorageBackendLogicalCreateVol(virConnectPtr conn,
virStoragePoolObjPtr pool,
virStorageVolDefPtr vol)
{
- int fd = -1;
+ int fdret, fd = -1;
char size[100];
const char *cmdargvnew[] = {
LVCREATE, "--name", vol->name, "-L", size,
@@ -602,12 +602,9 @@ virStorageBackendLogicalCreateVol(virConnectPtr conn,
if (virRun(cmdargv, NULL) < 0)
return -1;
- if ((fd = open(vol->target.path, O_RDONLY)) < 0) {
- virReportSystemError(errno,
- _("cannot read path '%s'"),
- vol->target.path);
+ if ((fdret = virStorageBackendVolOpen(vol->target.path)) < 0)
goto cleanup;
- }
+ fd = fdret;
/* We can only chown/grp if root */
if (getuid() == 0) {
diff --git a/src/storage/storage_backend_mpath.c b/src/storage/storage_backend_mpath.c
index 8d0a92a..a62dc3d 100644
--- a/src/storage/storage_backend_mpath.c
+++ b/src/storage/storage_backend_mpath.c
@@ -44,14 +44,11 @@ virStorageBackendMpathUpdateVolTargetInfo(virStorageVolTargetPtr target,
unsigned long long *capacity)
{
int ret = -1;
- int fd = -1;
+ int fdret, fd = -1;
- if ((fd = open(target->path, O_RDONLY)) < 0) {
- virReportSystemError(errno,
- _("cannot open volume '%s'"),
- target->path);
+ if ((fdret = virStorageBackendVolOpen(target->path)) < 0)
goto out;
- }
+ fd = fdret;
if (virStorageBackendUpdateVolTargetInfoFD(target,
fd,
diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c
index 40f4fd8..71492cf 100644
--- a/src/storage/storage_backend_scsi.c
+++ b/src/storage/storage_backend_scsi.c
@@ -135,14 +135,12 @@ virStorageBackendSCSIUpdateVolTargetInfo(virStorageVolTargetPtr target,
unsigned long long *allocation,
unsigned long long *capacity)
{
- int fd, ret = -1;
+ int fdret, fd = -1;
+ int ret = -1;
- if ((fd = open(target->path, O_RDONLY)) < 0) {
- virReportSystemError(errno,
- _("cannot open volume '%s'"),
- target->path);
- return -1;
- }
+ if ((fdret = virStorageBackendVolOpen(target->path)) < 0)
+ goto cleanup;
+ fd = fdret;
if (virStorageBackendUpdateVolTargetInfoFD(target,
fd,
@@ -155,8 +153,9 @@ virStorageBackendSCSIUpdateVolTargetInfo(virStorageVolTargetPtr target,
ret = 0;
- cleanup:
- close(fd);
+cleanup:
+ if (fd >= 0)
+ close(fd);
return ret;
}
--
1.6.6.1
14 years, 11 months
[libvirt] [v3 PATCH] add 802.1Qbh handling for port-profiles based on Stefan's previous patches
by Scott Feldman
From: Scott Feldman <scofeldm(a)cisco.com>
This patch builds on the work recently posted by Stefan Berger. It builds
on top of Stefan's two posted patches:
[PATCH v8] vepa: parsing for 802.1Qb{g|h} XML
[RFC][PATCH 1/3] vepa+vsi: Introduce dependency on libnl
[PATCH v3] Add host UUID (to libvirt capabilities)
Stefan's RFC patches 2/3 and 3/3 are incorporated into my patch.
Changes from v2 to v3:
- remove debug fprintfs
- use virGetHostUUID (thanks Stefan!)
- fix compile issue when latest if_link.h isn't available
- change poll timeout to 10s, at 1/8 intervals
- if polling times out, log msg and return -ETIMEDOUT
Changes from v1 to v2:
- Add Stefan's code for getPortProfileStatus
- Poll for up to 2 secs for port-profile status, at 1/8 sec intervals:
- if status indicates error, abort openMacvtapTap
- if status indicates success, exit polling
- if status is "in-progress" after 2 secs of polling, exit
polling loop silently, without error
My patch finishes out the 802.1Qbh parts, which Stefan had mostly complete.
I've tested using the recent kernel updates for VF_PORT netlink msgs and
enic for Cisco's 10G Ethernet NIC. I tested many VMs, each with several
direct interfaces, each configured with a port-profile per the XML. VM-to-VM,
and VM-to-external work as expected. VM-to-VM on same host (using same NIC)
works same as VM-to-VM where VMs are on diff hosts. I'm able to change
settings on the port-profile while the VM is running to change the virtual
port behaviour. For example, adjusting a QoS setting like rate limit. All
VMs with interfaces using that port-profile immediatly see the effect of the
change to the port-profile.
I don't have a SR-IOV device to test so source dev is a non-SR-IOV device,
but most of the code paths include support for specifing the source dev and
VF index. We'll need to complete this by discovering the PF given the VF
linkdev. Once we have the PF, we'll also have the VF index. All this info-
mation is available from sysfs.
Signed-off-by: Scott Feldman <scofeldm(a)cisco.com>
---
configure.ac | 16 +
src/util/macvtap.c | 802 +++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 805 insertions(+), 13 deletions(-)
diff --git a/configure.ac b/configure.ac
index 36ba703..885b0ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2005,13 +2005,26 @@ if test "$with_macvtap" != "no" ; then
fi
AM_CONDITIONAL([WITH_MACVTAP], [test "$with_macvtap" = "yes"])
+AC_TRY_COMPILE([ #include <sys/socket.h>
+ #include <linux/rtnetlink.h> ],
+ [ int x = IFLA_PORT_MAX; ],
+ [ with_virtualport=yes ],
+ [ with_virtualport=no ])
+if test "$with_virtualport" = "yes"; then
+ val=1
+else
+ val=0
+fi
+AC_DEFINE_UNQUOTED([WITH_VIRTUALPORT], $val, [whether vsi vepa support is enabled])
+AM_CONDITIONAL([WITH_VIRTUALPORT], [test "$with_virtualport" = "yes"])
+
dnl netlink library
LIBNL_CFLAGS=""
LIBNL_LIBS=""
-if test "$with_macvtap" = "yes"; then
+if test "$with_macvtap" = "yes" || "$with_virtualport" = "yes"; then
PKG_CHECK_MODULES([LIBNL], [libnl-1 >= $LIBNL_REQUIRED], [
], [
AC_MSG_ERROR([libnl >= $LIBNL_REQUIRED is required for macvtap support])
@@ -2084,6 +2097,7 @@ AC_MSG_NOTICE([ Network: $with_network])
AC_MSG_NOTICE([Libvirtd: $with_libvirtd])
AC_MSG_NOTICE([ netcf: $with_netcf])
AC_MSG_NOTICE([ macvtap: $with_macvtap])
+AC_MSG_NOTICE([virtport: $with_virtualport])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Storage Drivers])
AC_MSG_NOTICE([])
diff --git a/src/util/macvtap.c b/src/util/macvtap.c
index da81447..76eea37 100644
--- a/src/util/macvtap.c
+++ b/src/util/macvtap.c
@@ -27,7 +27,7 @@
#include <config.h>
-#if WITH_MACVTAP
+#if WITH_MACVTAP || WITH_VIRTUALPORT
# include <stdio.h>
# include <errno.h>
@@ -41,6 +41,8 @@
# include <linux/rtnetlink.h>
# include <linux/if_tun.h>
+# include <netlink/msg.h>
+
# include "util.h"
# include "memory.h"
# include "logging.h"
@@ -48,6 +50,7 @@
# include "interface.h"
# include "conf/domain_conf.h"
# include "virterror_internal.h"
+# include "uuid.h"
# define VIR_FROM_THIS VIR_FROM_NET
@@ -58,15 +61,23 @@
# define MACVTAP_NAME_PREFIX "macvtap"
# define MACVTAP_NAME_PATTERN "macvtap%d"
+# define MICROSEC_PER_SEC (1000 * 1000)
+
static int associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort,
- int vf,
const unsigned char *vmuuid);
static int disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort);
+enum virVirtualPortOp {
+ ASSOCIATE = 0x1,
+ DISASSOCIATE = 0x2,
+};
+
static int nlOpen(void)
{
@@ -159,6 +170,156 @@ err_exit:
}
+# ifdef IFLA_VF_PORT_MAX
+
+/**
+ * nlCommWaitSuccess:
+ *
+ * @nlmsg: pointer to netlink message
+ * @nl_grousp: the netlink multicast groups to send to
+ * @respbuf: pointer to pointer where response buffer will be allocated
+ * @respbuflen: pointer to integer holding the size of the response buffer
+ * on return of the function.
+ * @to_usecs: timeout in microseconds to wait for a success message
+ * to be returned
+ *
+ * Send the given message to the netlink multicast group and receive
+ * responses. Skip responses indicating an error and keep on receiving
+ * responses until a success response is returned.
+ * Returns 0 on success, -1 on error. In case of error, no response
+ * buffer will be returned.
+ */
+static int
+nlCommWaitSuccess(struct nlmsghdr *nlmsg, int nl_groups,
+ char **respbuf, int *respbuflen, long to_usecs)
+{
+ int rc = 0;
+ struct sockaddr_nl nladdr = {
+ .nl_family = AF_NETLINK,
+ .nl_pid = getpid(),
+ .nl_groups = nl_groups,
+ };
+ int rcvChunkSize = 1024; // expecting less than that
+ int rcvoffset = 0;
+ ssize_t nbytes;
+ int n;
+ struct timeval tv = {
+ .tv_sec = to_usecs / MICROSEC_PER_SEC,
+ .tv_usec = to_usecs % MICROSEC_PER_SEC,
+ };
+ fd_set rfds;
+ bool gotvalid = false;
+ int fd = nlOpen();
+ static uint32_t seq = 0x1234;
+ uint32_t myseq = seq++;
+ uint32_t mypid = getpid();
+
+ if (fd < 0)
+ return -1;
+
+ nlmsg->nlmsg_pid = mypid;
+ nlmsg->nlmsg_seq = myseq;
+ nlmsg->nlmsg_flags |= NLM_F_ACK;
+
+ nbytes = sendto(fd, (void *)nlmsg, nlmsg->nlmsg_len, 0,
+ (struct sockaddr *)&nladdr, sizeof(nladdr));
+ if (nbytes < 0) {
+ virReportSystemError(errno,
+ "%s", _("cannot send to netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+
+ while (!gotvalid) {
+ rcvoffset = 0;
+ while (1) {
+ socklen_t addrlen = sizeof(nladdr);
+
+ if (VIR_REALLOC_N(*respbuf, rcvoffset+rcvChunkSize) < 0) {
+ virReportOOMError();
+ rc = -1;
+ goto err_exit;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ n = select(fd + 1, &rfds, NULL, NULL, &tv);
+ if (n == 0) {
+ rc = -1;
+ goto err_exit;
+ }
+
+ nbytes = recvfrom(fd, &((*respbuf)[rcvoffset]), rcvChunkSize, 0,
+ (struct sockaddr *)&nladdr, &addrlen);
+ if (nbytes < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ virReportSystemError(errno, "%s",
+ _("error receiving from netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+ rcvoffset += nbytes;
+ break;
+ }
+ *respbuflen = rcvoffset;
+
+ /* check message for error */
+ if (*respbuflen > NLMSG_LENGTH(0) && *respbuf != NULL) {
+ struct nlmsghdr *resp = (struct nlmsghdr *)*respbuf;
+ struct nlmsgerr *err;
+
+ if (resp->nlmsg_pid != mypid ||
+ resp->nlmsg_seq != myseq)
+ continue;
+
+ /* skip reflected message */
+ if (resp->nlmsg_type & 0x10)
+ continue;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len >= NLMSG_LENGTH(sizeof(*err))) {
+ if (-err->error != EOPNOTSUPP) {
+ /* assuming error msg from daemon */
+ gotvalid = true;
+ break;
+ }
+ }
+ /* whatever this is, skip it */
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ break;
+
+ case NLMSG_DONE:
+ gotvalid = true;
+ break;
+
+ default:
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ break;
+ }
+ }
+ }
+
+err_exit:
+ if (rc == -1) {
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ }
+
+ nlClose(fd);
+ return rc;
+}
+
+#endif
+
static struct rtattr *
rtattrCreate(char *buffer, int bufsize, int type,
const void *data, int datalen)
@@ -204,6 +365,8 @@ nlAppend(struct nlmsghdr *nlm, int totlen, const void *data, int datalen)
}
+# if WITH_MACVTAP
+
static int
link_add(const char *type,
const unsigned char *macaddress, int macaddrsize,
@@ -651,8 +814,8 @@ create_name:
}
if (associatePortProfileId(cr_ifname,
+ net->data.direct.linkdev,
&net->data.direct.virtPortProfile,
- -1,
vmuuid) != 0) {
rc = -1;
goto link_del_exit;
@@ -685,6 +848,7 @@ create_name:
disassociate_exit:
disassociatePortProfileId(cr_ifname,
+ net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
link_del_exit:
@@ -707,21 +871,606 @@ delMacvtap(virDomainNetDefPtr net)
{
if (net->ifname) {
disassociatePortProfileId(net->ifname,
+ net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
link_del(net->ifname);
}
}
-#endif
+# endif
+
+
+# ifdef IFLA_PORT_MAX
+
+static struct nla_policy ifla_policy[IFLA_MAX + 1] =
+{
+ [IFLA_VF_PORTS] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_vf_ports_policy[IFLA_VF_PORT_MAX + 1] =
+{
+ [IFLA_VF_PORT] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] =
+{
+ [IFLA_PORT_RESPONSE] = { .type = NLA_U16 },
+};
+
+
+static int
+link_dump(int ifindex, const char *ifname, struct nlattr **tb,
+ char **recvbuf)
+{
+ int rc = 0;
+ char nlmsgbuf[256] = { 0, };
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ char rtattbuf[64];
+ struct rtattr *rta;
+ struct ifinfomsg i = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex
+ };
+ int recvbuflen;
+
+ *recvbuf = NULL;
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_GETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &i, sizeof(i)))
+ goto buffer_too_small;
+
+ if (ifindex < 0 && ifname != NULL) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
+ ifname, strlen(ifname) + 1);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (nlComm(nlm, recvbuf, &recvbuflen) < 0)
+ return -1;
+
+ if (recvbuflen < NLMSG_LENGTH(0) || *recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)*recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ switch (-err->error) {
+ case 0:
+ break;
+
+ default:
+ virReportSystemError(-err->error,
+ _("error dumping %d interface"),
+ ifindex);
+ rc = -1;
+ }
+ break;
+
+ case GENL_ID_CTRL:
+ case NLMSG_DONE:
+ if (nlmsg_parse(resp, sizeof(struct ifinfomsg),
+ tb, IFLA_MAX, ifla_policy)) {
+ goto malformed_resp;
+ }
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ if (rc != 0)
+ VIR_FREE(*recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(*recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+getPortProfileStatus(struct nlattr **tb, int32_t vf, uint16_t *status)
+{
+ int rc = 1;
+ const char *msg = NULL;
+ struct nlattr *tb2[IFLA_VF_PORT_MAX + 1],
+ *tb3[IFLA_PORT_MAX+1];
+
+ if (vf == PORT_SELF_VF) {
+ if (tb[IFLA_PORT_SELF]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb[IFLA_PORT_SELF],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ } else {
+ if (tb[IFLA_VF_PORTS]) {
+ if (nla_parse_nested(tb2, IFLA_VF_PORT_MAX, tb[IFLA_VF_PORTS],
+ ifla_vf_ports_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORTS part");
+ goto err_exit;
+ }
+ if (tb2[IFLA_VF_PORT]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb2[IFLA_VF_PORT],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ }
+ }
+
+ if (tb3[IFLA_PORT_RESPONSE]) {
+ *status = *(uint16_t *)RTA_DATA(tb3[IFLA_PORT_RESPONSE]);
+ rc = 0;
+ } else {
+ msg = _("no IFLA_PORT_RESPONSE found in netlink message");
+ goto err_exit;
+ }
+
+err_exit:
+ if (msg)
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
+
+ return rc;
+}
+
+
+static int
+doPortProfileOpSetLink(bool multicast,
+ const char *ifname, int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc = 0;
+ char nlmsgbuf[256];
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ char rtattbuf[64];
+ struct rtattr *rta, *vfports, *vfport;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex,
+ };
+ char *recvbuf = NULL;
+ int recvbuflen = 0;
+
+ memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+ goto buffer_too_small;
+
+ if (vf == PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_SELF, NULL, 0);
+ } else {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORTS, NULL, 0);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!(vfports = nlAppend(nlm, sizeof(nlmsgbuf),
+ rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ /* beging nesting vfports */
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORT, NULL, 0);
+ }
+
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!(vfport = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ if (profileId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_PROFILE,
+ profileId, strlen(profileId) + 1);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+ if (portVsi) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VSI_TYPE,
+ portVsi, sizeof(*portVsi));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (instanceId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_INSTANCE_UUID,
+ instanceId, VIR_UUID_BUFLEN);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (hostUUID) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_HOST_UUID,
+ hostUUID, VIR_UUID_BUFLEN);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (vf != PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VF,
+ &vf, sizeof(vf));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_REQUEST,
+ &op, sizeof(op));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+
+ /* end nesting of vport */
+ vfport->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfport;
+
+ if (vf != PORT_SELF_VF) {
+ /* end nesting of vfports */
+ vfports->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfports;
+ }
+
+ if (!multicast) {
+ if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+ return -1;
+ } else {
+ if (nlCommWaitSuccess(nlm, RTMGRP_LINK, &recvbuf, &recvbuflen,
+ 5 * MICROSEC_PER_SEC) < 0)
+ return -1;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ switch (-err->error) {
+ case 0:
+ break;
+
+ default:
+ virReportSystemError(-err->error,
+ _("error during virtual port configuration of %s interface"),
+ ifname);
+ rc = -1;
+ }
+ break;
+
+ case NLMSG_DONE:
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ VIR_FREE(recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+doPortProfileOpCommon(bool multicast,
+ const char *ifname, int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc;
+ char *recvbuf = NULL;
+ struct nlattr *tb[IFLA_MAX + 1];
+ int repeats = 80;
+ uint16_t status = 0;
+
+ rc = doPortProfileOpSetLink(multicast,
+ ifname, ifindex,
+ profileId,
+ portVsi,
+ instanceId,
+ hostUUID,
+ vf,
+ op);
+
+ if (rc != 0) {
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("sending of PortProfileRequest failed.\n"));
+ return rc;
+ }
+
+ if (!multicast) {
+ while (--repeats) {
+ rc = link_dump(ifindex, ifname, tb, &recvbuf);
+ if (rc)
+ goto err_exit;
+ rc = getPortProfileStatus(tb, vf, &status);
+ if (rc == 0) {
+ if (status == PORT_PROFILE_RESPONSE_SUCCESS ||
+ status == PORT_VDP_RESPONSE_SUCCESS) {
+ break;
+ } else if (status == PORT_PROFILE_RESPONSE_INPROGRESS) {
+ // keep trying...
+ } else {
+ virReportSystemError(EINVAL,
+ _("error %d during port-profile setlink on %s"),
+ status, ifname);
+ rc = 1;
+ break;
+ }
+ }
+ usleep(125000);
+
+ VIR_FREE(recvbuf);
+ }
+ }
+
+ if (status == PORT_PROFILE_RESPONSE_INPROGRESS) {
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("port-profile setlink timed out"));
+ rc = -ETIMEDOUT;
+ }
+
+err_exit:
+ VIR_FREE(recvbuf);
+
+ return rc;
+}
+
+# endif /* IFLA_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbg(const char *ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ int32_t vf,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (void)virtPort;
+ (void)vf;
+ (void)virtPortOp;
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Kernel VF Port support was missing at compile time."));
+ rc = 1;
+
+# else /* IFLA_VF_PORT_MAX */
+
+ int op = PORT_REQUEST_ASSOCIATE;
+ struct ifla_port_vsi portVsi = {
+ .vsi_mgr_id = virtPort->u.virtPort8021Qbg.managerID,
+ .vsi_type_version = virtPort->u.virtPort8021Qbg.typeIDVersion,
+ };
+ bool multicast = true;
+ int ifindex;
+
+ if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ portVsi.vsi_type_id[2] = virtPort->u.virtPort8021Qbg.typeID >> 16;
+ portVsi.vsi_type_id[1] = virtPort->u.virtPort8021Qbg.typeID >> 8;
+ portVsi.vsi_type_id[0] = virtPort->u.virtPort8021Qbg.typeID;
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, NULL, ifindex,
+ NULL,
+ &portVsi,
+ virtPort->u.virtPort8021Qbg.instanceID,
+ NULL,
+ vf,
+ op);
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
+
+
+static int
+doPortProfileOp8021Qbh(const char *ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ int32_t vf,
+ const unsigned char *host_uuid,
+ const unsigned char *vm_uuid,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (void)virtPort;
+ (void)vf;
+ (void)host_uuid;
+ (void)vm_uuid;
+ (void)virtPortOp;
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Kernel VF Port support was missing at compile time."));
+ rc = 1;
+
+# else /* IFLA_VF_PORT_MAX */
+
+ int op = PORT_REQUEST_ASSOCIATE;
+ bool multicast = false;
+ int ifindex;
+
+ if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, ifname, ifindex,
+ virtPort->u.virtPort8021Qbh.profileID,
+ NULL,
+ vm_uuid,
+ host_uuid,
+ vf,
+ op);
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ ifaceUp(ifname);
+ break;
+ case DISASSOCIATE:
+ ifaceDown(ifname);
+ break;
+ }
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
+
+static int
+getPhysfn(const char *linkdev,
+ int32_t *vf,
+ char **physfndev)
+{
+ int rc = 0;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)linkdev;
+ (void)vf;
+ (void)physfndev;
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Kernel VF Port support was missing at compile time."));
+ rc = 1;
+
+# else /* IFLA_VF_PORT_MAX */
+
+ bool virtfn = false;
+
+ if (virtfn) {
+
+ // XXX: if linkdev is SR-IOV VF, then set vf = VF index
+ // XXX: and set linkdev = PF device
+ // XXX: need to use get_physical_function_linux() or
+ // XXX: something like that to get PF
+ // XXX: device and figure out VF index
+
+ rc = 1;
+
+ } else {
+
+ /* Not SR-IOV VF: physfndev is linkdev and VF index
+ * refers to linkdev self
+ */
+
+ *vf = PORT_SELF_VF;
+ *physfndev = (char *)linkdev;
+ }
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
/**
* associatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
* @virtPort: pointer to the object holding port profile parameters
- * @vf: virtual function number, -1 if to be ignored
* @vmuuid : the UUID of the virtual machine
+ * @hostuuid : the UUID of the host machine
*
* Associate a port on a swtich with a profile. This function
* may notify a kernel driver or an external daemon to run
@@ -734,15 +1483,25 @@ delMacvtap(virDomainNetDefPtr net)
*/
static int
associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort,
- int vf,
const unsigned char *vmuuid)
{
+ char *physfndev;
+ unsigned char hostuuid[VIR_UUID_BUFLEN];
+ int32_t vf;
int rc = 0;
+
VIR_DEBUG("Associating port profile '%p' on link device '%s'",
virtPort, macvtap_ifname);
- (void)vf;
- (void)vmuuid;
+
+ rc = virGetHostUUID(hostuuid);
+ if (rc)
+ goto err_exit;
+
+ rc = getPhysfn(linkdev, &vf, &physfndev);
+ if (rc)
+ goto err_exit;
switch (virtPort->virtPortType) {
case VIR_VIRTUALPORT_NONE:
@@ -750,14 +1509,18 @@ associatePortProfileId(const char *macvtap_ifname,
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort, vf,
+ ASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(physfndev, virtPort, vf,
+ hostuuid, vmuuid,
+ ASSOCIATE);
break;
}
+err_exit:
return rc;
}
@@ -766,6 +1529,7 @@ associatePortProfileId(const char *macvtap_ifname,
* disassociatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
* @virtPort: point to object holding port profile parameters
*
* Returns 0 in case of success, != 0 otherwise with error
@@ -773,25 +1537,39 @@ associatePortProfileId(const char *macvtap_ifname,
*/
static int
disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort)
{
+ char *physfndev;
+ int32_t vf;
int rc = 0;
+
VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
virtPort, macvtap_ifname);
+ rc = getPhysfn(linkdev, &vf, &physfndev);
+ if (rc)
+ goto err_exit;
+
switch (virtPort->virtPortType) {
case VIR_VIRTUALPORT_NONE:
case VIR_VIRTUALPORT_TYPE_LAST:
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort, vf,
+ DISASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(physfndev, virtPort, vf,
+ NULL, NULL,
+ DISASSOCIATE);
break;
}
+err_exit:
return rc;
}
+
+#endif
14 years, 11 months
[libvirt] [PATCH v9] vepa: parsing for 802.1Qb{g|h} XML
by Stefan Berger
Below is David Alan's original patch with lots of changes.
In particular, it now parses the following two XML descriptions, one
for 802.1Qbg and 802.1Qbh and stored the data internally. The actual
triggering of the switch setup protocol has not been implemented
here but the relevant code to do that should go into the functions
associatePortProfileId() and disassociatePortProfileId().
<interface type='direct'>
<source dev='eth0.100' mode='vepa'/>
<model type='virtio'/>
<virtualport type='802.1Qbg'>
<parameters managerid='12' typeid='0x123456' typeidversion='1'
instanceid='fa9b7fff-b0a0-4893-8e0e-beef4ff18f8f'/>
</virtualport>
<filterref filter='clean-traffic'/>
</interface>
<interface type='direct'>
<source dev='eth0.100' mode='vepa'/>
<model type='virtio'/>
<virtualport type='802.1Qbh'>
<parameters profileid='my_profile'/>
</virtualport>
</interface>
I'd suggest to use this patch as a base for triggering the setup
protocol with the 802.1Qb{g|h} switch.
V9:
-Addressing Daniel Berrange's comments:
- removing macvtap.h's dependency on domain_conf.h by
moving the virVirtualPortProfileDef structure into macvtap.h
and not passing virtDomainNetDefPtr to any functions in
macvtap.c
Changes from V7 to V8:
- Addressed most of Chris Wright's comments:
- indicating error in case virtualport XML node cannot be parsed
properly
- parsing hex and decimal numbers using virStrToLong_ui() with
parameter '0' for base
- tgifname (target interface name) variable wasn't necessary
to pass to openMacvtapTap function anymore
- assigning the virtual port data structure to the virDomainNetDef
only if it was previously parsed
-> still leaving possibility to start a domain with macvtap but no profile
Changes from V6 to V7:
- make sure that the error code returned by openMacvtapTap() is a negative number
in case the associatePortProfileId() function failed.
Changes from V5 to V6:
- renaming vsi in the XML to virtualport
- replace all occurrences of vsi in the source as well
Changes from V4 to V5:
- removing mode and MAC address parameters from the functions that
will communicate with the hareware diretctly or indirectly
Changes from V3 to V4:
- moving the associate and disassociate functions to the end of the
file for subsequent patches to easier make them generally available
for export
- passing the macvtap interface name rather than the link device since
this otherwise gives funny side effects when using netlink messages
where IFLA_IFNAME and IFLA_ADDRESS are specified and the link dev
all of a sudden gets the MAC address of the macvtap interface.
- Removing rc = -1 error indications in the case of 802.1Qbg|h setup in case
we wanted to use hook scripts for the setup and so the setup doesn't fail
here.
Changes from V2 to V3:
- if instance ID UUID is not supplied it will automatically be generated
- adapted schema to make instance ID UUID optional
- added test case
Some of the changes from V1 to V2:
- parser and XML generator have been separated into their own
functions so they can be re-used elsewhere (passthrough case
for example)
- Adapted XML parser and generator support the above shown type
(802.1Qbg, 802.1Qbh).
- Adapted schema to above XML
- Adapted test XML to above XML
- Passing through the VM's UUID which seems to be necessary for
802.1Qbh -- sorry no host UUID
- adding virtual function ID to association function, in case it's
necessary to use (for SR-IOV)
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
>From a945107f047c7cd71f9c1b74fd74c47d8cdc3670 Mon Sep 17 00:00:00 2001
From: David Allan <dallan(a)redhat.com>
Date: Fri, 12 Mar 2010 13:25:04 -0500
Subject: [PATCH 1/1] POC of port profile id support
* Modified schema per DanPB's feedback
* Added test for modified schema
---
docs/schemas/domain.rng | 69 +++++++++++
src/conf/domain_conf.c | 202 +++++++++++++++++++++++++++++++++
src/conf/domain_conf.h | 2
src/qemu/qemu_conf.c | 21 +--
src/qemu/qemu_conf.h | 5
src/qemu/qemu_driver.c | 19 +--
src/util/macvtap.c | 137 ++++++++++++++++++++--
src/util/macvtap.h | 45 ++++++-
tests/domainschemadata/portprofile.xml | 36 +++++
9 files changed, 498 insertions(+), 38 deletions(-)
create mode 100644 tests/domainschemadata/portprofile.xml
Index: libvirt-acl/docs/schemas/domain.rng
===================================================================
--- libvirt-acl.orig/docs/schemas/domain.rng
+++ libvirt-acl/docs/schemas/domain.rng
@@ -817,6 +817,9 @@
</optional>
<empty/>
</element>
+ <optional>
+ <ref name="virtualPortProfile"/>
+ </optional>
<ref name="interface-options"/>
</interleave>
</group>
@@ -902,6 +905,45 @@
</optional>
</interleave>
</define>
+ <define name="virtualPortProfile">
+ <choice>
+ <group>
+ <element name="virtualport">
+ <attribute name="type">
+ <value>802.1Qbg</value>
+ </attribute>
+ <element name="parameters">
+ <attribute name="managerid">
+ <ref name="uint8range"/>
+ </attribute>
+ <attribute name="typeid">
+ <ref name="uint24range"/>
+ </attribute>
+ <attribute name="typeidversion">
+ <ref name="uint8range"/>
+ </attribute>
+ <optional>
+ <attribute name="instanceid">
+ <ref name="UUID"/>
+ </attribute>
+ </optional>
+ </element>
+ </element>
+ </group>
+ <group>
+ <element name="virtualport">
+ <attribute name="type">
+ <value>802.1Qbh</value>
+ </attribute>
+ <element name="parameters">
+ <attribute name="profileid">
+ <ref name="virtualPortProfileID"/>
+ </attribute>
+ </element>
+ </element>
+ </group>
+ </choice>
+ </define>
<!--
An emulator description is just a path to the binary used for the task
-->
@@ -1769,4 +1811,31 @@
<param name="pattern">[a-zA-Z0-9_\.:]+</param>
</data>
</define>
+ <define name="uint8range">
+ <choice>
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,2}</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">255</param>
+ </data>
+ </choice>
+ </define>
+ <define name="uint24range">
+ <choice>
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,6}</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">16777215</param>
+ </data>
+ </choice>
+ </define>
+ <define name="virtualPortProfileID">
+ <data type="string">
+ <param name="maxLength">39</param>
+ </data>
+ </define>
</grammar>
Index: libvirt-acl/src/conf/domain_conf.c
===================================================================
--- libvirt-acl.orig/src/conf/domain_conf.c
+++ libvirt-acl/src/conf/domain_conf.c
@@ -242,6 +242,11 @@ VIR_ENUM_IMPL(virDomainNetdevMacvtap, VI
"private",
"bridge")
+VIR_ENUM_IMPL(virVirtualPort, VIR_VIRTUALPORT_TYPE_LAST,
+ "none",
+ "802.1Qbg",
+ "802.1Qbh")
+
VIR_ENUM_IMPL(virDomainClockOffset, VIR_DOMAIN_CLOCK_OFFSET_LAST,
"utc",
"localtime",
@@ -1807,6 +1812,190 @@ cleanup:
}
+static int
+virVirtualPortProfileDefParseXML(xmlNodePtr node,
+ virVirtualPortProfileDefPtr virtPort)
+{
+ int ret = -1;
+ char *virtPortType;
+ char *virtPortManagerID = NULL;
+ char *virtPortTypeID = NULL;
+ char *virtPortTypeIDVersion = NULL;
+ char *virtPortInstanceID = NULL;
+ char *virtPortProfileID = NULL;
+ xmlNodePtr cur = node->children;
+ const char *msg = NULL;
+
+ virtPortType = virXMLPropString(node, "type");
+ if (!virtPortType)
+ return -1;
+
+ while (cur != NULL) {
+ if (xmlStrEqual(cur->name, BAD_CAST "parameters")) {
+
+ virtPortManagerID = virXMLPropString(cur, "managerid");
+ virtPortTypeID = virXMLPropString(cur, "typeid");
+ virtPortTypeIDVersion = virXMLPropString(cur, "typeidversion");
+ virtPortInstanceID = virXMLPropString(cur, "instanceid");
+ virtPortProfileID = virXMLPropString(cur, "profileid");
+
+ break;
+ }
+
+ cur = cur->next;
+ }
+
+ virtPort->virtPortType = VIR_VIRTUALPORT_NONE;
+
+ switch (virVirtualPortTypeFromString(virtPortType)) {
+
+ case VIR_VIRTUALPORT_8021QBG:
+ if (virtPortManagerID != NULL && virtPortTypeID != NULL &&
+ virtPortTypeIDVersion != NULL) {
+ unsigned int val;
+
+ if (virStrToLong_ui(virtPortManagerID, NULL, 0, &val)) {
+ msg = _("cannot parse value of managerid parameter");
+ goto err_exit;
+ }
+
+ if (val > 0xff) {
+ msg = _("value of managerid out of range");
+ goto err_exit;
+ }
+
+ virtPort->u.virtPort8021Qbg.managerID = (uint8_t)val;
+
+ if (virStrToLong_ui(virtPortTypeID, NULL, 0, &val)) {
+ msg = _("cannot parse value of typeid parameter");
+ goto err_exit;
+ }
+
+ if (val > 0xffffff) {
+ msg = _("value for typeid out of range");
+ goto err_exit;
+ }
+
+ virtPort->u.virtPort8021Qbg.typeID = (uint32_t)val;
+
+ if (virStrToLong_ui(virtPortTypeIDVersion, NULL, 0, &val)) {
+ msg = _("cannot parse value of typeidversion parameter");
+ goto err_exit;
+ }
+
+ if (val > 0xff) {
+ msg = _("value of typeidversion out of range");
+ goto err_exit;
+ }
+
+ virtPort->u.virtPort8021Qbg.typeIDVersion = (uint8_t)val;
+
+ if (virtPortInstanceID != NULL) {
+ if (virUUIDParse(virtPortInstanceID,
+ virtPort->u.virtPort8021Qbg.instanceID)) {
+ msg = _("cannot parse instanceid parameter as a uuid");
+ goto err_exit;
+ }
+ } else {
+ if (virUUIDGenerate(virtPort->u.virtPort8021Qbg.instanceID)) {
+ msg = _("cannot generate a random uuid for instanceid");
+ goto err_exit;
+ }
+ }
+
+ virtPort->virtPortType = VIR_VIRTUALPORT_8021QBG;
+ ret = 0;
+ } else {
+ msg = _("a parameter is missing for 802.1Qbg description");
+ goto err_exit;
+ }
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+ if (virtPortProfileID != NULL) {
+ if (virStrcpyStatic(virtPort->u.virtPort8021Qbh.profileID,
+ virtPortProfileID) != NULL) {
+ virtPort->virtPortType = VIR_VIRTUALPORT_8021QBH;
+ ret = 0;
+ } else {
+ msg = _("profileid parameter too long");
+ goto err_exit;
+ }
+ } else {
+ msg = _("profileid parameter is missing for 802.1Qbh descripion");
+ goto err_exit;
+ }
+ break;
+
+
+ default:
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ msg = _("unknown virtualport type");
+ goto err_exit;
+ break;
+ }
+
+err_exit:
+
+ if (msg)
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
+
+ VIR_FREE(virtPortManagerID);
+ VIR_FREE(virtPortTypeID);
+ VIR_FREE(virtPortTypeIDVersion);
+ VIR_FREE(virtPortInstanceID);
+ VIR_FREE(virtPortProfileID);
+ VIR_FREE(virtPortType);
+
+ return ret;
+}
+
+
+static void
+virVirtualPortProfileFormat(virBufferPtr buf,
+ virVirtualPortProfileDefPtr virtPort,
+ const char *indent)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+ if (virtPort->virtPortType == VIR_VIRTUALPORT_NONE)
+ return;
+
+ virBufferVSprintf(buf, "%s<virtualport type='%s'>\n",
+ indent,
+ virVirtualPortTypeToString(virtPort->virtPortType));
+
+ switch (virtPort->virtPortType) {
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ break;
+
+ case VIR_VIRTUALPORT_8021QBG:
+ virUUIDFormat(virtPort->u.virtPort8021Qbg.instanceID,
+ uuidstr);
+ virBufferVSprintf(buf,
+ "%s <parameters managerid='%d' typeid='%d' "
+ "typeidversion='%d' instanceid='%s'/>\n",
+ indent,
+ virtPort->u.virtPort8021Qbg.managerID,
+ virtPort->u.virtPort8021Qbg.typeID,
+ virtPort->u.virtPort8021Qbg.typeIDVersion,
+ uuidstr);
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+ virBufferVSprintf(buf,
+ "%s <parameters profileid='%s'/>\n",
+ indent,
+ virtPort->u.virtPort8021Qbh.profileID);
+ break;
+ }
+
+ virBufferVSprintf(buf, "%s</virtualport>\n", indent);
+}
+
+
/* Parse the XML definition for a network interface
* @param node XML nodeset to parse for net definition
* @return 0 on success, -1 on failure
@@ -1832,6 +2021,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
char *devaddr = NULL;
char *mode = NULL;
virNWFilterHashTablePtr filterparams = NULL;
+ virVirtualPortProfileDef virtPort;
+ bool virtPortParsed = false;
if (VIR_ALLOC(def) < 0) {
virReportOOMError();
@@ -1873,6 +2064,12 @@ virDomainNetDefParseXML(virCapsPtr caps,
xmlStrEqual(cur->name, BAD_CAST "source")) {
dev = virXMLPropString(cur, "dev");
mode = virXMLPropString(cur, "mode");
+ } else if ((virtPortParsed == false) &&
+ (def->type == VIR_DOMAIN_NET_TYPE_DIRECT) &&
+ xmlStrEqual(cur->name, BAD_CAST "virtualport")) {
+ if (virVirtualPortProfileDefParseXML(cur, &virtPort))
+ goto error;
+ virtPortParsed = true;
} else if ((network == NULL) &&
((def->type == VIR_DOMAIN_NET_TYPE_SERVER) ||
(def->type == VIR_DOMAIN_NET_TYPE_CLIENT) ||
@@ -2048,6 +2245,9 @@ virDomainNetDefParseXML(virCapsPtr caps,
} else
def->data.direct.mode = VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA;
+ if (virtPortParsed)
+ def->data.direct.virtPortProfile = virtPort;
+
def->data.direct.linkdev = dev;
dev = NULL;
@@ -5140,6 +5340,8 @@ virDomainNetDefFormat(virBufferPtr buf,
virBufferVSprintf(buf, " mode='%s'",
virDomainNetdevMacvtapTypeToString(def->data.direct.mode));
virBufferAddLit(buf, "/>\n");
+ virVirtualPortProfileFormat(buf, &def->data.direct.virtPortProfile,
+ " ");
break;
case VIR_DOMAIN_NET_TYPE_USER:
Index: libvirt-acl/src/conf/domain_conf.h
===================================================================
--- libvirt-acl.orig/src/conf/domain_conf.h
+++ libvirt-acl/src/conf/domain_conf.h
@@ -38,6 +38,7 @@
# include "network.h"
# include "nwfilter_params.h"
# include "nwfilter_conf.h"
+# include "macvtap.h"
/* Private component of virDomainXMLFlags */
typedef enum {
@@ -290,6 +291,7 @@ struct _virDomainNetDef {
struct {
char *linkdev;
int mode;
+ virVirtualPortProfileDef virtPortProfile;
} direct;
} data;
char *ifname;
Index: libvirt-acl/src/util/macvtap.c
===================================================================
--- libvirt-acl.orig/src/util/macvtap.c
+++ libvirt-acl/src/util/macvtap.c
@@ -43,6 +43,7 @@
# include "util.h"
# include "memory.h"
+# include "logging.h"
# include "macvtap.h"
# include "interface.h"
# include "conf/domain_conf.h"
@@ -57,6 +58,16 @@
# define MACVTAP_NAME_PREFIX "macvtap"
# define MACVTAP_NAME_PATTERN "macvtap%d"
+
+static int associatePortProfileId(const char *macvtap_ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ int vf,
+ const unsigned char *vmuuid);
+
+static int disassociatePortProfileId(const char *macvtap_ifname,
+ const virVirtualPortProfileDefPtr virtPort);
+
+
static int nlOpen(void)
{
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
@@ -577,8 +588,10 @@ configMacvtapTap(int tapfd, int vnet_hdr
* be NULL if this function is supposed to choose a name
* @macaddress: The MAC address for the macvtap device
* @linkdev: The interface name of the NIC to connect to the external bridge
- * @mode_str: String describing the mode. Valid are 'bridge', 'vepa' and
- * 'private'.
+ * @mode: int describing the mode for 'bridge', 'vepa' or 'private'.
+ * @vnet_hdr: 1 to enable IFF_VNET_HDR, 0 to disable it
+ * @vmuuid: The UUID of the VM the macvtap belongs to
+ * @virtPortProfile: pointer to object holding the virtual port profile data
* @res_ifname: Pointer to a string pointer where the actual name of the
* interface will be stored into if everything succeeded. It is up
* to the caller to free the string.
@@ -592,8 +605,10 @@ openMacvtapTap(const char *tgifname,
const unsigned char *macaddress,
const char *linkdev,
int mode,
- char **res_ifname,
- int vnet_hdr)
+ int vnet_hdr,
+ const unsigned char *vmuuid,
+ virVirtualPortProfileDefPtr virtPortProfile,
+ char **res_ifname)
{
const char *type = "macvtap";
int c, rc;
@@ -639,6 +654,14 @@ create_name:
cr_ifname = ifname;
}
+ if (associatePortProfileId(cr_ifname,
+ virtPortProfile,
+ -1,
+ vmuuid) != 0) {
+ rc = -1;
+ goto link_del_exit;
+ }
+
rc = ifaceUp(cr_ifname);
if (rc != 0) {
virReportSystemError(errno,
@@ -647,7 +670,7 @@ create_name:
"MAC address"),
cr_ifname);
rc = -1;
- goto link_del_exit;
+ goto disassociate_exit;
}
rc = openTap(cr_ifname, 10);
@@ -656,14 +679,18 @@ create_name:
if (configMacvtapTap(rc, vnet_hdr) < 0) {
close(rc);
rc = -1;
- goto link_del_exit;
+ goto disassociate_exit;
}
*res_ifname = strdup(cr_ifname);
} else
- goto link_del_exit;
+ goto disassociate_exit;
return rc;
+disassociate_exit:
+ disassociatePortProfileId(cr_ifname,
+ virtPortProfile);
+
link_del_exit:
link_del(cr_ifname);
@@ -674,13 +701,103 @@ link_del_exit:
/**
* delMacvtap:
* @ifname : The name of the macvtap interface
+ * @virtPortProfile: pointer to object holding the virtual port profile data
*
- * Delete an interface given its name.
+ * Delete an interface given its name. Disassociate
+ * it with the switch if port profile parameters
+ * were provided.
*/
void
-delMacvtap(const char *ifname)
+delMacvtap(const char *ifname,
+ virVirtualPortProfileDefPtr virtPortProfile)
{
- link_del(ifname);
+ if (ifname) {
+ disassociatePortProfileId(ifname,
+ virtPortProfile);
+ link_del(ifname);
+ }
}
#endif
+
+
+/**
+ * associatePortProfile
+ *
+ * @macvtap_ifname: The name of the macvtap device
+ * @virtPort: pointer to the object holding port profile parameters
+ * @vf: virtual function number, -1 if to be ignored
+ * @vmuuid : the UUID of the virtual machine
+ *
+ * Associate a port on a swtich with a profile. This function
+ * may notify a kernel driver or an external daemon to run
+ * the setup protocol. If profile parameters were not supplied
+ * by the user, then this function returns without doing
+ * anything.
+ *
+ * Returns 0 in case of success, != 0 otherwise with error
+ * having been reported.
+ */
+static int
+associatePortProfileId(const char *macvtap_ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ int vf,
+ const unsigned char *vmuuid)
+{
+ int rc = 0;
+ VIR_DEBUG("Associating port profile '%p' on link device '%s'",
+ virtPort, macvtap_ifname);
+ (void)vf;
+ (void)vmuuid;
+
+ switch (virtPort->virtPortType) {
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ break;
+
+ case VIR_VIRTUALPORT_8021QBG:
+
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+
+ break;
+ }
+
+ return rc;
+}
+
+
+/**
+ * disassociatePortProfile
+ *
+ * @macvtap_ifname: The name of the macvtap device
+ * @virtPort: point to object holding port profile parameters
+ *
+ * Returns 0 in case of success, != 0 otherwise with error
+ * having been reported.
+ */
+static int
+disassociatePortProfileId(const char *macvtap_ifname,
+ const virVirtualPortProfileDefPtr virtPort)
+{
+ int rc = 0;
+ VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
+ virtPort, macvtap_ifname);
+
+ switch (virtPort->virtPortType) {
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ break;
+
+ case VIR_VIRTUALPORT_8021QBG:
+
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+
+ break;
+ }
+
+ return rc;
+}
Index: libvirt-acl/src/util/macvtap.h
===================================================================
--- libvirt-acl.orig/src/util/macvtap.h
+++ libvirt-acl/src/util/macvtap.h
@@ -24,6 +24,40 @@
# include <config.h>
+
+enum virVirtualPortType {
+ VIR_VIRTUALPORT_NONE,
+ VIR_VIRTUALPORT_8021QBG,
+ VIR_VIRTUALPORT_8021QBH,
+
+ VIR_VIRTUALPORT_TYPE_LAST,
+};
+
+# ifdef IFLA_VF_PORT_PROFILE_MAX
+# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX IFLA_VF_PORT_PROFILE_MAX
+# else
+# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX 40
+# endif
+
+/* profile data for macvtap (VEPA) */
+typedef struct _virVirtualPortProfileDef virVirtualPortProfileDef;
+typedef virVirtualPortProfileDef *virVirtualPortProfileDefPtr;
+struct _virVirtualPortProfileDef {
+ enum virVirtualPortType virtPortType;
+ union {
+ struct {
+ uint8_t managerID;
+ uint32_t typeID; // 24 bit valid
+ uint8_t typeIDVersion;
+ unsigned char instanceID[VIR_UUID_BUFLEN];
+ } virtPort8021Qbg;
+ struct {
+ char profileID[LIBVIRT_IFLA_VF_PORT_PROFILE_MAX];
+ } virtPort8021Qbh;
+ } u;
+};
+
+
# if defined(WITH_MACVTAP)
# include "internal.h"
@@ -32,10 +66,13 @@ int openMacvtapTap(const char *ifname,
const unsigned char *macaddress,
const char *linkdev,
int mode,
- char **res_ifname,
- int vnet_hdr);
+ int vnet_hdr,
+ const unsigned char *vmuuid,
+ virVirtualPortProfileDefPtr virtPortProfile,
+ char **res_ifname);
-void delMacvtap(const char *ifname);
+void delMacvtap(const char *ifname,
+ virVirtualPortProfileDefPtr virtPortProfile);
# endif /* WITH_MACVTAP */
@@ -44,4 +81,6 @@ void delMacvtap(const char *ifname);
# define MACVTAP_MODE_BRIDGE_STR "bridge"
+VIR_ENUM_DECL(virVirtualPort)
+
#endif /* __UTIL_MACVTAP_H__ */
Index: libvirt-acl/tests/domainschemadata/portprofile.xml
===================================================================
--- /dev/null
+++ libvirt-acl/tests/domainschemadata/portprofile.xml
@@ -0,0 +1,36 @@
+<domain type='lxc'>
+ <name>portprofile</name>
+ <uuid>00000000-0000-0000-0000-000000000000</uuid>
+ <memory>1048576</memory>
+ <os>
+ <type>exe</type>
+ <init>/sh</init>
+ </os>
+ <devices>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ <virtualport type='802.1Qbg'>
+ <parameters managerid='12' typeid='1193046' typeidversion='1'
+ instanceid='fa9b7fff-b0a0-4893-8e0e-beef4ff18f8f'/>
+ </virtualport>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ <virtualport type='802.1Qbg'>
+ <parameters managerid='12' typeid='1193046' typeidversion='1'/>
+ </virtualport>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ <virtualport type='802.1Qbh'>
+ <parameters profileid='my_profile'/>
+ </virtualport>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ </interface>
+ </devices>
+</domain>
Index: libvirt-acl/src/qemu/qemu_conf.h
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_conf.h
+++ libvirt-acl/src/qemu/qemu_conf.h
@@ -274,9 +274,8 @@ qemudOpenVhostNet(virDomainNetDefPtr net
int qemudPhysIfaceConnect(virConnectPtr conn,
struct qemud_driver *driver,
virDomainNetDefPtr net,
- char *linkdev,
- int brmode,
- unsigned long long qemuCmdFlags);
+ unsigned long long qemuCmdFlags,
+ const unsigned char *vmuuid);
int qemudProbeMachineTypes (const char *binary,
virCapsGuestMachinePtr **machines,
Index: libvirt-acl/src/qemu/qemu_driver.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_driver.c
+++ libvirt-acl/src/qemu/qemu_driver.c
@@ -3708,10 +3708,9 @@ static void qemudShutdownVMDaemon(struct
def = vm->def;
for (i = 0; i < def->nnets; i++) {
virDomainNetDefPtr net = def->nets[i];
- if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
- if (net->ifname)
- delMacvtap(net->ifname);
- }
+ if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT)
+ delMacvtap(net->ifname,
+ &net->data.direct.virtPortProfile);
}
#endif
@@ -7467,9 +7466,8 @@ static int qemudDomainAttachNetDevice(vi
}
if ((tapfd = qemudPhysIfaceConnect(conn, driver, net,
- net->data.direct.linkdev,
- net->data.direct.mode,
- qemuCmdFlags)) < 0)
+ qemuCmdFlags,
+ vm->def->uuid)) < 0)
return -1;
}
@@ -8512,10 +8510,9 @@ qemudDomainDetachNetDevice(struct qemud_
virNWFilterTearNWFilter(detach);
#if WITH_MACVTAP
- if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
- if (detach->ifname)
- delMacvtap(detach->ifname);
- }
+ if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT)
+ delMacvtap(detach->ifname,
+ &detach->data.direct.virtPortProfile);
#endif
if ((driver->macFilter) && (detach->ifname != NULL)) {
Index: libvirt-acl/src/qemu/qemu_conf.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_conf.c
+++ libvirt-acl/src/qemu/qemu_conf.c
@@ -1470,9 +1470,8 @@ int
qemudPhysIfaceConnect(virConnectPtr conn,
struct qemud_driver *driver,
virDomainNetDefPtr net,
- char *linkdev,
- int brmode,
- unsigned long long qemuCmdFlags)
+ unsigned long long qemuCmdFlags,
+ const unsigned char *vmuuid)
{
int rc;
#if WITH_MACVTAP
@@ -1484,8 +1483,9 @@ qemudPhysIfaceConnect(virConnectPtr conn
net->model && STREQ(net->model, "virtio"))
vnet_hdr = 1;
- rc = openMacvtapTap(net->ifname, net->mac, linkdev, brmode,
- &res_ifname, vnet_hdr);
+ rc = openMacvtapTap(net->ifname, net->mac, net->data.direct.linkdev,
+ net->data.direct.mode, vnet_hdr, vmuuid,
+ &net->data.direct.virtPortProfile, &res_ifname);
if (rc >= 0) {
VIR_FREE(net->ifname);
net->ifname = res_ifname;
@@ -1505,17 +1505,17 @@ qemudPhysIfaceConnect(virConnectPtr conn
if (err) {
close(rc);
rc = -1;
- delMacvtap(net->ifname);
+ delMacvtap(net->ifname,
+ &net->data.direct.virtPortProfile);
}
}
}
#else
(void)conn;
(void)net;
- (void)linkdev;
- (void)brmode;
(void)qemuCmdFlags;
(void)driver;
+ (void)vmuuid;
qemuReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("No support for macvtap device"));
rc = -1;
@@ -4135,9 +4135,8 @@ int qemudBuildCommandLine(virConnectPtr
goto no_memory;
} else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
int tapfd = qemudPhysIfaceConnect(conn, driver, net,
- net->data.direct.linkdev,
- net->data.direct.mode,
- qemuCmdFlags);
+ qemuCmdFlags,
+ def->uuid);
if (tapfd < 0)
goto error;
14 years, 11 months
[libvirt] [PATCH v2] Fix up basic migration.
by Chris Lalancette
Basic live migration was broken by the commit that added
non-shared block support in two ways:
1) It added a virCheckFlags() to doNativeMigrate(). Besides
the fact that typical usage of virCheckFlags() is in driver
entry points, and doNativeMigrate() is not an entry point,
it was missing important flags like VIR_MIGRATE_LIVE. Move
the virCheckFlags to the top-level qemuDomainMigratePrepare2
and friends. Unfortunately this required adding a new variant
of the virCheckFlags() macro that could handle unsigned long
arguments, but it should be easily extendable from here.
2) It also added a memory leak in qemuMonitorTextMigrate()
by not freeing the memory used by virBufferContentAndReset().
This is fixed by storing the pointer in a temporary variable
and freeing it at the end.
With this patch in place, normal live migration works again.
Signed-off-by: Chris Lalancette <clalance(a)redhat.com>
---
src/esx/esx_driver.c | 18 +++++-----
src/internal.h | 10 +++++-
src/nwfilter/nwfilter_driver.c | 2 +-
src/qemu/qemu_driver.c | 64 +++++++++++++++++++++++++++------------
src/qemu/qemu_monitor_text.c | 10 ++++--
src/storage/storage_driver.c | 2 +-
src/vbox/vbox_tmpl.c | 24 +++++++-------
src/xen/xend_internal.c | 6 ++--
8 files changed, 85 insertions(+), 51 deletions(-)
diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c
index 7c9c50e..7a3609b 100644
--- a/src/esx/esx_driver.c
+++ b/src/esx/esx_driver.c
@@ -3310,7 +3310,7 @@ esxDomainSnapshotCreateXML(virDomainPtr domain, const char *xmlDesc,
esxVI_TaskInfoState taskInfoState;
virDomainSnapshotPtr snapshot = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
if (esxVI_EnsureSession(priv->host) < 0) {
goto failure;
@@ -3383,7 +3383,7 @@ esxDomainSnapshotDumpXML(virDomainSnapshotPtr snapshot,
char uuid_string[VIR_UUID_STRING_BUFLEN] = "";
char *xml = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
memset(&def, 0, sizeof (virDomainSnapshotDef));
@@ -3435,7 +3435,7 @@ esxDomainSnapshotNum(virDomainPtr domain, unsigned int flags)
esxPrivate *priv = domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
if (esxVI_EnsureSession(priv->host) < 0) {
goto failure;
@@ -3469,7 +3469,7 @@ esxDomainSnapshotListNames(virDomainPtr domain, char **names, int nameslen,
esxPrivate *priv = domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
if (names == NULL || nameslen < 0) {
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s", _("Invalid argument"));
@@ -3514,7 +3514,7 @@ esxDomainSnapshotLookupByName(virDomainPtr domain, const char *name,
esxVI_VirtualMachineSnapshotTree *snapshotTreeParent = NULL;
virDomainSnapshotPtr snapshot = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
if (esxVI_EnsureSession(priv->host) < 0) {
goto cleanup;
@@ -3545,7 +3545,7 @@ esxDomainHasCurrentSnapshot(virDomainPtr domain, unsigned int flags)
esxPrivate *priv = domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *currentSnapshotTree = NULL;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
if (esxVI_EnsureSession(priv->host) < 0) {
goto failure;
@@ -3581,7 +3581,7 @@ esxDomainSnapshotCurrent(virDomainPtr domain, unsigned int flags)
esxPrivate *priv = domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *currentSnapshotTree = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
if (esxVI_EnsureSession(priv->host) < 0) {
goto cleanup;
@@ -3614,7 +3614,7 @@ esxDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, unsigned int flags)
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
if (esxVI_EnsureSession(priv->host) < 0) {
goto failure;
@@ -3667,7 +3667,7 @@ esxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags)
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
- virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
+ virCheckFlagsUI(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
if (esxVI_EnsureSession(priv->host) < 0) {
goto failure;
diff --git a/src/internal.h b/src/internal.h
index c3c5311..d39a35e 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -210,7 +210,7 @@
* Returns nothing. Exits the caller function if unsupported flags were
* passed to it.
*/
-# define virCheckFlags(supported, retval) \
+# define virCheckFlags(supported, retval, format) \
do { \
if ((flags & ~(supported))) { \
virReportErrorHelper(NULL, \
@@ -219,10 +219,16 @@
__FILE__, \
__FUNCTION__, \
__LINE__, \
- _("%s: unsupported flags (0x%x)"), \
+ _("%s: unsupported flags (0x" #format ")"),\
__FUNCTION__, flags & ~(supported)); \
return retval; \
} \
} while (0)
+# define virCheckFlagsUI(supported, retval) \
+ virCheckFlags(supported, retval, %x)
+
+# define virCheckFlagsUL(supported, retval) \
+ virCheckFlags(supported, retval, %lx)
+
#endif /* __VIR_INTERNAL_H__ */
diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c
index 3ded2be..b356e95 100644
--- a/src/nwfilter/nwfilter_driver.c
+++ b/src/nwfilter/nwfilter_driver.c
@@ -389,7 +389,7 @@ nwfilterDumpXML(virNWFilterPtr obj,
virNWFilterPoolObjPtr pool;
char *ret = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
nwfilterDriverLock(driver);
pool = virNWFilterPoolObjFindByUUID(&driver->pools, obj->uuid);
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 51ee320..6e93755 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -5252,7 +5252,7 @@ qemuDomainManagedSave(virDomainPtr dom, unsigned int flags)
int ret = -1;
int compressed;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -5313,7 +5313,7 @@ qemuDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags)
int ret = -1;
char *name = NULL;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -5347,7 +5347,7 @@ qemuDomainManagedSaveRemove(virDomainPtr dom, unsigned int flags)
int ret = -1;
char *name = NULL;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -8077,9 +8077,9 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom,
virCgroupPtr cgroup = NULL;
int ret = -1;
- virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
- VIR_DOMAIN_DEVICE_MODIFY_LIVE |
- VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
+ virCheckFlagsUI(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
+ VIR_DOMAIN_DEVICE_MODIFY_LIVE |
+ VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
qemuReportError(VIR_ERR_OPERATION_INVALID,
@@ -9457,7 +9457,7 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom,
struct stat sb;
int i;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -10149,6 +10149,15 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
int ret = -1;
int internalret;
+ virCheckFlagsUL(VIR_MIGRATE_LIVE |
+ VIR_MIGRATE_PEER2PEER |
+ VIR_MIGRATE_TUNNELLED |
+ VIR_MIGRATE_PERSIST_DEST |
+ VIR_MIGRATE_UNDEFINE_SOURCE |
+ VIR_MIGRATE_PAUSED |
+ VIR_MIGRATE_NON_SHARED_DISK |
+ VIR_MIGRATE_NON_SHARED_INC, -1);
+
*uri_out = NULL;
qemuDriverLock(driver);
@@ -10330,9 +10339,6 @@ static int doNativeMigrate(struct qemud_driver *driver,
qemuDomainObjPrivatePtr priv = vm->privateData;
unsigned int background_flags = 0;
- virCheckFlags(VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC,
- -1);
-
/* Issue the migrate command. */
if (STRPREFIX(uri, "tcp:") && !STRPREFIX(uri, "tcp://")) {
/* HACK: source host generates bogus URIs, so fix them up */
@@ -10753,6 +10759,15 @@ qemudDomainMigratePerform (virDomainPtr dom,
int resume = 0;
qemuDomainObjPrivatePtr priv;
+ virCheckFlagsUL(VIR_MIGRATE_LIVE |
+ VIR_MIGRATE_PEER2PEER |
+ VIR_MIGRATE_TUNNELLED |
+ VIR_MIGRATE_PERSIST_DEST |
+ VIR_MIGRATE_UNDEFINE_SOURCE |
+ VIR_MIGRATE_PAUSED |
+ VIR_MIGRATE_NON_SHARED_DISK |
+ VIR_MIGRATE_NON_SHARED_INC, -1);
+
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
if (!vm) {
@@ -10856,6 +10871,15 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
virErrorPtr orig_err;
int newVM = 1;
+ virCheckFlagsUL(VIR_MIGRATE_LIVE |
+ VIR_MIGRATE_PEER2PEER |
+ VIR_MIGRATE_TUNNELLED |
+ VIR_MIGRATE_PERSIST_DEST |
+ VIR_MIGRATE_UNDEFINE_SOURCE |
+ VIR_MIGRATE_PAUSED |
+ VIR_MIGRATE_NON_SHARED_DISK |
+ VIR_MIGRATE_NON_SHARED_INC, NULL);
+
/* Migration failed. Save the current error so nothing squashes it */
orig_err = virSaveLastError();
@@ -11224,7 +11248,7 @@ qemuDomainMigrateSetMaxDowntime(virDomainPtr dom,
qemuDomainObjPrivatePtr priv;
int ret = -1;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -11394,7 +11418,7 @@ static virDomainSnapshotPtr qemuDomainSnapshotCreateXML(virDomainPtr domain,
const char *qemuimgarg[] = { NULL, "snapshot", "-c", NULL, NULL, NULL };
int i;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
qemuDriverLock(driver);
virUUIDFormat(domain->uuid, uuidstr);
@@ -11514,7 +11538,7 @@ static int qemuDomainSnapshotListNames(virDomainPtr domain, char **names,
virDomainObjPtr vm = NULL;
int n = -1;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, domain->uuid);
@@ -11542,7 +11566,7 @@ static int qemuDomainSnapshotNum(virDomainPtr domain,
virDomainObjPtr vm = NULL;
int n = -1;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, domain->uuid);
@@ -11572,7 +11596,7 @@ static virDomainSnapshotPtr qemuDomainSnapshotLookupByName(virDomainPtr domain,
virDomainSnapshotObjPtr snap = NULL;
virDomainSnapshotPtr snapshot = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, domain->uuid);
@@ -11607,7 +11631,7 @@ static int qemuDomainHasCurrentSnapshot(virDomainPtr domain,
virDomainObjPtr vm;
int ret = -1;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, domain->uuid);
@@ -11635,7 +11659,7 @@ static virDomainSnapshotPtr qemuDomainSnapshotCurrent(virDomainPtr domain,
virDomainObjPtr vm;
virDomainSnapshotPtr snapshot = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, domain->uuid);
@@ -11671,7 +11695,7 @@ static char *qemuDomainSnapshotDumpXML(virDomainSnapshotPtr snapshot,
virDomainSnapshotObjPtr snap = NULL;
char uuidstr[VIR_UUID_STRING_BUFLEN];
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
qemuDriverLock(driver);
virUUIDFormat(snapshot->domain->uuid, uuidstr);
@@ -11711,7 +11735,7 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
qemuDomainObjPrivatePtr priv;
int rc;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
virUUIDFormat(snapshot->domain->uuid, uuidstr);
@@ -11949,7 +11973,7 @@ static int qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
char uuidstr[VIR_UUID_STRING_BUFLEN];
struct snap_remove rem;
- virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
+ virCheckFlagsUI(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
qemuDriverLock(driver);
virUUIDFormat(snapshot->domain->uuid, uuidstr);
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index 3df078a..66988aa 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -1134,6 +1134,7 @@ static int qemuMonitorTextMigrate(qemuMonitorPtr mon,
int ret = -1;
char *safedest = qemuMonitorEscapeArg(dest);
virBuffer extra = VIR_BUFFER_INITIALIZER;
+ char *extrastr = NULL;
if (!safedest) {
virReportOOMError();
@@ -1149,10 +1150,12 @@ static int qemuMonitorTextMigrate(qemuMonitorPtr mon,
if (virBufferError(&extra)) {
virBufferFreeAndReset(&extra);
virReportOOMError();
- free(safedest);
- return -1;
+ goto cleanup;
}
- if (virAsprintf(&cmd, "migrate %s\"%s\"", virBufferContentAndReset(&extra), safedest) < 0) {
+
+ extrastr = virBufferContentAndReset(&extra);
+ if (virAsprintf(&cmd, "migrate %s\"%s\"", extrastr ? extrastr : "",
+ safedest) < 0) {
virReportOOMError();
goto cleanup;
}
@@ -1181,6 +1184,7 @@ static int qemuMonitorTextMigrate(qemuMonitorPtr mon,
ret = 0;
cleanup:
+ VIR_FREE(extrastr);
VIR_FREE(safedest);
VIR_FREE(info);
VIR_FREE(cmd);
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index b148e39..27aaf86 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -1681,7 +1681,7 @@ storageVolumeWipe(virStorageVolPtr obj,
virStorageVolDefPtr vol = NULL;
int ret = -1;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
storageDriverLock(driver);
pool = virStoragePoolObjFindByName(&driver->pools, obj->pool);
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index 6a9a2bf..ce4d659 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -4877,9 +4877,9 @@ static int vboxDomainAttachDeviceFlags(virDomainPtr dom, const char *xml,
static int vboxDomainUpdateDeviceFlags(virDomainPtr dom, const char *xml,
unsigned int flags) {
- virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
- VIR_DOMAIN_DEVICE_MODIFY_LIVE |
- VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
+ virCheckFlagsUI(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
+ VIR_DOMAIN_DEVICE_MODIFY_LIVE |
+ VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
vboxError(VIR_ERR_OPERATION_INVALID, "%s",
@@ -5189,7 +5189,7 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom,
PRInt32 result;
#endif
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
if (!(def = virDomainSnapshotDefParseString(xmlDesc, 1)))
goto cleanup;
@@ -5304,7 +5304,7 @@ vboxDomainSnapshotDumpXML(virDomainSnapshotPtr snapshot,
PRBool online = PR_FALSE;
char uuidstr[VIR_UUID_STRING_BUFLEN];
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(domiid) < 0) {
@@ -5415,7 +5415,7 @@ vboxDomainSnapshotNum(virDomainPtr dom,
nsresult rc;
PRUint32 snapshotCount;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(iid) < 0) {
@@ -5462,7 +5462,7 @@ vboxDomainSnapshotListNames(virDomainPtr dom,
int count = 0;
int i;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(iid) < 0) {
@@ -5532,7 +5532,7 @@ vboxDomainSnapshotLookupByName(virDomainPtr dom,
ISnapshot *snapshot = NULL;
nsresult rc;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(iid) < 0) {
@@ -5571,7 +5571,7 @@ vboxDomainHasCurrentSnapshot(virDomainPtr dom,
ISnapshot *snapshot = NULL;
nsresult rc;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(iid) < 0) {
@@ -5618,7 +5618,7 @@ vboxDomainSnapshotCurrent(virDomainPtr dom,
char *name = NULL;
nsresult rc;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(iid) < 0) {
@@ -5793,7 +5793,7 @@ vboxDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
PRUint32 state;
nsresult rc;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(domiid) < 0) {
@@ -5961,7 +5961,7 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
PRUint32 state;
nsresult rc;
- virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
+ virCheckFlagsUI(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(domiid) < 0) {
diff --git a/src/xen/xend_internal.c b/src/xen/xend_internal.c
index a203a8d..f1267cd 100644
--- a/src/xen/xend_internal.c
+++ b/src/xen/xend_internal.c
@@ -4205,9 +4205,9 @@ xenDaemonUpdateDeviceFlags(virDomainPtr domain, const char *xml,
virBuffer buf = VIR_BUFFER_INITIALIZER;
char class[8], ref[80];
- virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
- VIR_DOMAIN_DEVICE_MODIFY_LIVE |
- VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
+ virCheckFlagsUI(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
+ VIR_DOMAIN_DEVICE_MODIFY_LIVE |
+ VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) {
virXendError(VIR_ERR_INVALID_ARG, __FUNCTION__);
--
1.6.6.1
14 years, 11 months
[libvirt] [PATCH] Domain snapshot RNG and tests.
by Chris Lalancette
Signed-off-by: Chris Lalancette <clalance(a)redhat.com>
---
docs/schemas/domainsnapshot.rng | 53 ++++++++++++++++++++
tests/Makefile.am | 6 ++-
tests/domainsnapshotschematest | 10 ++++
tests/domainsnapshotxml2xmlin/description_only.xml | 3 +
tests/domainsnapshotxml2xmlin/empty.xml | 1 +
.../name_and_description.xml | 4 ++
tests/domainsnapshotxml2xmlin/name_only.xml | 3 +
tests/domainsnapshotxml2xmlout/all_parameters.xml | 13 +++++
tests/domainsnapshotxml2xmlout/noparent.xml | 9 +++
.../noparent_nodescription.xml | 7 +++
.../noparent_nodescription_noactive.xml | 6 ++
11 files changed, 114 insertions(+), 1 deletions(-)
create mode 100644 docs/schemas/domainsnapshot.rng
create mode 100755 tests/domainsnapshotschematest
create mode 100644 tests/domainsnapshotxml2xmlin/description_only.xml
create mode 100644 tests/domainsnapshotxml2xmlin/empty.xml
create mode 100644 tests/domainsnapshotxml2xmlin/name_and_description.xml
create mode 100644 tests/domainsnapshotxml2xmlin/name_only.xml
create mode 100644 tests/domainsnapshotxml2xmlout/all_parameters.xml
create mode 100644 tests/domainsnapshotxml2xmlout/noparent.xml
create mode 100644 tests/domainsnapshotxml2xmlout/noparent_nodescription.xml
create mode 100644 tests/domainsnapshotxml2xmlout/noparent_nodescription_noactive.xml
diff --git a/docs/schemas/domainsnapshot.rng b/docs/schemas/domainsnapshot.rng
new file mode 100644
index 0000000..86bab0b
--- /dev/null
+++ b/docs/schemas/domainsnapshot.rng
@@ -0,0 +1,53 @@
+<!-- A Relax NG schema for the libvirt domain snapshot properties XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <ref name='domainsnapshot'/>
+ </start>
+
+ <define name='domainsnapshot'>
+ <element name='domainsnapshot'>
+ <interleave>
+ <optional>
+ <element name='name'>
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name='description'>
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name='state'>
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name='creationTime'>
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name='active'>
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name='domain'>
+ <element name='uuid'>
+ <text/>
+ </element>
+ </element>
+ </optional>
+ <optional>
+ <element name='parent'>
+ <element name='name'>
+ <text/>
+ </element>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+</grammar>
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c5e52e3..caf8cd0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -76,6 +76,9 @@ EXTRA_DIST = \
nwfilterxml2xmlout \
nwfilterxml2xmlin \
nwfilterschematest \
+ domainsnapshotschematest \
+ domainsnapshotxml2xmlout \
+ domainsnapshotxml2xmlin \
$(patsubst %,qemuhelpdata/%,$(qemuhelpdata))
noinst_PROGRAMS = virshtest conftest \
@@ -123,7 +126,8 @@ test_scripts = \
storagevolschematest \
domainschematest \
nodedevschematest \
- nwfilterschematest
+ nwfilterschematest \
+ domainsnapshotschematest
if WITH_LIBVIRTD
test_scripts += \
diff --git a/tests/domainsnapshotschematest b/tests/domainsnapshotschematest
new file mode 100755
index 0000000..1bdc539
--- /dev/null
+++ b/tests/domainsnapshotschematest
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+: ${srcdir=.}
+. $srcdir/test-lib.sh
+. $abs_srcdir/schematestutils.sh
+
+DIRS="domainsnapshotxml2xmlin domainsnapshotxml2xmlout"
+SCHEMA="domainsnapshot.rng"
+
+check_schema "$DIRS" "$SCHEMA"
diff --git a/tests/domainsnapshotxml2xmlin/description_only.xml b/tests/domainsnapshotxml2xmlin/description_only.xml
new file mode 100644
index 0000000..b76fca2
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlin/description_only.xml
@@ -0,0 +1,3 @@
+<domainsnapshot>
+ <description>My description</description>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlin/empty.xml b/tests/domainsnapshotxml2xmlin/empty.xml
new file mode 100644
index 0000000..f2bcfbe
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlin/empty.xml
@@ -0,0 +1 @@
+<domainsnapshot/>
diff --git a/tests/domainsnapshotxml2xmlin/name_and_description.xml b/tests/domainsnapshotxml2xmlin/name_and_description.xml
new file mode 100644
index 0000000..90ce774
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlin/name_and_description.xml
@@ -0,0 +1,4 @@
+<domainsnapshot>
+ <name>snap1</name>
+ <description>A longer description!</description>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlin/name_only.xml b/tests/domainsnapshotxml2xmlin/name_only.xml
new file mode 100644
index 0000000..8123a02
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlin/name_only.xml
@@ -0,0 +1,3 @@
+<domainsnapshot>
+ <name>snapshot1</name>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlout/all_parameters.xml b/tests/domainsnapshotxml2xmlout/all_parameters.xml
new file mode 100644
index 0000000..ed4a600
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlout/all_parameters.xml
@@ -0,0 +1,13 @@
+<domainsnapshot>
+ <name>my snap name</name>
+ <description>!@#$%^</description>
+ <parent>
+ <name>earlier_snap</name>
+ </parent>
+ <state>running</state>
+ <creationTime>1272917631</creationTime>
+ <domain>
+ <uuid>9d37b878-a7cc-9f9a-b78f-49b3abad25a8</uuid>
+ </domain>
+ <active>1</active>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlout/noparent.xml b/tests/domainsnapshotxml2xmlout/noparent.xml
new file mode 100644
index 0000000..cbac0d8
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlout/noparent.xml
@@ -0,0 +1,9 @@
+<domainsnapshot>
+ <name>my snap name</name>
+ <description>!@#$%^</description>
+ <state>running</state>
+ <creationTime>1272917631</creationTime>
+ <domain>
+ <uuid>9d37b878-a7cc-9f9a-b78f-49b3abad25a8</uuid>
+ </domain>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlout/noparent_nodescription.xml b/tests/domainsnapshotxml2xmlout/noparent_nodescription.xml
new file mode 100644
index 0000000..0de202d
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlout/noparent_nodescription.xml
@@ -0,0 +1,7 @@
+<domainsnapshot>
+ <name>my snap name</name>
+ <description>!@#$%^</description>
+ <state>running</state>
+ <creationTime>1272917631</creationTime>
+ <active>1</active>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlout/noparent_nodescription_noactive.xml b/tests/domainsnapshotxml2xmlout/noparent_nodescription_noactive.xml
new file mode 100644
index 0000000..25b60f3
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlout/noparent_nodescription_noactive.xml
@@ -0,0 +1,6 @@
+<domainsnapshot>
+ <name>my snap name</name>
+ <description>!@#$%^</description>
+ <state>running</state>
+ <creationTime>1272917631</creationTime>
+</domainsnapshot>
--
1.6.6.1
14 years, 11 months
[libvirt] [PATCH v2] storage: Sanitize pool target paths
by Cole Robinson
Spurious / in a pool target path makes life difficult for apps using the
GetVolByPath, and doing other path based comparisons with pools. This
has caused a few issues for virt-manager users:
https://bugzilla.redhat.com/show_bug.cgi?id=494005
https://bugzilla.redhat.com/show_bug.cgi?id=593565
Add a new util API which removes spurious /, virFileSanitizePath. Sanitize
target paths when parsing pool XML, and for paths passed to GetVolByPath.
v2: Leading // must be preserved, properly sanitize path=/, sanitize
away /./ -> /
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/conf/storage_conf.c | 8 ++++++-
src/libvirt_private.syms | 1 +
src/storage/storage_driver.c | 8 ++++++-
src/util/util.c | 49 ++++++++++++++++++++++++++++++++++++++++++
src/util/util.h | 3 ++
5 files changed, 67 insertions(+), 2 deletions(-)
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
index 9aad081..422e76a 100644
--- a/src/conf/storage_conf.c
+++ b/src/conf/storage_conf.c
@@ -602,6 +602,7 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) {
xmlNodePtr source_node;
char *type = NULL;
char *uuid = NULL;
+ char *tmppath;
if (VIR_ALLOC(ret) < 0) {
virReportOOMError();
@@ -699,11 +700,16 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) {
}
}
- if ((ret->target.path = virXPathString("string(./target/path)", ctxt)) == NULL) {
+ if ((tmppath = virXPathString("string(./target/path)", ctxt)) == NULL) {
virStorageReportError(VIR_ERR_XML_ERROR,
"%s", _("missing storage pool target path"));
goto cleanup;
}
+ ret->target.path = virFileSanitizePath(tmppath);
+ VIR_FREE(tmppath);
+ if (!ret->target.path)
+ goto cleanup;
+
if (virStorageDefParsePerms(ctxt, &ret->target.perms,
"./target/permissions", 0700) < 0)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 1594a08..2c74b08 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -667,6 +667,7 @@ virFileReadLimFD;
virFilePid;
virFileReadPid;
virFileLinkPointsTo;
+virFileSanitizePath;
virParseNumber;
virParseVersionString;
virPipeReadUntilEOF;
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index b148e39..0870f74 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -1204,6 +1204,11 @@ storageVolumeLookupByPath(virConnectPtr conn,
virStorageDriverStatePtr driver = conn->storagePrivateData;
unsigned int i;
virStorageVolPtr ret = NULL;
+ char *cleanpath;
+
+ cleanpath = virFileSanitizePath(path);
+ if (!cleanpath)
+ return NULL;
storageDriverLock(driver);
for (i = 0 ; i < driver->pools.count && !ret ; i++) {
@@ -1213,7 +1218,7 @@ storageVolumeLookupByPath(virConnectPtr conn,
const char *stable_path;
stable_path = virStorageBackendStablePath(driver->pools.objs[i],
- path);
+ cleanpath);
/*
* virStorageBackendStablePath already does
* virStorageReportError if it fails; we just need to keep
@@ -1242,6 +1247,7 @@ storageVolumeLookupByPath(virConnectPtr conn,
"%s", _("no storage vol with matching path"));
cleanup:
+ VIR_FREE(cleanpath);
storageDriverUnlock(driver);
return ret;
}
diff --git a/src/util/util.c b/src/util/util.c
index 92b9a0f..b141406 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -1921,6 +1921,55 @@ int virFileAbsPath(const char *path, char **abspath)
return 0;
}
+/* Remove spurious / characters from a path. The result must be freed */
+char *
+virFileSanitizePath(const char *path)
+{
+ const char *cur = path;
+ char *cleanpath;
+ int idx = 0;
+
+ cleanpath = strdup(path);
+ if (!cleanpath) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ /* Starting with // is valid posix, but ///foo == /foo */
+ if (cur[0] == '/' && cur[1] == '/' && cur[2] != '/') {
+ cleanpath[0] = '/';
+ cleanpath[1] = '/';
+ idx = 2;
+ cur += 2;
+ }
+
+ /* Sanitize path in place */
+ while (*cur != '\0') {
+ if (*cur != '/') {
+ cleanpath[idx++] = *cur++;
+ continue;
+ }
+
+ /* Skip all extra / */
+ while (*cur == '/') {
+ cur++;
+
+ /* Resolve away /./ to just / */
+ if (cur[0] == '.' && cur[1] == '/')
+ cur++;
+ }
+
+ /* Don't add a trailing / unless path is only made of / */
+ if (idx != 0 && *cur == '\0')
+ break;
+
+ cleanpath[idx++] = '/';
+ }
+ cleanpath[idx] = '\0';
+
+ return cleanpath;
+}
+
/* Like strtol, but produce an "int" result, and check more carefully.
Return 0 upon success; return -1 to indicate failure.
When END_PTR is NULL, the byte after the final valid digit must be NUL.
diff --git a/src/util/util.h b/src/util/util.h
index 6bf6bcc..302bddf 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -118,6 +118,9 @@ char *virFindFileInPath(const char *file);
int virFileExists(const char *path);
+char *virFileSanitizePath(const char *path)
+ATTRIBUTE_NONNULL(1);
+
enum {
VIR_FILE_OP_NONE = 0,
VIR_FILE_OP_AS_UID = (1 << 0),
--
1.6.6.1
14 years, 11 months
[libvirt] [PATCH] network: bridge: Don't start network if it collides with host routing
by Cole Robinson
Fedora bug https://bugzilla.redhat.com/show_bug.cgi?id=235961
If using the default virtual network, an easy way to lose guest network
connectivity is to install libvirt inside the VM. The autostarted
default network inside the guest collides with host virtual network
routing. This is a long standing issue that has caused users quite a
bit of pain and confusion.
On network startup, parse /proc/net/route and compare the requested
IP+netmask against host routing destinations: if any matches are found,
refuse to start the network.
Caveat: I'm not very networking savvy
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/network/bridge_driver.c | 92 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 92 insertions(+), 0 deletions(-)
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 5d7ef19..79da757 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -42,6 +42,8 @@
#include <stdio.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
#include "virterror_internal.h"
#include "datatypes.h"
@@ -908,6 +910,92 @@ cleanup:
return ret;
}
+#define PROC_NET_ROUTE "/proc/net/route"
+
+static int networkCheckRouteCollision(virNetworkObjPtr network)
+{
+ int ret = -1, len;
+ char *cur, *buf = NULL;
+ enum {MAX_ROUTE_SIZE = 1024*64};
+ struct in_addr inaddress, innetmask;
+ char *netaddr = NULL;
+
+ if (!network->def->ipAddress || !network->def->netmask)
+ return 0;
+
+ if (inet_pton(AF_INET, network->def->ipAddress, &inaddress) <= 0) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse IP address '%s'"),
+ network->def->ipAddress);
+ goto error;
+ }
+ if (inet_pton(AF_INET, network->def->netmask, &innetmask) <= 0) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse netmask '%s'"),
+ network->def->netmask);
+ goto error;
+ }
+
+ inaddress.s_addr &= innetmask.s_addr;
+ netaddr = strdup(inet_ntoa(inaddress));
+ if (!netaddr) {
+ virReportOOMError();
+ goto error;
+ }
+
+ /* Read whole routing table into memory */
+ if ((len = virFileReadAll(PROC_NET_ROUTE, MAX_ROUTE_SIZE, &buf)) < 0)
+ goto error;
+
+ /* Dropping the last character shouldn't hurt */
+ buf[len-1] = '\0';
+
+ /* First line is just headings, skip it */
+ cur = strchr(buf, '\n');
+
+ while (cur) {
+ char iface[17];
+ char dest_buf[128];
+ char *dest_ip;
+ struct in_addr in;
+ int num;
+ unsigned int addr_val;
+
+ cur++;
+ num = sscanf(cur, "%16s %127s", iface, dest_buf);
+ if (num != 2) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed to parse %s"), PROC_NET_ROUTE);
+ goto error;
+ }
+
+ if (virStrToLong_ui(dest_buf, NULL, 16, &addr_val) < 0) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed not convert network address %s"),
+ dest_buf);
+ goto error;
+ }
+
+ in.s_addr = addr_val;
+ dest_ip = inet_ntoa(in);
+
+ if (STREQ(netaddr, dest_ip)) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Network destination %s is already in use "
+ "by interface %s"), netaddr, iface);
+ goto error;
+ }
+
+ cur = strchr(cur, '\n');
+ }
+
+ ret = 0;
+error:
+ VIR_FREE(buf);
+ VIR_FREE(netaddr);
+ return ret;
+}
+
static int networkStartNetworkDaemon(struct network_driver *driver,
virNetworkObjPtr network)
{
@@ -919,6 +1007,10 @@ static int networkStartNetworkDaemon(struct network_driver *driver,
return -1;
}
+ /* Check to see if network collides with an existing route */
+ if (networkCheckRouteCollision(network) < 0)
+ return -1;
+
if ((err = brAddBridge(driver->brctl, network->def->bridge))) {
virReportSystemError(err,
_("cannot create bridge '%s'"),
--
1.6.6.1
14 years, 11 months
[libvirt] [RFC PATCH v3] qemu: Add suport for pci-assign.configfd option
by Alex Williamson
This allows libvirt to open the PCI device sysfs config file prior
to dropping privileges so qemu can access the full config space.
Without this, a de-privileged qemu can only access the first 64
bytes of config space.
Signed-off-by: Alex Williamson <alex.williamson(a)redhat.com>
---
Note: this is still dependent on the configfd option being accepted
into qemu/kvm.
v2:
- fix qemuargs, two separate args (thanks Chris)
- don't hardcode pci segment
- error message of open() fail
v3:
- add hotplug support
- incorporate Daniel's comments
src/qemu/qemu_conf.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++-
src/qemu/qemu_conf.h | 6 ++-
src/qemu/qemu_driver.c | 28 +++++++++++++-
3 files changed, 128 insertions(+), 4 deletions(-)
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 2755545..926ce48 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -1350,6 +1350,48 @@ fail:
return -1;
}
+static void qemudParsePCIDeviceStrs(const char *qemu, unsigned long long *flags)
+{
+ const char *const qemuarg[] = { qemu, "-device", "pci-assign,?", NULL };
+ const char *const qemuenv[] = { "LC_ALL=C", NULL };
+ pid_t child;
+ int status;
+ int newstderr = -1;
+
+ if (virExec(qemuarg, qemuenv, NULL,
+ &child, -1, NULL, &newstderr, VIR_EXEC_CLEAR_CAPS) < 0)
+ return;
+
+ char *pciassign = NULL;
+ enum { MAX_PCI_OUTPUT_SIZE = 1024*4 };
+ int len = virFileReadLimFD(newstderr, MAX_PCI_OUTPUT_SIZE, &pciassign);
+ if (len < 0) {
+ virReportSystemError(errno,
+ _("Unable to read %s pci-assign device output"),
+ qemu);
+ goto cleanup;
+ }
+
+ if (strstr(pciassign, "pci-assign.configfd"))
+ *flags |= QEMUD_CMD_FLAG_PCI_CONFIGFD;
+
+cleanup:
+ VIR_FREE(pciassign);
+ close(newstderr);
+rewait:
+ if (waitpid(child, &status, 0) != child) {
+ if (errno == EINTR)
+ goto rewait;
+
+ VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"),
+ WEXITSTATUS(status), (unsigned long)child);
+ }
+ if (WEXITSTATUS(status) != 0) {
+ VIR_WARN("Unexpected exit status '%d', qemu probably failed",
+ WEXITSTATUS(status));
+ }
+}
+
int qemudExtractVersionInfo(const char *qemu,
unsigned int *retversion,
unsigned long long *retflags) {
@@ -1383,6 +1425,9 @@ int qemudExtractVersionInfo(const char *qemu,
&version, &is_kvm, &kvm_version) == -1)
goto cleanup2;
+ if (flags & QEMUD_CMD_FLAG_DEVICE)
+ qemudParsePCIDeviceStrs(qemu, &flags);
+
if (retversion)
*retversion = version;
if (retflags)
@@ -2892,8 +2937,33 @@ error:
}
+int
+qemudOpenPCIConfig(virDomainHostdevDefPtr dev)
+{
+ char *path = NULL;
+ int configfd = -1;
+
+ if (virAsprintf(&path, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/config",
+ dev->source.subsys.u.pci.domain,
+ dev->source.subsys.u.pci.bus,
+ dev->source.subsys.u.pci.slot,
+ dev->source.subsys.u.pci.function) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+
+ configfd = open(path, O_RDWR, 0);
+
+ if (configfd < 0)
+ virReportSystemError(errno, _("Failed opening %s"), path);
+
+ VIR_FREE(path);
+
+ return configfd;
+}
+
char *
-qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev)
+qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, const char *configfd)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
@@ -2903,6 +2973,8 @@ qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev)
dev->source.subsys.u.pci.slot,
dev->source.subsys.u.pci.function);
virBufferVSprintf(&buf, ",id=%s", dev->info.alias);
+ if (configfd && *configfd)
+ virBufferVSprintf(&buf, ",configfd=%s", configfd);
if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0)
goto error;
@@ -4605,8 +4677,30 @@ int qemudBuildCommandLine(virConnectPtr conn,
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ char *configfd_name = NULL;
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) {
+ int configfd = qemudOpenPCIConfig(hostdev);
+
+ if (configfd >= 0) {
+ if (virAsprintf(&configfd_name, "%d", configfd) < 0) {
+ close(configfd);
+ virReportOOMError();
+ goto no_memory;
+ }
+
+ if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) {
+ VIR_FREE(configfd_name);
+ close(configfd);
+ goto no_memory;
+ }
+
+ (*vmfds)[(*nvmfds)++] = configfd;
+ }
+ }
ADD_ARG_LIT("-device");
- if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev)))
+ devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name);
+ VIR_FREE(configfd_name);
+ if (!devstr)
goto error;
ADD_ARG(devstr);
} else if (qemuCmdFlags & QEMUD_CMD_FLAG_PCIDEVICE) {
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index 8fd8d79..d5ce4ce 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -89,6 +89,7 @@ enum qemud_cmd_flags {
QEMUD_CMD_FLAG_NO_HPET = (1LL << 33), /* -no-hpet flag is supported */
QEMUD_CMD_FLAG_NO_KVM_PIT = (1LL << 34), /* -no-kvm-pit-reinjection supported */
QEMUD_CMD_FLAG_TDF = (1LL << 35), /* -tdf flag (user-mode pit catchup) */
+ QEMUD_CMD_FLAG_PCI_CONFIGFD = (1LL << 36), /* pci-assign.configfd */
};
/* Main driver state */
@@ -245,7 +246,10 @@ char * qemuBuildSoundDevStr(virDomainSoundDefPtr sound);
/* Legacy, pre device support */
char * qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev);
/* Current, best practice */
-char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev);
+char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev,
+ const char *configfd);
+
+int qemudOpenPCIConfig(virDomainHostdevDefPtr dev);
/* Current, best practice */
char * qemuBuildChrChardevStr(virDomainChrDefPtr dev);
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index afdc718..52f2374 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -7655,6 +7655,8 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
pciDevice *pci;
int ret;
char *devstr = NULL;
+ int configfd = -1;
+ char *configfd_name = NULL;
if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) {
virReportOOMError();
@@ -7685,8 +7687,26 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
goto error;
if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) < 0)
goto error;
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) {
+ configfd = qemudOpenPCIConfig(hostdev);
+ if (configfd >= 0) {
+ if (virAsprintf(&configfd_name, "fd-%s",
+ hostdev->info.alias) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuMonitorSendFileHandle(priv->mon, configfd_name,
+ configfd) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ goto error;
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ }
+ }
- if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev)))
+ if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name)))
goto error;
qemuDomainObjEnterMonitorWithDriver(driver, vm);
@@ -7710,6 +7730,9 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
VIR_FREE(devstr);
+ VIR_FREE(configfd_name);
+ if (configfd >= 0)
+ close(configfd);
return 0;
@@ -7721,6 +7744,9 @@ error:
VIR_FREE(devstr);
pciDeviceListDel(driver->activePciHostdevs, pci);
+ VIR_FREE(configfd_name);
+ if (configfd >= 0)
+ close(configfd);
return -1;
}
14 years, 11 months
[libvirt] [v2 PATCH] add 802.1Qbh handling for port-profiles based on Stefan's previous patches
by Scott Feldman
From: Scott Feldman <scofeldm(a)cisco.com>
This patch builds on the work recently posted by Stefan Berger. It builds
on top of Stefan's two posted patches:
[PATCH v8] vepa: parsing for 802.1Qb{g|h} XML
[RFC][PATCH 1/3] vepa+vsi: Introduce dependency on libnl
Stefan's patches 2/3 and 3/3 are incorporated into my patch.
Changes from v1 to v2:
- Add Stefan's code for getPortProfileStatus
- Poll for up to 2 secs for port-profile status, at 1/8 sec intervals:
- if status indicates error, abort openMacvtapTap
- if status indicates success, exit polling
- if status is "in-progress" after 2 secs of polling, exit
polling loop silently, without error
My patch finishes out the 802.1Qbh parts, which Stefan had mostly complete.
I've tested using the recent kernel updates for VF_PORT netlink msgs and
enic for Cisco's 10G Ethernet NIC. I tested many VMs, each with several
direct interfaces, each configured with a port-profile per the XML. VM-to-VM,
and VM-to-external work as expected. VM-to-VM on same host (using same NIC)
works same as VM-to-VM where VMs are on diff hosts. I'm able to change
settings on the port-profile while the VM is running to change the virtual
port behaviour. For example, adjusting a QoS setting like rate limit. All
VMs with interfaces using that port-profile immediatly see the effect of the
change to the port-profile.
I don't have a SR-IOV device to test so source dev is a non-SR-IOV device,
but most of the code paths include support for specifing the source dev and
VF index. We'll need to complete this by discovering the PF given the VF
linkdev. Once we have the PF, we'll also have the VF index. All this info-
mation is available from sysfs.
Signed-off-by: Scott Feldman <scofeldm(a)cisco.com>
---
configure.ac | 16 +
src/util/macvtap.c | 778 +++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 778 insertions(+), 16 deletions(-)
diff --git a/configure.ac b/configure.ac
index 36ba703..885b0ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2005,13 +2005,26 @@ if test "$with_macvtap" != "no" ; then
fi
AM_CONDITIONAL([WITH_MACVTAP], [test "$with_macvtap" = "yes"])
+AC_TRY_COMPILE([ #include <sys/socket.h>
+ #include <linux/rtnetlink.h> ],
+ [ int x = IFLA_PORT_MAX; ],
+ [ with_virtualport=yes ],
+ [ with_virtualport=no ])
+if test "$with_virtualport" = "yes"; then
+ val=1
+else
+ val=0
+fi
+AC_DEFINE_UNQUOTED([WITH_VIRTUALPORT], $val, [whether vsi vepa support is enabled])
+AM_CONDITIONAL([WITH_VIRTUALPORT], [test "$with_virtualport" = "yes"])
+
dnl netlink library
LIBNL_CFLAGS=""
LIBNL_LIBS=""
-if test "$with_macvtap" = "yes"; then
+if test "$with_macvtap" = "yes" || "$with_virtualport" = "yes"; then
PKG_CHECK_MODULES([LIBNL], [libnl-1 >= $LIBNL_REQUIRED], [
], [
AC_MSG_ERROR([libnl >= $LIBNL_REQUIRED is required for macvtap support])
@@ -2084,6 +2097,7 @@ AC_MSG_NOTICE([ Network: $with_network])
AC_MSG_NOTICE([Libvirtd: $with_libvirtd])
AC_MSG_NOTICE([ netcf: $with_netcf])
AC_MSG_NOTICE([ macvtap: $with_macvtap])
+AC_MSG_NOTICE([virtport: $with_virtualport])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Storage Drivers])
AC_MSG_NOTICE([])
diff --git a/src/util/macvtap.c b/src/util/macvtap.c
index da81447..9ecc7f1 100644
--- a/src/util/macvtap.c
+++ b/src/util/macvtap.c
@@ -27,7 +27,7 @@
#include <config.h>
-#if WITH_MACVTAP
+#if WITH_MACVTAP || WITH_VIRTUALPORT
# include <stdio.h>
# include <errno.h>
@@ -41,6 +41,8 @@
# include <linux/rtnetlink.h>
# include <linux/if_tun.h>
+# include <netlink/msg.h>
+
# include "util.h"
# include "memory.h"
# include "logging.h"
@@ -58,15 +60,24 @@
# define MACVTAP_NAME_PREFIX "macvtap"
# define MACVTAP_NAME_PATTERN "macvtap%d"
+# define MICROSEC_PER_SEC (1000 * 1000)
+
static int associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort,
- int vf,
- const unsigned char *vmuuid);
+ const unsigned char *vmuuid,
+ const unsigned char *hostuuid);
static int disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort);
+enum virVirtualPortOp {
+ ASSOCIATE = 0x1,
+ DISASSOCIATE = 0x2,
+};
+
static int nlOpen(void)
{
@@ -159,6 +170,169 @@ err_exit:
}
+# ifdef IFLA_VF_PORT_MAX
+
+/**
+ * nlCommWaitSuccess:
+ *
+ * @nlmsg: pointer to netlink message
+ * @nl_grousp: the netlink multicast groups to send to
+ * @respbuf: pointer to pointer where response buffer will be allocated
+ * @respbuflen: pointer to integer holding the size of the response buffer
+ * on return of the function.
+ * @to_usecs: timeout in microseconds to wait for a success message
+ * to be returned
+ *
+ * Send the given message to the netlink multicast group and receive
+ * responses. Skip responses indicating an error and keep on receiving
+ * responses until a success response is returned.
+ * Returns 0 on success, -1 on error. In case of error, no response
+ * buffer will be returned.
+ */
+static int
+nlCommWaitSuccess(struct nlmsghdr *nlmsg, int nl_groups,
+ char **respbuf, int *respbuflen, long to_usecs)
+{
+ int rc = 0;
+ struct sockaddr_nl nladdr = {
+ .nl_family = AF_NETLINK,
+ .nl_pid = getpid(),
+ .nl_groups = nl_groups,
+ };
+ int rcvChunkSize = 1024; // expecting less than that
+ int rcvoffset = 0;
+ ssize_t nbytes;
+ int n;
+ struct timeval tv = {
+ .tv_sec = to_usecs / MICROSEC_PER_SEC,
+ .tv_usec = to_usecs % MICROSEC_PER_SEC,
+ };
+ fd_set rfds;
+ bool gotvalid = false;
+ int fd = nlOpen();
+ static uint32_t seq = 0x1234;
+ uint32_t myseq = seq++;
+ uint32_t mypid = getpid();
+
+ if (fd < 0)
+ return -1;
+
+ nlmsg->nlmsg_pid = mypid;
+ nlmsg->nlmsg_seq = myseq;
+ nlmsg->nlmsg_flags |= NLM_F_ACK;
+
+ nbytes = sendto(fd, (void *)nlmsg, nlmsg->nlmsg_len, 0,
+ (struct sockaddr *)&nladdr, sizeof(nladdr));
+ if (nbytes < 0) {
+ virReportSystemError(errno,
+ "%s", _("cannot send to netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+ fprintf(stderr,"sent %d bytes\n", (int)nbytes);
+
+ while (!gotvalid) {
+ rcvoffset = 0;
+ while (1) {
+ socklen_t addrlen = sizeof(nladdr);
+
+ if (VIR_REALLOC_N(*respbuf, rcvoffset+rcvChunkSize) < 0) {
+ virReportOOMError();
+ rc = -1;
+ goto err_exit;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ n = select(fd + 1, &rfds, NULL, NULL, &tv);
+ if (n == 0) {
+ fprintf(stderr,"Select timed out.\n");
+ rc = -1;
+ goto err_exit;
+ }
+
+ nbytes = recvfrom(fd, &((*respbuf)[rcvoffset]), rcvChunkSize, 0,
+ (struct sockaddr *)&nladdr, &addrlen);
+ fprintf(stderr,"got something... %d bytes\n",(int)nbytes);
+ if (nbytes < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ virReportSystemError(errno, "%s",
+ _("error receiving from netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+ rcvoffset += nbytes;
+ break;
+ }
+ *respbuflen = rcvoffset;
+
+ /* check message for error */
+ if (*respbuflen > NLMSG_LENGTH(0) && *respbuf != NULL) {
+ struct nlmsghdr *resp = (struct nlmsghdr *)*respbuf;
+ struct nlmsgerr *err;
+
+ fprintf(stderr,"resp->nlmsg_type=%d\n", resp->nlmsg_type);
+ fprintf(stderr,"resp->nlmsg_flags=0x%x\n", resp->nlmsg_flags);
+ fprintf(stderr,"resp->nlmsg_seq=%d (%d)\n", resp->nlmsg_seq, myseq);
+ fprintf(stderr,"resp->nlmsg_pid=%d (%d)\n", resp->nlmsg_pid, mypid);
+
+ if (resp->nlmsg_pid != mypid ||
+ resp->nlmsg_seq != myseq)
+ continue;
+
+ /* skip reflected message */
+ if (resp->nlmsg_type & 0x10)
+ continue;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len >= NLMSG_LENGTH(sizeof(*err))) {
+ fprintf(stderr,"error %d\n", -err->error);
+ if (-err->error != EOPNOTSUPP) {
+ /* assuming error msg from daemon */
+ fprintf(stderr,"Got error %d, but assuming its response from lldpad: len = %d\n",
+ -err->error,
+ *respbuflen);
+ gotvalid = true;
+ break;
+ }
+ }
+ /* whatever this is, skip it */
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ fprintf(stderr,"Skipping error resp. msg.\n");
+ break;
+
+ case NLMSG_DONE:
+ gotvalid = true;
+ break;
+
+ default:
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ break;
+ }
+ }
+ }
+
+err_exit:
+ if (rc == -1) {
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ }
+
+ nlClose(fd);
+ return rc;
+}
+
+#endif
+
static struct rtattr *
rtattrCreate(char *buffer, int bufsize, int type,
const void *data, int datalen)
@@ -204,6 +378,8 @@ nlAppend(struct nlmsghdr *nlm, int totlen, const void *data, int datalen)
}
+# if WITH_MACVTAP
+
static int
link_add(const char *type,
const unsigned char *macaddress, int macaddrsize,
@@ -651,9 +827,10 @@ create_name:
}
if (associatePortProfileId(cr_ifname,
+ net->data.direct.linkdev,
&net->data.direct.virtPortProfile,
- -1,
- vmuuid) != 0) {
+ vmuuid,
+ NULL /* hostuuid */) != 0) {
rc = -1;
goto link_del_exit;
}
@@ -685,6 +862,7 @@ create_name:
disassociate_exit:
disassociatePortProfileId(cr_ifname,
+ net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
link_del_exit:
@@ -707,21 +885,572 @@ delMacvtap(virDomainNetDefPtr net)
{
if (net->ifname) {
disassociatePortProfileId(net->ifname,
+ net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
link_del(net->ifname);
}
}
-#endif
+# endif
+
+
+# ifdef IFLA_PORT_MAX
+
+static struct nla_policy ifla_policy[IFLA_MAX + 1] =
+{
+ [IFLA_VF_PORTS] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_vf_ports_policy[IFLA_VF_PORT_MAX + 1] =
+{
+ [IFLA_VF_PORT] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] =
+{
+ [IFLA_PORT_RESPONSE] = { .type = NLA_U16 },
+};
+
+
+static int
+link_dump(int ifindex, const char *ifname, struct nlattr **tb,
+ char **recvbuf)
+{
+ int rc = 0;
+ char nlmsgbuf[256] = { 0, };
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ char rtattbuf[64];
+ struct rtattr *rta;
+ struct ifinfomsg i = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex
+ };
+ int recvbuflen;
+
+ *recvbuf = NULL;
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_GETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &i, sizeof(i)))
+ goto buffer_too_small;
+
+ if (ifindex < 0 && ifname != NULL) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
+ ifname, strlen(ifname) + 1);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (nlComm(nlm, recvbuf, &recvbuflen) < 0)
+ return -1;
+
+ if (recvbuflen < NLMSG_LENGTH(0) || *recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)*recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ switch (-err->error) {
+ case 0:
+ break;
+
+ default:
+ virReportSystemError(-err->error,
+ _("error dumping %d interface"),
+ ifindex);
+ rc = -1;
+ }
+ break;
+
+ case GENL_ID_CTRL:
+ case NLMSG_DONE:
+ if (nlmsg_parse(resp, sizeof(struct ifinfomsg),
+ tb, IFLA_MAX, ifla_policy)) {
+ goto malformed_resp;
+ }
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ if (rc != 0)
+ VIR_FREE(*recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(*recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+getPortProfileStatus(struct nlattr **tb, int32_t vf, uint16_t *status)
+{
+ int rc = 1;
+ const char *msg = NULL;
+ struct nlattr *tb2[IFLA_VF_PORT_MAX + 1],
+ *tb3[IFLA_PORT_MAX+1];
+
+ if (vf == PORT_SELF_VF) {
+ if (tb[IFLA_PORT_SELF]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb[IFLA_PORT_SELF],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ } else {
+ if (tb[IFLA_VF_PORTS]) {
+ if (nla_parse_nested(tb2, IFLA_VF_PORT_MAX, tb[IFLA_VF_PORTS],
+ ifla_vf_ports_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORTS part");
+ goto err_exit;
+ }
+ if (tb2[IFLA_VF_PORT]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb2[IFLA_VF_PORT],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ }
+ }
+
+ if (tb3[IFLA_PORT_RESPONSE]) {
+ *status = *(uint16_t *)RTA_DATA(tb3[IFLA_PORT_RESPONSE]);
+ rc = 0;
+ } else {
+ msg = _("no IFLA_PORT_RESPONSE found in netlink message");
+ goto err_exit;
+ }
+
+err_exit:
+ if (msg)
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
+
+ return rc;
+}
+
+
+static int
+doPortProfileOpSetLink(bool multicast,
+ const char *ifname, int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc = 0;
+ char nlmsgbuf[256];
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ char rtattbuf[64];
+ struct rtattr *rta, *vfports, *vfport;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex,
+ };
+ char *recvbuf = NULL;
+ int recvbuflen = 0;
+
+ memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+ goto buffer_too_small;
+
+ if (vf == PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_SELF, NULL, 0);
+ } else {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORTS, NULL, 0);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!(vfports = nlAppend(nlm, sizeof(nlmsgbuf),
+ rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ /* beging nesting vfports */
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORT, NULL, 0);
+ }
+
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!(vfport = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ if (profileId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_PROFILE,
+ profileId, strlen(profileId) + 1);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (portVsi) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VSI_TYPE,
+ portVsi, sizeof(*portVsi));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (instanceId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_INSTANCE_UUID,
+ instanceId, VIR_UUID_BUFLEN);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (hostUUID) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_HOST_UUID,
+ hostUUID, VIR_UUID_BUFLEN);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (vf != PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VF,
+ &vf, sizeof(vf));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_REQUEST,
+ &op, sizeof(op));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+
+ /* end nesting of vport */
+ vfport->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfport;
+
+ if (vf != PORT_SELF_VF) {
+ /* end nesting of vfports */
+ vfports->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfports;
+ }
+
+ if (!multicast) {
+ if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+ return -1;
+ } else {
+ if (nlCommWaitSuccess(nlm, RTMGRP_LINK, &recvbuf, &recvbuflen,
+ 5 * MICROSEC_PER_SEC) < 0)
+ return -1;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ switch (-err->error) {
+ case 0:
+ break;
+
+ default:
+ virReportSystemError(-err->error,
+ _("error during virtual port configuration of %s interface"), ifname);
+ rc = -1;
+ }
+ break;
+
+ case NLMSG_DONE:
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ VIR_FREE(recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+doPortProfileOpCommon(bool multicast,
+ const char *ifname, int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc;
+ char *recvbuf = NULL;
+ struct nlattr *tb[IFLA_MAX + 1];
+ int repeats = 16;
+ uint16_t status;
+
+ rc = doPortProfileOpSetLink(multicast,
+ ifname, ifindex,
+ profileId,
+ portVsi,
+ instanceId,
+ hostUUID,
+ vf,
+ op);
+
+ if (rc != 0) {
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("sending of PortProfileRequest failed.\n"));
+ return rc;
+ }
+
+ if (!multicast) {
+ while (--repeats) {
+ rc = link_dump(ifindex, ifname, tb, &recvbuf);
+ if (rc)
+ goto err_exit;
+ rc = getPortProfileStatus(tb, vf, &status);
+ if (rc == 0) {
+ if (status == PORT_PROFILE_RESPONSE_SUCCESS ||
+ status == PORT_VDP_RESPONSE_SUCCESS)
+ break;
+ if (status != PORT_PROFILE_RESPONSE_INPROGRESS)
+ rc = 1;
+ }
+ usleep(125000);
+
+ VIR_FREE(recvbuf);
+ }
+ }
+
+err_exit:
+ VIR_FREE(recvbuf);
+
+ return rc;
+}
+
+# endif /* IFLA_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbg(const char *ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ int32_t vf,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (void)virtPort;
+ (void)vf;
+ (void)virtPortOp;
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Kernel VF Port support was missing at compile time."));
+ rc = 1;
+# else /* IFLA_VF_PORT_MAX */
+
+ int op = PORT_REQUEST_ASSOCIATE;
+ struct ifla_port_vsi portVsi = {
+ .vsi_mgr_id = virtPort->u.virtPort8021Qbg.managerID,
+ .vsi_type_version = virtPort->u.virtPort8021Qbg.typeIDVersion,
+ };
+ bool multicast = true;
+ int ifindex;
+
+ if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ portVsi.vsi_type_id[2] = virtPort->u.virtPort8021Qbg.typeID >> 16;
+ portVsi.vsi_type_id[1] = virtPort->u.virtPort8021Qbg.typeID >> 8;
+ portVsi.vsi_type_id[0] = virtPort->u.virtPort8021Qbg.typeID;
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, NULL, ifindex,
+ NULL,
+ &portVsi,
+ virtPort->u.virtPort8021Qbg.instanceID,
+ NULL,
+ vf,
+ op);
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
+
+
+static int
+doPortProfileOp8021Qbh(const char *ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ int32_t vf,
+ const unsigned char *host_uuid,
+ const unsigned char *vm_uuid,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (void)virtPort;
+ (void)host_uuid;
+ (void)vm_uuid;
+ (void)virtPortOp;
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Kernel VF Port support was missing at compile time."));
+ rc = 1;
+
+# else /* IFLA_VF_PORT_MAX */
+
+ int op = PORT_REQUEST_ASSOCIATE;
+ bool multicast = false;
+ int ifindex;
+
+ if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, ifname, ifindex,
+ virtPort->u.virtPort8021Qbh.profileID,
+ NULL,
+ vm_uuid,
+ host_uuid,
+ vf,
+ op);
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ ifaceUp(ifname);
+ break;
+ case DISASSOCIATE:
+ ifaceDown(ifname);
+ break;
+ }
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
+
+static void
+getPhysfn(const char *linkdev,
+ int32_t *vf,
+ char **physfndev)
+{
+ bool virtfn = false;
+
+ if (virtfn) {
+
+ // XXX: if linkdev is SR-IOV VF, then set vf = VF index
+ // XXX: and set linkdev = PF device
+ // XXX: need to use get_physical_function_linux() or
+ // XXX: something like that to get PF
+ // XXX: device and figure out VF index
+
+ } else {
+
+ /* Not SR-IOV VF: physfndev is linkdev and VF index
+ * refers to linkdev self
+ */
+
+ *vf = PORT_SELF_VF;
+ *physfndev = (char *)linkdev;
+ }
+}
/**
* associatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
* @virtPort: pointer to the object holding port profile parameters
- * @vf: virtual function number, -1 if to be ignored
* @vmuuid : the UUID of the virtual machine
+ * @hostuuid : the UUID of the host machine
*
* Associate a port on a swtich with a profile. This function
* may notify a kernel driver or an external daemon to run
@@ -734,15 +1463,19 @@ delMacvtap(virDomainNetDefPtr net)
*/
static int
associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort,
- int vf,
- const unsigned char *vmuuid)
+ const unsigned char *vmuuid,
+ const unsigned char *hostuuid)
{
+ char *physfndev;
+ int32_t vf;
int rc = 0;
+
VIR_DEBUG("Associating port profile '%p' on link device '%s'",
virtPort, macvtap_ifname);
- (void)vf;
- (void)vmuuid;
+
+ getPhysfn(linkdev, &vf, &physfndev);
switch (virtPort->virtPortType) {
case VIR_VIRTUALPORT_NONE:
@@ -750,11 +1483,14 @@ associatePortProfileId(const char *macvtap_ifname,
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort, vf,
+ ASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(physfndev, virtPort, vf,
+ hostuuid, vmuuid,
+ ASSOCIATE);
break;
}
@@ -766,6 +1502,7 @@ associatePortProfileId(const char *macvtap_ifname,
* disassociatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
* @virtPort: point to object holding port profile parameters
*
* Returns 0 in case of success, != 0 otherwise with error
@@ -773,25 +1510,36 @@ associatePortProfileId(const char *macvtap_ifname,
*/
static int
disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort)
{
+ char *physfndev;
+ int32_t vf;
int rc = 0;
+
VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
virtPort, macvtap_ifname);
+ getPhysfn(linkdev, &vf, &physfndev);
+
switch (virtPort->virtPortType) {
case VIR_VIRTUALPORT_NONE:
case VIR_VIRTUALPORT_TYPE_LAST:
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort, vf,
+ DISASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(physfndev, virtPort, vf,
+ NULL, NULL,
+ DISASSOCIATE);
break;
}
return rc;
}
+
+#endif
14 years, 11 months