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 directly
+ reachable from the virtualization host, but *are* reachable from
+ a guest domain that is itself reachable from the
+ 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 network
+ interface with no IPv4 or IPv6 addresses. Such networks are
+ useful to provide host connectivity to networks which are only
+ reachable via a guest. A guest with connectivity both to the
+ guest-only network and to another network that is directly
+ reachable from the host can act as a gateway between the
+ networks. A static route added to the "host-visible" network
+ definition 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 particular route would *not* be preferred if there was
+ another existing rout on the system with the same address and
+ prefix but with a lower value for the metric. If there is a
+ route in the host system configuration that should be overriden
+ by a route in a virtual network whenever the virtual network is
+ running, the configuration for the system-defined route should
+ be modified to have a higher metric, and the route on the
+ virtual network given a lower metric (for example, the default
+ metric of "1").
+ </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>
</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>
</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
+ directly visible on the bridge interface but there will be 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.6</span>
+ </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>
VIR_FREE(def->bootfile);
}
+static void
+virNetworkRouteDefClear(virNetworkRouteDefPtr def)
+{
+ VIR_FREE(def->family);
+}
+
static void
virNetworkDNSTxtDefClear(virNetworkDNSTxtDefPtr 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]);
}
return result;
}
+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;
+ }
+ if (metricRc == 0) {
+ def->has_metric = true;
+ if (metric == 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid metric value, must be > 0 "
+ "in route definition of network '%s'"),
+ networkName);
+ goto cleanup;
+ }
+ }
+ 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_XML_ERROR,
+ def->family == NULL ?
+ _("No family specified for non-IPv4 address '%s' "
+ "in route definition of network '%s'") :
+ _("IPv4 family specified for non-IPv4 address '%s' "
+ "in route definition of network '%s'"),
+ address, networkName);
+ goto cleanup;
+ }
+ if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->gateway, AF_INET)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ def->family == NULL ?
+ _("No family specified for non-IPv4 gateway '%s' "
+ "in route definition of network '%s'") :
+ _("IPv4 family specified for non-IPv4 gateway '%s' "
+ "in route definition of network '%s'"),
+ 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_XML_ERROR,
+ _("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_XML_ERROR,
+ _("Route definition '%s' cannot have both "
+ "a prefix and a netmask"),
+ networkName);
+ goto cleanup;
+ }
+ }
+ if (def->prefix > 32) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid prefix %u specified "
+ "in route definition of network '%s', "
+ "must be 0 - 32"),
+ def->prefix, networkName);
+ goto cleanup;
+ }
+ } else if (STREQ(def->family, "ipv6")) {
+ if (!VIR_SOCKET_ADDR_IS_FAMILY(&def->address, AF_INET6)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("ipv6 family specified for non-IPv6 address '%s' "
+ "in route definition of network '%s'"),
+ address, networkName);
+ goto cleanup;
+ }
+ if (netmask) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("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_XML_ERROR,
+ _("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_XML_ERROR,
+ _("Invalid prefix %u specified "
+ "in route definition of network '%s', "
+ "must be 0 - 128"),
+ def->prefix, networkName);
+ 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_INTERNAL_ERROR,
+ _("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_INTERNAL_ERROR,
+ _("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_XML_ERROR,
+ _("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,
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;
}
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;
+ }
+ }
+ }
+ VIR_FREE(routeNodes);
+
forwardNode = virXPathNode("./forward", ctxt);
if (forwardNode &&
virNetworkForwardDefParseXML(def->name, forwardNode, ctxt, &def->forward) < 0) {
return def;
error:
+ VIR_FREE(routeNodes);
VIR_FREE(stp);
virNetworkDefFree(def);
VIR_FREE(ipNodes);
return result;
}
+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);
+ }
+ virBufferAddLit(buf, "/>\n");
+
+ result = 0;
+error:
+ return result;
+}
+
static int
virPortGroupDefFormat(virBufferPtr buf,
const virPortGroupDefPtr def)
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;
return ret;
}
+/* add an IP address to a bridge */
static int
networkAddAddrToBridge(virNetworkObjPtr network,
virNetworkIpDefPtr ipdef)
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);
+ }
+
+ 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)
bool v4present = false, v6present = false;
virErrorPtr save_err = NULL;
virNetworkIpDefPtr ipdef;
+ virNetworkRouteDefPtr routedef;
char *macTapIfName = NULL;
int tapfd = -1;
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) {