Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set IPv4 netmask programmatically on macOS

On macOS, I have a utun interface that I've created. I'd like to, in C, assign it an IPv4 address and netmask. The former I can do via the SIOCSIFADDR ioctl command. However, I'm having some trouble with setting the netmask. On Linux, I'd do

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
}

My problem is, though sys/sockio.h on my Macbook defines the value SIOCSIFNETMASK, struct ifreq doesn't appear to have any netmask field. net/if.h shows me

struct ifreq {
    char ifr_name[IFNAMESIZ];
    union {
        struct sockaddr ifru_addr;
        struct sockaddr ifru_dstaddr;
        struct sockaddr ifru_broadaddr;
        short ifru_flags;
        ...

On Linux, it looks like

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;
        ...

Now, it occurred to me that this is just a union and it shouldn't matter that ifru_netmask isn't defined on macOS. I can just set the netmask value in ifru_addr. I tried that and, while the SIOCSIFNETMASK command succeeded, ifconfig shows that the device has its default netmask of 0xff000000:

utun5: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
    inet 1.2.3.4 --> 0.0.0.0 netmask 0xff000000

Am I missing something obvious?

Here's my 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;
}
like image 581
Daniel Walker Avatar asked Sep 12 '26 09:09

Daniel Walker


1 Answers

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

should be using a "valid" address, i.e.

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 
like image 183
Darius Avatar answered Sep 14 '26 01:09

Darius



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!