Devel
Threads by month
- ----- 2026 -----
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- 17 participants
- 40169 discussions
24 May '10
We were squashing error messages in a few cases. Recode to follow common
ret = -1 convention.
v2: Handle more error squashing issues further up in MakeNewVol and
CreateVols. Use ret = -1 convention in MakeVols.
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/storage/storage_backend_mpath.c | 40 ++++++++++------------------------
1 files changed, 12 insertions(+), 28 deletions(-)
diff --git a/src/storage/storage_backend_mpath.c b/src/storage/storage_backend_mpath.c
index 8318969..78d6b31 100644
--- a/src/storage/storage_backend_mpath.c
+++ b/src/storage/storage_backend_mpath.c
@@ -43,39 +43,26 @@ virStorageBackendMpathUpdateVolTargetInfo(virStorageVolTargetPtr target,
unsigned long long *allocation,
unsigned long long *capacity)
{
- int ret = 0;
+ int ret = -1;
int fd = -1;
if ((fd = open(target->path, O_RDONLY)) < 0) {
virReportSystemError(errno,
_("cannot open volume '%s'"),
target->path);
- ret = -1;
goto out;
}
if (virStorageBackendUpdateVolTargetInfoFD(target,
fd,
allocation,
- capacity) < 0) {
-
- virStorageReportError(VIR_ERR_INTERNAL_ERROR,
- _("Failed to update volume target info for '%s'"),
- target->path);
-
- ret = -1;
+ capacity) < 0)
goto out;
- }
- if (virStorageBackendUpdateVolTargetFormatFD(target, fd) < 0) {
- virStorageReportError(VIR_ERR_INTERNAL_ERROR,
- _("Failed to update volume target format for '%s'"),
- target->path);
-
- ret = -1;
+ if (virStorageBackendUpdateVolTargetFormatFD(target, fd) < 0)
goto out;
- }
+ ret = 0;
out:
if (fd != -1) {
close(fd);
@@ -112,10 +99,6 @@ virStorageBackendMpathNewVol(virStoragePoolObjPtr pool,
if (virStorageBackendMpathUpdateVolTargetInfo(&vol->target,
&vol->allocation,
&vol->capacity) < 0) {
-
- virStorageReportError(VIR_ERR_INTERNAL_ERROR,
- _("Failed to update volume for '%s'"),
- vol->target.path);
goto cleanup;
}
@@ -230,7 +213,7 @@ static int
virStorageBackendCreateVols(virStoragePoolObjPtr pool,
struct dm_names *names)
{
- int retval = 0, is_mpath = 0;
+ int retval = -1, is_mpath = 0;
char *map_device = NULL;
uint32_t minor = -1;
@@ -238,7 +221,6 @@ virStorageBackendCreateVols(virStoragePoolObjPtr pool,
is_mpath = virStorageBackendIsMultipath(names->name);
if (is_mpath < 0) {
- retval = -1;
goto out;
}
@@ -246,18 +228,19 @@ virStorageBackendCreateVols(virStoragePoolObjPtr pool,
if (virAsprintf(&map_device, "mapper/%s", names->name) < 0) {
virReportOOMError();
- retval = -1;
goto out;
}
if (virStorageBackendGetMinorNumber(names->name, &minor) < 0) {
- retval = -1;
+ virStorageReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed to get %s minor number"),
+ names->name);
goto out;
}
- virStorageBackendMpathNewVol(pool,
- minor,
- map_device);
+ if (virStorageBackendMpathNewVol(pool, minor, map_device) < 0) {
+ goto out;
+ }
VIR_FREE(map_device);
}
@@ -268,6 +251,7 @@ virStorageBackendCreateVols(virStoragePoolObjPtr pool,
} while (names->next);
+ retval = 0;
out:
return retval;
}
--
1.6.6.1
2
2
This patch adds documentation of the nwfilter subsystem of libvirt to
the existing (web) docs.
Previous post with attached PDF of the docs is probably stuck in the mail filter of this list.
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>
1
0
This patch adds the host UUID (to the capabilities of libvirt). The user
may provide it in libvirtd.conf overriding whatever dmidecode may
return. If none or no valid UUID is provided in libvirtd.conf, dmidecode
is being used. If that function doesn't provide a valid (not all digits
may be equal), generate a temporary one.
virSetHostUUIDStr() should be called first with the UUID read from
libvirtd.conf, but may only be called once. Subsequently the functions
virGetHostUUID[Str]() can be called to get the UUID of the host.
Besides that, this patch
- adds a dependency on the dmidecode package
- adds uuid to the capabilties XML schema
- displays the UUID in in 'virsh capabilities'
- adds 3 public functions to uuid.h for setting and getting the UUID of
the host
This patch contains recycled code from Scott Feldman
(getDMISystemUUID()).
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
---
daemon/libvirtd.c | 5 +
src/conf/capabilities.c | 4 +
src/util/uuid.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++
src/util/uuid.h | 4 +
4 files changed, 139 insertions(+)
Index: libvirt-acl/daemon/libvirtd.c
===================================================================
--- libvirt-acl.orig/daemon/libvirtd.c
+++ libvirt-acl/daemon/libvirtd.c
@@ -2718,6 +2718,7 @@ remoteReadConfigFile (struct qemud_serve
char *unix_sock_rw_perms = NULL;
char *unix_sock_group = NULL;
char *buf = NULL;
+ char *host_uuid = NULL;
#if HAVE_POLKIT
/* Change the default back to no auth for non-root */
@@ -2840,6 +2841,10 @@ remoteReadConfigFile (struct qemud_serve
GET_CONF_INT (conf, filename, max_requests);
GET_CONF_INT (conf, filename, max_client_requests);
+ GET_CONF_STR (conf, filename, host_uuid);
+ virSetHostUUIDStr(host_uuid);
+ VIR_FREE(host_uuid);
+
virConfFree (conf);
return 0;
Index: libvirt-acl/src/util/uuid.h
===================================================================
--- libvirt-acl.orig/src/util/uuid.h
+++ libvirt-acl/src/util/uuid.h
@@ -22,6 +22,10 @@
#ifndef __VIR_UUID_H__
# define __VIR_UUID_H__
+int virSetHostUUIDStr(const char *host_uuid);
+int virGetHostUUID(unsigned char *host_uuid);
+int virGetHostUUIDStr(char *host_uuid);
+
int virUUIDGenerate(unsigned char *uuid);
int virUUIDParse(const char *uuidstr,
Index: libvirt-acl/src/util/uuid.c
===================================================================
--- libvirt-acl.orig/src/util/uuid.c
+++ libvirt-acl/src/util/uuid.c
@@ -38,11 +38,14 @@
#include "util.h"
#include "virterror_internal.h"
#include "logging.h"
+#include "memory.h"
#ifndef ENODATA
# define ENODATA EIO
#endif
+static unsigned char host_uuid[VIR_UUID_BUFLEN];
+
static int
virUUIDGenerateRandomBytes(unsigned char *buf,
int buflen)
@@ -208,3 +211,154 @@ void virUUIDFormat(const unsigned char *
uuid[12], uuid[13], uuid[14], uuid[15]);
uuidstr[VIR_UUID_STRING_BUFLEN-1] = '\0';
}
+
+
+
+/**
+ * isValidHostUUID
+ *
+ * @uuid: The UUID to test
+ *
+ * Do some basic tests to check whether the given UUID is
+ * valid as a host UUID.
+ * Basic tests:
+ * - Not all of the digits may be equal
+ */
+static int
+isValidHostUUID(unsigned char *uuid)
+{
+ unsigned int i, ctr = 1;
+ unsigned char c;
+
+ if (!uuid)
+ return 0;
+
+ c = uuid[0];
+
+ for (i = 1; i < VIR_UUID_BUFLEN; i++)
+ if (uuid[i] == c)
+ ctr++;
+
+ return (ctr != VIR_UUID_BUFLEN);
+}
+
+static int
+getDMISystemUUID(char *uuid, int len)
+{
+ const char *dmidecodearg[] = { "dmidecode", "-s", "system-uuid", NULL };
+ const char *const dmidecodeenv[] = { "LC_ALL=C", NULL };
+ char *binary, *newline;
+ int dmidecodestdout = -1;
+ int ret = -1;
+ pid_t child;
+
+ binary = virFindFileInPath(dmidecodearg[0]);
+ if (binary == NULL || access(binary, X_OK) != 0) {
+ VIR_FREE(binary);
+ return -1;
+ }
+ dmidecodearg[0] = binary;
+
+ if (virExec(dmidecodearg, dmidecodeenv, NULL,
+ &child, -1, &dmidecodestdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) {
+ ret = -1;
+ goto cleanup;
+ }
+
+ if((ret = saferead(dmidecodestdout, uuid, len)) <= 0) {
+ ret = -1;
+ goto cleanup;
+ }
+ uuid[ret-1] = '\0';
+
+ /* strip newline */
+ newline = strrchr(uuid, '\n');
+ if (newline)
+ *newline = '\0';
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(binary);
+
+ if (close(dmidecodestdout) < 0)
+ ret = -1;
+
+ return ret;
+}
+
+
+/**
+ * setHostUUID
+ *
+ * @host_uuid: UUID that the host is supposed to have
+ *
+ * Set the UUID of the host if it hasn't been set, yet
+ * Returns 0 in case of success, an error code in case of error.
+ */
+int
+virSetHostUUIDStr(const char *uuid)
+{
+ char dmiuuid[VIR_UUID_STRING_BUFLEN];
+
+ if (isValidHostUUID(host_uuid))
+ return EEXIST;
+
+ if (!uuid) {
+ if (!getDMISystemUUID(dmiuuid, sizeof(dmiuuid))) {
+ if (!virUUIDParse(dmiuuid, host_uuid)) {
+ return 0;
+ }
+ }
+ } else {
+ if (!virUUIDParse(uuid, host_uuid))
+ return 0;
+ }
+
+ if (!isValidHostUUID(host_uuid))
+ return virUUIDGenerate(host_uuid);
+ return 0;
+}
+
+/**
+ * getHostUUID:
+ *
+ * @host_uuid: memory to store the host_uuid into
+ *
+ * Get the UUID of the host. Returns 0 in case of success,
+ * an error code otherwise.
+ * Returns 0 in case of success, an error code in case of error.
+ */
+int virGetHostUUID(unsigned char *uuid)
+{
+ int ret = 0;
+
+ if (!isValidHostUUID(host_uuid))
+ ret = virSetHostUUIDStr(NULL);
+
+ memcpy(uuid, host_uuid, sizeof(host_uuid));
+
+ return ret;
+}
+
+
+/**
+ * getHostUUID:
+ *
+ * @host_uuid: memory to store the host_uuid into
+ *
+ * Get the UUID of the host. Returns 0 in case of success,
+ * an error code otherwise.
+ * Returns 0 in case of success, an error code in case of error.
+ */
+int virGetHostUUIDStr(char *uuid)
+{
+ int ret = 0;
+
+ if (!isValidHostUUID(host_uuid))
+ ret = virSetHostUUIDStr(NULL);
+
+ virUUIDFormat(host_uuid, uuid);
+
+ return ret;
+}
Index: libvirt-acl/src/conf/capabilities.c
===================================================================
--- libvirt-acl.orig/src/conf/capabilities.c
+++ libvirt-acl/src/conf/capabilities.c
@@ -27,6 +27,7 @@
#include "buf.h"
#include "memory.h"
#include "util.h"
+#include "uuid.h"
#include "cpu_conf.h"
/**
@@ -662,9 +663,12 @@ virCapabilitiesFormatXML(virCapsPtr caps
{
virBuffer xml = VIR_BUFFER_INITIALIZER;
int i, j, k;
+ char host_uuid[VIR_UUID_STRING_BUFLEN];
+ virGetHostUUIDStr(host_uuid);
virBufferAddLit(&xml, "<capabilities>\n\n");
virBufferAddLit(&xml, " <host>\n");
+ virBufferVSprintf(&xml," <uuid>%s</uuid>\n", host_uuid);
virBufferAddLit(&xml, " <cpu>\n");
virBufferVSprintf(&xml, " <arch>%s</arch>\n",
caps->host.arch);
Index: libvirt-acl/docs/schemas/capability.rng
===================================================================
--- libvirt-acl.orig/docs/schemas/capability.rng
+++ libvirt-acl/docs/schemas/capability.rng
@@ -18,6 +18,9 @@
<define name='hostcaps'>
<element name='host'>
+ <element name='uuid'>
+ <ref name='UUID'/>
+ </element>
<element name='cpu'>
<element name='arch'>
<ref name='archnames'/>
@@ -349,4 +352,15 @@
<param name='pattern'>[a-zA-Z0-9\-_]+</param>
</data>
</define>
+
+ <define name="UUID">
+ <choice>
+ <data type="string">
+ <param name="pattern">[a-fA-F0-9]{32}</param>
+ </data>
+ <data type="string">
+ <param name="pattern">[a-fA-F0-9]{8}\-([a-fA-F0-9]{4}\-){3}[a-fA-F0-9]{12}</param>
+ </data>
+ </choice>
+ </define>
</grammar>
Index: libvirt-acl/src/libvirt_private.syms
===================================================================
--- libvirt-acl.orig/src/libvirt_private.syms
+++ libvirt-acl/src/libvirt_private.syms
@@ -708,6 +708,9 @@ usbDeviceFileIterate;
virUUIDFormat;
virUUIDGenerate;
virUUIDParse;
+virSetHostUUIDStr;
+virGetHostUUID;
+virGetHostUUIDStr;
# virterror_internal.h
Index: libvirt-acl/daemon/libvirtd.conf
===================================================================
--- libvirt-acl.orig/daemon/libvirtd.conf
+++ libvirt-acl/daemon/libvirtd.conf
@@ -312,3 +312,12 @@
# e.g.:
# log_outputs="3:syslog:libvirtd"
# to log all warnings and errors to syslog under the libvirtd ident
+
+# UUID of the host:
+# Provide the UUID of the host here in case the command
+# 'dmidecode -s system-uuid' does not provide a valid uuid. In case
+# 'dmidecode' does not provide a valid UUID and none is provided here, a
+# temporary UUID will be generated.
+# Keep the format of the example UUID below.
+
+#host_uuid = '8510b1a1-1afa-4da6-8111-785fae202c1d'
Index: libvirt-acl/libvirt.spec.in
===================================================================
--- libvirt-acl.orig/libvirt.spec.in
+++ libvirt-acl/libvirt.spec.in
@@ -192,6 +192,7 @@ Requires: %{name}-client = %{version}-%{
# daemon is present
%if %{with_libvirtd}
Requires: bridge-utils
+Requires: dmidecode
%endif
%if %{with_network}
Requires: dnsmasq
2
1
Re: [libvirt] [PATCH] network: bridge: Don't start network if it collides with host routing
by Neil Wilson 24 May '10
by Neil Wilson 24 May '10
24 May '10
You need to be very careful not to hit a valid use case here.
RFC3069 provides for a mechanism whereby a supernet/subnet system is
setup to allow better use of available IPv4 address space. This is
becoming of increasing concern as IPv4 space exhausts.
To implement this you turn on Proxy ARP on a host and inject /32
routes advertised from other hosts into the routing table. The network
on the current host has a wide subnet mask (for example 10.0.0.0/8),
so that the host responds to ARPs for VM guests that are actually on a
completely different host.
So you could have the situation where you have routes picked up via a
dynamic routing protocol (say OSPF) on a host that are inside the
range for the defined network in libvirt. Â Failing to start the
network in that case would be incorrect.
--
Neil Wilson
--
Neil Wilson
2
3
Dear all:
I encounter a problem when I running the libvirt java bindings test program. I download the libvirt-java-0.4.3.tar.gz. Then compiled successful, but the test program can't running.
# ant build
Buildfile: build.xml
init:
[copy] Copying 1 file to /home/dustin/libvirt-java-0.4.3
build:
BUILD SUCCESSFUL
Total time: 0 seconds
# javac -classpath /home/dustin/libvirt-java-0.4.3/target/libvirt-0.4.3.jar test.java
Note: test.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
(I think this will not affect the use of libvirt library. It‘s just a warning.
javac -classpath /home/dustin/libvirt-java-0.4.3/target/libvirt-0.4.3.jar test.java -Xlint:deprecation
test.java:142: warning: [deprecation] networkLookupByUUID(int[]) in org.libvirt.Connect has been deprecated
testNetwork = conn.networkLookupByUUID(UUIDArray);
^
1 warning
Maybe I am wrong?)
# java -classpath .:/usr/share/java/jna.jar:/home/dustin/libvirt-java-0.4.3/target/libvirt-0.4.3.jar test
Exception in thread "main" java.lang.UnsatisfiedLinkError: jnidispatch (/com/sun/jna/linux-i386/libjnidispatch.so) not found in resource path
at com.sun.jna.Native.loadNativeLibraryFromJar(Native.java:592)
at com.sun.jna.Native.loadNativeLibrary(Native.java:574)
at com.sun.jna.Native.<clinit>(Native.java:104)
at org.libvirt.jna.Libvirt.<clinit>(Unknown Source)
at org.libvirt.Connect.<clinit>(Unknown Source)
at test.main(test.java:43)
I test other test.java, the results are the same.
I don't know the libjnidispatch.so can't be find is caused by the libvirt-java or JNA library.
How can I resolve the problem?
My java environment as below:
libjna-java-3.1.0-1
sun-java6-jdk-6-15-1
junit-3.8.2-3
Thank you very much.
-Dustin
2010-5-24
_________________________________________________________________
Hotmail: Trusted email with Microsoft’s powerful SPAM protection.
https://signup.live.com/signup.aspx?id=60969
2
1
24 May '10
Gerd today posted patches to upstream QEMU which support SPICE graphics.
They have not been accepted / merged yet, but I'm optimistic that the
command line syntax will not change significantly.
http://lists.gnu.org/archive/html/qemu-devel/2010-04/msg00967.html
This patch series adds corresponding support in libvirt's QEMU driver.
Although this patch series adds support in the XML RNG for a password
expiry time, this functionality isn't wired up in this patch series
since I need more time to write fallback code for cases where QEMU
doesn't do expiry itself.
Daniel
3
14
[libvirt] [RFC] [PATCH 3/3 v2] vepa+vsi: Some experimental code for 802.1Qbh
by Stefan Berger 24 May '10
by Stefan Berger 24 May '10
24 May '10
This patch may get 802.1Qbh devices working. I am adding some code to
poll for the status of an 802.1Qbh device and loop for a while until the
status indicates success. This part for sure needs more work and
testing...
I am recycling link_dump from a previous patch.
Changes from V1 to V2:
- Following tree
Signed-off-by: Stefan Berger <stefanb(a)linux.vnet.ibm.com>
---
src/util/macvtap.c | 116
+++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
Index: libvirt-acl/src/util/macvtap.c
===================================================================
--- libvirt-acl.orig/src/util/macvtap.c
+++ libvirt-acl/src/util/macvtap.c
@@ -960,6 +960,95 @@ getPortProfileStatus(struct nlmsghdr *nl
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
doPortProfileOpSetLink(bool multicast,
const char *ifname, int ifindex,
const char *profileId,
@@ -1151,6 +1240,10 @@ doPortProfileOpCommon(bool multicast,
uint8_t op)
{
int rc;
+ char *recvbuf = NULL;
+ struct nlattr *tb[IFLA_MAX + 1];
+ int repeats = 5;
+ uint16_t status;
rc = doPortProfileOpSetLink(multicast,
ifname, ifindex,
@@ -1167,6 +1260,30 @@ doPortProfileOpCommon(bool multicast,
return rc;
}
+ if (!multicast) {
+ /* 802.1Qbh -- query for status */
+ while (--repeats) {
+ rc = link_dump(ifindex, ifname, tb, &recvbuf);
+ if (rc)
+ goto err_exit;
+ rc = getPortProfileStatus((struct nlmsghdr *)recvbuf,
&status);
+ if (rc == 0) {
+ if (status == 0)
+ break;
+ if (status != 0) {
+ fprintf(stderr,"Current status: %d\n", status);
+ rc = 1;
+ }
+ }
+ usleep(10000);
+
+ VIR_FREE(recvbuf);
+ }
+ }
+
+err_exit:
+ VIR_FREE(recvbuf);
+
return rc;
}
4
12
24 May '10
We were squashing error messages in a few cases. Recode to follow common
ret = -1 convention.
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/storage/storage_backend_mpath.c | 20 ++++----------------
1 files changed, 4 insertions(+), 16 deletions(-)
diff --git a/src/storage/storage_backend_mpath.c b/src/storage/storage_backend_mpath.c
index 8318969..6351fe1 100644
--- a/src/storage/storage_backend_mpath.c
+++ b/src/storage/storage_backend_mpath.c
@@ -43,39 +43,27 @@ virStorageBackendMpathUpdateVolTargetInfo(virStorageVolTargetPtr target,
unsigned long long *allocation,
unsigned long long *capacity)
{
- int ret = 0;
+ int ret = -1;
int fd = -1;
if ((fd = open(target->path, O_RDONLY)) < 0) {
virReportSystemError(errno,
_("cannot open volume '%s'"),
target->path);
- ret = -1;
goto out;
}
if (virStorageBackendUpdateVolTargetInfoFD(target,
fd,
allocation,
- capacity) < 0) {
+ capacity) < 0)
- virStorageReportError(VIR_ERR_INTERNAL_ERROR,
- _("Failed to update volume target info for '%s'"),
- target->path);
-
- ret = -1;
goto out;
- }
-
- if (virStorageBackendUpdateVolTargetFormatFD(target, fd) < 0) {
- virStorageReportError(VIR_ERR_INTERNAL_ERROR,
- _("Failed to update volume target format for '%s'"),
- target->path);
- ret = -1;
+ if (virStorageBackendUpdateVolTargetFormatFD(target, fd) < 0)
goto out;
- }
+ ret = 0;
out:
if (fd != -1) {
close(fd);
--
1.6.6.1
3
8
[libvirt] [V2 PATCH] V2 port-profile ID support using new IFLA_VF_PORTS netlink msg
by Scott Feldman 24 May '10
by Scott Feldman 24 May '10
24 May '10
From: Scott Feldman <scofeldm(a)cisco.com>
Use the new IFLA_VF_PORTS netlink msg to associate/disassociate port-profiles on a virtual port backing the VM device. The new netlink msg type has been accepted by netdev kernel maintainer.
Tested with Cisco's 10G Ethernet NIC using example XML:
<interface type='direct'>
<mac address='52:54:00:07:70:cf'/>
<source dev='eth2' mode='private' profileid='test-network'/>
<target dev='macvtap0'/>
<model type='virtio'/>
</interface>
Changes since V1:
- port to new netlink msg types
Next steps for V3, etc:
1) merge with Stefan's latest patch, as much as possible
2) assign VM device mac addr to eth and set macvtap mac addr to random
3) figure out where host_uuid lives
Signed-off-by: Scott Feldman <scofeldm(a)cisco.com>
Signed-off-by: Roopa Prabhu<roprabhu(a)cisco.com>
---
src/conf/domain_conf.c | 13 ++
src/conf/domain_conf.h | 1
src/libvirt_macvtap.syms | 2
src/qemu/qemu_conf.c | 7 +
src/qemu/qemu_driver.c | 10 +-
src/util/macvtap.c | 260 +++++++++++++++++++++++++++++++++++++++++++++-
src/util/macvtap.h | 7 +
7 files changed, 294 insertions(+), 6 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 20c9c51..577c2ea 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -484,6 +484,7 @@ void virDomainNetDefFree(virDomainNetDefPtr def)
case VIR_DOMAIN_NET_TYPE_DIRECT:
VIR_FREE(def->data.direct.linkdev);
+ VIR_FREE(def->data.direct.profileid);
break;
case VIR_DOMAIN_NET_TYPE_USER:
@@ -1831,6 +1832,7 @@ virDomainNetDefParseXML(virCapsPtr caps,
char *internal = NULL;
char *devaddr = NULL;
char *mode = NULL;
+ char *profileid = NULL;
virNWFilterHashTablePtr filterparams = NULL;
if (VIR_ALLOC(def) < 0) {
@@ -1873,6 +1875,7 @@ virDomainNetDefParseXML(virCapsPtr caps,
xmlStrEqual(cur->name, BAD_CAST "source")) {
dev = virXMLPropString(cur, "dev");
mode = virXMLPropString(cur, "mode");
+ profileid = virXMLPropString(cur, "profileid");
} else if ((network == NULL) &&
((def->type == VIR_DOMAIN_NET_TYPE_SERVER) ||
(def->type == VIR_DOMAIN_NET_TYPE_CLIENT) ||
@@ -2050,6 +2053,11 @@ virDomainNetDefParseXML(virCapsPtr caps,
} else
def->data.direct.mode = VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA;
+ if (profileid != NULL) {
+ def->data.direct.profileid = profileid;
+ profileid = NULL;
+ }
+
def->data.direct.linkdev = dev;
dev = NULL;
@@ -2115,6 +2123,7 @@ cleanup:
VIR_FREE(internal);
VIR_FREE(devaddr);
VIR_FREE(mode);
+ VIR_FREE(profileid);
virNWFilterHashTableFree(filterparams);
return def;
@@ -5141,6 +5150,10 @@ virDomainNetDefFormat(virBufferPtr buf,
def->data.direct.linkdev);
virBufferVSprintf(buf, " mode='%s'",
virDomainNetdevMacvtapTypeToString(def->data.direct.mode));
+ if (def->data.direct.profileid) {
+ virBufferEscapeString(buf, " profileid='%s'",
+ def->data.direct.profileid);
+ }
virBufferAddLit(buf, "/>\n");
break;
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index fadc8bd..30ebf07 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -290,6 +290,7 @@ struct _virDomainNetDef {
struct {
char *linkdev;
int mode;
+ char *profileid;
} direct;
} data;
char *ifname;
diff --git a/src/libvirt_macvtap.syms b/src/libvirt_macvtap.syms
index ae229a0..9d4652e 100644
--- a/src/libvirt_macvtap.syms
+++ b/src/libvirt_macvtap.syms
@@ -3,3 +3,5 @@
# macvtap.h
openMacvtapTap;
delMacvtap;
+setPortProfileId;
+unsetPortProfileId;
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 3e334dc..ea74f1c 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -1479,6 +1479,11 @@ qemudPhysIfaceConnect(virConnectPtr conn,
net->model && STREQ(net->model, "virtio"))
vnet_hdr = 1;
+ if (net->data.direct.profileid)
+ setPortProfileId(net->data.direct.linkdev,
+ net->data.direct.profileid,
+ NULL, NULL);
+
rc = openMacvtapTap(net->ifname, net->mac, linkdev, brmode,
&res_ifname, vnet_hdr);
if (rc >= 0) {
@@ -1501,6 +1506,8 @@ qemudPhysIfaceConnect(virConnectPtr conn,
close(rc);
rc = -1;
delMacvtap(net->ifname);
+ if (net->data.direct.profileid)
+ unsetPortProfileId(net->data.direct.linkdev);
}
}
}
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 65ca117..c5f2f78 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -3695,8 +3695,11 @@ static void qemudShutdownVMDaemon(struct qemud_driver *driver,
for (i = 0; i < def->nnets; i++) {
virDomainNetDefPtr net = def->nets[i];
if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
- if (net->ifname)
+ if (net->ifname) {
delMacvtap(net->ifname);
+ if (net->data.direct.profileid)
+ unsetPortProfileId(net->data.direct.linkdev);
+ }
}
}
#endif
@@ -8387,8 +8390,11 @@ qemudDomainDetachNetDevice(struct qemud_driver *driver,
#if WITH_MACVTAP
if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
- if (detach->ifname)
+ if (detach->ifname) {
delMacvtap(detach->ifname);
+ if (detach->data.direct.profileid)
+ unsetPortProfileId(detach->data.direct.linkdev);
+ }
}
#endif
diff --git a/src/util/macvtap.c b/src/util/macvtap.c
index 1f8dd29..7803bc8 100644
--- a/src/util/macvtap.c
+++ b/src/util/macvtap.c
@@ -85,14 +85,14 @@ static void nlClose(int fd)
* buffer will be returned.
*/
static
-int nlComm(struct nlmsghdr *nlmsg,
+int nlComm(struct nlmsghdr *nlmsg, int nlgroups,
char **respbuf, int *respbuflen)
{
int rc = 0;
struct sockaddr_nl nladdr = {
.nl_family = AF_NETLINK,
.nl_pid = 0,
- .nl_groups = 0,
+ .nl_groups = nlgroups,
};
int rcvChunkSize = 1024; // expecting less than that
int rcvoffset = 0;
@@ -192,6 +192,27 @@ nlAppend(struct nlmsghdr *nlm, int totlen, const void *data, int datalen)
return pos;
}
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+static struct rtattr *nlNest(struct nlmsghdr *nlm, int totlen, int type)
+{
+ struct rtattr *nest = NLMSG_TAIL(nlm);
+
+ if (nlm->nlmsg_len + NLMSG_ALIGN(sizeof(*nest)) > totlen)
+ return NULL;
+ nest->rta_type = type;
+ nest->rta_len = RTA_LENGTH(0);
+ nlm->nlmsg_len += sizeof(*nest);
+ nlAlign(nlm);
+ return nest;
+}
+
+static int nlNestEnd(struct nlmsghdr *nlm, struct rtattr *nest)
+{
+ nest->rta_len = (char *)NLMSG_TAIL(nlm) - (char *)nest;
+ return nlm->nlmsg_len;
+}
static int
link_add(const char *type,
@@ -287,7 +308,7 @@ link_add(const char *type,
li->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)li;
- if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+ if (nlComm(nlm, 0, &recvbuf, &recvbuflen) < 0)
return -1;
if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
@@ -371,7 +392,7 @@ link_del(const char *name)
if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
goto buffer_too_small;
- if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+ if (nlComm(nlm, 0, &recvbuf, &recvbuflen) < 0)
return -1;
if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
@@ -568,6 +589,237 @@ configMacvtapTap(int tapfd, int vnet_hdr)
return 0;
}
+#if 0
+static int
+get_host_uuid(char *host_uuid, int len)
+{
+ const char *dmidecodearg[] = { "dmidecode", "-s", "system-uuid", NULL };
+ const char *const dmidecodeenv[] = { "LC_ALL=C", NULL };
+ char *binary, *newline;
+ int dmidecodestdout = -1;
+ int ret = -1;
+ pid_t child;
+
+ binary = virFindFileInPath(dmidecodearg[0]);
+ if (binary == NULL || access(binary, X_OK) != 0) {
+ VIR_FREE(binary);
+ return -1;
+ }
+ dmidecodearg[0] = binary;
+
+ if (virExec(dmidecodearg, dmidecodeenv, NULL,
+ &child, -1, &dmidecodestdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) {
+ ret = -1;
+ goto cleanup;
+ }
+
+ if((ret = saferead(dmidecodestdout, host_uuid, len)) <= 0) {
+ ret = -1;
+ goto cleanup;
+ }
+ host_uuid[ret-1] = '\0';
+
+ /* strip newline */
+ newline = strrchr(host_uuid, '\n');
+ if (newline)
+ *newline = '\0';
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(binary);
+
+ if (close(dmidecodestdout) < 0)
+ ret = -1;
+
+ return ret;
+}
+#endif
+
+static int
+portProfileIdMcast(const char *linkdev,
+ const char request,
+ const char *profileid,
+ const unsigned short *instance_uuid,
+ const unsigned short *host_uuid)
+{
+ struct rtattr *port_self;
+ char nlmsgbuf[512];
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct ifinfomsg infomsg = { .ifi_family = AF_UNSPEC };
+ char rtattbuf[256];
+ struct rtattr *rta;
+ char *recvbuf = NULL;
+ int recvbuflen;
+ struct nlmsgerr *err;
+ int rc = 0;
+
+ memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
+ nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &infomsg, sizeof(infomsg)))
+ goto buffer_too_small;
+
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
+ linkdev, strlen(linkdev) + 1);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+
+ port_self = nlNest(nlm, sizeof(nlmsgbuf), IFLA_PORT_SELF);
+ if (!port_self)
+ return -1;
+
+ switch (request) {
+ case PORT_REQUEST_ASSOCIATE:
+ if (profileid && strlen(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 (instance_uuid) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf),
+ IFLA_PORT_INSTANCE_UUID,
+ instance_uuid, PORT_UUID_MAX);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+ if (host_uuid) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf),
+ IFLA_PORT_HOST_UUID,
+ host_uuid, PORT_UUID_MAX);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+ break;
+ case PORT_REQUEST_DISASSOCIATE:
+ break;
+ }
+
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_REQUEST,
+ &request, 1);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+
+ nlNestEnd(nlm, port_self);
+
+ if (nlComm(nlm, RTNLGRP_LINK, &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 setting port profile on %s"),
+ linkdev);
+ 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;
+}
+
+int
+setPortProfileId(const char *linkdev,
+ char *profileid,
+ unsigned short *instance_uuid,
+ unsigned short *host_uuid)
+{
+ int rc;
+
+ rc = ifaceDown(linkdev);
+ if (rc != 0) {
+ virReportSystemError(errno,
+ _("cannot 'down' interface %s"),
+ linkdev);
+ return -1;
+ }
+
+ rc = portProfileIdMcast(linkdev, PORT_REQUEST_ASSOCIATE,
+ profileid, instance_uuid, host_uuid);
+ if (rc)
+ return rc;
+
+ rc = ifaceUp(linkdev);
+ if (rc != 0) {
+ virReportSystemError(errno,
+ _("cannot 'up' interface %s"),
+ linkdev);
+ return -1;
+ }
+
+ return rc;
+}
+
+int
+unsetPortProfileId(const char *linkdev)
+{
+ int rc;
+
+ rc = portProfileIdMcast(linkdev, PORT_REQUEST_DISASSOCIATE,
+ NULL, NULL, NULL);
+ if (rc)
+ return rc;
+
+ rc = ifaceDown(linkdev);
+ if (rc != 0) {
+ virReportSystemError(errno,
+ _("cannot 'down' interface %s"),
+ linkdev);
+ return -1;
+ }
+
+ return rc;
+}
/**
* openMacvtapTap:
diff --git a/src/util/macvtap.h b/src/util/macvtap.h
index 5d4ea5e..136779e 100644
--- a/src/util/macvtap.h
+++ b/src/util/macvtap.h
@@ -37,6 +37,13 @@ int openMacvtapTap(const char *ifname,
void delMacvtap(const char *ifname);
+int setPortProfileId(const char *linkdev,
+ char *profile_id,
+ unsigned short *instance_uuid,
+ unsigned short *host_uuid);
+
+int unsetPortProfileId(const char *linkdev);
+
# endif /* WITH_MACVTAP */
# define MACVTAP_MODE_PRIVATE_STR "private"
3
2
24 May '10
Note: The configfd option for qemu is still pending upstream, so
this patch will depend on that being accepted. This patch also
depends on the the s/tapfd/vmfd/ rename patch posted to the list.
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>
---
src/qemu/qemu_conf.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++-
src/qemu/qemu_conf.h | 8 ++++-
src/qemu/qemu_driver.c | 2 +
3 files changed, 91 insertions(+), 4 deletions(-)
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index d7bc798..a41012c 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -1345,6 +1345,48 @@ fail:
return -1;
}
+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) {
@@ -1378,6 +1420,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)
@@ -2887,8 +2932,29 @@ error:
}
+int
+qemudOpenPCIConfig(virDomainHostdevDefPtr dev)
+{
+ char *path = NULL;
+ int configfd = -1;
+
+ /* XXX don't hard code segment */
+ if (virAsprintf(&path, "/sys/bus/pci/devices/0000:%02x:%02x.%01x/config",
+ 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);
+
+ VIR_FREE(path);
+
+ return configfd;
+}
+
char *
-qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev)
+qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, int configfd)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
@@ -2898,6 +2964,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 >= 0)
+ virBufferVSprintf(&buf, ",configfd=%d", configfd);
if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0)
goto error;
@@ -4600,8 +4668,21 @@ 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) {
+ int configfd = -1;
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) {
+ configfd = qemudOpenPCIConfig(hostdev);
+
+ if (configfd >= 0) {
+ if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) {
+ close(configfd);
+ goto no_memory;
+ }
+
+ (*vmfds)[(*nvmfds)++] = configfd;
+ }
+ }
ADD_ARG_LIT("-device");
- if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev)))
+ if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd)))
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 a101e47..64fab60 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -88,6 +88,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 */
@@ -190,6 +191,9 @@ int qemudParseHelpStr (const char *qemu,
unsigned int *is_kvm,
unsigned int *kvm_version);
+void qemudParsePCIDeviceStrs (const char *qemu,
+ unsigned long long *qemuCmdFlags);
+
int qemudBuildCommandLine (virConnectPtr conn,
struct qemud_driver *driver,
virDomainDefPtr def,
@@ -242,7 +246,9 @@ char * qemuBuildSoundDevStr(virDomainSoundDefPtr sound);
/* Legacy, pre device support */
char * qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev);
/* Current, best practice */
-char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev);
+char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, int 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 dd5bd24..219a973 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -7567,7 +7567,7 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) < 0)
goto error;
- if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev)))
+ if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, -1)))
goto error;
}
3
5
24 May '10
The event is already generated and sent by qemudDomainObjStart, no need
to do anything about here.
---
src/qemu/qemu_driver.c | 3 ---
1 files changed, 0 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 2f644c9..5c8ab38 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -6630,7 +6630,6 @@ static int qemudDomainStart(virDomainPtr dom) {
struct qemud_driver *driver = dom->conn->privateData;
virDomainObjPtr vm;
int ret = -1;
- virDomainEventPtr event = NULL;
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -6661,8 +6660,6 @@ endjob:
cleanup:
if (vm)
virDomainObjUnlock(vm);
- if (event)
- qemuDomainEventQueue(driver, event);
qemuDriverUnlock(driver);
return ret;
}
--
1.7.1
2
2
[libvirt] [PATCH] add 802.1Qbh handling for port-profiles based on Stefan's previous patches
by Scott Feldman 23 May '10
by Scott Feldman 23 May '10
23 May '10
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 v7] 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.
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.
I removed the RTM_GETLINK which was checking for status after the RTM_SETLINK
call because there is no practical upper bound on when the port-profile
association may complete. I didn't want to block in libvirt polling for
status. For troubleshooting issues with port-profile association, we'll
need to use an async method to get status. In a sense, (atleast for enic's
case) successful port-profile association is indicated by link UP on the
linkdev. I/O will not move until link is UP on the linkdev. To get more
detailed status of the port-profile association for troubleshooting, we can
rely on ip cmd line tool to get status. This is analogous to checking for
link status on a real physical link using the ip cmd.
Signed-off-by: Scott Feldman <scofeldm(a)cisco.com>
Signed-off-by: Roopa Prabhu<roprabhu(a)cisco.com>
---
configure.ac | 16 +
src/util/macvtap.c | 598 +++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 598 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..50f1b89 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,392 @@ 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 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;
+
+ 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;
+ }
+
+ 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 +1283,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 +1303,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 +1322,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 +1330,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
1
0
[libvirt] [PATCH] libvirtd: mark strings for translation, including --help output
by Jim Meyering 22 May '10
by Jim Meyering 22 May '10
22 May '10
Saw these while fixing the previous bug:
>From ab3d0ba4f5c004a45da5b5a240a0b5caef21df99 Mon Sep 17 00:00:00 2001
From: Jim Meyering <meyering(a)redhat.com>
Date: Thu, 20 May 2010 10:01:32 +0200
Subject: [PATCH] libvirtd: mark strings for translation, including --help output
* daemon/libvirtd.c (daemonForkIntoBackground, main): Mark strings
for translation.
(usage): Rework --help so that it is translatable, replacing
each embedded, configuration-dependent, macro with an `%s'.
libvirtd: don't ignore virInitialize failure
* daemon/libvirtd.c (main): Diagnose virInitialize failure
and exit nonzero.
---
daemon/libvirtd.c | 36 +++++++++++++++++++++---------------
1 files changed, 21 insertions(+), 15 deletions(-)
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
index aac2d08..d52b6eb 100644
--- a/daemon/libvirtd.c
+++ b/daemon/libvirtd.c
@@ -484,8 +484,8 @@ static int daemonForkIntoBackground(void) {
if (ret == 1 && status != 0) {
fprintf(stderr,
- "error: %s. Check /var/log/messages or run without "
- "--daemon for more info.\n",
+ _("error: %s. Check /var/log/messages or run without "
+ "--daemon for more info.\n"),
virDaemonErrTypeToString(status));
}
_exit(ret == 1 && status == 0 ? 0 : 1);
@@ -2963,7 +2963,7 @@ static void
usage (const char *argv0)
{
fprintf (stderr,
- "\n\
+ _("\n\
Usage:\n\
%s [options]\n\
\n\
@@ -2981,27 +2981,33 @@ libvirt management daemon:\n\
Default paths:\n\
\n\
Configuration file (unless overridden by -f):\n\
- " SYSCONF_DIR "/libvirt/libvirtd.conf\n\
+ %s/libvirt/libvirtd.conf\n\
\n\
Sockets (as root):\n\
- " LOCAL_STATE_DIR "/run/libvirt/libvirt-sock\n\
- " LOCAL_STATE_DIR "/run/libvirt/libvirt-sock-ro\n\
+ %s/run/libvirt/libvirt-sock\n\
+ %s/run/libvirt/libvirt-sock-ro\n\
\n\
Sockets (as non-root):\n\
$HOME/.libvirt/libvirt-sock (in UNIX abstract namespace)\n\
\n\
TLS:\n\
- CA certificate: " LIBVIRT_CACERT "\n\
- Server certificate: " LIBVIRT_SERVERCERT "\n\
- Server private key: " LIBVIRT_SERVERKEY "\n\
+ CA certificate: %s\n\
+ Server certificate: %s\n\
+ Server private key: %s\n\
\n\
PID file (unless overridden by --pid-file):\n\
%s\n\
-\n",
- argv0,
- REMOTE_PID_FILE[0] != '\0'
- ? REMOTE_PID_FILE
- : "(disabled in ./configure)");
+\n"),
+ argv0,
+ SYSCONF_DIR,
+ LOCAL_STATE_DIR,
+ LOCAL_STATE_DIR,
+ LIBVIRT_CACERT,
+ LIBVIRT_SERVERCERT,
+ LIBVIRT_SERVERKEY,
+ (REMOTE_PID_FILE[0] != '\0'
+ ? REMOTE_PID_FILE
+ : "(disabled in ./configure)"));
}
enum {
@@ -3083,7 +3089,7 @@ int main(int argc, char **argv) {
return 2;
default:
- fprintf (stderr, "libvirtd: internal error: unknown flag: %c\n",
+ fprintf (stderr, _("libvirtd: internal error: unknown flag: %c\n"),
c);
exit (EXIT_FAILURE);
}
--
1.7.1.259.g3aef8
2
6
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.
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 | 155 +++++++++++++++++++++++++++++++++
src/conf/domain_conf.h | 35 +++++++
src/qemu/qemu_conf.c | 18 +--
src/qemu/qemu_conf.h | 5 -
src/qemu/qemu_driver.c | 17 +--
src/util/macvtap.c | 151 +++++++++++++++++++++++++++-----
src/util/macvtap.h | 10 +-
tests/domainschemadata/portprofile.xml | 36 +++++++
9 files changed, 446 insertions(+), 50 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,145 @@ cleanup:
}
+static void
+virVirtualPortProfileDefParseXML(xmlNodePtr node,
+ virVirtualPortProfileDefPtr virtPort)
+{
+ char *virtPortType;
+ char *virtPortManagerID = NULL;
+ char *virtPortTypeID = NULL;
+ char *virtPortTypeIDVersion = NULL;
+ char *virtPortInstanceID = NULL;
+ char *virtPortProfileID = NULL;
+ xmlNodePtr cur = node->children;
+
+ virtPortType = virXMLPropString(node, "type");
+ if (!virtPortType)
+ return;
+
+ 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, 10, &val) &&
+ virStrToLong_ui(virtPortManagerID, NULL, 16, &val) ) ||
+ val > 0xff)
+ break;
+
+ virtPort->u.virtPort8021Qbg.managerID = (uint8_t)val;
+
+ if ((virStrToLong_ui(virtPortTypeID, NULL, 10, &val) &&
+ virStrToLong_ui(virtPortTypeID, NULL, 16, &val) ) ||
+ val > 0xffffff)
+ break;
+
+ virtPort->u.virtPort8021Qbg.typeID = (uint32_t)val;
+
+ if ((virStrToLong_ui(virtPortTypeIDVersion, NULL, 10, &val) &&
+ virStrToLong_ui(virtPortTypeIDVersion, NULL, 16, &val) ) ||
+ val > 0xff)
+ break;
+
+ virtPort->u.virtPort8021Qbg.typeIDVersion = (uint8_t)val;
+
+ if (virtPortInstanceID != NULL) {
+ if (virUUIDParse(virtPortInstanceID, virtPort->u.virtPort8021Qbg.instanceID))
+ break;
+ } else {
+ if (virUUIDGenerate(virtPort->u.virtPort8021Qbg.instanceID))
+ break;
+ }
+
+ virtPort->virtPortType = VIR_VIRTUALPORT_8021QBG;
+ }
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+ if (virtPortProfileID != NULL) {
+ if (virStrcpyStatic(virtPort->u.virtPort8021Qbh.profileID,
+ virtPortProfileID) != NULL)
+ virtPort->virtPortType = VIR_VIRTUALPORT_8021QBH;
+ }
+ break;
+
+
+ default:
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ break;
+ }
+
+ VIR_FREE(virtPortManagerID);
+ VIR_FREE(virtPortTypeID);
+ VIR_FREE(virtPortTypeIDVersion);
+ VIR_FREE(virtPortInstanceID);
+ VIR_FREE(virtPortProfileID);
+ VIR_FREE(virtPortType);
+}
+
+
+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 +1976,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 +2019,11 @@ 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")) {
+ virVirtualPortProfileDefParseXML(cur, &virtPort);
+ virtPortParsed = true;
} else if ((network == NULL) &&
((def->type == VIR_DOMAIN_NET_TYPE_SERVER) ||
(def->type == VIR_DOMAIN_NET_TYPE_CLIENT) ||
@@ -2048,6 +2199,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
} else
def->data.direct.mode = VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA;
+ def->data.direct.virtPortProfile = virtPort;
+
def->data.direct.linkdev = dev;
dev = NULL;
@@ -5140,6 +5293,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
@@ -259,6 +259,39 @@ enum virDomainNetdevMacvtapType {
};
+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;
+};
+
+
/* Stores the virtual network interface configuration */
typedef struct _virDomainNetDef virDomainNetDef;
typedef virDomainNetDef *virDomainNetDefPtr;
@@ -290,6 +323,7 @@ struct _virDomainNetDef {
struct {
char *linkdev;
int mode;
+ virVirtualPortProfileDef virtPortProfile;
} direct;
} data;
char *ifname;
@@ -1089,6 +1123,7 @@ VIR_ENUM_DECL(virDomainSeclabel)
VIR_ENUM_DECL(virDomainClockOffset)
VIR_ENUM_DECL(virDomainNetdevMacvtap)
+VIR_ENUM_DECL(virVirtualPort)
VIR_ENUM_DECL(virDomainTimerName)
VIR_ENUM_DECL(virDomainTimerTrack)
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);
@@ -567,39 +578,38 @@ configMacvtapTap(int tapfd, int vnet_hdr
return 0;
}
-
-
/**
* openMacvtapTap:
* Create an instance of a macvtap device and open its tap character
* device.
* @tgifname: Interface name that the macvtap is supposed to have. May
* 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'.
+ * @net: pointer to the virDomainNetDef object describing the direct
+ * type if an interface
* @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.
+ * @vnet_hdr: Whether to enable IFF_VNET_HDR on the interface
+ * @vmuuid: The (raw) UUID of the VM
*
* Returns file descriptor of the tap device in case of success,
* negative value otherwise with error reported.
*
+ * Open a macvtap device and trigger the switch setup protocol
+ * if valid port profile parameters were provided.
*/
int
openMacvtapTap(const char *tgifname,
- const unsigned char *macaddress,
- const char *linkdev,
- int mode,
+ virDomainNetDefPtr net,
char **res_ifname,
- int vnet_hdr)
+ int vnet_hdr,
+ const unsigned char *vmuuid)
{
const char *type = "macvtap";
int c, rc;
char ifname[IFNAMSIZ];
int retries, do_retry = 0;
- uint32_t macvtapMode = macvtapModeFromInt(mode);
+ uint32_t macvtapMode = macvtapModeFromInt(net->data.direct.mode);
const char *cr_ifname;
int ifindex;
@@ -616,7 +626,7 @@ openMacvtapTap(const char *tgifname,
return -1;
}
cr_ifname = tgifname;
- rc = link_add(type, macaddress, 6, tgifname, linkdev,
+ rc = link_add(type, net->mac, 6, tgifname, net->data.direct.linkdev,
macvtapMode, &do_retry);
if (rc)
return -1;
@@ -626,7 +636,8 @@ create_name:
for (c = 0; c < 8192; c++) {
snprintf(ifname, sizeof(ifname), MACVTAP_NAME_PATTERN, c);
if (ifaceGetIndex(false, ifname, &ifindex) == ENODEV) {
- rc = link_add(type, macaddress, 6, ifname, linkdev,
+ rc = link_add(type, net->mac, 6, ifname,
+ net->data.direct.linkdev,
macvtapMode, &do_retry);
if (rc == 0)
break;
@@ -639,6 +650,14 @@ create_name:
cr_ifname = ifname;
}
+ if (associatePortProfileId(cr_ifname,
+ &net->data.direct.virtPortProfile,
+ -1,
+ vmuuid) != 0) {
+ rc = -1;
+ goto link_del_exit;
+ }
+
rc = ifaceUp(cr_ifname);
if (rc != 0) {
virReportSystemError(errno,
@@ -647,7 +666,7 @@ create_name:
"MAC address"),
cr_ifname);
rc = -1;
- goto link_del_exit;
+ goto disassociate_exit;
}
rc = openTap(cr_ifname, 10);
@@ -656,14 +675,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,
+ &net->data.direct.virtPortProfile);
+
link_del_exit:
link_del(cr_ifname);
@@ -673,14 +696,102 @@ link_del_exit:
/**
* delMacvtap:
- * @ifname : The name of the macvtap interface
+ * @net: pointer to virDomainNetDef object
*
- * 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(virDomainNetDefPtr net)
{
- link_del(ifname);
+ if (net->ifname) {
+ disassociatePortProfileId(net->ifname,
+ &net->data.direct.virtPortProfile);
+ link_del(net->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
@@ -27,15 +27,15 @@
# if defined(WITH_MACVTAP)
# include "internal.h"
+# include "conf/domain_conf.h"
int openMacvtapTap(const char *ifname,
- const unsigned char *macaddress,
- const char *linkdev,
- int mode,
+ virDomainNetDefPtr net,
char **res_ifname,
- int vnet_hdr);
+ int vnet_hdr,
+ const unsigned char *vmuuid);
-void delMacvtap(const char *ifname);
+void delMacvtap(virDomainNetDefPtr net);
# endif /* WITH_MACVTAP */
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
@@ -3702,10 +3702,8 @@ 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);
}
#endif
@@ -7464,9 +7462,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;
}
@@ -8509,10 +8506,8 @@ 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);
#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,7 @@ 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, &res_ifname, vnet_hdr, vmuuid);
if (rc >= 0) {
VIR_FREE(net->ifname);
net->ifname = res_ifname;
@@ -1505,17 +1503,16 @@ qemudPhysIfaceConnect(virConnectPtr conn
if (err) {
close(rc);
rc = -1;
- delMacvtap(net->ifname);
+ delMacvtap(net);
}
}
}
#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 +4132,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;
2
2
[libvirt] [RFC] [PATCH 2/3] vepa+vsi: add handling of 802.1Qbg using netlink messages
by Stefan Berger 22 May '10
by Stefan Berger 22 May '10
22 May '10
This is so far a more or less experimental patch that adds code for
communicating with the external lldpad daemon using the recently added
netlink messages. It's communicating using multicast netlink messages in
case of 802.1Qbg and unicast using 802.1Qbh. In the multicast case I am
currently expecting an immediate response back from the (dummy) server
to indicate the status of the port profile setup (IFLA_PORT_RESPONSE). A
real server should probably then mimic the kernel driver and wait for a
RTM_GETLINK to return the response...
After adding netlink code for 802.1Qbg it wasn't difficult to add code
for 802.1Qbh anymore, so some code for 802.1Qbh is already here.
I am also adding detection code to configure.ac on whether the recently
added netlink messages and the defines are available and set the
WITH_VSI_VEPA #define if it is.
Lots of debugging output is still in this patch.
Changes from V1 to V2:
- replaced all occurrences of 'vsi'
- following tree
Signed-off-by: Stefan Berger <stefanb(a)linux.vnet.ibm.com>
---
configure.ac | 16 +
src/util/macvtap.c | 597
++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 605 insertions(+), 8 deletions(-)
Index: libvirt-acl/src/util/macvtap.c
===================================================================
--- libvirt-acl.orig/src/util/macvtap.c
+++ libvirt-acl/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,6 +60,10 @@
# define MACVTAP_NAME_PREFIX "macvtap"
# define MACVTAP_NAME_PATTERN "macvtap%d"
+# define MICROSEC_PER_SEC (1000 * 1000)
+
+
+# define USE_IFLA_PORT_SELF
static int associatePortProfileId(const char *macvtap_ifname,
const virVirtualPortProfileDefPtr
virtPort,
@@ -67,6 +73,11 @@ static int associatePortProfileId(const
static int disassociatePortProfileId(const char *macvtap_ifname,
const virVirtualPortProfileDefPtr
virtPort);
+enum virVirtualPortOp {
+ ASSOCIATE = 0x1,
+ DISASSOCIATE = 0x2,
+};
+
static int nlOpen(void)
{
@@ -159,6 +170,156 @@ err_exit:
}
+/**
+ * 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 = 0,
+ .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;
+
+ switch (resp->nlmsg_type) {
+ default:
+ 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;
+ }
+ }
+ }
+
+err_exit:
+ if (rc == -1) {
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ }
+
+ nlClose(fd);
+ return rc;
+}
+
+
static struct rtattr *
rtattrCreate(char *buffer, int bufsize, int type,
const void *data, int datalen)
@@ -204,6 +365,8 @@ nlAppend(struct nlmsghdr *nlm, int totle
}
+# if WITH_MACVTAP
+
static int
link_add(const char *type,
const unsigned char *macaddress, int macaddrsize,
@@ -652,7 +815,7 @@ create_name:
rc = associatePortProfileId(cr_ifname,
&net->data.direct.virtPortProfile,
- -1,
+ PORT_SELF_VF,
vmuuid);
if (rc != 0)
goto link_del_exit;
@@ -711,8 +874,422 @@ delMacvtap(virDomainNetDefPtr net)
}
}
-#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
+getPortProfileStatus(struct nlmsghdr *nlm, uint16_t *status)
+{
+ int rc = 1;
+ const char *msg = NULL;
+ struct nlattr *tb[IFLA_MAX+1],
+ *tb2[IFLA_VF_PORT_MAX + 1],
+ *tb3[IFLA_PORT_MAX+1];
+
+ if (nlmsg_parse(nlm, sizeof(struct ifinfomsg),
+ (struct nlattr **)&tb, IFLA_MAX, ifla_policy)) {
+ msg = _("error parsing netlink response.\n");
+ goto err_exit;
+ }
+
+# ifndef USE_IFLA_PORT_SELF
+ 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;
+ }
+# else
+ (void)tb2;
+ (void)ifla_vf_ports_policy;
+ if (tb[IFLA_PORT_SELF]) {
+ if (1) {
+ 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;
+ }
+# endif
+
+ 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;
+ }
+ } else {
+ msg = _("no IFLA_VF_PORT found in netlink message");
+ goto err_exit;
+ }
+ } else {
+ msg = _("no IFLA_VF_PORTS 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;
+ uint16_t status;
+
+ memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+ goto buffer_too_small;
+
+# ifndef USE_IFLA_PORT_SELF
+
+ 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);
+# else
+ (void)vfports;
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_SELF,
NULL, 0);
+# endif
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!(vfport = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf,
rta->rta_len)))
+ goto buffer_too_small;
+
+ /* begin nesting of vfport */
+ 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 >= 0) {
+ 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;
+
+# ifndef USE_IFLA_PORT_SELF
+ /* end nesting of vfports */
+ vfports->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfports;
+# endif
+
+ 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:
+ rc = getPortProfileStatus(resp, &status);
+ if (rc == 0 && status != PORT_VDP_RESPONSE_SUCCESS) {
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("Response code from port profile setup: %
d"),
+ status);
+ rc = -1;
+ }
+ 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;
+
+ 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;
+ }
+
+ 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 virVirtualPortProfileDefPtr virtPort,
+ const unsigned char *host_uuid,
+ const unsigned char *vm_uuid,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (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;
+
+ 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, -1,
+ virtPort->u.virtPort8021Qbh.profileID,
+ NULL,
+ host_uuid,
+ vm_uuid,
+ -1,
+ op);
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
/**
* associatePortProfile
@@ -749,11 +1326,13 @@ associatePortProfileId(const char *macvt
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort, vf,
+ ASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(virtPort, vmuuid /* host_uuid */,
vmuuid,
+ ASSOCIATE);
break;
}
@@ -784,13 +1363,17 @@ disassociatePortProfileId(const char *ma
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort, -1,
+ DISASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(virtPort, NULL, NULL,
+ DISASSOCIATE);
break;
}
return rc;
}
+
+#endif
Index: libvirt-acl/configure.ac
===================================================================
--- libvirt-acl.orig/configure.ac
+++ libvirt-acl/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([])
2
1
* src/Makefile.am (EXTRA_DIST): Add THREADS.txt.
---
I noticed this while working on back-porting a patch to RHEL-6 - the
qemu tree in my git repository had more files than were in the
unpacked 0.8.1 tarball.
src/Makefile.am | 3 ++-
1 file changed, 2 insertions(+), 1 deletions(-)
diff --git a/src/Makefile.am b/src/Makefile.am
index 889de8e..4c9fdf8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -588,7 +588,8 @@ augeas_DATA += qemu/libvirtd_qemu.aug
augeastest_DATA += qemu/test_libvirtd_qemu.aug
endif
-EXTRA_DIST += qemu/qemu.conf qemu/libvirtd_qemu.aug qemu/test_libvirtd_qemu.aug
+EXTRA_DIST += qemu/qemu.conf qemu/libvirtd_qemu.aug \
+ qemu/test_libvirtd_qemu.aug qemu/THREADS.txt
if WITH_LXC
--
1.7.0.1
3
6
21 May '10
Sorry for the delay ... V2 changes noted in the patches.
The qemu driver contains a subtle race in the logic to find next
available vnc port. Currently it iterates through all available ports
and returns the first for which bind(2) succeeds. However it is possible
that a previously issued port has not yet been bound by qemu, resulting
in the same port used for a subsequent domain.
The issue was briefly discussed on IRC and the consensus there was to
track port allocation with a bitmap. The first patch adds some simple
bitmap operations to utils. Second patch defines VNC_PORT_{MIN,MAX} and
uses them in place of explicit values. Third patch makes use of the
bitmap impl to track vnc port allocations.
The race was consistently encountered in HA environments [1]. This patch
series has been successfully tested in such environment.
[1] http://www.linux-ha.org/wiki/VirtualDomain_%28resource_agent%29#VNC_port_au…
Jim Fehlig (3):
Add simple bitmap operations to utils
Add defines for QEMU_VNC_PORT_{MIN,MAX} and use them
Fix race in finding available vnc port
cfg.mk | 1 +
src/Makefile.am | 1 +
src/libvirt_private.syms | 8 +++
src/qemu/qemu_conf.h | 3 +
src/qemu/qemu_driver.c | 39 +++++++++++-
src/util/bitmap.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++
src/util/bitmap.h | 60 ++++++++++++++++++
7 files changed, 264 insertions(+), 3 deletions(-)
create mode 100644 src/util/bitmap.c
create mode 100644 src/util/bitmap.h
2
12
21 May '10
/etc/sysconfig/libvirtd has a few environment variables for configuring
libvirt SDL audio. The libvirtd process doesn't see these, however, because
they are never exported. Let's export the variables after sourcing the
sysconfig script.
There is another problem here that the commented out values in the
sysconfig script are not neccessarily the actual defaults, we are qemus
mercy here. Not sure how to solve that.
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
daemon/libvirtd.init.in | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/daemon/libvirtd.init.in b/daemon/libvirtd.init.in
index 809433e..aa7870c 100644
--- a/daemon/libvirtd.init.in
+++ b/daemon/libvirtd.init.in
@@ -45,6 +45,9 @@ KRB5_KTNAME=/etc/libvirt/krb5.tab
test -f @sysconfdir@/sysconfig/libvirtd && . @sysconfdir@/sysconfig/libvirtd
+export QEMU_AUDIO_DRV
+export SDL_AUDIODRIVER
+
LIBVIRTD_CONFIG_ARGS=
if [ -n "$LIBVIRTD_CONFIG" ]
then
--
1.6.6.1
2
2
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.
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 | 35 +++++++++++++++++++++++++++++++++++
src/util/util.h | 2 ++
5 files changed, 52 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 bdeab0f..8e1555c 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -666,6 +666,7 @@ virFileReadLimFD;
virFilePid;
virFileReadPid;
virFileLinkPointsTo;
+virFileSanitizePath;
virParseNumber;
virParseVersionString;
virPipeReadUntilEOF;
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index 2c69ba9..1a10221 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 e937d39..8f86ed6 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -1921,6 +1921,41 @@ 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;
+ }
+
+ /* Sanitize path in place */
+ while (*cur != '\0') {
+ if (*cur == '/') {
+ /* Skip all extra / */
+ while (*++cur == '/')
+ continue;
+
+ /* Don't add a trailing / */
+ if (*cur == '\0')
+ break;
+
+ cleanpath[idx++] = '/';
+ }
+
+ cleanpath[idx++] = *cur++;
+ }
+ 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..abc2688 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -118,6 +118,8 @@ char *virFindFileInPath(const char *file);
int virFileExists(const char *path);
+char *virFileSanitizePath(const char *path);
+
enum {
VIR_FILE_OP_NONE = 0,
VIR_FILE_OP_AS_UID = (1 << 0),
--
1.6.6.1
3
6
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.
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 | 155 +++++++++++++++++++++++++++++++++
src/conf/domain_conf.h | 35 +++++++
src/qemu/qemu_conf.c | 18 +--
src/qemu/qemu_conf.h | 5 -
src/qemu/qemu_driver.c | 17 +--
src/util/macvtap.c | 150 +++++++++++++++++++++++++++----
src/util/macvtap.h | 10 +-
tests/domainschemadata/portprofile.xml | 36 +++++++
9 files changed, 445 insertions(+), 50 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,145 @@ cleanup:
}
+static void
+virVirtualPortProfileDefParseXML(xmlNodePtr node,
+ virVirtualPortProfileDefPtr virtPort)
+{
+ char *virtPortType;
+ char *virtPortManagerID = NULL;
+ char *virtPortTypeID = NULL;
+ char *virtPortTypeIDVersion = NULL;
+ char *virtPortInstanceID = NULL;
+ char *virtPortProfileID = NULL;
+ xmlNodePtr cur = node->children;
+
+ virtPortType = virXMLPropString(node, "type");
+ if (!virtPortType)
+ return;
+
+ 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, 10, &val) &&
+ virStrToLong_ui(virtPortManagerID, NULL, 16, &val) ) ||
+ val > 0xff)
+ break;
+
+ virtPort->u.virtPort8021Qbg.managerID = (uint8_t)val;
+
+ if ((virStrToLong_ui(virtPortTypeID, NULL, 10, &val) &&
+ virStrToLong_ui(virtPortTypeID, NULL, 16, &val) ) ||
+ val > 0xffffff)
+ break;
+
+ virtPort->u.virtPort8021Qbg.typeID = (uint32_t)val;
+
+ if ((virStrToLong_ui(virtPortTypeIDVersion, NULL, 10, &val) &&
+ virStrToLong_ui(virtPortTypeIDVersion, NULL, 16, &val) ) ||
+ val > 0xff)
+ break;
+
+ virtPort->u.virtPort8021Qbg.typeIDVersion = (uint8_t)val;
+
+ if (virtPortInstanceID != NULL) {
+ if (virUUIDParse(virtPortInstanceID, virtPort->u.virtPort8021Qbg.instanceID))
+ break;
+ } else {
+ if (virUUIDGenerate(virtPort->u.virtPort8021Qbg.instanceID))
+ break;
+ }
+
+ virtPort->virtPortType = VIR_VIRTUALPORT_8021QBG;
+ }
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+ if (virtPortProfileID != NULL) {
+ if (virStrcpyStatic(virtPort->u.virtPort8021Qbh.profileID,
+ virtPortProfileID) != NULL)
+ virtPort->virtPortType = VIR_VIRTUALPORT_8021QBH;
+ }
+ break;
+
+
+ default:
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ break;
+ }
+
+ VIR_FREE(virtPortManagerID);
+ VIR_FREE(virtPortTypeID);
+ VIR_FREE(virtPortTypeIDVersion);
+ VIR_FREE(virtPortInstanceID);
+ VIR_FREE(virtPortProfileID);
+ VIR_FREE(virtPortType);
+}
+
+
+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 +1976,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 +2019,11 @@ 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")) {
+ virVirtualPortProfileDefParseXML(cur, &virtPort);
+ virtPortParsed = true;
} else if ((network == NULL) &&
((def->type == VIR_DOMAIN_NET_TYPE_SERVER) ||
(def->type == VIR_DOMAIN_NET_TYPE_CLIENT) ||
@@ -2048,6 +2199,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
} else
def->data.direct.mode = VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA;
+ def->data.direct.virtPortProfile = virtPort;
+
def->data.direct.linkdev = dev;
dev = NULL;
@@ -5140,6 +5293,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
@@ -259,6 +259,39 @@ enum virDomainNetdevMacvtapType {
};
+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;
+};
+
+
/* Stores the virtual network interface configuration */
typedef struct _virDomainNetDef virDomainNetDef;
typedef virDomainNetDef *virDomainNetDefPtr;
@@ -290,6 +323,7 @@ struct _virDomainNetDef {
struct {
char *linkdev;
int mode;
+ virVirtualPortProfileDef virtPortProfile;
} direct;
} data;
char *ifname;
@@ -1089,6 +1123,7 @@ VIR_ENUM_DECL(virDomainSeclabel)
VIR_ENUM_DECL(virDomainClockOffset)
VIR_ENUM_DECL(virDomainNetdevMacvtap)
+VIR_ENUM_DECL(virVirtualPort)
VIR_ENUM_DECL(virDomainTimerName)
VIR_ENUM_DECL(virDomainTimerTrack)
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);
@@ -567,39 +578,38 @@ configMacvtapTap(int tapfd, int vnet_hdr
return 0;
}
-
-
/**
* openMacvtapTap:
* Create an instance of a macvtap device and open its tap character
* device.
* @tgifname: Interface name that the macvtap is supposed to have. May
* 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'.
+ * @net: pointer to the virDomainNetDef object describing the direct
+ * type if an interface
* @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.
+ * @vnet_hdr: Whether to enable IFF_VNET_HDR on the interface
+ * @vmuuid: The (raw) UUID of the VM
*
* Returns file descriptor of the tap device in case of success,
* negative value otherwise with error reported.
*
+ * Open a macvtap device and trigger the switch setup protocol
+ * if valid port profile parameters were provided.
*/
int
openMacvtapTap(const char *tgifname,
- const unsigned char *macaddress,
- const char *linkdev,
- int mode,
+ virDomainNetDefPtr net,
char **res_ifname,
- int vnet_hdr)
+ int vnet_hdr,
+ const unsigned char *vmuuid)
{
const char *type = "macvtap";
int c, rc;
char ifname[IFNAMSIZ];
int retries, do_retry = 0;
- uint32_t macvtapMode = macvtapModeFromInt(mode);
+ uint32_t macvtapMode = macvtapModeFromInt(net->data.direct.mode);
const char *cr_ifname;
int ifindex;
@@ -616,7 +626,7 @@ openMacvtapTap(const char *tgifname,
return -1;
}
cr_ifname = tgifname;
- rc = link_add(type, macaddress, 6, tgifname, linkdev,
+ rc = link_add(type, net->mac, 6, tgifname, net->data.direct.linkdev,
macvtapMode, &do_retry);
if (rc)
return -1;
@@ -626,7 +636,8 @@ create_name:
for (c = 0; c < 8192; c++) {
snprintf(ifname, sizeof(ifname), MACVTAP_NAME_PATTERN, c);
if (ifaceGetIndex(false, ifname, &ifindex) == ENODEV) {
- rc = link_add(type, macaddress, 6, ifname, linkdev,
+ rc = link_add(type, net->mac, 6, ifname,
+ net->data.direct.linkdev,
macvtapMode, &do_retry);
if (rc == 0)
break;
@@ -639,6 +650,13 @@ create_name:
cr_ifname = ifname;
}
+ rc = associatePortProfileId(cr_ifname,
+ &net->data.direct.virtPortProfile,
+ -1,
+ vmuuid);
+ if (rc != 0)
+ goto link_del_exit;
+
rc = ifaceUp(cr_ifname);
if (rc != 0) {
virReportSystemError(errno,
@@ -647,7 +665,7 @@ create_name:
"MAC address"),
cr_ifname);
rc = -1;
- goto link_del_exit;
+ goto disassociate_exit;
}
rc = openTap(cr_ifname, 10);
@@ -656,14 +674,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,
+ &net->data.direct.virtPortProfile);
+
link_del_exit:
link_del(cr_ifname);
@@ -673,14 +695,102 @@ link_del_exit:
/**
* delMacvtap:
- * @ifname : The name of the macvtap interface
+ * @net: pointer to virDomainNetDef object
*
- * 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(virDomainNetDefPtr net)
{
- link_del(ifname);
+ if (net->ifname) {
+ disassociatePortProfileId(net->ifname,
+ &net->data.direct.virtPortProfile);
+ link_del(net->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
@@ -27,15 +27,15 @@
# if defined(WITH_MACVTAP)
# include "internal.h"
+# include "conf/domain_conf.h"
int openMacvtapTap(const char *ifname,
- const unsigned char *macaddress,
- const char *linkdev,
- int mode,
+ virDomainNetDefPtr net,
char **res_ifname,
- int vnet_hdr);
+ int vnet_hdr,
+ const unsigned char *vmuuid);
-void delMacvtap(const char *ifname);
+void delMacvtap(virDomainNetDefPtr net);
# endif /* WITH_MACVTAP */
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
@@ -271,9 +271,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
@@ -3678,10 +3678,8 @@ 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);
}
#endif
@@ -7425,9 +7423,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;
}
@@ -8470,10 +8467,8 @@ 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);
#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,7 @@ 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, &res_ifname, vnet_hdr, vmuuid);
if (rc >= 0) {
VIR_FREE(net->ifname);
net->ifname = res_ifname;
@@ -1505,17 +1503,16 @@ qemudPhysIfaceConnect(virConnectPtr conn
if (err) {
close(rc);
rc = -1;
- delMacvtap(net->ifname);
+ delMacvtap(net);
}
}
}
#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 +4132,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;
1
0
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='static' mode='vepa'/>
<model type='virtio'/>
<vsi type='802.1Qbg'>
<parameters managerid='12' typeid='0x123456' typeidversion='1'
instanceid='fa9b7fff-b0a0-4893-8e0e-beef4ff18f8f'/>
</vsi>
<filterref filter='clean-traffic'/>
</interface>
<interface type='direct'>
<source dev='static' mode='vepa'/>
<model type='virtio'/>
<vsi type='802.1Qbh'>
<parameters profileid='my_profile'/>
</vsi>
</interface>
I'd suggest to use this patch as a base for triggering the setup
protocol with the 802.1Qb{g|h} switch.
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
>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 | 155 +++++++++++++++++++++++++++++++
src/conf/domain_conf.h | 35 +++++++
src/qemu/qemu_conf.c | 18 +--
src/qemu/qemu_conf.h | 5 -
src/qemu/qemu_driver.c | 17 +--
src/util/macvtap.c | 165 +++++++++++++++++++++++++++++----
src/util/macvtap.h | 10 +-
tests/domainschemadata/portprofile.xml | 36 +++++++
9 files changed, 460 insertions(+), 50 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="vsiProfile"/>
+ </optional>
<ref name="interface-options"/>
</interleave>
</group>
@@ -902,6 +905,45 @@
</optional>
</interleave>
</define>
+ <define name="vsiProfile">
+ <choice>
+ <group>
+ <element name="vsi">
+ <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="vsi">
+ <attribute name="type">
+ <value>802.1Qbh</value>
+ </attribute>
+ <element name="parameters">
+ <attribute name="profileid">
+ <ref name="vsiProfileID"/>
+ </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="vsiProfileID">
+ <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(virVSI, VIR_VSI_TYPE_LAST,
+ "none",
+ "802.1Qbg",
+ "802.1Qbh")
+
VIR_ENUM_IMPL(virDomainClockOffset, VIR_DOMAIN_CLOCK_OFFSET_LAST,
"utc",
"localtime",
@@ -1807,6 +1812,145 @@ isValidIfname(const char *ifname) {
}
+static void
+virVSIProfileDefParseXML(xmlNodePtr node,
+ virVSIProfileDefPtr vsi)
+{
+ char *vsiType;
+ char *vsiManagerID = NULL;
+ char *vsiTypeID = NULL;
+ char *vsiTypeIDVersion = NULL;
+ char *vsiInstanceID = NULL;
+ char *vsiProfileID = NULL;
+ xmlNodePtr cur = node->children;
+
+ vsiType = virXMLPropString(node, "type");
+ if (!vsiType)
+ return;
+
+ while (cur != NULL) {
+ if (xmlStrEqual(cur->name, BAD_CAST "parameters")) {
+
+ vsiManagerID = virXMLPropString(cur, "managerid");
+ vsiTypeID = virXMLPropString(cur, "typeid");
+ vsiTypeIDVersion = virXMLPropString(cur, "typeidversion");
+ vsiInstanceID = virXMLPropString(cur, "instanceid");
+ vsiProfileID = virXMLPropString(cur, "profileid");
+
+ break;
+ }
+
+ cur = cur->next;
+ }
+
+ vsi->vsiType = VIR_VSI_NONE;
+
+ switch (virVSITypeFromString(vsiType)) {
+
+ case VIR_VSI_8021QBG:
+ if (vsiManagerID != NULL && vsiTypeID != NULL &&
+ vsiTypeIDVersion != NULL) {
+ unsigned int val;
+
+ if ((virStrToLong_ui(vsiManagerID, NULL, 10, &val) &&
+ virStrToLong_ui(vsiManagerID, NULL, 16, &val) ) ||
+ val > 0xff)
+ break;
+
+ vsi->u.vsi8021Qbg.managerID = (uint8_t)val;
+
+ if ((virStrToLong_ui(vsiTypeID, NULL, 10, &val) &&
+ virStrToLong_ui(vsiTypeID, NULL, 16, &val) ) ||
+ val > 0xffffff)
+ break;
+
+ vsi->u.vsi8021Qbg.typeID = (uint32_t)val;
+
+ if ((virStrToLong_ui(vsiTypeIDVersion, NULL, 10, &val) &&
+ virStrToLong_ui(vsiTypeIDVersion, NULL, 16, &val) ) ||
+ val > 0xff)
+ break;
+
+ vsi->u.vsi8021Qbg.typeIDVersion = (uint8_t)val;
+
+ if (vsiInstanceID != NULL) {
+ if (virUUIDParse(vsiInstanceID, vsi->u.vsi8021Qbg.instanceID))
+ break;
+ } else {
+ if (virUUIDGenerate(vsi->u.vsi8021Qbg.instanceID))
+ break;
+ }
+
+ vsi->vsiType = VIR_VSI_8021QBG;
+ }
+ break;
+
+ case VIR_VSI_8021QBH:
+ if (vsiProfileID != NULL) {
+ if (virStrcpyStatic(vsi->u.vsi8021Qbh.profileID,
+ vsiProfileID) != NULL)
+ vsi->vsiType = VIR_VSI_8021QBH;
+ }
+ break;
+
+
+ default:
+ case VIR_VSI_NONE:
+ case VIR_VSI_TYPE_LAST:
+ break;
+ }
+
+ VIR_FREE(vsiManagerID);
+ VIR_FREE(vsiTypeID);
+ VIR_FREE(vsiTypeIDVersion);
+ VIR_FREE(vsiInstanceID);
+ VIR_FREE(vsiProfileID);
+ VIR_FREE(vsiType);
+}
+
+
+static void
+virVSIProfileFormat(virBufferPtr buf, virVSIProfileDefPtr vsi,
+ const char *indent)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+ if (vsi->vsiType == VIR_VSI_NONE)
+ return;
+
+ virBufferVSprintf(buf, "%s<vsi type='%s'>\n",
+ indent, virVSITypeToString(vsi->vsiType));
+
+ switch (vsi->vsiType) {
+ case VIR_VSI_NONE:
+ case VIR_VSI_TYPE_LAST:
+ break;
+
+ case VIR_VSI_8021QBG:
+ virUUIDFormat(vsi->u.vsi8021Qbg.instanceID,
+ uuidstr);
+ virBufferVSprintf(buf,
+ "%s <parameters managerid='%d' typeid='%d' "
+ "typeidversion='%d' instanceid='%s'/>\n",
+ indent,
+ vsi->u.vsi8021Qbg.managerID,
+ vsi->u.vsi8021Qbg.typeID,
+ vsi->u.vsi8021Qbg.typeIDVersion,
+ uuidstr);
+ break;
+
+ case VIR_VSI_8021QBH:
+ virBufferVSprintf(buf,
+ "%s <parameters profileid='%s'/>\n",
+ indent,
+ vsi->u.vsi8021Qbh.profileID);
+ break;
+ }
+
+ virBufferVSprintf(buf, "%s</vsi>\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 +1976,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
char *devaddr = NULL;
char *mode = NULL;
virNWFilterHashTablePtr filterparams = NULL;
+ virVSIProfileDef vsi;
+ bool vsiParsed = false;
if (VIR_ALLOC(def) < 0) {
virReportOOMError();
@@ -1873,6 +2019,11 @@ virDomainNetDefParseXML(virCapsPtr caps,
xmlStrEqual(cur->name, BAD_CAST "source")) {
dev = virXMLPropString(cur, "dev");
mode = virXMLPropString(cur, "mode");
+ } else if ((vsiParsed == false) &&
+ (def->type == VIR_DOMAIN_NET_TYPE_DIRECT) &&
+ xmlStrEqual(cur->name, BAD_CAST "vsi")) {
+ virVSIProfileDefParseXML(cur, &vsi);
+ vsiParsed = true;
} else if ((network == NULL) &&
((def->type == VIR_DOMAIN_NET_TYPE_SERVER) ||
(def->type == VIR_DOMAIN_NET_TYPE_CLIENT) ||
@@ -2050,6 +2201,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
} else
def->data.direct.mode = VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA;
+ def->data.direct.vsiProfile = vsi;
+
def->data.direct.linkdev = dev;
dev = NULL;
@@ -5142,6 +5295,8 @@ virDomainNetDefFormat(virBufferPtr buf,
virBufferVSprintf(buf, " mode='%s'",
virDomainNetdevMacvtapTypeToString(def->data.direct.mode));
virBufferAddLit(buf, "/>\n");
+ virVSIProfileFormat(buf, &def->data.direct.vsiProfile,
+ " ");
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
@@ -259,6 +259,39 @@ enum virDomainNetdevMacvtapType {
};
+enum virVSIType {
+ VIR_VSI_NONE,
+ VIR_VSI_8021QBG,
+ VIR_VSI_8021QBH,
+
+ VIR_VSI_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 _virVSIProfileDef virVSIProfileDef;
+typedef virVSIProfileDef *virVSIProfileDefPtr;
+struct _virVSIProfileDef {
+ enum virVSIType vsiType;
+ union {
+ struct {
+ uint8_t managerID;
+ uint32_t typeID; // 24 bit valid
+ uint8_t typeIDVersion;
+ unsigned char instanceID[VIR_UUID_BUFLEN];
+ } vsi8021Qbg;
+ struct {
+ char profileID[LIBVIRT_IFLA_VF_PORT_PROFILE_MAX];
+ } vsi8021Qbh;
+ } u;
+};
+
+
/* Stores the virtual network interface configuration */
typedef struct _virDomainNetDef virDomainNetDef;
typedef virDomainNetDef *virDomainNetDefPtr;
@@ -290,6 +323,7 @@ struct _virDomainNetDef {
struct {
char *linkdev;
int mode;
+ virVSIProfileDef vsiProfile;
} direct;
} data;
char *ifname;
@@ -1089,6 +1123,7 @@ VIR_ENUM_DECL(virDomainSeclabel)
VIR_ENUM_DECL(virDomainClockOffset)
VIR_ENUM_DECL(virDomainNetdevMacvtap)
+VIR_ENUM_DECL(virVSI)
VIR_ENUM_DECL(virDomainTimerName)
VIR_ENUM_DECL(virDomainTimerTrack)
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 virVSIProfileDefPtr vsi,
+ int vf,
+ const unsigned char *vmuuid);
+
+static int disassociatePortProfileId(const char *macvtap_ifname,
+ const virVSIProfileDefPtr vsi);
+
+
static int nlOpen(void)
{
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
@@ -567,39 +578,38 @@ configMacvtapTap(int tapfd, int vnet_hdr
return 0;
}
-
-
/**
* openMacvtapTap:
* Create an instance of a macvtap device and open its tap character
* device.
* @tgifname: Interface name that the macvtap is supposed to have. May
* 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'.
+ * @net: pointer to the virDomainNetDef object describing the direct
+ * type if an interface
* @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.
+ * @vnet_hdr: Whether to enable IFF_VNET_HDR on the interface
+ * @vmuuid: The (raw) UUID of the VM
*
* Returns file descriptor of the tap device in case of success,
* negative value otherwise with error reported.
*
+ * Open a macvtap device and trigger the switch setup protocol
+ * if valid port profile parameters were provided.
*/
int
openMacvtapTap(const char *tgifname,
- const unsigned char *macaddress,
- const char *linkdev,
- int mode,
+ virDomainNetDefPtr net,
char **res_ifname,
- int vnet_hdr)
+ int vnet_hdr,
+ const unsigned char *vmuuid)
{
const char *type = "macvtap";
int c, rc;
char ifname[IFNAMSIZ];
int retries, do_retry = 0;
- uint32_t macvtapMode = macvtapModeFromInt(mode);
+ uint32_t macvtapMode = macvtapModeFromInt(net->data.direct.mode);
const char *cr_ifname;
int ifindex;
@@ -616,7 +626,7 @@ openMacvtapTap(const char *tgifname,
return -1;
}
cr_ifname = tgifname;
- rc = link_add(type, macaddress, 6, tgifname, linkdev,
+ rc = link_add(type, net->mac, 6, tgifname, net->data.direct.linkdev,
macvtapMode, &do_retry);
if (rc)
return -1;
@@ -626,7 +636,8 @@ create_name:
for (c = 0; c < 8192; c++) {
snprintf(ifname, sizeof(ifname), MACVTAP_NAME_PATTERN, c);
if (ifaceGetIndex(false, ifname, &ifindex) == ENODEV) {
- rc = link_add(type, macaddress, 6, ifname, linkdev,
+ rc = link_add(type, net->mac, 6, ifname,
+ net->data.direct.linkdev,
macvtapMode, &do_retry);
if (rc == 0)
break;
@@ -639,6 +650,13 @@ create_name:
cr_ifname = ifname;
}
+ rc = associatePortProfileId(cr_ifname,
+ &net->data.direct.vsiProfile,
+ -1,
+ vmuuid);
+ if (rc != 0)
+ goto link_del_exit;
+
rc = ifaceUp(cr_ifname);
if (rc != 0) {
virReportSystemError(errno,
@@ -647,7 +665,7 @@ create_name:
"MAC address"),
cr_ifname);
rc = -1;
- goto link_del_exit;
+ goto disassociate_exit;
}
rc = openTap(cr_ifname, 10);
@@ -656,14 +674,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,
+ &net->data.direct.vsiProfile);
+
link_del_exit:
link_del(cr_ifname);
@@ -673,14 +695,102 @@ link_del_exit:
/**
* delMacvtap:
- * @ifname : The name of the macvtap interface
+ * @net: pointer to virDomainNetDef object
*
- * 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(virDomainNetDefPtr net)
{
- link_del(ifname);
+ if (net->ifname) {
+ disassociatePortProfileId(net->ifname,
+ &net->data.direct.vsiProfile);
+ link_del(net->ifname);
+ }
}
#endif
+
+
+/**
+ * associatePortProfile
+ *
+ * @macvtap_ifname: The name of the macvtap device
+ * @vsi: 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 virVSIProfileDefPtr vsi,
+ int vf,
+ const unsigned char *vmuuid)
+{
+ int rc = 0;
+ VIR_DEBUG("Associating port profile '%p' on link device '%s'",
+ vsi, macvtap_ifname);
+ (void)vf;
+ (void)vmuuid;
+
+ switch (vsi->vsiType) {
+ case VIR_VSI_NONE:
+ case VIR_VSI_TYPE_LAST:
+ break;
+
+ case VIR_VSI_8021QBG:
+
+ break;
+
+ case VIR_VSI_8021QBH:
+
+ break;
+ }
+
+ return rc;
+}
+
+
+/**
+ * disassociatePortProfile
+ *
+ * @macvtap_ifname: The name of the macvtap device
+ * @vsi: 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 virVSIProfileDefPtr vsi)
+{
+ int rc = 0;
+ VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
+ vsi, macvtap_ifname);
+
+ switch (vsi->vsiType) {
+ case VIR_VSI_NONE:
+ case VIR_VSI_TYPE_LAST:
+ break;
+
+ case VIR_VSI_8021QBG:
+
+ break;
+
+ case VIR_VSI_8021QBH:
+
+ break;
+ }
+
+ return rc;
+}
Index: libvirt-acl/src/util/macvtap.h
===================================================================
--- libvirt-acl.orig/src/util/macvtap.h
+++ libvirt-acl/src/util/macvtap.h
@@ -27,15 +27,15 @@
# if defined(WITH_MACVTAP)
# include "internal.h"
+# include "conf/domain_conf.h"
int openMacvtapTap(const char *ifname,
- const unsigned char *macaddress,
- const char *linkdev,
- int mode,
+ virDomainNetDefPtr net,
char **res_ifname,
- int vnet_hdr);
+ int vnet_hdr,
+ const unsigned char *vmuuid);
-void delMacvtap(const char *ifname);
+void delMacvtap(virDomainNetDefPtr net);
# endif /* WITH_MACVTAP */
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'/>
+ <vsi type='802.1Qbg'>
+ <parameters managerid='12' typeid='1193046' typeidversion='1'
+ instanceid='fa9b7fff-b0a0-4893-8e0e-beef4ff18f8f'/>
+ </vsi>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ <vsi type='802.1Qbg'>
+ <parameters managerid='12' typeid='1193046' typeidversion='1'/>
+ </vsi>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ <vsi type='802.1Qbh'>
+ <parameters profileid='my_profile'/>
+ </vsi>
+ </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
@@ -271,9 +271,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
@@ -3694,10 +3694,8 @@ 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);
}
#endif
@@ -7341,9 +7339,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;
}
@@ -8386,10 +8383,8 @@ 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);
#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
@@ -1465,9 +1465,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
@@ -1479,8 +1478,7 @@ 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, &res_ifname, vnet_hdr, vmuuid);
if (rc >= 0) {
VIR_FREE(net->ifname);
net->ifname = res_ifname;
@@ -1500,17 +1498,16 @@ qemudPhysIfaceConnect(virConnectPtr conn
if (err) {
close(rc);
rc = -1;
- delMacvtap(net->ifname);
+ delMacvtap(net);
}
}
}
#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;
@@ -4130,9 +4127,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;
2
3
[libvirt] Update of libvirt website required. not all information about XML tags
by Марк Коренберг 21 May '10
by Марк Коренберг 21 May '10
21 May '10
The problem desribed here: https://bugzilla.redhat.com/show_bug.cgi?id=593266
Please update http://www.libvirt.org/formatdomain.html and describe
NEW xml tags, like <serial>xxxx</serial> for hard drive and all cpu
tags, other if any.
--
Segmentation fault
2
5
21 May '10
Example output during shutdown:
Running guests on default URI: console, rhel6-1, rhel5-64
Running guests on lxc:/// URI: lxc-shell
Running guests on xen:/// URI: error: no hypervisor driver available for xen:///
error: failed to connect to the hypervisor
Running guests on vbox+tcp://orkuz/system URI: no running guests.
Suspending guests on default URI...
Suspending console: done
Suspending rhel6-1: done
Suspending rhel5-64: done
Suspending guests on lxc:/// URI...
Suspending lxc-shell: error: Failed to save domain 9cba8bfb-56f4-6589-2d12-8a58c886dd3b state
error: this function is not supported by the hypervisor: virDomainManagedSave
Note, the "Suspending $guest: " shows progress during the suspend phase
if domjobinfo gives meaningful output.
Example output during boot:
Resuming guests on default URI...
Resuming guest rhel6-1: done
Resuming guest rhel5-64: done
Resuming guest console: done
Resuming guests on lxc:/// URI...
Resuming guest lxc-shell: already active
Configuration used for generating the examples above:
URIS='default lxc:/// xen:/// vbox+tcp://orkuz/system'
The script uses /var/lib/libvirt/libvirt-guests files to note all active
guest it should try to resume on next boot. It's content looks like:
default 7f8b9d93-30e1-f0b9-47a7-cb408482654b 085b4c95-5da2-e8e1-712f-6ea6a4156af2 fb4d8360-5305-df3a-2da1-07d682891b8c
lxc:/// 9cba8bfb-56f4-6589-2d12-8a58c886dd3b
---
Version 2 changes:
- fixes suggested by Eric
- configurable ON_BOOT and ON_SHUTDOWN behavior inspired by Gerd
daemon/Makefile.am | 16 ++-
daemon/libvirt-guests.init.in | 295 +++++++++++++++++++++++++++++++++++++++++
daemon/libvirt-guests.sysconf | 24 ++++
libvirt.spec.in | 4 +
4 files changed, 335 insertions(+), 4 deletions(-)
create mode 100644 daemon/libvirt-guests.init.in
create mode 100644 daemon/libvirt-guests.sysconf
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index a82e9a9..ed469bf 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -29,6 +29,8 @@ EXTRA_DIST = \
libvirtd.lxc.logrotate.in \
libvirtd.uml.logrotate.in \
test_libvirtd.aug \
+ libvirt-guests.init.in \
+ libvirt-guests.sysconf \
$(AVAHI_SOURCES) \
$(DAEMON_SOURCES)
@@ -216,21 +218,27 @@ install-logrotate: $(LOGROTATE_CONFS)
$(INSTALL_DATA) libvirtd.uml.logrotate $(DESTDIR)$(sysconfdir)/logrotate.d/libvirtd.uml
if LIBVIRT_INIT_SCRIPT_RED_HAT
-install-init: libvirtd.init
+install-init: libvirtd.init libvirt-guests.init
mkdir -p $(DESTDIR)$(sysconfdir)/rc.d/init.d
$(INSTALL_SCRIPT) libvirtd.init \
$(DESTDIR)$(sysconfdir)/rc.d/init.d/libvirtd
+ $(INSTALL_SCRIPT) libvirt-guests.init \
+ $(DESTDIR)$(sysconfdir)/rc.d/init.d/libvirt-guests
mkdir -p $(DESTDIR)$(sysconfdir)/sysconfig
$(INSTALL_SCRIPT) $(srcdir)/libvirtd.sysconf \
$(DESTDIR)$(sysconfdir)/sysconfig/libvirtd
+ $(INSTALL_SCRIPT) $(srcdir)/libvirt-guests.sysconf \
+ $(DESTDIR)$(sysconfdir)/sysconfig/libvirt-guests
uninstall-init:
rm -f $(DESTDIR)$(sysconfdir)/rc.d/init.d/libvirtd \
- $(DESTDIR)$(sysconfdir)/sysconfig/libvirtd
+ $(DESTDIR)$(sysconfdir)/sysconfig/libvirtd \
+ $(DESTDIR)$(sysconfdir)/rc.d/init.d/libvirt-guests \
+ $(DESTDIR)$(sysconfdir)/sysconfig/libvirt-guests
-BUILT_SOURCES += libvirtd.init
+BUILT_SOURCES += libvirtd.init libvirt-guests.init
-libvirtd.init: libvirtd.init.in
+%.init: %.init.in
$(AM_V_GEN)sed \
-e s!\@localstatedir\@!@localstatedir@!g \
-e s!\@sbindir\@!@sbindir@!g \
diff --git a/daemon/libvirt-guests.init.in b/daemon/libvirt-guests.init.in
new file mode 100644
index 0000000..826f415
--- /dev/null
+++ b/daemon/libvirt-guests.init.in
@@ -0,0 +1,295 @@
+#!/bin/sh
+
+# the following is the LSB init header
+#
+### BEGIN INIT INFO
+# Provides: libvirt-guests
+# Required-Start: libvirtd
+# Required-Stop: libvirtd
+# Default-Start: 3 4 5
+# Short-Description: suspend/resume libvirt guests on shutdown/boot
+# Description: This is a script for suspending active libvirt guests
+# on shutdown and resuming them on next boot
+# See http://libvirt.org
+### END INIT INFO
+
+# the following is chkconfig init header
+#
+# libvirt-guests: suspend/resume libvirt guests on shutdown/boot
+#
+# chkconfig: 345 98 02
+# description: This is a script for suspending active libvirt guests
+# on shutdown and resuming them on next boot
+# See http://libvirt.org
+#
+
+sysconfdir=@sysconfdir@
+localstatedir=@localstatedir@
+
+# Source function library.
+. "$sysconfdir"/rc.d/init.d/functions
+
+URIS=default
+ON_BOOT=start
+ON_SHUTDOWN=suspend
+SHUTDOWN_TIMEOUT=0
+
+test -f "$sysconfdir"/sysconfig/libvirt-guests && . "$sysconfdir"/sysconfig/libvirt-guests
+
+LISTFILE="$localstatedir"/lib/libvirt/libvirt-guests
+
+RETVAL=0
+
+retval() {
+ "$@"
+ if [ $? -ne 0 ]; then
+ RETVAL=1
+ return 1
+ else
+ return 0
+ fi
+}
+
+run_virsh() {
+ uri=$1
+ shift
+
+ if [ "x$uri" = xdefault ]; then
+ conn=
+ else
+ conn="-c $uri"
+ fi
+
+ virsh $conn "$@"
+}
+
+run_virsh_c() {
+ ( export LC_ALL=C; run_virsh "$@" )
+}
+
+list_guests() {
+ uri=$1
+
+ list=$(run_virsh_c $uri list)
+ if [ $? -ne 0 ]; then
+ RETVAL=1
+ return 1
+ fi
+
+ uuids=
+ for id in $(echo "$list" | awk 'NR > 2 {print $1}'); do
+ uuid=$(run_virsh_c $uri dominfo $id | awk '/^UUID:/{print $2}')
+ if [ -z "$uuid" ]; then
+ RETVAL=1
+ return 1
+ fi
+ uuids="$uuids $uuid"
+ done
+
+ echo $uuids
+}
+
+guest_name() {
+ uri=$1
+ uuid=$2
+
+ name=$(run_virsh_c $uri dominfo $uuid 2>/dev/null | \
+ awk '/^Name:/{print $2}')
+ [ -n "$name" ] || name=$uuid
+
+ echo "$name"
+}
+
+guest_is_on() {
+ uri=$1
+ uuid=$2
+
+ guest_running=false
+ info=$(run_virsh_c $uri dominfo $uuid)
+ if [ $? -ne 0 ]; then
+ RETVAL=1
+ return 1
+ fi
+
+ id=$(echo "$info" | awk '/^Id:/{print $2}')
+
+ [ -n "$id" ] && [ "x$id" != x- ] && guest_running=true
+ return 0
+}
+
+start() {
+ [ -f $LISTFILE ] || return 0
+
+ if [ "x$ON_BOOT" != xstart ]; then
+ echo $"libvirt-guests is configured not to start any guests on boot"
+ rm -f $LISTFILE
+ return 0
+ fi
+
+ while read uri list; do
+ configured=false
+ for confuri in $URIS; do
+ if [ $confuri = $uri ]; then
+ configured=true
+ break
+ fi
+ done
+ if ! $configured; then
+ echo $"Ignoring guests on $uri URI"
+ continue
+ fi
+
+ echo $"Resuming guests on $uri URI..."
+ for guest in $list; do
+ name=$(guest_name $uri $guest)
+ echo -n $"Resuming guest $name: "
+ if guest_is_on $uri $guest; then
+ if $guest_running; then
+ echo $"already active"
+ else
+ retval run_virsh $uri start "$name" >/dev/null && \
+ echo $"done"
+ fi
+ fi
+ done
+ done <$LISTFILE
+
+ rm -f $LISTFILE
+}
+
+suspend_guest()
+{
+ uri=$1
+ guest=$2
+
+ name=$(guest_name $uri $guest)
+ label=$"Suspending $name: "
+ echo -n "$label"
+ run_virsh $uri managedsave $guest >/dev/null &
+ virsh_pid=$!
+ while true; do
+ sleep 1
+ kill -0 $virsh_pid >&/dev/null || break
+ progress=$(run_virsh_c $uri domjobinfo $guest 2>/dev/null | \
+ awk '/^Data processed:/{print $3, $4}')
+ if [ -n "$progress" ]; then
+ printf '\r%s%12s ' "$label" "$progress"
+ else
+ printf '\r%s%-12s ' "$label" "..."
+ fi
+ done
+ retval wait $virsh_pid && printf '\r%s%-12s\n' "$label" $"done"
+}
+
+shutdown_guest()
+{
+ uri=$1
+ guest=$2
+
+ name=$(guest_name $uri $guest)
+ label=$"Shutting down $name: "
+ echo -n "$label"
+ retval run_virsh $uri shutdown $guest >/dev/null || return
+ timeout=$SHUTDOWN_TIMEOUT
+ while [ $timeout -gt 0 ]; do
+ sleep 1
+ timeout=$[timeout - 1]
+ guest_is_on $uri $guest || return
+ $guest_running || break
+ printf '\r%s%-12d ' "$label" $timeout
+ done
+
+ if guest_is_on $uri $guest; then
+ if $guest_running; then
+ printf '\r%s%-12s\n' "$label" $"failed to shutdown in time"
+ else
+ printf '\r%s%-12s\n' "$label" $"done"
+ fi
+ fi
+}
+
+stop() {
+ # last stop was not followed by start
+ [ -f $LISTFILE ] && return 0
+
+ suspending=true
+ if [ "x$ON_SHUTDOWN" = xshutdown ]; then
+ suspending=false
+ if [ $SHUTDOWN_TIMEOUT -le 0 ]; then
+ echo $"Shutdown action requested but SHUTDOWN_TIMEOUT was not set"
+ RETVAL=6
+ return
+ fi
+ fi
+
+ : >$LISTFILE
+ for uri in $URIS; do
+ echo -n $"Running guests on $uri URI: "
+ list=$(list_guests $uri)
+ if [ $? -eq 0 ]; then
+ empty=true
+ for uuid in $list; do
+ $empty || printf ", "
+ echo -n $(guest_name $uri $uuid)
+ empty=false
+ done
+ if $empty; then
+ echo $"no running guests."
+ else
+ echo
+ echo $uri $list >>$LISTFILE
+ fi
+ fi
+ done
+
+ while read uri list; do
+ if $suspending; then
+ echo $"Suspending guests on $uri URI..."
+ else
+ echo $"Shutting down guests on $uri URI..."
+ fi
+
+ for guest in $list; do
+ if $suspending; then
+ suspend_guest $uri $guest
+ else
+ shutdown_guest $uri $guest
+ fi
+ done
+ done <$LISTFILE
+}
+
+gueststatus() {
+ for uri in $URIS; do
+ echo "* $uri URI:"
+ retval run_virsh $uri list || echo
+ done
+}
+
+# See how we were called.
+case "$1" in
+ start|stop|gueststatus)
+ $1
+ ;;
+ restart)
+ stop && start
+ ;;
+ force-reload)
+ ;;
+ status)
+ if [ -f $LISTFILE ]; then
+ RETVAL=3
+ else
+ RETVAL=0
+ fi
+ ;;
+ shutdown)
+ ON_SHUTDOWN=shutdown
+ stop
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|restart|force-reload|gueststatus|shutdown}"
+ exit 3
+ ;;
+esac
+exit $RETVAL
diff --git a/daemon/libvirt-guests.sysconf b/daemon/libvirt-guests.sysconf
new file mode 100644
index 0000000..cd58728
--- /dev/null
+++ b/daemon/libvirt-guests.sysconf
@@ -0,0 +1,24 @@
+# URIs to check for running guests
+# example: URIS='default xen:/// vbox+tcp://host/system lxc:///'
+#URIS=default
+
+# action taken on host boot
+# - start all guests which were running on shutdown are started on boot
+# regardless on their autostart settings
+# - ignore libvirt-guests init script won't start any guest on boot, however,
+# guests marked as autostart will still be automatically started by
+# libvirtd
+#ON_BOOT=start
+
+# action taken on host shutdown
+# - suspend all running guests are suspended using virsh managedsave
+# - shutdown all running guests are asked to shutdown. Please be careful with
+# this settings since there is no way to distinguish between a
+# guest which is stuck or ignores shutdown requests and a guest
+# which just needs a long time to shutdown. When setting
+# ON_SHUTDOWN=shutdown, you must also set SHUTDOWN_TIMEOUT to a
+# value suitable for your guests.
+#ON_SHUTDOWN=suspend
+
+# number of seconds we're willing to wait for a guest to shut down
+#SHUTDOWN_TIMEOUT=0
diff --git a/libvirt.spec.in b/libvirt.spec.in
index 7d5ea85..3d3b871 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -830,6 +830,10 @@ fi
%{_datadir}/libvirt/cpu_map.xml
+%{_sysconfdir}/rc.d/init.d/libvirt-guests
+%config(noreplace) %{_sysconfdir}/sysconfig/libvirt-guests
+%dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt
+
%if %{with_sasl}
%config(noreplace) %{_sysconfdir}/sasl2/libvirt.conf
%endif
--
1.7.1
2
3
21 May '10
Hi,
The fix required some refactoring in various places. More details about why and
what had to be reworked can be found in each of the patches.
Jirka
Jiri Denemark (4):
Refactor qemudDomainRestore
Factor out def assignment to existing domain from virDomainAssignDef
Refactor qemudDomainStart
Autostart domains using virDomainObjStart
src/conf/domain_conf.c | 36 +++--
src/conf/domain_conf.h | 3 +
src/libvirt_private.syms | 1 +
src/qemu/qemu_driver.c | 370 +++++++++++++++++++++++++++++-----------------
4 files changed, 262 insertions(+), 148 deletions(-)
2
9
[libvirt] [RFC] [PATCH 3/3] vepa+vsi: Some experimental code for 802.1Qbh
by Stefan Berger 21 May '10
by Stefan Berger 21 May '10
21 May '10
This patch may get 802.1Qbh devices working. I am adding some code to
poll for the status of an 802.1Qbh device and loop for a while until the
status indicates success. This part for sure needs more work and
testing...
I am recycling link_dump from a previous patch.
Signed-off-by: Stefan Berger <stefanb(a)linux.vnet.ibm.com>
---
src/util/macvtap.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
Index: libvirt-acl/src/util/macvtap.c
===================================================================
--- libvirt-acl.orig/src/util/macvtap.c
+++ libvirt-acl/src/util/macvtap.c
@@ -960,6 +960,95 @@ getPortProfileStatus(struct nlmsghdr *nl
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
doPortProfileOpSetLink(bool multicast,
const char *ifname, int ifindex,
const char *profileId,
@@ -1151,6 +1240,10 @@ doPortProfileOpCommon(bool multicast,
uint8_t op)
{
int rc;
+ char *recvbuf = NULL;
+ struct nlattr *tb[IFLA_MAX + 1];
+ int repeats = 5;
+ uint16_t status;
rc = doPortProfileOpSetLink(multicast,
ifname, ifindex,
@@ -1167,6 +1260,30 @@ doPortProfileOpCommon(bool multicast,
return rc;
}
+ if (!multicast) {
+ /* 802.1Qbh -- query for status */
+ while (--repeats) {
+ rc = link_dump(ifindex, ifname, tb, &recvbuf);
+ if (rc)
+ goto err_exit;
+ rc = getPortProfileStatus((struct nlmsghdr *)recvbuf, &status);
+ if (rc == 0) {
+ if (status == 0)
+ break;
+ if (status != 0) {
+ fprintf(stderr,"Current status: %d\n", status);
+ rc = 1;
+ }
+ }
+ usleep(10000);
+
+ VIR_FREE(recvbuf);
+ }
+ }
+
+err_exit:
+ VIR_FREE(recvbuf);
+
return rc;
}
2
2