[libvirt] [PATCH-v5.1] Support for static routes on a virtual bridge
Laine Stump
laine at laine.org
Mon May 13 20:16:19 UTC 2013
On 05/07/2013 01:42 PM, Gene Czarcinski wrote:
> network: static route support for <network>
>
> This update includes Laine Stump's comments/suggestions. Once
> again he has improved my over-engineered solutions. My original
> patch and his patch have been squashed/merged with this being the
> result. That plus a little simplified code added to
> bridge_driver.c to handle the zero address situations.
>
> This code does not restrict any combination of
> address/netmask/prefix for a static route specification. The
> metric attribute has been added so that existing route specifications
> can be overridden.
>
> Documentation has been updated to reflect a v1.0.6 target.
>
> This patch adds the <route> subelement of <network> to define a static
> route. the address and prefix (or netmask) attribute identify the
> destination network, and the gateway attribute specifies the next hop
> address (which must be directly reachable from the containing
> <network>) which is to receive the packets destined for
> "address/(prefix|netmask)".
>
> Tests are done to validate that the input definitions are
> correct. For example, for a static route ip definition,
> the address must be a network address and not a host address.
> Additional checks are added to ensure that the specified gateway
> is directly reachable via a network defined on this bridge.
>
> The command used is of the following form:
>
> ip route add <address>/<prefix> via <gateway> dev <virbr-bridge> \
> proto static metric 1
This should say "metric <metric>".
>
> For family='ipv4' address='0.0.0.0' netmask='0.0.0.0' or prefix='0' is
> supported.
>
> For family='ipv6' address='::' prefix=0' is supported.
>
> Anytime an attempt is made to define a static route which duplicates
> an existing static route (for example, address=::, prefix=0, metric=1),
> the following error message will be sent to syslog:
> RTNETLINK answers: File exists
>
> This can be overridden by increasing the value of metric. Caution should
> be used when doing this ... especially for a default route.
>
> Note: The use of the command-line interface should be replaced by
> direct use of libnl so that error conditions can be handled better. But,
> that is being left as an exersize for another day.
exercise
> .
> Signed-off-by: Gene Czarcinski <gene at czarc.net>
> ---
> docs/formatnetwork.html.in | 77 +++++
> docs/schemas/network.rng | 22 ++
> src/conf/network_conf.c | 340 ++++++++++++++++++++-
> src/conf/network_conf.h | 22 ++
> src/libvirt_private.syms | 1 +
> src/network/bridge_driver.c | 64 ++++
> src/util/virnetdev.c | 46 +++
> src/util/virnetdev.h | 7 +
> .../networkxml2xmlin/dhcp6host-routed-network.xml | 2 +
> .../networkxml2xmlout/dhcp6host-routed-network.xml | 2 +
> 10 files changed, 582 insertions(+), 1 deletion(-)
>
> diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
> index d72bd0a..4c7e74f 100644
> --- a/docs/formatnetwork.html.in
> +++ b/docs/formatnetwork.html.in
> @@ -546,6 +546,56 @@
> starting.
> </p>
>
> + <h5><a name="elementsStaticroute">Static Routes</a></h5>
> + <p>
> + Static route definitions are used to provide routing
> + information to the virtualization host for networks which are not
> + defined as a network on one of the virtual bridges so that such
which are not directly reachable from the virtualization host, but *are*
reachable from a guest domain that is itself reachable from the host.
> + networks can be reachable from the virtualization
> + host <span class="since">Since 1.0.6</span>.
> + </p>
> +
> + <p>
> + As shown in <a href="formatnetwork.html#examplesNoGateway">this example</a>,
> + it is possible to define a virtual bridge interface with no
s/virtual bridge/virtual network/
> + IPv4 or IPv6 networks. Such interfaces are useful in supporting
s/networks/addresses/
s/interfaces/networks/
> + networks which have no visibility or direct connectivity with the
> + virtualization host, but can be used to support
> + virtual networks which only have guest connectivity.
which are only reachable via a guest.
> A guest
> + with connectivity to the guest-only network and another network
"with connectivity *both* to .... and ...."
> + that is directly reachable from the host can act as a gateway between the
> + networks. A static route added to the "visible" network definition
"host-visible"
> + provides the routing information so that IP packets can be sent
> + from the virtualization host to guests on the hidden network.
> + </p>
> +
> + <p>
> + Here is a fragment of a definition which shows the static
> + route specification as well as the IPv4 and IPv6 definitions
> + for network addresses which are referred to in the
> + <code>gateway</code> gateway address specifications. Note
> + that the third static route specification includes the
> + <code>metric</code> attribute specification with a value of 2.
> + This indicates that this particular definition is overriding another
> + route definition with the same address and prefix but with a lower
> + value for the metric.
Actually it's the opposite - the lower the metric, the more preferred it
is. You can think of the metric as the "cost" of travelling in this
direction.
If someone wanted to have a route that automatically overrode an
existing route when the libvirt network was started, they would need to
setup their system config to give the "permanent" route a higher metric.
> + </p>
> +
> + <pre>
> + ...
> + <ip address="192.168.122.1" netmask="255.255.255.0">
> + <dhcp>
> + <range start="192.168.122.128" end="192.168.122.254" />
> + </dhcp>
> + </ip>
> + <route address="192.168.222.0" prefix="24" gateway="192.168.122.2" />
> + <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" />
> + <route family="ipv6" address="2001:db8:ca2:3::" prefix="64" gateway="2001:db8:ca2:2::2"/>
> + <route family="ipv6" address="2001:db9:4:1::" prefix="64" gateway="2001:db8:ca2:2::3" metric='2'>
> + </route>
> + ...
> + </pre>
> +
> <h3><a name="elementsAddress">Addressing</a></h3>
>
> <p>
> @@ -577,6 +627,7 @@
> </dhcp>
> </ip>
> <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" />
> + <route family="ipv6" address="2001:db9:ca1:1::" prefix="64" gateway="2001:db8:ca2:2::2" />
> </network></pre>
>
> <dl>
> @@ -826,6 +877,32 @@
> </ip>
> </network></pre>
>
> + <p>
> + Below is yet another IPv6 variation. This variation has only IPv6
> + defined with DHCPv6 on the primary IPv6 network. A static link
> + if defined for a second IPv6 network which will not be visable on
s/visable/visible/
> + the bridge interface but will have a static route defined for this
> + network via the specified gateway. Note that the gateway address
> + must be directly reachable via (on the same subnet as) one of the
> + <ip> addresses defined for this <network>.
> + <span class="since">Since 1.0.5</span>
s/1.0.5/1.0.6/
> + </p>
> +
> + <pre>
> + <network>
> + <name>net7</name>
> + <bridge name="virbr7" />
> + <forward mode="route"/>
> + <ip family="ipv6" address="2001:db8:ca2:7::1" prefix="64" >
> + <dhcp>
> + <range start="2001:db8:ca2:7::100" end="2001:db8:ca2::1ff" />
> + <host id="0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63" name="lucas" ip="2001:db8:ca2:2:3::4" />
> + </dhcp>
> + </ip>
> + <route family="ipv6" address="2001:db8:ca2:8::" prefix="64" gateway="2001:db8:ca2:7::4" >
> + </route>
> + </network></pre>
> +
> <h3><a name="examplesPrivate">Isolated network config</a></h3>
>
> <p>
> diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
> index 493edae..ded8580 100644
> --- a/docs/schemas/network.rng
> +++ b/docs/schemas/network.rng
> @@ -316,6 +316,28 @@
> </optional>
> </element>
> </zeroOrMore>
> + <!-- <route> element -->
> + <zeroOrMore>
> + <!-- The (static) route element specifies a network address and gateway
> + address to access that network. Both the network address and
> + the gateway address must be specified. -->
> + <element name="route">
> + <optional>
> + <attribute name="family"><ref name="addr-family"/></attribute>
> + </optional>
> + <attribute name="address"><ref name="ipAddr"/></attribute>
> + <optional>
> + <choice>
> + <attribute name="netmask"><ref name="ipv4Addr"/></attribute>
> + <attribute name="prefix"><ref name="ipPrefix"/></attribute>
> + </choice>
> + </optional>
> + <attribute name="gateway"><ref name="ipAddr"/></attribute>
> + <optional>
> + <attribute name="metric"><ref name="unsignedInt"/></attribute>
> + </optional>
> + </element>
> + </zeroOrMore>
> </interleave>
> </element>
> </define>
> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
> index 8abfa53..12ac879 100644
> --- a/src/conf/network_conf.c
> +++ b/src/conf/network_conf.c
> @@ -143,6 +143,12 @@ virNetworkIpDefClear(virNetworkIpDefPtr def)
> }
>
> static void
> +virNetworkRouteDefClear(virNetworkRouteDefPtr def)
> +{
> + VIR_FREE(def->family);
> +}
> +
> +static void
> virNetworkDNSTxtDefClear(virNetworkDNSTxtDefPtr def)
> {
> VIR_FREE(def->name);
> @@ -221,6 +227,11 @@ virNetworkDefFree(virNetworkDefPtr def)
> }
> VIR_FREE(def->ips);
>
> + for (ii = 0 ; ii < def->nroutes && def->routes ; ii++) {
> + virNetworkRouteDefClear(&def->routes[ii]);
> + }
> + VIR_FREE(def->routes);
> +
> for (ii = 0; ii < def->nPortGroups && def->portGroups; ii++) {
> virPortGroupDefClear(&def->portGroups[ii]);
> }
> @@ -1270,6 +1281,219 @@ cleanup:
> }
>
> static int
> +virNetworkRouteDefParseXML(const char *networkName,
> + xmlNodePtr node,
> + xmlXPathContextPtr ctxt,
> + virNetworkRouteDefPtr def)
> +{
> + /*
> + * virNetworkRouteDef object is already allocated as part
> + * of an array. On failure clear: it out, but don't free it.
> + */
> +
> + xmlNodePtr save;
> + char *address = NULL, *netmask = NULL;
> + char *gateway = NULL;
> + unsigned long prefix = 0, metric = 0;
> + int result = -1;
> + int prefixRc, metricRc;
> + virSocketAddr testAddr;
> +
> + save = ctxt->node;
> + ctxt->node = node;
> +
> + /* grab raw data from XML */
> + def->family = virXPathString("string(./@family)", ctxt);
> + address = virXPathString("string(./@address)", ctxt);
> + netmask = virXPathString("string(./@netmask)", ctxt);
> + gateway = virXPathString("string(./@gateway)", ctxt);
> + prefixRc = virXPathULong("string(./@prefix)", ctxt, &prefix);
> + if (prefixRc == -2) {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("Invalid prefix specified "
> + "in route definition of network '%s'"),
> + networkName);
> + goto cleanup;
> + }
> + def->has_prefix = (prefixRc == 0);
> + def->prefix = prefix;
> + metricRc = virXPathULong("string(./@metric)", ctxt, &metric);
> + if (metricRc == -2) {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("Invalid metric specified "
> + "in route definition of network '%s'"),
> + networkName);
> + goto cleanup;
> + }
> + def->has_metric = (metricRc == 0);
> + def->metric = metric;
> +
> + /* Note: both network and gateway addresses must be specified */
> +
> + if (!address) {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("Missing required address attribute "
> + "in route definition of network '%s'"),
> + networkName);
> + goto cleanup;
> + }
> +
> + if (!gateway) {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("Missing required gateway attribute "
> + "in route definition of network '%s'"),
> + networkName);
> + goto cleanup;
> + }
> +
> + if (virSocketAddrParse(&def->address, address, AF_UNSPEC) < 0) {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("Bad network address '%s' "
> + "in route definition of network '%s'"),
> + address, networkName);
> + goto cleanup;
> + }
> +
> + if (virSocketAddrParse(&def->gateway, gateway, AF_UNSPEC) < 0) {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("Bad gateway address '%s' "
> + "in route definition of network '%s'"),
> + gateway, networkName);
> + goto cleanup;
> + }
> +
> + /* validate network address, etc. for each family */
> + if ((def->family == NULL) || (STREQ(def->family, "ipv4"))) {
> + if (!(VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET) ||
> + VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_UNSPEC))) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
This should be VIR_ERR_XML_ERROR. CONFIG_UNSUPPORTED is reserved for
cases where a configuration is theoretically okay, but this particular
driver doesn't support it. Same comment applies for all the other uses
of VIR_ERR_CONFIG_UNSUPPORTED in this patch.
> + _("%s family specified for non-IPv4 address '%s' "
> + "in route definition of network '%s'"),
> + def->family == NULL? "no" : "ipv4",
> + address, networkName);
> + goto cleanup;
> + }
> + if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->gateway, AF_INET)) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("%s family specified for non-IPv4 gateway '%s' "
> + "in route definition of network '%s'"),
> + def->family == NULL? "no" : "ipv4",
> + address, networkName);
> + goto cleanup;
> + }
> + if (netmask) {
> + if (virSocketAddrParse(&def->netmask, netmask, AF_UNSPEC) < 0) {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("Bad netmask address '%s' "
> + "in route definition of network '%s'"),
> + netmask, networkName);
> + goto cleanup;
> + }
> + if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->netmask, AF_INET)) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("network '%s' has invalid netmask '%s' "
> + "for address '%s' (both must be IPv4)"),
> + networkName, netmask, address);
> + goto cleanup;
> + }
> + if (def->has_prefix) {
> + /* can't have both netmask and prefix at the same time */
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("route definition '%s' cannot have both "
> + "a prefix and a netmask"),
> + networkName);
> + goto cleanup;
> + }
> + }
> + if (def->prefix > 32) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("Invalid IPv4 prefix %u for '%s' specified "
> + "in route definition of network '%s'"),
> + def->prefix,
> + def->family == NULL? "no" : "ipv4", networkName);
This should really just say "Invalid prefix %u specified in route
definition of network '%s'".
> + goto cleanup;
> + }
> + } else if (STREQ(def->family, "ipv6")) {
> + if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("ipv6 family specified for non-IPv6 address '%s' "
> + "in route definition of network '%s'"),
> + address, networkName);
> + goto cleanup;
> + }
> + if (netmask) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("specifying netmask invalid for IPv6 address '%s' "
> + "in route definition of network '%s'"),
> + address, networkName);
> + goto cleanup;
> + }
> + if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->gateway, AF_INET6)) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("ipv6 specified for non-IPv6 gateway address '%s' "
> + "in route definition of network '%s'"),
> + gateway, networkName);
> + goto cleanup;
> + }
> + if (def->prefix > 128) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("Invalid IPv6 prefix %u for '%s' specified "
> + "in route definition of network '%s'"),
> + def->prefix, def->family, networkName);
This one should also be simplified.
> + goto cleanup;
> + }
> + } else {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("Unrecognized family '%s' "
> + "in route definition of network'%s'"),
> + def->family, networkName);
> + goto cleanup;
> + }
> +
> + /* make sure the address is a network address */
> + if (netmask) {
> + if (virSocketAddrMask(&def->address, &def->netmask, &testAddr) < 0) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
This is an INTERNAL error - we've already validated address and netmask,
and assured that both of them are IPv4, so there shouldn't be any reason
for this to fail.
> + _("error converting address '%s' with netmask '%s' "
> + "to network-address "
> + "in route definition of network '%s'"),
> + address, netmask, networkName);
> + goto cleanup;
> + }
> + } else {
> + if (virSocketAddrMaskByPrefix(&def->address,
> + def->prefix, &testAddr) < 0) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
This is also INTERNAL.
> + _("error converting address '%s' with prefix %u "
> + "to network-address "
> + "in route definition of network '%s'"),
> + address, def->prefix, networkName);
> + goto cleanup;
> + }
> + }
> + if (!virSocketAddrEqual(&def->address, &testAddr)) {
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("address '%s' in route definition of network '%s' "
> + "is not a network address"),
> + address, networkName);
> + goto cleanup;
> + }
> +
> + result = 0;
> +
> +cleanup:
> + if (result < 0) {
> + virNetworkRouteDefClear(def);
> + }
> + VIR_FREE(address);
> + VIR_FREE(netmask);
> + VIR_FREE(gateway);
> +
> + ctxt->node = save;
> + return result;
> +}
> +
> +static int
> virNetworkPortGroupParseXML(virPortGroupDefPtr def,
> xmlNodePtr node,
> xmlXPathContextPtr ctxt)
> @@ -1684,8 +1908,9 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
> char *tmp;
> char *stp = NULL;
> xmlNodePtr *ipNodes = NULL;
> + xmlNodePtr *routeNodes = NULL;
> xmlNodePtr *portGroupNodes = NULL;
> - int nIps, nPortGroups;
> + int nIps, nPortGroups, nRoutes;
> xmlNodePtr dnsNode = NULL;
> xmlNodePtr virtPortNode = NULL;
> xmlNodePtr forwardNode = NULL;
> @@ -1839,6 +2064,68 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
> }
> VIR_FREE(ipNodes);
>
> + nRoutes = virXPathNodeSet("./route", ctxt, &routeNodes);
> + if (nRoutes < 0)
> + goto error;
> +
> + if (nRoutes > 0) {
> + int ii;
> +
> + /* allocate array to hold all the route definitions */
> + if (VIR_ALLOC_N(def->routes, nRoutes) < 0) {
> + virReportOOMError();
> + goto error;
> + }
> + /* parse each definition */
> + for (ii = 0; ii < nRoutes; ii++) {
> + int ret = virNetworkRouteDefParseXML(def->name, routeNodes[ii],
> + ctxt, &def->routes[ii]);
> + if (ret < 0)
> + goto error;
> + def->nroutes++;
> + }
> +
> + /* now validate the correctness of any static route gateways specified
> + *
> + * note: the parameters within each definition are verified/assumed valid;
> + * the question being asked and answered here is if the specified gateway
> + * is directly reachable from this bridge.
> + */
> + nRoutes = def->nroutes;
> + nIps = def->nips;
> + for (ii = 0; ii < nRoutes; ii++) {
> + int jj;
> + virSocketAddr testAddr, testGw;
> + bool addrMatch;
> + virNetworkRouteDefPtr gwdef = &def->routes[ii];
> + addrMatch = false;
> + for (jj = 0; jj < nIps; jj++) {
> + virNetworkIpDefPtr def2 = &def->ips[jj];
> + if (VIR_SOCKET_ADDR_FAMILY(&gwdef->gateway)
> + != VIR_SOCKET_ADDR_FAMILY(&def2->address)) {
> + continue;
> + }
> + int prefix = virNetworkIpDefPrefix(def2);
> + virSocketAddrMaskByPrefix(&def2->address, prefix, &testAddr);
> + virSocketAddrMaskByPrefix(&gwdef->gateway, prefix, &testGw);
> + if (VIR_SOCKET_ADDR_VALID(&testAddr) &&
> + VIR_SOCKET_ADDR_VALID(&testGw) &&
> + virSocketAddrEqual(&testAddr, &testGw)) {
> + addrMatch = true;
> + break;
> + }
> + }
> + if (!addrMatch) {
> + char *gw = virSocketAddrFormat(&gwdef->gateway);
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
> + _("unreachable static route gateway '%s' specified for network '%s'"),
> + gw, def->name);
> + VIR_FREE(gw);
> + goto error;
> + }
> + }
> + }
> +
Hmm. routeNodes is freed in the error case, but not in the normal case.
Following the pattern of the rest of this function, you would need a
VIR_FREE(routeNodes) right here.
> forwardNode = virXPathNode("./forward", ctxt);
> if (forwardNode &&
> virNetworkForwardDefParseXML(def->name, forwardNode, ctxt, &def->forward) < 0) {
> @@ -1911,6 +2198,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
> return def;
>
> error:
> + VIR_FREE(routeNodes);
> VIR_FREE(stp);
> virNetworkDefFree(def);
> VIR_FREE(ipNodes);
> @@ -2136,6 +2424,51 @@ error:
> }
>
> static int
> +virNetworkRouteDefFormat(virBufferPtr buf,
> + const virNetworkRouteDefPtr def)
> +{
> + int result = -1;
> +
> + virBufferAddLit(buf, "<route");
> +
> + if (def->family) {
> + virBufferAsprintf(buf, " family='%s'", def->family);
> + }
> + if (VIR_SOCKET_ADDR_VALID(&def->address)) {
> + char *addr = virSocketAddrFormat(&def->address);
> + if (!addr)
> + goto error;
> + virBufferAsprintf(buf, " address='%s'", addr);
> + VIR_FREE(addr);
> + }
> + if (VIR_SOCKET_ADDR_VALID(&def->netmask)) {
> + char *addr = virSocketAddrFormat(&def->netmask);
> + if (!addr)
> + goto error;
> + virBufferAsprintf(buf, " netmask='%s'", addr);
> + VIR_FREE(addr);
> + }
> + if (def->has_prefix) {
> + virBufferAsprintf(buf," prefix='%u'", def->prefix);
> + }
> + if (VIR_SOCKET_ADDR_VALID(&def->gateway)) {
> + char *addr = virSocketAddrFormat(&def->gateway);
> + if (!addr)
> + goto error;
> + virBufferAsprintf(buf, " gateway='%s'", addr);
> + VIR_FREE(addr);
> + }
> + if (def->has_metric && def->metric > 0) {
> + virBufferAsprintf(buf," metric='%u'", def->metric);
> + }
Ah yes - since a metric of 0 isn't allowed (it's reserved for direct
routes), we need to check for that in the parser.
> + virBufferAddLit(buf, "/>\n");
> +
> + result = 0;
> +error:
> + return result;
> +}
> +
> +static int
> virPortGroupDefFormat(virBufferPtr buf,
> const virPortGroupDefPtr def)
> {
> @@ -2347,6 +2680,11 @@ virNetworkDefFormatInternal(virBufferPtr buf,
> goto error;
> }
>
> + for (ii = 0; ii < def->nroutes; ii++) {
> + if (virNetworkRouteDefFormat(buf, &def->routes[ii]) < 0)
> + goto error;
> + }
> +
> if (virNetDevVPortProfileFormat(def->virtPortProfile, buf) < 0)
> goto error;
>
> diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
> index e187f05..43f80d4 100644
> --- a/src/conf/network_conf.h
> +++ b/src/conf/network_conf.h
> @@ -149,6 +149,25 @@ struct _virNetworkIpDef {
> virSocketAddr bootserver;
> };
>
> +typedef struct _virNetworkRouteDef virNetworkRouteDef;
> +typedef virNetworkRouteDef *virNetworkRouteDefPtr;
> +struct _virNetworkRouteDef {
> + char *family; /* ipv4 or ipv6 - default is ipv4 */
> + virSocketAddr address; /* Routed Network IP address */
> +
> + /* One or the other of the following two will be used for a given
> + * Network address, but never both. The parser guarantees this.
> + * The virSocketAddrGetIpPrefix() can be used to get a
> + * valid prefix.
> + */
> + virSocketAddr netmask; /* ipv4 - either netmask or prefix specified */
> + unsigned int prefix; /* ipv6 - only prefix allowed */
> + bool has_prefix; /* prefix= was specified */
> + unsigned int metric; /* value for metric (defaults to 1) */
> + bool has_metric; /* metric= was specified */
It's good that you duplicated the has_* idea for this.
> + virSocketAddr gateway; /* gateway IP address for ip-route */
> + };
> +
> typedef struct _virNetworkForwardIfDef virNetworkForwardIfDef;
> typedef virNetworkForwardIfDef *virNetworkForwardIfDefPtr;
> struct _virNetworkForwardIfDef {
> @@ -224,6 +243,9 @@ struct _virNetworkDef {
> size_t nips;
> virNetworkIpDefPtr ips; /* ptr to array of IP addresses on this network */
>
> + size_t nroutes;
> + virNetworkRouteDefPtr routes; /* ptr to array of static routes on this interface */
> +
> virNetworkDNSDef dns; /* dns related configuration */
> virNetDevVPortProfilePtr virtPortProfile;
>
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index d4cb4a3..dfa4779 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -1463,6 +1463,7 @@ virMacAddrSetRaw;
>
>
> # util/virnetdev.h
> +virNetDevAddRoute;
> virNetDevClearIPv4Address;
> virNetDevExists;
> virNetDevGetIndex;
> diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
> index 9c5a8ae..4392e20 100644
> --- a/src/network/bridge_driver.c
> +++ b/src/network/bridge_driver.c
> @@ -2391,6 +2391,7 @@ out:
> return ret;
> }
>
> +/* add an IP address to a bridge */
> static int
> networkAddAddrToBridge(virNetworkObjPtr network,
> virNetworkIpDefPtr ipdef)
> @@ -2411,6 +2412,55 @@ networkAddAddrToBridge(virNetworkObjPtr network,
> return 0;
> }
>
> +/* add an IP (static) route to a bridge */
> +static int
> +networkAddRouteToBridge(virNetworkObjPtr network,
> + virNetworkRouteDefPtr routedef)
> +{
> + int prefix = 0;
> + unsigned int metric;
> + virSocketAddrPtr addr = &routedef->address;
> + virSocketAddrPtr mask = &routedef->netmask;
> + virSocketAddr zero;
> +
> + /* this creates an all-0 address of the appropriate family */
> + ignore_value(virSocketAddrParse(&zero,
> + (VIR_SOCKET_ADDR_IS_FAMILY(addr,AF_INET)
> + ? "0.0.0.0" : "::"),
> + VIR_SOCKET_ADDR_FAMILY(addr)));
> +
> + if (virSocketAddrEqual(addr, &zero)) {
> + if (routedef->has_prefix && routedef->prefix == 0)
> + prefix = 0;
> + else if ((VIR_SOCKET_ADDR_IS_FAMILY(mask, AF_INET) &&
> + virSocketAddrEqual(mask, &zero)))
> + prefix = 0;
> + else
> + prefix = virSocketAddrGetIpPrefix(addr, mask, routedef->prefix);
> + } else {
> + prefix = virSocketAddrGetIpPrefix(addr, mask, routedef->prefix);
> + }
Much simpler! :-)
> +
> + if (prefix < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("network '%s' has an invalid netmask "
> + "or IP address in route definition"),
> + network->def->name);
> + return -1;
> + }
> +
> + if (routedef->has_metric && routedef->metric > 0)
> + metric = routedef->metric;
> + else
> + metric = 1;
> +
> + if (virNetDevAddRoute(network->def->bridge, &routedef->address,
> + prefix, &routedef->gateway, metric) < 0) {
> + return -1;
> + }
> + return 0;
> +}
> +
> static int
> networkStartNetworkVirtual(struct network_driver *driver,
> virNetworkObjPtr network)
> @@ -2419,6 +2469,7 @@ networkStartNetworkVirtual(struct network_driver *driver,
> bool v4present = false, v6present = false;
> virErrorPtr save_err = NULL;
> virNetworkIpDefPtr ipdef;
> + virNetworkRouteDefPtr routedef;
> char *macTapIfName = NULL;
> int tapfd = -1;
>
> @@ -2495,6 +2546,19 @@ networkStartNetworkVirtual(struct network_driver *driver,
> if (virNetDevSetOnline(network->def->bridge, 1) < 0)
> goto err2;
>
> + for (ii = 0; ii < network->def->nroutes; ii++) {
> + routedef = &network->def->routes[ii];
> + /* Add the IP route to the bridge */
> + /* ignore errors, error msg will be generated */
> + /* but libvirt will not know and net-destroy will work. */
> + if (VIR_SOCKET_ADDR_VALID(&routedef->gateway)) {
> + if (networkAddRouteToBridge(network, routedef) < 0) {
> + /* an error occurred adding the static route */
> + continue; /* for now, do nothing */
> + }
> + }
> + }
> +
> /* If forward.type != NONE, turn on global IP forwarding */
> if (network->def->forward.type != VIR_NETWORK_FORWARD_NONE &&
> networkEnableIpForwarding(v4present, v6present) < 0) {
> diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
> index d987b8e..01aaff1 100644
> --- a/src/util/virnetdev.c
> +++ b/src/util/virnetdev.c
> @@ -783,6 +783,52 @@ cleanup:
> }
>
> /**
> + * virNetDevSetGateway:
Oops. Forgot to change the name.
> + * @ifname: the interface name
> + * @addr: the IP network address (IPv4 or IPv6)
> + * @prefix: number of 1 bits in the netmask
> + * @gateway: via address for route (same as @addr)
> + *
> + * Add a route for a network IP address to an interface. This function
> + * *does not* remove any previously added IP static routes.
> + *
> + * Returns 0 in case of success or -1 in case of error.
> + */
> +
> +int
> +virNetDevAddRoute(const char *ifname,
> + virSocketAddrPtr addr,
> + unsigned int prefix,
> + virSocketAddrPtr gateway,
> + unsigned int metric)
> +{
> + virCommandPtr cmd = NULL;
> + char *addrstr = NULL, *gatewaystr = NULL;
> + int ret = -1;
> +
> + if (!(addrstr = virSocketAddrFormat(addr)))
> + goto cleanup;
> + if (!(gatewaystr = virSocketAddrFormat(gateway)))
> + goto cleanup;
> + cmd = virCommandNew(IP_PATH);
> + virCommandAddArgList(cmd, "route", "add", NULL);
> + virCommandAddArgFormat(cmd, "%s/%u", addrstr, prefix);
> + virCommandAddArgList(cmd, "via", gatewaystr, "dev", ifname,
> + "proto", "static", "metric", NULL);
> + virCommandAddArgFormat(cmd, "%u", metric);
> +
> + if (virCommandRun(cmd, NULL) < 0)
> + goto cleanup;
> +
> + ret = 0;
> +cleanup:
> + VIR_FREE(addrstr);
> + VIR_FREE(gatewaystr);
> + virCommandFree(cmd);
> + return ret;
> +}
> +
> +/**
> * virNetDevClearIPv4Address:
> * @ifname: the interface name
> * @addr: the IP address (IPv4 or IPv6)
> diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
> index 551ea22..0b394ad 100644
> --- a/src/util/virnetdev.h
> +++ b/src/util/virnetdev.h
> @@ -42,6 +42,13 @@ int virNetDevSetIPv4Address(const char *ifname,
> virSocketAddr *addr,
> unsigned int prefix)
> ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
> +int virNetDevAddRoute(const char *ifname,
> + virSocketAddrPtr addr,
> + unsigned int prefix,
> + virSocketAddrPtr gateway,
> + unsigned int metric)
> + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4)
> + ATTRIBUTE_NONNULL(5) ATTRIBUTE_RETURN_CHECK;
> int virNetDevClearIPv4Address(const char *ifname,
> virSocketAddr *addr,
> unsigned int prefix)
> diff --git a/tests/networkxml2xmlin/dhcp6host-routed-network.xml b/tests/networkxml2xmlin/dhcp6host-routed-network.xml
> index 2693d87..40f6dfe 100644
> --- a/tests/networkxml2xmlin/dhcp6host-routed-network.xml
> +++ b/tests/networkxml2xmlin/dhcp6host-routed-network.xml
> @@ -19,4 +19,6 @@
> <host id='0:1:0:1:18:aa:62:fe:0:16:3e:44:55:66' name='badbob' ip='2001:db8:ac10:fd01::1:24' />
> </dhcp>
> </ip>
> + <route address="192.168.222.0" netmask="255.255.255.0" gateway="192.168.122.10"/>
> + <route family="ipv6" address="2001:db8:ac10:fc00::" prefix="64" gateway="2001:db8:ac10:fd01::1:24"/>
> </network>
> diff --git a/tests/networkxml2xmlout/dhcp6host-routed-network.xml b/tests/networkxml2xmlout/dhcp6host-routed-network.xml
> index 1d3035b..fc8666b 100644
> --- a/tests/networkxml2xmlout/dhcp6host-routed-network.xml
> +++ b/tests/networkxml2xmlout/dhcp6host-routed-network.xml
> @@ -21,4 +21,6 @@
> <host id='0:1:0:1:18:aa:62:fe:0:16:3e:44:55:66' name='badbob' ip='2001:db8:ac10:fd01::1:24' />
> </dhcp>
> </ip>
> + <route address='192.168.222.0' netmask='255.255.255.0' gateway='192.168.122.10'/>
> + <route family='ipv6' address='2001:db8:ac10:fc00::' prefix='64' gateway='2001:db8:ac10:fd01::1:24'/>
> </network>
ACK with the above nits fixed. I have fixed them all (see attached
interdiff) and pushed.
Thanks for the contribution!
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-squash-into-network-route-patches.patch
Type: text/x-patch
Size: 13998 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20130513/93ca5f7f/attachment-0001.bin>
More information about the libvir-list
mailing list