如何与 Linux tun 驱动程序交互

发布于 2024-07-24 22:50:50 字数 845 浏览 4 评论 0原文

我很难解决这个问题 - 我正在尝试编写一个与 Linux 隧道驱动程序交互的程序。 在非常基础的层面上,我只是想创建一个能够通过网络隧道传输数据的应用程序。 但是,我完全不知道如何正确设置隧道驱动程序以实现此目的。

我正在 Ubuntu 9.04 上进行开发,并且加载了隧道驱动程序内核模块。

存在设备 /dev/net/tun,但不存在 /dev/tunX 设备。 我无法使用 ifconfig 创建这些设备 - 例如,每当我运行 /sbin/ifconfig tun0 up 时,我都会收到以下错误:

tun0:获取接口标志时出错:没有这样的设备。

如果我尝试查看 /dev/net/tun 设备,则会出现以下错误:

cat: /dev/net/tun: 文件描述符处于错误状态。

尝试通过小程序打开/dev/tunX,基本上,一个简单的

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

返回-1:应用程序以root身份运行,但仍然无法打开这个隧道设备。 可以打开 /dev/net/tun,但这似乎不会生成新的 /dev/tunX 设备来使用。

那么,总而言之,如何编写一个希望使用 Linux 隧道驱动程序的应用程序呢? 任何见解将不胜感激。

谢谢; 〜罗伯特

I'm having a hard time figuring this problem out - I am trying to write a program that will interact with the Linux tunnel driver. At a very basic level, I simply want to create an application that is able to transfer data over a network tunnel. However, I am completely at a loss as to how to properly set up the tunnel driver in order to accomplish this.

I am developing on Ubuntu 9.04, and I have the tunnel driver kernel module loaded.

There exists the device /dev/net/tun, however there are no /dev/tunX devices. I am unable to create these devices using ifconfig - whenever I run /sbin/ifconfig tun0 up, for example, I get the following error:

tun0: ERROR while getting interface flags: No such device.

If I attempt to look at the /dev/net/tun device, the following error is presented:

cat: /dev/net/tun: File descriptor in bad state.

Attempting to open /dev/tunX via a small program, basically, a simple

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

returns -1: the application is running as root and still cannot open this tunnel device. It is possible to open /dev/net/tun, however this does not appear to generate a new /dev/tunX device to use instead.

So, in summary - how does one go about writing an application that wishes to use the Linux tunnel driver? Any insights would be greatly appreciated.

Thanks;
~Robert

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

栖迟 2024-07-31 22:50:50

没有 /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< /code> 运行时,您将看到定期从 tun0 读取 88 字节

您还可以使用 netcat UDP 和 nc -u 10.0.3.3 2222 并输入文本 + Enter 进行测试。

如果没有打印任何内容,则很可能分配给 tun0 的 id 地址/IP 范围不可访问/可路由/可寻址。 确保 ip route get 10.0.3.4 显示 10.0.3.4 dev tun0 表明 Linux 内核知道发往 10.0.3.4 的数据包应发送到 tun0 设备。

要删除 tun0 ,请执行以下操作

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

There are no /dev/tunX device files. Instead, you open the /dev/net/tun and configure it via ioctl() to "point" to tun0. To show the basic procedure, I will create the TUN interface using the command line tool ip tun tap and then show the C code to read from that TUN device. So to create the tun interface via commands line:

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

The tun0 is created and all packets to destination IP address 10.0.3.x will be routed to tun0.

To read / write packets to this interface from an user space program you need to interact with the /dev/net/tun device file using ioctl(). Here is an example that will read the packets arriving at the tun0 interface and print the size:

#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;
}

If you have the ping 10.0.3.1 or ping 10.0.3.40 running, you will see Read 88 bytes from tun0 periodically.

You can also test using netcat UDP with nc -u 10.0.3.3 2222 and typing text + Enter.

If nothing is being printed is most likely that the id address / ip range assigned to the tun0 is not reachable/routable/addressable. Make sure that the ip route get 10.0.3.4 shows 10.0.3.4 dev tun0 indicating that the linux kernels knows that packets to 10.0.3.4 should be sent to the tun0 device.

To delete the tun0 do

sudo ip link set dev tun0 down
sudo ip tuntap del mode tun dev tun0
妄断弥空 2024-07-31 22:50:50

阅读 /usr/src/linux/Documentation/networking/tuntap.rst

您应该打开 /dev/net/tun 设备。 open fd 上的后续 ioctl 将创建 tun0(或任何您希望命名的名称)网络接口。 Linux 的网络接口不对应于任何 /dev/* 设备。

Read /usr/src/linux/Documentation/networking/tuntap.rst.

You are supposed to open the /dev/net/tun device. A subsequent ioctl on the open fd will create the tun0 (or whatever you wish to name it) network interface. Linux's network interfaces do not correspond to any /dev/* device.

二货你真萌 2024-07-31 22:50:50

我发现了一个关于这个的很好的介绍教程

http://backreference.org/2010 /03/26/tuntap-interface-tutorial/

它带有源 tarball。

它与这个问题在同一组谷歌结果中。 :-)

I came across a nice intro tutorial about this

http://backreference.org/2010/03/26/tuntap-interface-tutorial/

It comes with a source tarball.

It was in the same set of Google results as this question. :-)

暗藏城府 2024-07-31 22:50:50

另一种实现是在 c11 的 stdio.h 中使用 api fopen fclose fread,而不是 unistd 中的 api .h

#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;
}

another implementation using api fopen fclose fread in stdio.h in c11, instead of api in unistd.h

#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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文