secchan: Better tolerate failing controller admission control in fail-open.
[openvswitch] / lib / netdev.c
index 6644f7e82a5426f3e17102130dc2a7634744ff72..9a92a5f9724f72a5f3822f8876e8ecde0a0223ea 100644 (file)
@@ -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; 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);
@@ -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'.