5

I am trying to write a tunneling program in C that will take UDP packets from a TUNTAP interface and send them to a serial interface.

What I do is allocate the interface from the clone device /dev/net/tun, turn it on and give it an ip address :

int tun_setup(char *dev, int flags) {

  struct sockaddr_in  my_addr;
  struct ifreq ifr;
  int fd, err;
  string clonedev = "/dev/net/tun";

  // Open clone device file descriptor
  if( (fd = open(clonedev.c_str() , O_RDWR)) < 0 ) {
    perror("Opening /dev/net/tun");
    return fd;
  }

  // Initialise interface parameters structure
  memset(&ifr, 0, sizeof(ifr));

  // Set up flags
  ifr.ifr_flags = flags;

  // Set up interface name
  if (*dev) {
    strncpy(ifr.ifr_name, dev, IFNAMSIZ);
  }

  // Put interface in TUN mode
  if( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) {
    perror("ioctl(TUNSETIFF)");
    close(fd);
    return err;
  }

  strcpy(dev, ifr.ifr_name);

  // Create a socket
  if ( (s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  perror("socket");
      exit(1);
  }

  // Get interface flags
  if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
    perror("cannot get interface flags");
    exit(1);
  }

  // Turn on interface
  ifr.ifr_flags |= IFF_UP;
  if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
    fprintf(stderr, "ifup: failed ");
    perror(ifr.ifr_name);
    exit(1);
  }

  // Set interface address
  bzero((char *) &my_addr, sizeof(my_addr));
  my_addr.sin_family = AF_INET;
  my_addr.sin_addr.s_addr = htonl(inet_network("192.168.2.1"));
  memcpy(&ifr.ifr_addr, &my_addr, sizeof(struct sockaddr));

  if (ioctl(s, SIOCSIFADDR, &ifr) < 0) {
    fprintf(stderr, "Cannot set IP address. ");
    perror(ifr.ifr_name);
    exit(1);
  }

  // Return interface file descriptor
  return fd;
}

Then I create a thread that will poll() on the file descriptor of the created interface and do read() + some other stuff when an event occurs.

void* tun_readThreadProc (void* param) {
  struct pollfd fds[1];
  int nread;
  unsigned char buffer[BUFFERSIZE];
  fds[0].fd = tun_fd;
  fds[0].events = POLLIN;

  printf("%s : Entered. tun_fd = %d \n",__FUNCTION__,tun_fd);

  for(;;)
  {
    printf("%s : Entered loop\n",__FUNCTION__);
    if((poll(fds, 1, -1)) == -1)
    {
      perror("poll");
      exit(1);
    }

    printf("%s : Poll sensed something\n",__FUNCTION__);

    if((nread = read(tun_fd, buffer, BUFFERSIZE)) < 0)
    {
      perror("read");
      close(tun_fd);
      exit(1);
    }

    printf("%s : Read something : %d bytes\n",__FUNCTION__,nread);
  }
  return 0;
}

On an other part of the program, I bind a UDP socket to the IP address of this TUNTAP interface.

void socketInit( void )
{
  int                 on = 1;
  struct sockaddr_in  my_addr;
  unsigned short DefaultPort = 47808;

  // Create a socket
  if ( (s1 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("socket");
    exit(1);
  }

  // Bind to it
  bzero((char *) &my_addr, sizeof(my_addr));
  my_addr.sin_family = AF_INET;
  my_addr.sin_addr.s_addr = htonl(inet_network("192.168.2.1"));
  my_addr.sin_port = htons(DefaultPort);

  if ( (bind(s, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) ) {
    perror("bind");
  }
  // Allow it to broadcast
  if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on)) < 0) {
    perror("setsockopt");
  }
}

In another function, I use sendto() to send packets with this socket. I am supposed to capture those packets with the poll() + read() thread and then send them on a serial port but poll() never captures events on the TUNTAP interface.

I can ping through this interface by using ping -I tun0 [some destination] (tun0 = name of the TUNTAP interface)

But if I use ping -I 192.168.2.1 [some destination] (192.168.2.1 = TUNTAP interface address) it goes through the default interface (eth0, the physical NIC).

I was able to verify that with Wireshark.

This is most probably an ip route configuration problem...

I would be really glad if anybody can help me.

  • Socket creation code here : http://pastebin.com/raw.php?i=JZLKkuxS – Alex On The Moon Feb 21 '12 at 08:13
  • What do you mean by UDP packets "on this interface"? Do you mean to destinations routed through that interface? – David Schwartz Feb 21 '12 at 08:17
  • I added your code from pastebin here, it's easier to read through (And if the FBI closes pastebin for helping people too much, StackOverfow will still be able to live :P !) – Eregrith Feb 21 '12 at 08:21
  • @Eregrith Thx, though I don't see anything yet – Alex On The Moon Feb 21 '12 at 08:24
  • @Eregrith Just approved your changes, thx for your help : ) – Alex On The Moon Feb 21 '12 at 08:28
  • @AlexOnTheMoon I had to correct your indentation again, please be careful on what you post. SO is not a "paste bin". Also, you should ___really___ break down your code in functions, and avoid preprocessor in the middle of them. – Eregrith Feb 21 '12 at 09:38
  • @Eregrith I am currently rewriting this part of the code into a separate program to try to isolate the problem. I will replace the current code with the new one when its ready. I removed the preprocessor stuff already in my new code. – Alex On The Moon Feb 21 '12 at 10:09
  • To isolate the problem can you try sending UDP packet to the tun tap using sendip command. Its done as follows `sendip -v -p ipv4 -is <> -p udp -us <> -ud <> <>` Now let me know if your poll loop is working – snibu Feb 21 '12 at 14:18
  • @AlexOnTheMoon: If you think it would help to have an in-the-wild example of TAP/TUN code, take a look at the 'unix' port of [LwIP](http://savannah.nongnu.org/projects/lwip/) in the "contrib" package. I'm pretty sure it works with UDP. In fact, you might even try building their demo's and see if it will work with your interface. If not, it's probably an issue with your interface setup, and maybe not with your code. – Brian McFarland Feb 21 '12 at 17:08
  • @BrianMcFarland Thanks for pointing out LwIP to me. I'll try to build it and see how it behaves with my interface. – Alex On The Moon Feb 22 '12 at 05:20
  • @user718895 I will try this out and let you know. – Alex On The Moon Feb 22 '12 at 05:21

3 Answers3

2

How did you set the IP address of the tun tap interface? Using "ip addr add" command? Have you checked/configured the tun tap to be up ie "ip link set <<tun tap interface name>> up"? Lastly are you sure your UDP socket sendto code runs after the previous said two conditions are satisfied ie it has the correct ip address and tun tap interface is up.

Update

I think you are getting the concept wrong or I didnt understand you very well. AFAIK you dont need to do these many things. The concept of the tun tap device is that whatever packets the tun tap interface receives its sent to the userspace program and whatever the userspace program writes to the tun tap device, its sent to the network. Having said this and reading that your requirement is to tunnel the UDP packets to the serial interface, what you should do is the following

1) Set the default route to the tun tap IP address. In this way all the packets will be got by userspace program. route add default gw 192.168.2.1 tun0

So now in the userspace program you get the whole packet with the IP and UDP headers. Now this whole packet will be the message that you want to transfer via the serial interface. So when we send it via the serial interface using UDP or TCP(whichever you prefer), we are automatically again encapsulting the whole packet with one more UDP and IP header.

2) You also need to add a host rule with the serial interface address. In this way the above said encapsulated packets for the serial interface are sent to the serial interface and they dont come back again to the TUN TAP device due to the default rule. route add -host <<serial ip address>> dev <<serial device>>

Give route -n and check if the routing is right. Make sure that you dont have other unwanted routes. If yes delete them.

Note: Also you can extract just the payload from the packet and using RAW sockets create a custom UDP header with the destination as the serial interface's IP address and write using the TUN TAP device. This packet will be seen as the kernel routing instance as belonging to the serial interface and will be forwarded to there because of the host rule. If you are going for this case you need to create a do a custom IP and UDP header and also CRC calculation.

snibu
  • 581
  • 4
  • 16
  • I used ioctl to set the interface address when I open the udp socket : ioctl(portSocket, SIOCSIFADDR, &ifr) . I also turn it up with ioctl : ifr.IRFFLAGS |= IFF_UP; (ioctl(portSocket, SIOCSIFFLAGS, &ifr); So I'm pretty sure those steps are done before sending any packets. Anyways, I am rewriting the code for now and will update my question with this new code when it's ready. I hope it will help anybody (including me) with figuring out what's wrong. – Alex On The Moon Feb 22 '12 at 05:14
  • I Cannot use serial line IP (SLIP). I must capture the packets "manually" from the tun tap and then encapsulate them in a specific way related to the receiving device. I didn't talk about that here as it is not directly related to my problem. – Alex On The Moon Feb 23 '12 at 05:21
  • But doing `route add default gw 192.168.2.1 tun0` is routing all the traffic through tun0, its not what I want... – Alex On The Moon Feb 23 '12 at 05:42
  • 1) Please paste the output of route -n 2) Have you tried sending UDP using `sendip` to the TUN TAP? Is the poll working fine in this case? – snibu Feb 23 '12 at 09:03
  • When I use `route add default gw 192.168.2.1 tun0` the poll / read does its job, but on the other hand, all the traffic goes through tun0 which is useless. So as a temporary solution i'm just using `route add -net 192.168.2.0 netmask 255.255.255.0 dev tun0` ... Which allows me to do some testing within the context of my project. Though I would like to have a permanent solution that allows me to make sure that any packet with source ip 192.168.2.1 (the tuntap address) is indeed going through the tuntap – Alex On The Moon Feb 23 '12 at 09:35
  • Here is `route -n` after doing `route add default gw 192.168.2.1 tun0` `Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 192.168.56.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 0.0.0.0 192.168.2.1 0.0.0.0 UG 0 0 0 tun0 0.0.0.0 192.168.0.1 0.0.0.0 UG 0 0 0 eth0` – Alex On The Moon Feb 23 '12 at 09:40
  • Must be a bit hard to read like this. Here's a pastebin : http://pastebin.com/raw.php?i=4UcYS1dN – Alex On The Moon Feb 23 '12 at 09:41
  • From your routing table I can see that there is no entry with ip address of tun0 other than the default route. This means the setting of the interface address using ioctl(portSocket, SIOCSIFADDR, &ifr) is not working fine. Please delete the network route and try running the below commands **after you have run the program** `ip link set tun0 up` `ip addr add 192.168.2.1/24 dev tun0` – snibu Feb 23 '12 at 10:00
1

It seems that you only assigned an IP address (192.168.2.1) to the tun device, but did not specify the subnet mask or set up the route. Without a specific route defined for 192.168.2.0/24 network, the IP layer will consider this packet is intended to be sent through default route.

You can either specify subnet mask (/24) when set IP address for tun device, or set a route for 192.168.2.0/24 network manually.

One of ways to do it is to call "ip" with system() function. To specify address with subnet mask (with header already included):

// assuming the tun device name is "tun0"
system("ip addr add 192.168.2.1/24 dev tun0"); 

Also if you already have the IP address assigned to the device, you can use ip route comamnd to set up routes:

ip route add 192.168.2.0/24 dev tun0
Song Gao
  • 2,008
  • 15
  • 18
0

Did you cleared the events on pollfd. fds[0].revents=0?

We need to clear the received events.

Src: http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/

Suman
  • 4,221
  • 7
  • 44
  • 64