监听虚拟网络接口
Listening to virtual network interface
不要被我谈论的 L2TP 搞糊涂了。虽然我的问题与 L2TP 有关,但它本身并不是 L2TP 问题。这更像是一个网络问题。
背景
我正在编写一个使用 L2TP 的应用程序。这是我第一次使用 L2TP 和 linux L2TP 子系统,所以我希望我做对了。
创建 L2TP 以太网会话时,子系统会自动创建一个虚拟网络接口。
启动界面后,我可以使用 Wireshark 检查,确实所需的数据已发送到界面。这是没有任何包装的。它不在以太网帧或任何东西中,而只是包含在 L2TP 数据包中的数据字节。
我无法控制实际创建设备,但我可以查询它的名称以及它的索引等,到目前为止一切顺利。
实际问题
我的问题实际上很简单:如何将发送到虚拟接口的数据导入我的用户空间应用程序?
我在 unix 上没有太多网络经验,但我的期望是这是一个相当简单的问题,可以通过获取我可以使用的文件描述符来解决 read
/ recv
或者以某种方式将套接字绑定到该网络接口。
我找不到任何 (gen-)netlink / ioctl API(或其他任何东西)来执行此操作或类似的操作。
尽管我的应用程序是用 GO 而不是 C 编写的,但 C 中的解决方案就完全足够了。说实话,在这一点上,我很乐意以编程方式解决此问题的任何方法。 :)
非常感谢
我刚刚找到了一个教程来回答我自己的问题。使用 AF_PACKET
套接字实际上非常容易。
有一个 lovely tutorial on microhowto.info,它解释了 AF_PACKET
套接字的工作原理,比我以前能做的更好。 它甚至包括一个部分 “仅从特定网络接口捕获”。
这是一个最小的例子,适用于我的用例:
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <linux/if_packet.h>
#include <sys/socket.h>
// [...]
// Create socket
int fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (fd == -1) {
perror("ERROR socket");
exit(1);
}
// Interface index (i.e. obtainable via ioctl SIOCGIFINDEX)
int ifindex = 1337;
// create link layer socket address
struct sockaddr_ll addr = {0};
addr.sll_family = AF_PACKET;
addr.sll_ifindex = ifindex;
addr.sll_protocol = htons(ETH_P_ALL)
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("ERROR bind");
exit(1);
}
char buffer[65535];
ssize_t len;
do {
len = recv(fd, buffer, sizeof(buffer) -1, 0);
if (len < 0) {
perror("ERROR recvfrom");
exit(1);
}
printf("recived data (length: %i)\n", (int) len);
} while (len > 0);
不要被我谈论的 L2TP 搞糊涂了。虽然我的问题与 L2TP 有关,但它本身并不是 L2TP 问题。这更像是一个网络问题。
背景
我正在编写一个使用 L2TP 的应用程序。这是我第一次使用 L2TP 和 linux L2TP 子系统,所以我希望我做对了。
创建 L2TP 以太网会话时,子系统会自动创建一个虚拟网络接口。
启动界面后,我可以使用 Wireshark 检查,确实所需的数据已发送到界面。这是没有任何包装的。它不在以太网帧或任何东西中,而只是包含在 L2TP 数据包中的数据字节。
我无法控制实际创建设备,但我可以查询它的名称以及它的索引等,到目前为止一切顺利。
实际问题
我的问题实际上很简单:如何将发送到虚拟接口的数据导入我的用户空间应用程序?
我在 unix 上没有太多网络经验,但我的期望是这是一个相当简单的问题,可以通过获取我可以使用的文件描述符来解决 read
/ recv
或者以某种方式将套接字绑定到该网络接口。
我找不到任何 (gen-)netlink / ioctl API(或其他任何东西)来执行此操作或类似的操作。
尽管我的应用程序是用 GO 而不是 C 编写的,但 C 中的解决方案就完全足够了。说实话,在这一点上,我很乐意以编程方式解决此问题的任何方法。 :)
非常感谢
我刚刚找到了一个教程来回答我自己的问题。使用 AF_PACKET
套接字实际上非常容易。
有一个 lovely tutorial on microhowto.info,它解释了 AF_PACKET
套接字的工作原理,比我以前能做的更好。 它甚至包括一个部分 “仅从特定网络接口捕获”。
这是一个最小的例子,适用于我的用例:
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <linux/if_packet.h>
#include <sys/socket.h>
// [...]
// Create socket
int fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (fd == -1) {
perror("ERROR socket");
exit(1);
}
// Interface index (i.e. obtainable via ioctl SIOCGIFINDEX)
int ifindex = 1337;
// create link layer socket address
struct sockaddr_ll addr = {0};
addr.sll_family = AF_PACKET;
addr.sll_ifindex = ifindex;
addr.sll_protocol = htons(ETH_P_ALL)
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("ERROR bind");
exit(1);
}
char buffer[65535];
ssize_t len;
do {
len = recv(fd, buffer, sizeof(buffer) -1, 0);
if (len < 0) {
perror("ERROR recvfrom");
exit(1);
}
printf("recived data (length: %i)\n", (int) len);
} while (len > 0);