在 macOS 上以编程方式设置 IPv4 子网掩码

3
在macOS上,我有一个我创建的utun接口。我想用C语言为它分配一个IPv4地址和子网掩码。前者我可以通过SIOCSIFADDR ioctl命令来实现。然而,我在设置子网掩码方面遇到了一些问题。在Linux上,我会这样做。
struct sockaddr_in addr = {.sin_family = AF_INET};
struct ifreq ifr = {.ifr_name = "utun5"};

inet_pton(AF_INET, "255.255.255.0", &addr.sin_addr);
memcpy(&ifr.ifr_netmask, &addr, sizeof(addr));
if ( ioctl(sock, SIOCSIFNETMASK, &ifr) == -1 ) {
    // handle the error
}

我的问题是,尽管我的Macbook上的sys/sockio.h定义了值SIOCSIFNETMASK,但是struct ifreq似乎没有任何netmask字段。net/if.h向我展示了这一点。
struct ifreq {
    char ifr_name[IFNAMESIZ];
    union {
        struct sockaddr ifru_addr;
        struct sockaddr ifru_dstaddr;
        struct sockaddr ifru_broadaddr;
        short ifru_flags;
        ...

在Linux上,它看起来像这样
struct ifreq {
    union {
        char ifrn_name[IFNAMSIZ];
    } ifr_ifrn;
    union {
        struct sockaddr ifru_addr;
        struct sockaddr ifru_dstaddr;
        struct sockaddr ifru_broadaddr;
        struct sockaddr ifru_netmask;
        struct sockaddr ifru_hwaddr;
        ...

现在,我突然想到这只是一个联合体,而且在 macOS 上没有定义 ifru_netmask 也无关紧要。我可以在 ifru_addr 中设置子网掩码的值。我尝试过了,虽然 SIOCSIFNETMASK 命令成功执行,但是 ifconfig 显示设备仍然具有默认的子网掩码 0xff000000
utun5: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
    inet 1.2.3.4 --> 0.0.0.0 netmask 0xff000000

我是不是漏掉了什么显而易见的东西?
这是我的最小可复现示例(MRE):
#include <arpa/inet.h>
#include <net/if.h>
#include <net/if_utun.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/kern_control.h>
#include <sys/kern_event.h>
#include <sys/socket.h>
#include <unistd.h>

int
tun_device_create(char *if_name)
{
    int fd;
    struct ctl_info info = {0};
    struct sockaddr_ctl addr = {
        .sc_family = AF_SYSTEM, .sc_len = sizeof(struct sockaddr_ctl), .ss_sysaddr = AF_SYS_CONTROL};

    fd = socket(AF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
    if (fd < 0) {
        perror("socket (AF_SYSTEM)");
        return -1;
    }

    snprintf(info.ctl_name, sizeof(info.ctl_name), "%s", UTUN_CONTROL_NAME);
    if (ioctl(fd, CTLIOCGINFO, &info) == -1) {
        perror("ioctl (CTLIOCGINFO)");
        goto error;
    }
    addr.sc_id = info.ctl_id;

    if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
        perror("connect (AF_SYSTEM)");
        goto error;
    }

    if (getsockopt(fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, if_name, &(socklen_t){IFNAMSIZ}) != 0) {
        perror("getsockopt (SYSPROTO_CONTROL)");
        goto error;
    }

    return fd;

error:
    close(fd);
    return -1;
}

int
set_addr_and_bring_up(const char *if_name, const char *ipv4_address, const char *ipv4_netmask)
{
    int sock, ret = -1;
    struct ifreq ifr = {0};
    struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr;

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket");
        return -1;
    }

    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", if_name);

    addr->sin_family = AF_INET;
    inet_pton(AF_INET, ipv4_address, &addr->sin_addr);
    if (ioctl(sock, SIOCSIFADDR, &ifr) == -1) {
        perror("ioctl (SIOCSIFADDR)");
        goto done;
    }

    inet_pton(AF_INET, ipv4_netmask, &addr->sin_addr);
    if (ioctl(sock, SIOCSIFNETMASK, &ifr) == -1) {
        perror("ioctl (SIOCSIFNETMASK)");
        goto done;
    }

    ifr.ifr_flags = IFF_UP;
    if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) {
        perror("ioctl (SIOCSIFFLAGS)");
        goto done;
    }

    ret = 0;

done:
    close(sock);
    return ret;
}

int
main()
{
    int fd;
    char if_name[IFNAMSIZ];

    fd = tun_device_create(if_name);
    if (fd < 0) {
        return 1;
    }

    if (set_addr_and_bring_up(if_name, "1.2.3.4", "255.255.255.0") != 0) {
        close(fd);
        return 1;
    }

    printf("Tun device created: %s\n", if_name);
    pause();

    close(fd);
    return 0;
}

1
你能提供一个最简代码示例吗?这样有助于进行实验。 - undefined
@Darius,给你。 - undefined
1
我发现只有第一个八位组发生变化。也就是说你可以使用254.0.0.0252.0.0.0等等。 - undefined
也许这是因为1.0.0.0是一个A类网络。 - undefined
1个回答

1
if (set_addr_and_bring_up(if_name, "1.2.3.4", "255.255.255.0") != 0) {
    close(fd);
    return 1;
}

应该使用一个“有效”的地址,即。
if (set_addr_and_bring_up(if_name, "192.168.0.1", "255.255.255.0") != 0) {
    close(fd);
    return 1;
}
utun7: flags=8050<POINTOPOINT,RUNNING,MULTICAST> mtu 1500
    inet 192.168.0.1 --> 0.0.0.0 netmask 0xffffff00 

1
很奇怪,我无法指定比IPv4类更具体的子网。 - undefined
@DanielWalker 你是什么意思? - undefined
以1.0.0.0为例,这是一个8位网络。但是,如果它被分割成16位子网呢? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接