X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fnetdev.c;h=9a92a5f9724f72a5f3822f8876e8ecde0a0223ea;hb=7778bd15dacc1e410b60ff6ec2996c475a875e6e;hp=6644f7e82a5426f3e17102130dc2a7634744ff72;hpb=79c720a8302aafcd471b535833e88dae0e653671;p=openvswitch diff --git a/lib/netdev.c b/lib/netdev.c index 6644f7e8..9a92a5f9 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -572,7 +572,7 @@ netdev_recv(struct netdev *netdev, struct ofpbuf *buffer) if (n_bytes < 0) { if (errno != EAGAIN) { VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s", - strerror(errno), netdev->name); + netdev->name, strerror(errno)); } return errno; } else { @@ -781,10 +781,12 @@ netdev_set_advertisements(struct netdev *netdev, uint32_t advertise) return do_ethtool(netdev, &ecmd, ETHTOOL_SSET, "ETHTOOL_SSET"); } -/* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if - * 'in4' is non-null) and returns true. Otherwise, returns false. */ +/* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address + * and '*mask' to the netmask (if they are non-null) and returns true. + * Otherwise, returns false. */ bool -netdev_nodev_get_in4(const char *netdev_name, struct in_addr *in4) +netdev_nodev_get_in4(const char *netdev_name, struct in_addr *in4, + struct in_addr *mask) { struct ifreq ifr; struct in_addr ip = { INADDR_ANY }; @@ -804,13 +806,25 @@ netdev_nodev_get_in4(const char *netdev_name, struct in_addr *in4) if (in4) { *in4 = ip; } + + if (mask) { + if (ioctl(af_inet_sock, SIOCGIFNETMASK, &ifr) == 0) { + struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; + *mask = sin->sin_addr; + } else { + VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFNETMASK) failed: %s", + netdev_name, strerror(errno)); + } + } + return ip.s_addr != INADDR_ANY; } bool -netdev_get_in4(const struct netdev *netdev, struct in_addr *in4) +netdev_get_in4(const struct netdev *netdev, struct in_addr *in4, struct + in_addr *mask) { - return netdev_nodev_get_in4(netdev->name, in4); + return netdev_nodev_get_in4(netdev->name, in4, mask); } static void @@ -1309,7 +1323,7 @@ netdev_find_dev_by_in4(const struct in_addr *in4, char **netdev_name) struct svec dev_list; /* Check the hint first. */ - if (*netdev_name && (netdev_nodev_get_in4(*netdev_name, &dev_in4)) + if (*netdev_name && (netdev_nodev_get_in4(*netdev_name, &dev_in4, NULL)) && (dev_in4.s_addr == in4->s_addr)) { return true; } @@ -1319,7 +1333,7 @@ netdev_find_dev_by_in4(const struct in_addr *in4, char **netdev_name) netdev_enumerate(&dev_list); for (i=0; is_addr)) { *netdev_name = xstrdup(dev_list.names[i]); svec_destroy(&dev_list); @@ -1331,6 +1345,73 @@ netdev_find_dev_by_in4(const struct in_addr *in4, char **netdev_name) return false; } +/* Looks up the next hop for 'ip'. If the next hop can be found, the + * address is stored in 'next_hop'. If a gateway is not required to + * reach 'ip', zero is stored in 'next_hop'. In either case, zero is + * returned and a copy of the name of the device to reach 'ip' is stored + * in 'netdev_name', which the caller is responsible for freeing. If a + * route could not be determined, a positive errno is returned. */ +int +netdev_get_next_hop(const struct in_addr *host, struct in_addr *next_hop, + char **netdev_name) +{ + static const char fn[] = "/proc/net/route"; + FILE *stream; + char line[256]; + int ln; + + *netdev_name = NULL; + stream = fopen(fn, "r"); + if (stream == NULL) { + VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(errno)); + return errno; + } + + ln = 0; + while (fgets(line, sizeof line, stream)) { + if (++ln >= 2) { + char iface[17]; + uint32_t dest, gateway, mask; + int refcnt, metric, mtu; + unsigned int flags, use, window, irtt; + + if (sscanf(line, + "%16s %"SCNx32" %"SCNx32" %04X %d %u %d %"SCNx32 + " %d %u %u\n", + iface, &dest, &gateway, &flags, &refcnt, + &use, &metric, &mask, &mtu, &window, &irtt) != 11) { + + VLOG_WARN_RL(&rl, "%s: could not parse line %d: %s", + fn, ln, line); + continue; + } + if (!(flags & RTF_UP)) { + /* Skip routes that aren't up. */ + continue; + } + + /* The output of 'dest', 'mask', and 'gateway' were given in + * network byte order, so we don't need need any endian + * conversions here. */ + if ((dest & mask) == (host->s_addr & mask)) { + if (!gateway) { + /* The host is directly reachable. */ + next_hop->s_addr = 0; + } else { + /* To reach the host, we must go through a gateway. */ + next_hop->s_addr = gateway; + } + *netdev_name = xstrdup(iface); + fclose(stream); + return 0; + } + } + } + + fclose(stream); + return ENXIO; +} + /* Obtains the current flags for the network device named 'netdev_name' and * stores them into '*flagsp'. Returns 0 if successful, otherwise a positive * errno value. On error, stores 0 into '*flagsp'.