secchan: Fix OFPPS_LINK_DOWN detection.
authorBen Pfaff <blp@nicira.com>
Wed, 8 Apr 2009 17:36:59 +0000 (10:36 -0700)
committerBen Pfaff <blp@nicira.com>
Wed, 8 Apr 2009 17:36:59 +0000 (10:36 -0700)
netdev_get_flags() was supposed to return NETDEV_CARRIER if carrier was
detected by the network device PHY, by checking for the IFF_LOWER_UP bit
in the device flags returned by the SIOCGIFFLAGS ioctl.  Unfortunately,
IFF_LOWER_UP has value 0x10000 and that ioctl returns a short int, so this
bit was always read as 0, indicating that carrier was off.

There are at least two other ways to get the carrier status.  One is via
rtnetlink with RTM_GETLINK.  Unfortunately that is only supported on Linux
2.6.19 and up.  So we fall back to the other possibility, which is
/sys/net/class/<device>/carrier.  I hope that our users mount sysfs.

lib/netdev.c
lib/netdev.h
secchan/ofproto.c

index aa4c47ebdeb47a7262c0821608175d27b18baa86..f1dd5c49ba46f98e9555ff91c9113335d593ace7 100644 (file)
@@ -1121,6 +1121,56 @@ get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats)
     return ENODEV;
 }
 
+int
+netdev_get_carrier(const struct netdev *netdev, bool *carrier)
+{
+    char line[8];
+    int retval;
+    int error;
+    char *fn;
+    int fd;
+
+    *carrier = false;
+
+    fn = xasprintf("/sys/class/net/%s/carrier", netdev->name);
+    fd = open(fn, O_RDONLY);
+    if (fd < 0) {
+        error = errno;
+        VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(error));
+        goto exit;
+    }
+
+    retval = read(fd, line, sizeof line);
+    if (retval < 0) {
+        error = errno;
+        if (error == EINVAL) {
+            /* This is the normal return value when we try to check carrier if
+             * the network device is not up. */
+        } else {
+            VLOG_WARN_RL(&rl, "%s: read failed: %s", fn, strerror(error));
+        }
+        goto exit_close;
+    } else if (retval == 0) {
+        error = EPROTO;
+        VLOG_WARN_RL(&rl, "%s: unexpected end of file", fn);
+        goto exit_close;
+    }
+
+    if (line[0] != '0' && line[0] != '1') {
+        error = EPROTO;
+        VLOG_WARN_RL(&rl, "%s: value is %c (expected 0 or 1)", fn, line[0]);
+        goto exit_close;
+    }
+    *carrier = line[0] != '0';
+    error = 0;
+
+exit_close:
+    close(fd);
+exit:
+    free(fn);
+    return error;
+}
+
 int
 netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
 {
@@ -1244,9 +1294,6 @@ netdev_nodev_get_flags(const char *netdev_name, enum netdev_flags *flagsp)
     if (flags & IFF_PROMISC) {
         *flagsp |= NETDEV_PROMISC;
     }
-    if (flags & IFF_LOWER_UP) {
-        *flagsp |= NETDEV_CARRIER;
-    }
     return 0;
 }
 
index 2e69e8aec712fce72e26a78016c9a792c30f7e85..ca45c97317f99048df2893de1d4b0a75a09e0ba3 100644 (file)
@@ -51,8 +51,7 @@ struct svec;
 
 enum netdev_flags {
     NETDEV_UP = 0x0001,         /* Device enabled? */
-    NETDEV_PROMISC = 0x0002,    /* Promiscuous mode? */
-    NETDEV_CARRIER = 0x0004     /* Carrier detected? */
+    NETDEV_PROMISC = 0x0002     /* Promiscuous mode? */
 };
 
 enum netdev_pseudo_ethertype {
@@ -117,6 +116,7 @@ int netdev_set_flags(struct netdev *, enum netdev_flags, bool permanent);
 int netdev_turn_flags_on(struct netdev *, enum netdev_flags, bool permanent);
 int netdev_turn_flags_off(struct netdev *, enum netdev_flags, bool permanent);
 int netdev_arp_lookup(const struct netdev *, uint32_t ip, uint8_t mac[6]);
+int netdev_get_carrier(const struct netdev *, bool *carrier);
 int netdev_get_stats(const struct netdev *, struct netdev_stats *);
 int netdev_set_policing(struct netdev *, uint32_t kbits_rate, 
                         uint32_t kbits_burst);
index e11eed2a115eb7b60af7d4dc489af9d48c9dcfc9..6a5ef79b583bfa26d8712837b3f1b80d7885be5e 100644 (file)
@@ -1093,6 +1093,7 @@ make_ofport(const struct odp_port *odp_port)
     enum netdev_flags flags;
     struct ofport *ofport;
     struct netdev *netdev;
+    bool carrier;
     int error;
 
     error = netdev_open(odp_port->devname, NETDEV_ETH_TYPE_NONE, &netdev);
@@ -1114,7 +1115,9 @@ make_ofport(const struct odp_port *odp_port)
 
     netdev_get_flags(netdev, &flags);
     ofport->opp.config = flags & NETDEV_UP ? 0 : OFPPC_PORT_DOWN;
-    ofport->opp.state = flags & NETDEV_CARRIER ? 0 : OFPPS_LINK_DOWN;
+
+    netdev_get_carrier(netdev, &carrier);
+    ofport->opp.state = carrier ? 0 : OFPPS_LINK_DOWN;
 
     netdev_get_features(netdev,
                         &ofport->opp.curr, &ofport->opp.advertised,