[libvirt] [PATCH v3] network: bridge: Don't start network if it collides with host routing
Jim Meyering
jim at meyering.net
Thu May 27 08:54:24 UTC 2010
Cole Robinson wrote:
> Fedora bug https://bugzilla.redhat.com/show_bug.cgi?id=235961
>
> If using the default virtual network, an easy way to lose guest network
> connectivity is to install libvirt inside the VM. The autostarted
> default network inside the guest collides with host virtual network
> routing. This is a long standing issue that has caused users quite a
> bit of pain and confusion.
>
> On network startup, parse /proc/net/route and compare the requested
> IP+netmask against host routing destinations: if any matches are found,
> refuse to start the network.
>
> v2: Drop sscanf, fix a comment typo, comment that function could use
> libnl instead of /proc
>
> v3: Consider route netmask. Compare binary data rather than convert to
> string.
...
> +static int networkCheckRouteCollision(virNetworkObjPtr network)
> +{
> + int ret = -1, len;
> + char *cur, *buf = NULL;
> + enum {MAX_ROUTE_SIZE = 1024*64};
> + struct in_addr inaddress, innetmask;
> + char netaddr[32];
> +
> + if (!network->def->ipAddress || !network->def->netmask)
> + return 0;
> +
> + if (inet_pton(AF_INET, network->def->ipAddress, &inaddress) <= 0) {
> + networkReportError(VIR_ERR_INTERNAL_ERROR,
> + _("cannot parse IP address '%s'"),
> + network->def->ipAddress);
> + goto error;
> + }
> + if (inet_pton(AF_INET, network->def->netmask, &innetmask) <= 0) {
> + networkReportError(VIR_ERR_INTERNAL_ERROR,
> + _("cannot parse netmask '%s'"),
> + network->def->netmask);
> + goto error;
> + }
> +
> + inaddress.s_addr &= innetmask.s_addr;
> + if (!inet_ntop(AF_INET, &inaddress, netaddr, sizeof(netaddr))) {
> + virReportSystemError(errno, "%s",
> + _("failed to format network address"));
> + goto error;
> + }
> +
> + /* Read whole routing table into memory */
> + if ((len = virFileReadAll(PROC_NET_ROUTE, MAX_ROUTE_SIZE, &buf)) < 0)
> + goto error;
> + /* Dropping the last character shouldn't hurt */
Hi Cole,
Not sure how much you want to invest in manual parsing,
but in case this code ends up staying with us for a while,
here are some suggestions.
Handle the case in which the file is empty.
This will appease static checkers like clang and coverity.
Either change the "< 0" test above to "<= 0"
or do e.g.,
if (len > 0)
> + buf[len-1] = '\0';
Future proof it by skipping that first line only
if it looks like the heading we see now:
if (STRPREFIX (buf, "Iface"))
> + /* First line is just headings, skip it */
> + cur = strchr(buf, '\n');
> +
> + while (cur) {
> + char *data[8];
> + char *dest, *iface, *mask;
> + unsigned int addr_val, mask_val;
> + int i;
It's slightly better to make "i" unsigned.
The following assumes no leading white space, which is currently true
at least on F13. Future/portability-proof it by skipping leading white space:
while (c_isblank (*cur))
cur++;
> + cur++;
> +
> + /* Delimit interface field */
> + for (i = 0; i < sizeof(data); ++i) {
Oops. Won't this make "i" iterate from 0 up to 31 or 63,
depending on sizeof char*?
You probably mean this:
for (i = 0; i < ARRAY_CARDINALITY(data); ++i) {
> + data[i] = cur;
Otherwise, this would overrun the 8-element "data" array
and clobber the stack.
> + /* Parse fields and delimit */
> + while(*cur > ' ') {
Using "> ' '" works as long as each line has 8 or more fields.
If there are fewer, it would treat the newline just like a regular field
separator and continue getting fields from the next line.
If you use c_isblank you have to handle end of line differently,
but that's a good thing.
How about using c_isblank here, too, rather than relying on
ASCII "space" being smaller than "interesting" byte codes?
On F13, the fields of /proc/net/route are TAB-delimited.
Besides, I think y
while (*cur && !c_isblank (*cur)) {
> + cur++;
> + }
> + *cur++ = '\0';
You'll want something here to keep "cur" from going more than 1 beyond
the end of the buffer, in case the last row has fewer than 8 columns.
> + }
> +
> + iface = data[0];
> + dest = data[1];
> + mask = data[7];
If a line had fewer than 8 columns, at least mask is not valid.
Similarly for dest if there are fewer than two columns.
> + if (virStrToLong_ui(dest, NULL, 16, &addr_val) < 0) {
> + networkReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Failed to convert network address %s"),
> + dest);
> + goto error;
> + }
More information about the libvir-list
mailing list