Linux ifIndex persistence for SNMP

We are using OpManager to montior interface devices on remote linux boxes. These boxes have VLANs that we use to collect information about the networks that are being trunked to the boxes. For example we have eth0.2, eth0.3, eth0.12, eth0.13, eth0.22, eth0.23 (corrisponding to vlans 2,3,12,13,22,23 respectively).

We are using SNMP on a management IP address to check and make sure the interfaces are up. However, we run into an issue with the interface indexes changing if we have to restart the networking service. We will get error messages like:

Interface 'eth0.23 - eth0.23' is shutdown. Interface description is 'eth0.23' and index is 11. Circuit ID not configured.

Even though the interface is up and running.

How can we change the index values to be persistent through reboots. We have seen this if we change an interface from static IP to dynamic as well.


Solution 1:

Short answer, the SNMP RFCs do not require ifIndex persistence between network manager reinitialization. net-snmp does not provide any special facility to provide this functionality.

From RFC 2863:

The requirement for constancy (between re-initializations) of an interface's ifIndex value is met by requiring that after an interface is dynamically removed, its ifIndex value is not re-used by a different dynamically added interface until after the following re-initialization of the network management system.

The takeaway is that ifIndex entries are explicitly allowed to be used for any interface when the system is re-initialized (i.e. rebooted).

From the Linux kernel (net/core/dev.c):

static int dev_new_index(struct net *net)
{
    static int ifindex;
    for (;;) {
        if (++ifindex <= 0)
            ifindex = 1;
        if (!__dev_get_by_index(net, ifindex))
            return ifindex;
    }
}

The ifindex allocation within the kernel uses a simple incrementation algorithm. This is relevant because in net-snmp (agent/mibgroup/if-mib/data_access/interface_ioctl.c):

oid
netsnmp_access_interface_ioctl_ifindex_get(int fd, const char *name)
{
#ifndef SIOCGIFINDEX
    return 0;
#else
    struct ifreq    ifrq;
    int rc = 0;

    DEBUGMSGTL(("access:interface:ioctl", "ifindex_get\n"));

    rc = _ioctl_get(fd, SIOCGIFINDEX, &ifrq, name);
    if (rc < 0) {
        DEBUGMSGTL(("access:interface:ioctl",
                   "ifindex_get error on inerface '%s'\n", name));
        return 0;
    }

    return ifrq.ifr_ifindex;
#endif /* SIOCGIFINDEX */
}

This function ends up being called to populate the ifindex, and it simply uses the IOCTL interface to retreive the SIOCGIFINDEX value from the Linux kernel.

When I ran into a problem of this nature with an SNMP based monitoring system, I ended up using an alternative means to reference the network interfaces. In particular, I used the interface name instead of the interface index number (i.e. "eth0", "eth1", "vlan150", etc.).

Solution 2:

You can think of using if_indextoname(index_val, index_name). Where index_val is of unsigned int type and index_name is of char * type.

Pass the index_val and linux kernel will map it to correct index_name which you can use in your code as index_name is same across reboots.