0

Question:

Is there a way of using ioctl to change only a desired interface component without affecting the other parts of the network interface?

Reasoning

I'm writing a C++ program that allows a user to change the IP address, Broadcast address, Netmask, and Default Gateway independently of one another on a Linux machine. I modified this code for the IP, Bcast and NMask solution. However, changing the IP address with ioctl is automatically modifying my Broadcast/Netmask, and clearing the Kernel IP Routing table.

Here's an example. Before running the code below, this is the result of ifconfig and route -n:

enter image description here

This is a functional version of the code that modifies the IP address:

#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <string.h>
#include <iostream>

int main(int argc, const char *argv[]) {
    struct ifreq ifr;
    struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr;
    const char * name = "eth0";
    int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);

    strncpy(ifr.ifr_name, name, IFNAMSIZ);

    ifr.ifr_addr.sa_family = AF_INET;
    inet_pton(AF_INET, "10.10.2.59", &addr->sin_addr);
    if(ioctl(fd, SIOCSIFADDR, &ifr) < 0)
    {
        std::cout << "Failed to set IP: " << strerror(errno) << std::endl;
        return -1;
    }

    if(ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)
    {
        std::cout << "Failed to get flags: " << strerror(errno) << std::endl;
        return -2;
    }
    strncpy(ifr.ifr_name, name, IFNAMSIZ);
    ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);

    if(ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
    {
        std::cout << "Failed to set flags: " << strerror(errno) << std::endl;
        return -3;
    }

    return 0;
}

This is the resulting network card status after running the above program:

enter image description here

As you can see, the IP address was modified as desired, but the rest of the interface changed (which is not desirable).

I've been unable to find anything on the internet or the netdevice man page about preventing ioctl from automatically modifying other parts of the network interface.

I know I could set the values of struct ifreq so the Bcast and Mask components don't change, but I would prefer to be able to modify each component individually without worrying about the values of the other ones. I especially don't want to keep track of the default gateway and have to add it in every time I make a change to the IP address.

UPDATE

After doing more tests and research, I've found that this issue still occurs when running the system commands ifconfig or ip. For example, if instead of running the above code one ran ifconfig eth0 10.10.2.59 the result is the same.

Using the ip command is somewhat different, as changing the ip address requires running ip addr del 10.10.2.58/16 dev eth0 && ip addr add 10.10.2.59/16 dev eth0. Thus, you delete a known address/netmask combo and add another. Since the broadcast address was not specified, it is set to 0.0.0.0. However, this command still removes the default gateway from the routing table.

Community
  • 1
  • 1
adanmoran
  • 69
  • 4
  • Does this happen if you use standard utilities like `ifconfig` as well? – Barmar Apr 17 '17 at 21:48
  • 1
    Perhaps you should look at their source code and see how they do it. – Barmar Apr 18 '17 at 02:14
  • I've made an update to the post, it appears standard utilities do in fact cause the same issue. – adanmoran Apr 18 '17 at 11:54
  • I suspect you only lose your default gateway because it's on the same interface whose configuration you're changing. If you had two interfaces and changed a different one, I expect the default gw to be left alone. – Barmar Apr 18 '17 at 15:02
  • I suspected that's what was happening, but I still have no idea how to prevent it from being removed - if it's even possible to prevent that. – adanmoran Apr 18 '17 at 15:29

1 Answers1

1

It seems (through experimentation) that there is some form of prioritized ordering of ioctl flags in how they affect the rest of the network interface:

  • SIOCSIFADDR forces a reset of the Netmask, Broadcast Address, and Routing Table entries for the given network interface
  • SIOCSIFNETMASK forces a reset of the Broadcast Address and Routing Table
  • SIOCSIFBRDADDR forces a reset of only the Routing Table

The implication of this is that the order in which you run ioctl calls matters - first you must set IP, then Subnet Mask, then Broadcast Address, then any Routes. Otherwise ioctl automatically overwrites changes you made previously.

Because of that, I ended up having to keep track of all sub-components that were affected when changing only part of the network interface.

For example, if the subnet mask is going to be changed I first read the old Broadcast Address (an ioctl call with the SIOCGIFBRDADDR flag) and Default Gateway (see here on reading that programatically) and store them. Then after changing the subnet mask with ioctl, I re-assigned the Broadcast Address and Default Gateway (in that order) and it appears to the user that only the subnet mask was changed.

This doesn't exactly answer the original question, but I couldn't find any other way of modifying only one component of a network interface without affecting the others. If anyone finds a better way of doing it I would be very happy to know.

adanmoran
  • 69
  • 4