Search code examples
cnetwork-programmingioctltun

How to set IP address of tun device and set link UP through C program


I am writing a C program for tunnelling using Linux tun device. I am creating a tun device, setting its owner, group and persistence using ioctl() calls. However, for doing the tunnelling, I need to set ip address to the device and set the link up. I am currently doing this using

# ip addr add
# ip link set

I want to set the address, mask and set the link as up. Is there any way to do it through the program without invoking ip command?

I tried using ioctl() with SIOCSIFADDR command, but it doesn't work for tun device, and works for eth0. I get the error ioctl() - Invalid argument.

Following is the code which works on eth0 but not on something like tun2:

280 void setip (int fd) {
281 
282     struct ifreq ifr;
283     struct sockaddr_in addr;
284     int stat;
285     
286     memset(&ifr, 0, sizeof(ifr));
287     memset(&addr, 0, sizeof(addr));
288     strncpy(ifr.ifr_name, in.dev.device, IFNAMSIZ);     // device name: eg. tun2
289     
290     addr.sin_family = AF_INET;
291     stat = inet_pton(addr.sin_family, in.dev.ip_addr, &addr.sin_addr); // ip string
292     if (stat == 0)
293         raise_error("inet_pton() - invalid ip");
294     if (stat == -1)
295         raise_error("inet_pton() - invalid family");
296         
297     if (stat == 1);
298     else
299         raise_error("inet_pton()");
300         
301     ifr.ifr_addr = *(struct sockaddr *) &addr;
302     /* This is just to test if address conversion happened properly */
303     char buff[BUFF_SIZE];
304     char * foo;
305     foo = inet_ntop(AF_INET, &addr.sin_addr, buff, BUFF_SIZE);
306     if (foo == NULL)
307         raise_error("inet_ntop()");
308     else
309         printf("main = %s, addr = %s\n",in.dev.ip_addr, buff);
310         
311     if (ioctl(fd, SIOCSIFADDR, (caddr_t) &ifr) == -1)
312         raise_error("ioctl() - SIOCSIFADDR"); 
313 }

I searched for methods to do this for tun devices but could not find any. All the posts I got to use ip or ifconfig or something of the sort.

Is there any way to do this through the program? Thanks


Solution

  • I figured out the problem. In case of eth0, I am using a socket descriptor as the argument to ioctl(), in case of tun device, I am using tun device descriptor. I created a socket in case of tun device too and the ioctl() worked. Following is the code:

    280 void setip (int fd) {
    281 
    282     struct ifreq ifr;
    283     struct sockaddr_in addr;
    284     int stat, s;
    285 
    286     memset(&ifr, 0, sizeof(ifr));
    287     memset(&addr, 0, sizeof(addr));
    288     strncpy(ifr.ifr_name, in.dev.device, IFNAMSIZ);
    289 
    290     addr.sin_family = AF_INET;
    291     s = socket(addr.sin_family, SOCK_DGRAM, 0);
    292     stat = inet_pton(addr.sin_family, in.dev.ip_addr, &addr.sin_addr);
    293     if (stat == 0)
    294         raise_error("inet_pton() - invalid ip");
    295     if (stat == -1)
    296         raise_error("inet_pton() - invalid family");
    297     
    298     if (stat == 1);
    299     else
    300         raise_error("inet_pton()");
    301     
    302     ifr.ifr_addr = *(struct sockaddr *) &addr;
    303     /* This is just to test if address conversion happened properly */
    304     char buff[BUFF_SIZE];
    305     char * foo;
    306     foo = inet_ntop(AF_INET, &addr.sin_addr, buff, BUFF_SIZE);
    307     if (foo == NULL)
    308         raise_error("inet_ntop()");
    309     else
    310         printf("main = %s, addr = %s\n",in.dev.ip_addr, buff);
    311 
    312     //if (ioctl(fd, SIOCSIFADDR, (caddr_t) &ifr) == -1)
    313     if (ioctl(s, SIOCSIFADDR, (caddr_t) &ifr) == -1)
    314         raise_error("ioctl() - SIOCSIFADDR");
    315 }
    

    Thanks Giuseppe Pes for your reply :)