Antoine Bourgeois
Antoine Bourgeois

Reputation: 11

Setting an IP address to a TUN in C

I'm trying to create a tunnel in C and im struggling to set its IP address up. I have an "invalid argument" error in ioctl with SIOCSIFADDR argument. Can someone explain me how to set up the IP address if this function doesn't work with tun ?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if_tun.h>
#include <linux/if.h>
#include <fcntl.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>


#define IP_DEST "127.0.0.1"
#define IP_SOURCE "127.0.0.1"



int tun_alloc(char* dev) {
    //dev will store the name of the tun created

    struct ifreq ifr;
    int fd, err;
    char *clonedev = "/dev/net/tun";

    //open the clone device 
    if( (fd = open(clonedev, O_RDWR)) < 0 ) {
        printf("Error opening directory");
        return fd;
    }

    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = IFF_TUN;  
    if (*dev) {
     strncpy(ifr.ifr_name, dev, IFNAMSIZ);
   }

    //Create the TUN
    if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
        close(fd);
        printf("Error creating the tun");
        return err;
    }
    strcpy(dev, ifr.ifr_name);

    return fd;
}

int main(int argc, char* argv[]) {

    //Creating the TUN interface
    char tun_name[IFNAMSIZ];
    strncpy(tun_name, "tun3", IFNAMSIZ);

    int tunfd = tun_alloc(tun_name);
    if (tunfd < 0) {
        perror("tun_create");
        return 1;
    }
    printf("TUN interface %s created\n", tun_name);

    //Setting its IP Adress
    struct ifreq ifr;
    struct sockaddr_in addr;

    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, tun_name, IFNAMSIZ);

    addr.sin_family = AF_INET;
    if(inet_pton(AF_INET,IP_DEST,&(addr.sin_addr))<0){
        fprintf(stderr,"ERROR with the IP address");
        return 1;
    };

    memcpy(&(ifr.ifr_addr), &addr, sizeof (struct sockaddr));
    if (ioctl(tunfd, SIOCSIFADDR, &ifr) < 0) {
        perror("ioctl");
        exit(1);
    }
    printf("TUN interface %s set IP address to %s\n", tun_name, IP_DEST);

    ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
    if(ioctl(tunfd, SIOCSIFFLAGS, &ifr)<0){
        perror("ioctl");
        exit(1);
    };
    printf("TUN running");
    return 0;
}

I don't really understand how iotcl works and the documentation hasn't helped me for tun interfaces.

Upvotes: 1

Views: 564

Answers (1)

The SIOCSIFADDR and SIOCSIFFLAGS (and other SIOwhatever) ioctl calls should be made towards any AF_INET socket, not towards the tun interface itself. The design is so that you can use them to configure any interface, not only the one you created. You have to create an AF_INET socket and then call ioctl on that socket.

If you think it's a bit weird that you have to make a socket, you're right. ioctl is used to make "special requests" about files, other than read or write. The request is interpreted depending on the type of file. Using a socket makes sure it is interpreted by the IP networking system as a request relating to IP sockets. It would make logical sense that you could also make IP-related special requests on tun interfaces, but apparently the kernel developers didn't think of that, and there is no need for them to work since you can just create a socket.

There is another way to set IP addresses using a system called "rtnetlink", but it is more complicated and unnecessary for a simple scenario like this.

Upvotes: 1

Related Questions