4

Within CentOS I use NetworkManager to handle my interfaces and network configuration. For a showcase, I need to quickly plug my network cable from port 1 to port 2 in my switch. Each switch port belongs to a different network. In each network, a separate DHCP server distributes IP addresses for its network. I should receive a new IP address whenever I plug my computer to a different port of the switch.

However, it takes about 5 seconds until IP addresses are removed from my interface when unplugging the cable. Hence, when quickly re-plugging my computer to a different port, the old IP address has not been removed yet, and no new one is assigned.

My first attempt was to use a custom script in /etc/NetworkManager/NetworkManager/dispatcher.d/ which flushes the IP addresses from an interface. Unfortunately, my script is called not until the ~5 seconds timeout has passed.

Is it possible to configure NetworkManager to flush IP address faster? If yes, how? If not with NetworkManager, is there another way I can get rid of IP addresses quickly?

Also, does anyone know what component of the network stack is responsible for this timeout? Is it possible to decrease it?

  • You don't have GATEWAY_PING_TIMEOUT set in your ifcfg files do you? – Raman Sailopal Oct 17 '17 at 11:59
  • @RamanSailopal no, I haven't set that. – Max Oct 17 '17 at 15:01
3

NM ignores the carrier-lost signal for 5 seconds, before deactivating the device. The timeout is currently not configurable (and in some cases the timeout is even extended to up to 10 seconds). It does so, because sometimes carrier can go away for a moment, for example when changing the MTU or because the switch decides to.

I am not aware of a simple solution, if you want to replug the cable quicker then 10 seconds. Of course, you can issue a nmcli device reapply "$DEV" or nmcli connection up "$CON", but that requires an additional manual intervention by you.

  • thanks for reply, thaller. I came up with a workaround managing my interface myself using a small program written in C. – Max Oct 19 '17 at 14:50
  • @Mac If NM wasn't a requirement then you could've just used ifplugd. – Duncan X Simpson Oct 21 '17 at 13:52
  • @DuncanXSimpson ya. NM is no requirement. Though that was clear from the question. Thanks for the hint. – Max Oct 23 '17 at 11:37
0

I am now using a service written in C to manage the interface myself. Its basically a wrapper for dhclient and ip. This is the code I am using to manage my interface:

#include <stdio.h>
#include <string.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/if.h>
#include <linux/rtnetlink.h>
#include <stdlib.h>
#include <stdbool.h>

#define ENTRY(x) {x, #x}
struct {
    unsigned flag;
    const char *name;
} ifi_flag_map[] = {
        ENTRY(IFF_UP),
        ENTRY(IFF_BROADCAST),
        ENTRY(IFF_DEBUG),
        ENTRY(IFF_LOOPBACK),
        ENTRY(IFF_POINTOPOINT),
        ENTRY(IFF_NOTRAILERS),
        ENTRY(IFF_RUNNING),
        ENTRY(IFF_NOARP),
        ENTRY(IFF_PROMISC),
        ENTRY(IFF_ALLMULTI),
        ENTRY(IFF_MASTER),
        ENTRY(IFF_SLAVE),
        ENTRY(IFF_MULTICAST),
        ENTRY(IFF_PORTSEL),
        ENTRY(IFF_AUTOMEDIA),
        ENTRY(IFF_DYNAMIC),
        ENTRY(IFF_LOWER_UP),
        ENTRY(IFF_DORMANT),
        ENTRY(IFF_ECHO),
};

int InterfaceIndex;
char InterfaceName[32];
char FlushCommand[64];
char DHCPCommand[64];

void check_flags(unsigned flags)
{
    size_t i;
    bool hasFlagLowerUp = false;

    for (i = 0; i < sizeof ifi_flag_map/sizeof ifi_flag_map[0]; i++) {
        if (flags & ifi_flag_map[i].flag) {
            if (ifi_flag_map[i].name == "IFF_LOWER_UP"){
                hasFlagLowerUp = true;
            }
        }
    }
    if (!hasFlagLowerUp){
        printf("Cable removed. Going to flush IPs now...\n");
        system(FlushCommand);
    } else {
        printf("Flag IFF_LOWER_UP present. Going to call %s.\n", DHCPCommand);
        system(DHCPCommand);
    }
}

void read_msg(int fd)
{
    int len;
    char buf[4096];
    struct iovec iov = { buf, sizeof(buf) };
    struct sockaddr_nl sa;
    struct msghdr msg = { (void *)&sa, sizeof(sa), &iov, 1, NULL, 0, 0 };
    struct nlmsghdr *nh;

    len = recvmsg(fd, &msg, 0);
    if(len == -1) {
        perror("recvmsg");
        return;
    }

    for (nh = (struct nlmsghdr *) buf; NLMSG_OK (nh, len);
         nh = NLMSG_NEXT (nh, len)) {
        struct ifinfomsg *ifimsg;

        if (nh->nlmsg_type == NLMSG_DONE)
            return;

        if (nh->nlmsg_type == NLMSG_ERROR) {
            continue;
        }

        ifimsg = NLMSG_DATA(nh);

        printf("Noticed event on interface with id %u\n", ifimsg->ifi_index);
        if (ifimsg->ifi_index == InterfaceIndex) {
            printf("Interface is supervised interface %s. Going to check if cable was removed...\n",InterfaceName);
            check_flags(ifimsg->ifi_flags);
        }

    }
}

int main(int argc, char *argv[])
{
    if (argc < 3){
        printf("Please provide interface id as first argument\n");
        printf("Please provide interface name as second argument\n\n");
        printf("Example: %s 2 eth0\n", argv[0]);
        return 1;
    }
    if (sscanf (argv[1], "%i", &InterfaceIndex) != 1) {
        fprintf(stderr, "Interface index must be integer\n");
        return 1;
    }
    strcpy(InterfaceName,argv[2]);
    sprintf(FlushCommand, "ip addr flush dev %s", InterfaceName);
    sprintf(DHCPCommand, "dhclient -r %s && dhclient %s", InterfaceName, InterfaceName);

    printf("Watching interface %s with id %u for events.\n", InterfaceName, InterfaceIndex);
    printf("Will flush IPs from interface %s when cable is removed.\n", InterfaceName);

    struct sockaddr_nl sa;
    int fd;

    memset(&sa, 0, sizeof(sa));
    sa.nl_family = AF_NETLINK;
    sa.nl_groups = RTMGRP_LINK;

    fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if(fd == -1) {
        perror("socket");
        return 1;
    }

    if(bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
        perror("bind");
        return 1;
    }
    for(;;) {
        read_msg(fd);
    }

    return 0;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.