使用ioctl

时间:2017-04-17 21:15:05

标签: c++ linux ioctl

问题:

是否有办法在不影响网络接口的其他部分的情况下使用ioctl更改仅限所需的接口组件

推理

我正在编写一个C ++程序,允许用户在Linux机器上相互独立地更改IP地址,广播地址,网络掩码和默认网关。我为IP,Bcast和NMask解决方案修改了this code。但是,使用ioctl更改IP地址会自动修改我的广播/网络掩码,并清除内核IP路由表。

这是一个例子。在运行下面的代码之前,这是ifconfigroute -n

的结果

enter image description here

这是修改IP地址的代码的功能版本:

#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;
}

这是运行上述程序后生成的网卡状态:

enter image description here

如您所见,IP地址已根据需要进行了修改,但界面的其余部分已更改(这是不可取的)。

我无法在互联网或 netdevice man page上找到任何关于阻止ioctl自动修改网络接口其他部分的内容。

我知道我可以设置struct ifreq的值,因此 Bcast Mask 组件不会更改,但我希望能够修改每个组件单独而不用担心其他组件的价值。我特别不想跟踪默认网关,每次更改IP地址时都必须添加它。

更新

在进行了更多测试和研究后,我发现在运行系统命令ifconfigip时仍会出现此问题。例如,如果不是运行上面的代码,而是运行ifconfig eth0 10.10.2.59,结果是相同的。

使用ip命令有些不同,因为更改IP地址需要运行ip addr del 10.10.2.58/16 dev eth0 && ip addr add 10.10.2.59/16 dev eth0。因此,您删除已知的地址/网络掩码组合并添加另一个。由于未指定广播地址,因此将其设置为0.0.0.0。但是,此命令仍会从路由表中删除默认网关。

1 个答案:

答案 0 :(得分:0)

似乎(通过实验)在ioctl标志中它们如何影响网络接口的其余部分存在某种形式的优先排序:

  • SIOCSIFADDR 强制重置给定网络接口的网络掩码,广播地址和路由表条目
  • SIOCSIFNETMASK 强制重置广播地址和路由表
  • SIOCSIFBRDADDR 强制仅重置路由表

这意味着运行ioctl调用的顺序很重要 - 首先必须设置IP,然后是子网掩码,然后是广播地址,然后是任何路由。否则ioctl会自动覆盖您之前所做的更改。

因此,我最终必须跟踪仅更改部分网络接口时受影响的所有子组件。

例如,如果要更改子网掩码,我首先要读取旧的广播地址(使用 SIOCGIFBRDADDR 标志进行ioctl调用)和默认网关(请参阅{{3以编程方式阅读并存储它们。然后在使用ioctl更改子网掩码后,我重新分配了广播地址和默认网关(按此顺序),并且用户看起来只更改了子网掩码。

这并没有完全回答原始问题,但我找不到任何其他方法只修改网络接口的一个组件而不影响其他组件。如果有人找到更好的方法,我会很高兴知道。