This is an old revision of the document!
Overview
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:
Flag/state bits
include/linux/netdevice.h
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,
};
include/uapi/linux/if.h
/* 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,
};
net/core/dev.c
/**
* 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;
}
Testing under sysfs
Unplugging and plugging:
$ cat carrier operstate 1 up $ cat carrier operstate 0 down $ cat carrier operstate 1 up $
netif* routines [netdevice.h]
netif_running()
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.
netif_oper_up()
This is the important one related to plugging and unplugging:
/**
* 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 */);
}
netif_carrier_ok()
/**
* 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);
}
Where is IF_OPER_DOWN set?
Philosophy
Clearly(?) based on presence or absence of carrier (see tests above).