TUNTAP interface in C (Linux): it is not possible to capture UDP packets sent to TUNTAP using sendto ()

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

What I do is select the interface from the clone device / dev / net / tun, enable it 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 stream that will poll () in the file descriptor of the created interface and will read () + some other things 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; } 

In another 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 have to capture these packets with poll () + read () and then send them to the serial port, but poll () never captures events on the TUNTAP interface.

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

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, physical network adapter).

I was able to verify this with Wireshark.

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

I would be very happy if anyone can help me.

+4
source share
3 answers

How did you set the tunnel interface IP address? Using the "ip addr add" command? You have checked / configured the tunnel for the connection, i.e. "ip link set <<tun tap interface name>> up" ? Finally, you are sure that your UDP socket send code starts after the previous two conditions are met, i.e. It has the correct IP address and channel forwarding interface.

Update

I think you misunderstood the concept, or I didn’t understand you very well. AFAIK you don’t have to do much. The concept of the channel switching device is that any packets that the tunnel interface transmits are sent to the user space program, and regardless of what the user space program writes to the forwarding device, it is sent to the network. Having said that and having read that your requirement is to tunnel UDP packets to a serial interface, you should do the following

1) Set the default route to the forwarding IP address. Thus, all packages will be received by userspace. route add default gw 192.168.2.1 tun0

So now in the user space program you get the whole packet with IP and UDP headers. Now the whole packet will be the message that you want to pass through the serial interface. Therefore, when we send it via the serial interface using UDP or TCP (depending on what you prefer), we will automatically encapsulate the whole packet again with another UDP and IP header.

2) You also need to add a host rule with the address of the serial interface. Thus, the aforementioned encapsulated packets for the serial interface are sent to the serial interface and they do not return again to the TUN TAP device due to the default rule. route add -host <<serial ip address>> dev <<serial device>>

Give route -n and verify that the routing is correct. Make sure you have no other unwanted routes. If so, delete them.

Note: You can also extract only the payload from the packet and use your RAW sockets to create your own UDP header with the serial interface as the IP address and record using the TUN TAP device. This packet will be considered as an instance of the kernel routing as belonging to the serial interface and will be redirected there due to the host rule. If you intend in this case, you need to create your own IP and UDP header, as well as CRC calculation.

+1
source

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

You can specify the subnet mask (/ 24) when setting the IP address for the tun device or configure the route for the 192.168.2.0/24 network manually.

One way to do this is to call "ip" using the system () function. To specify an address with a subnet mask (with the 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 an IP address assigned to the device, you can use ip route comamnd to configure routes:

 ip route add 192.168.2.0/24 dev tun0 
+1
source

You have cleared pollfd events. FDS [0] = 0.revents?

We need to clear the received events.

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

0
source

Source: https://habr.com/ru/post/1397567/


All Articles