C语言 如何与Linux tun驱动程序交互

lyfkaqu1  于 2023-03-01  发布在  Linux
关注(0)|答案(4)|浏览(159)

我很难弄清楚这个问题--我正在尝试编写一个与Linux隧道驱动程序交互的程序。在非常基本的层面上,我只是想创建一个能够通过网络隧道传输数据的应用程序。然而,我完全不知道如何正确地设置隧道驱动程序来实现这一点。
我正在Ubuntu 9.04上进行开发,并且已经加载了隧道驱动程序内核模块。
存在设备/dev/net/tun,但没有/dev/tunX设备。我无法使用ifconfig创建这些设备-例如,每当我运行/sbin/ifconfig tun0 up时,我会得到以下错误:
tun0:获取接口标志时出错:无此类器械。
如果我尝试查看/dev/net/tun设备,将显示以下错误:
目录号:/设备/网络/调谐器:文件描述符处于错误状态。
尝试通过一个小程序打开/dev/tunX,基本上是一个简单的

tun_fd = open( "/dev/tun0", O_RDWR )

返回-1:应用程序正在以root身份运行,但仍无法打开此隧道设备。可以打开/dev/net/tun,但这似乎不会生成要使用的新/dev/tunX设备。
所以,总而言之,如何编写一个希望使用Linux隧道驱动程序的应用程序呢?任何见解都将受到极大的欢迎。
谢谢;~罗伯特

yebdmbv4

yebdmbv41#

没有/dev/tunX设备文件。相反,您打开/dev/net/tun并通过ioctl()将其配置为“指向”tun0。为了显示基本过程,我将使用命令行工具ip tun tap创建TUN接口,然后显示从该TUN设备读取的C代码。因此,要通过命令行创建tun接口:

ip addr show # my eth0 inet address is 10.0.2.15/24 as Im running on a VirtualBox vm with Ubuntu 18.04 guest
sudo ip tuntap add mode tun dev tun0
sudo ip addr add 10.0.3.0/24 dev tun0  # give it an address (that does not conflict with existing IP)
sudo ip link set dev tun0 up  # bring the if up
ip route get 10.0.3.50  # check that packets to 10.0.3.x are going through tun0
# 10.0.3.50 dev tun0 src 10.0.3.0 uid 1000 
ping 10.0.3.50 # leave this running in another shell to be able to see the effect of the next example, nobody is responding to the ping

tun0已创建,所有发往目的IP地址10.0.3.x的数据包都将路由到tun0
要从用户空间程序读取/写入数据包到该接口,您需要使用ioctl()/dev/net/tun设备文件交互。下面是一个示例,它将读取到达tun0接口的数据包并打印大小:

#include <fcntl.h>     /* O_RDWR */
#include <stdio.h>     /* perror(), printf(), fprintf() */
#include <stdlib.h>    /* exit(), malloc(), free() */
#include <string.h>    /* memset(), memcpy() */
#include <sys/ioctl.h> /* ioctl() */
#include <unistd.h>    /* read(), close() */

/* includes for struct ifreq, etc */
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/socket.h>
#include <sys/types.h>

int tun_open(char* devname)
{
    struct ifreq ifr;
    int fd, err;

    if ((fd = open("/dev/net/tun", O_RDWR)) == -1) {
        perror("open /dev/net/tun");
        exit(1);
    }
    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = IFF_TUN;
    strncpy(ifr.ifr_name, devname, IFNAMSIZ); // devname = "tun0" or "tun1", etc

    /* ioctl will use ifr.if_name as the name of TUN
     * interface to open: "tun0", etc. */
    if ((err = ioctl(fd, TUNSETIFF, (void*)&ifr)) == -1) {
        perror("ioctl TUNSETIFF");
        close(fd);
        exit(1);
    }

    /* After the ioctl call the fd is "connected" to tun device specified
     * by devname ("tun0", "tun1", etc)*/

    return fd;
}

int main(int argc, char* argv[])
{
    int fd, nbytes;
    char buf[1600];

    fd = tun_open("tun0"); /* devname = ifr.if_name = "tun0" */
    printf("Device tun0 opened\n");
    while (1) {
        nbytes = read(fd, buf, sizeof(buf));
        printf("Read %d bytes from tun0\n", nbytes);
    }
    return 0;
}

如果您正在运行ping 10.0.3.1ping 10.0.3.40,您将定期看到Read 88 bytes from tun0
您还可以使用netcat UDP和nc -u 10.0.3.3 2222并键入text + Enter进行测试。
如果未打印任何内容,则很可能是分配给tun 0的id地址/ ip范围不可访问/不可路由/不可寻址。请确保ip route get 10.0.3.4显示10.0.3.4 dev tun0,表明linux内核知道应将发送到www.example.com的数据包10.0.3.4发送到tun 0设备。
要删除tun0 do

sudo ip link set dev tun0 down
sudo ip tuntap del mode tun dev tun0
izj3ouym

izj3ouym2#

读取/usr/src/linux/Documentation/networking/tuntap.rst
你应该对/dev/net/tun设备执行open操作,在open fd上执行后续的ioctl操作会创建tun0(或者你想给它起什么名字)网络接口,Linux的网络接口不对应任何/dev/*设备。

bzzcjhmw

bzzcjhmw3#

我看到了一个很好的入门教程
http://backreference.org/2010/03/26/tuntap-interface-tutorial/
它附带了一个源代码压缩包。
它和这个问题在同一组谷歌搜索结果中。:-)

uoifb46i

uoifb46i4#

另一种实现方式使用c11中stdio.h中的API fopenfclosefread,而不是unistd.h中的api

#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

FILE* tun_alloc(char *dev)
{
   struct ifreq ifr;
   FILE *fp;
   int err;

   // open in binary read+write mode
   if ((fp = fopen("/dev/net/tun", "r+b")) == 0)
   {
      perror("failed to open tun dev\n");
      exit(1);
   }

   memset(&ifr, 0, sizeof(ifr));

   /* Flags: IFF_TUN   - TUN device (no Ethernet headers)
    *        IFF_TAP   - TAP device
    *
    *        IFF_NO_PI - Do not provide packet information
    */
   ifr.ifr_flags = IFF_TUN;
   if (*dev)
      strncpy(ifr.ifr_name, dev, IFNAMSIZ);

   if ((err = ioctl(fileno(fp), TUNSETIFF, (void *)&ifr)) < 0)
   {
      fclose(fp);
      perror("failed to set mode!\n");
      exit(1);
   }
   strcpy(dev, ifr.ifr_name);
   return fp;
}

int main(void)
{
   char dev_name[IFNAMSIZ] = "tun0";
   char recv_buff[80];
   FILE* fp = tun_alloc(dev_name);

   // turn off buffering
   setbuf(fp, NULL); 

   while (1)
   {
      printf("reading from TUN device: %s\n", dev_name);
      size_t n = fread(recv_buff, sizeof(recv_buff[0]), sizeof(recv_buff), fp);
      printf("read bytes: %li\n", n);
   }
   return EXIT_SUCCESS;
}

相关问题