Dissection of how operstate
works (RFC 2863).
3.1.14. IfOperStatus in an Interface Stack When an interface is a part of an interface-stack, but is not the lowest interface in the stack, then: (1) ifOperStatus has the value 'up' if it is able to pass packets due to one or more interfaces below it in the stack being 'up', irrespective of whether other interfaces below it are 'down', ' dormant', 'notPresent', 'lowerLayerDown', 'unknown' or ' testing'. (2) ifOperStatus may have the value 'up' or 'dormant' if one or more interfaces below it in the stack are 'dormant', and all others below it are either 'down', 'dormant', 'notPresent', ' lowerLayerDown', 'unknown' or 'testing'. (3) ifOperStatus has the value 'lowerLayerDown' while all interfaces below it in the stack are either 'down', ' notPresent', 'lowerLayerDown', or 'testing'.
Links:
Among many other fields:
* @state: Generic network queuing layer state, see netdev_state_t * @operstate: RFC2863 operstate * @flags: Interface flags (a la BSD) struct net_device { unsigned long state; unsigned char operstate; unsigned int flags; };
Here are the possible values of net_device->state
:
/* These flag bits are private to the generic network queueing * layer; they may not be explicitly referenced by any other * code. */ enum netdev_state_t { __LINK_STATE_START, __LINK_STATE_PRESENT, __LINK_STATE_NOCARRIER, __LINK_STATE_LINKWATCH_PENDING, __LINK_STATE_DORMANT, };
Possible values for netdev->operstate
:
/* RFC 2863 operational status */ enum { IF_OPER_UNKNOWN, IF_OPER_NOTPRESENT, // currently unused IF_OPER_DOWN, IF_OPER_LOWERLAYERDOWN, IF_OPER_TESTING, // currently unused IF_OPER_DORMANT, IF_OPER_UP, };
/** * dev_get_flags - get flags reported to userspace * @dev: device * * Get the combination of flag bits exported through APIs to userspace. */ unsigned int dev_get_flags(const struct net_device *dev) { unsigned int flags; flags = (dev->flags & ~(IFF_PROMISC | IFF_ALLMULTI | IFF_RUNNING | IFF_LOWER_UP | IFF_DORMANT)) | (dev->gflags & (IFF_PROMISC | IFF_ALLMULTI)); if (netif_running(dev)) { if (netif_oper_up(dev)) flags |= IFF_RUNNING; if (netif_carrier_ok(dev)) flags |= IFF_LOWER_UP; if (netif_dormant(dev)) flags |= IFF_DORMANT; } return flags; }
Unplugging and plugging (administratively, the interface remains UP the whole time):
$ cat carrier operstate 1 up $ cat carrier operstate 0 down $ cat carrier operstate 1 up $
Based on private netdev_state_t
, administrative state:
/** * netif_running - test if up * @dev: network device * * Test if the device has been brought up. */ static inline bool netif_running(const struct net_device *dev) { return test_bit(__LINK_STATE_START, &dev->state); }
This should be set by dev_open()
, so it should be up regardless. See usage here.
This is the important one related to plugging and unplugging (ignore the possibility of IF_OPER_UNKNOWN
for now):
/** * netif_oper_up - test if device is operational * @dev: network device * * Check if carrier is operational */ static inline bool netif_oper_up(const struct net_device *dev) { return (dev->operstate == IF_OPER_UP || dev->operstate == IF_OPER_UNKNOWN /* backward compat */); }
This routine becomes important shortly:
/** * netif_carrier_ok - test if carrier present * @dev: network device * * Check if carrier is present on device */ static inline bool netif_carrier_ok(const struct net_device *dev) { return !test_bit(__LINK_STATE_NOCARRIER, &dev->state); }
Clearly(?) based on presence or absence of carrier (see tests above).
Consults netif_carrier_ok()
:
static ssize_t carrier_show(struct device *dev, struct device_attribute *attr, char *buf) { struct net_device *netdev = to_net_dev(dev); if (netif_running(netdev)) // if __LINK_STATE_START, which should be true return sprintf(buf, fmt_dec, !!netif_carrier_ok(netdev)); return -EINVAL; } static DEVICE_ATTR_RW(carrier);
defined as:
static inline bool netif_carrier_ok(const struct net_device *dev) { return !test_bit(__LINK_STATE_NOCARRIER, &dev->state); }
From net/core/link_watch.c
, it all comes back to netif_carrier_ok()
:
static unsigned char default_operstate(const struct net_device *dev) { if (!netif_carrier_ok(dev)) return (dev->ifindex != dev_get_iflink(dev) ? IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN); if (netif_dormant(dev)) return IF_OPER_DORMANT; return IF_OPER_UP; }
which brings us back to who sets __LINK_STATE_NOCARRIER
.
From net/sched/sch_generic.c
:
/** * netif_carrier_on - set carrier * @dev: network device * * Device has detected that carrier. */ void netif_carrier_on(struct net_device *dev) { if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) { if (dev->reg_state == NETREG_UNINITIALIZED) return; atomic_inc(&dev->carrier_up_count); linkwatch_fire_event(dev); if (netif_running(dev)) __netdev_watchdog_up(dev); } } EXPORT_SYMBOL(netif_carrier_on); /** * netif_carrier_off - clear carrier * @dev: network device * * Device has detected loss of carrier. */ void netif_carrier_off(struct net_device *dev) { if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) { if (dev->reg_state == NETREG_UNINITIALIZED) return; atomic_inc(&dev->carrier_down_count); linkwatch_fire_event(dev); } } EXPORT_SYMBOL(netif_carrier_off);
So who calls netif_carrier_off()
? Apparently, lots of people.