X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetdev-linux.c;fp=lib%2Fnetdev-linux.c;h=11d83e9716e559701de5e477a9259d1fbe9b7571;hb=f1acd62b54376a425a975f9af501c4c8c5689b39;hp=3e34044476be996653c16ceec841c0fc70dae9b9;hpb=2c7807ac4f578bfdd7a46b79028935c9aa34cde3;p=sliver-openvswitch.git diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 3e3404447..11d83e971 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -96,7 +96,7 @@ struct netdev_linux_cache { int ifindex; uint8_t etheraddr[ETH_ADDR_LEN]; - struct in_addr in4; + struct in_addr address, netmask; struct in6_addr in6; int mtu; int carrier; @@ -125,6 +125,8 @@ static int netdev_linux_do_ethtool(struct netdev *, struct ethtool_cmd *, int cmd, const char *cmd_name); static int netdev_linux_do_ioctl(const struct netdev *, struct ifreq *, int cmd, const char *cmd_name); +static int netdev_linux_get_ipv4(const struct netdev *, struct in_addr *, + int cmd, const char *cmd_name); static int get_flags(const struct netdev *, int *flagsp); static int set_flags(struct netdev *, int flags); static int do_get_ifindex(const char *netdev_name); @@ -935,49 +937,48 @@ netdev_linux_set_policing(struct netdev *netdev, return 0; } -/* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if - * 'in4' is non-null) and returns true. Otherwise, returns false. */ static int -netdev_linux_get_in4(const struct netdev *netdev_, struct in_addr *in4) +netdev_linux_get_in4(const struct netdev *netdev_, + struct in_addr *address, struct in_addr *netmask) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); if (!(netdev->cache->valid & VALID_IN4)) { - const struct sockaddr_in *sin; - struct ifreq ifr; int error; - ifr.ifr_addr.sa_family = AF_INET; - error = netdev_linux_do_ioctl(netdev_, &ifr, + error = netdev_linux_get_ipv4(netdev_, &netdev->cache->address, SIOCGIFADDR, "SIOCGIFADDR"); if (error) { return error; } - sin = (struct sockaddr_in *) &ifr.ifr_addr; - netdev->cache->in4 = sin->sin_addr; + error = netdev_linux_get_ipv4(netdev_, &netdev->cache->netmask, + SIOCGIFNETMASK, "SIOCGIFNETMASK"); + if (error) { + return error; + } + netdev->cache->valid |= VALID_IN4; } - *in4 = netdev->cache->in4; - return in4->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0; + *address = netdev->cache->address; + *netmask = netdev->cache->netmask; + return address->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0; } -/* Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask. If - * 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. Returns a - * positive errno value. */ static int -netdev_linux_set_in4(struct netdev *netdev_, struct in_addr addr, - struct in_addr mask) +netdev_linux_set_in4(struct netdev *netdev_, struct in_addr address, + struct in_addr netmask) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; - error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", addr); + error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", address); if (!error) { netdev->cache->valid |= VALID_IN4; - netdev->cache->in4 = addr; - if (addr.s_addr != INADDR_ANY) { + netdev->cache->address = address; + netdev->cache->netmask = netmask; + if (address.s_addr != INADDR_ANY) { error = do_set_addr(netdev_, SIOCSIFNETMASK, - "SIOCSIFNETMASK", mask); + "SIOCSIFNETMASK", netmask); } } return error; @@ -1076,6 +1077,67 @@ netdev_linux_add_router(struct netdev *netdev UNUSED, struct in_addr router) return error; } +static int +netdev_linux_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; +} + /* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be * successfully retrieved, it stores the corresponding MAC address in 'mac' and * returns 0. Otherwise, it returns a positive errno value; in particular, @@ -1269,6 +1331,7 @@ const struct netdev_class netdev_linux_class = { netdev_linux_set_in4, netdev_linux_get_in6, netdev_linux_add_router, + netdev_linux_get_next_hop, netdev_linux_arp_lookup, netdev_linux_update_flags, @@ -1312,6 +1375,7 @@ const struct netdev_class netdev_tap_class = { netdev_linux_set_in4, netdev_linux_get_in6, netdev_linux_add_router, + netdev_linux_get_next_hop, netdev_linux_arp_lookup, netdev_linux_update_flags, @@ -1591,3 +1655,19 @@ netdev_linux_do_ioctl(const struct netdev *netdev, struct ifreq *ifr, } return 0; } + +static int +netdev_linux_get_ipv4(const struct netdev *netdev, struct in_addr *ip, + int cmd, const char *cmd_name) +{ + struct ifreq ifr; + int error; + + ifr.ifr_addr.sa_family = AF_INET; + error = netdev_linux_do_ioctl(netdev, &ifr, cmd, cmd_name); + if (!error) { + const struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; + *ip = sin->sin_addr; + } + return error; +}