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 };
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
if (!attrs[IFLA_STATS]) {
VLOG_WARN_RL(&rl, "RTM_GETLINK reply lacks stats");
+ ofpbuf_delete(reply);
return EPROTO;
}
stats->tx_heartbeat_errors = rtnl_stats->tx_heartbeat_errors;
stats->tx_window_errors = rtnl_stats->tx_window_errors;
+ ofpbuf_delete(reply);
+
return 0;
}
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;
}
netdev_enumerate(&dev_list);
for (i=0; i<dev_list.n; i++) {
- if ((netdev_nodev_get_in4(dev_list.names[i], &dev_in4))
+ if ((netdev_nodev_get_in4(dev_list.names[i], &dev_in4, NULL))
&& (dev_in4.s_addr == in4->s_addr)) {
*netdev_name = xstrdup(dev_list.names[i]);
svec_destroy(&dev_list);
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'.