From 5ec8d678e6adf7743f9510938680cb8f656a50b2 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 8 Apr 2009 10:36:59 -0700 Subject: [PATCH] secchan: Fix OFPPS_LINK_DOWN detection. 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//carrier. I hope that our users mount sysfs. --- lib/netdev.c | 53 ++++++++++++++++++++++++++++++++++++++++++++--- lib/netdev.h | 4 ++-- secchan/ofproto.c | 5 ++++- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/lib/netdev.c b/lib/netdev.c index aa4c47eb..f1dd5c49 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -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; } diff --git a/lib/netdev.h b/lib/netdev.h index 2e69e8ae..ca45c973 100644 --- a/lib/netdev.h +++ b/lib/netdev.h @@ -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); diff --git a/secchan/ofproto.c b/secchan/ofproto.c index e11eed2a..6a5ef79b 100644 --- a/secchan/ofproto.c +++ b/secchan/ofproto.c @@ -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, -- 2.30.2