我如何使用 PCAP 或其他库来解析从 TUN 收到的第 3 层帧?
How may I use PCAP or other library to parse layer 3 frame received from TUN?
这就是我从 TUN 接收第 3 层帧的方式。
基于文档编写:
- https://www.kernel.org/doc/Documentation/networking/tuntap.txt
- http://backreference.org/2010/03/26/tuntap-interface-tutorial/
- 从 http://www.saminiir.com/lets-code-tcp-ip-stack-1-ethernet-arp/
解压
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <stdarg.h>
/* buffer for reading from tun/tap interface, must be >= 1500 */
#define BUFSIZE 2000
int tun_alloc(char *dev)
{
struct ifreq ifr;
int fd, err;
if (!dev) {
return -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
* IFF_MULTI_QUEUE - Create a queue of multiqueue device
*/
ifr.ifr_flags = IFF_TUN;
strcpy(ifr.ifr_name, dev);
if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
return fd;
err = ioctl(fd, TUNSETIFF, (void *)&ifr);
if (err) {
close(fd);
goto err;
}
strcpy(dev, ifr.ifr_name);
return fd;
err:
close(fd);
return err;
}
int main() {
char *tun_name;
tun_name = malloc(IFNAMSIZ);
tun_name[0] = '[=10=]';
int tun_fd = tun_alloc(tun_name);
if (tun_fd < 0) {
puts("Try as root");
exit(1);
}
if (ioctl(tun_fd, TUNSETPERSIST, 0) < 0) {
perror("disabling TUNSETPERSIST");
exit(1);
}
printf("Set interface '%s' nonpersistent\n", tun_name);
struct layer3_frame
{
uint16_t flags;
uint16_t proto;
uint8_t version;
unsigned char payload[];
} __attribute__((packed));
int nread;
char buffer[BUFSIZE];
while(1) {
nread = read(tun_fd, buffer, sizeof(buffer));
if(nread < 0) {
perror("Reading from interface");
close(tun_fd);
exit(1);
}
/* Do whatever with the data */
printf("Read %d bytes from device %s\n", nread, tun_name);
struct layer3_frame* l3f = (struct layer3_frame*)(buffer);
printf("FLAGS %d, PROTO %d, VER %d", l3f->flags, l3f->proto, l3f->version);
// E.g. FLAGS 0, PROTO 56710, VER 96
// Why PROTO is not 4 or 6, why VER is not 4 or 6?
// MAIN: HOW TO USE PCAP TO PARSE l3f FURTHER
// AND GET INFO UP TO SNI (server name indication), e.g.
}
return 0;
}
要玩:
gcc index.c
sudo ./a.out
sudo ip link set tun0 up
- PCAP 通常不用于解析数据包。
- 但是您可以使用:
#include <netinet/ip.h>
#include <netinet/ip6.h>
//...
struct layer3_frame
{
uint16_t flags; // FLAGS from TUN
uint16_t proto; // PRPTO from TUN
unsigned char payload[]; // FRAME/PACKET
} __attribute__((packed));
const struct ip* ippacket = (struct ip*)(l3p->payload);
printf("Version is %d", ippacket->ip_v)
3。关于 PROTO 的奇怪值,如 56710,请尝试 printf("FFF: %x", ntohs(56710))
你会得到 86dd,你可以在 https://en.wikipedia.org/wiki/EtherType
中查找
这就是我从 TUN 接收第 3 层帧的方式。 基于文档编写:
- https://www.kernel.org/doc/Documentation/networking/tuntap.txt
- http://backreference.org/2010/03/26/tuntap-interface-tutorial/
- 从 http://www.saminiir.com/lets-code-tcp-ip-stack-1-ethernet-arp/ 解压
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <stdarg.h>
/* buffer for reading from tun/tap interface, must be >= 1500 */
#define BUFSIZE 2000
int tun_alloc(char *dev)
{
struct ifreq ifr;
int fd, err;
if (!dev) {
return -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
* IFF_MULTI_QUEUE - Create a queue of multiqueue device
*/
ifr.ifr_flags = IFF_TUN;
strcpy(ifr.ifr_name, dev);
if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
return fd;
err = ioctl(fd, TUNSETIFF, (void *)&ifr);
if (err) {
close(fd);
goto err;
}
strcpy(dev, ifr.ifr_name);
return fd;
err:
close(fd);
return err;
}
int main() {
char *tun_name;
tun_name = malloc(IFNAMSIZ);
tun_name[0] = '[=10=]';
int tun_fd = tun_alloc(tun_name);
if (tun_fd < 0) {
puts("Try as root");
exit(1);
}
if (ioctl(tun_fd, TUNSETPERSIST, 0) < 0) {
perror("disabling TUNSETPERSIST");
exit(1);
}
printf("Set interface '%s' nonpersistent\n", tun_name);
struct layer3_frame
{
uint16_t flags;
uint16_t proto;
uint8_t version;
unsigned char payload[];
} __attribute__((packed));
int nread;
char buffer[BUFSIZE];
while(1) {
nread = read(tun_fd, buffer, sizeof(buffer));
if(nread < 0) {
perror("Reading from interface");
close(tun_fd);
exit(1);
}
/* Do whatever with the data */
printf("Read %d bytes from device %s\n", nread, tun_name);
struct layer3_frame* l3f = (struct layer3_frame*)(buffer);
printf("FLAGS %d, PROTO %d, VER %d", l3f->flags, l3f->proto, l3f->version);
// E.g. FLAGS 0, PROTO 56710, VER 96
// Why PROTO is not 4 or 6, why VER is not 4 or 6?
// MAIN: HOW TO USE PCAP TO PARSE l3f FURTHER
// AND GET INFO UP TO SNI (server name indication), e.g.
}
return 0;
}
要玩:
gcc index.c
sudo ./a.out
sudo ip link set tun0 up
- PCAP 通常不用于解析数据包。
- 但是您可以使用:
#include <netinet/ip.h>
#include <netinet/ip6.h>
//...
struct layer3_frame
{
uint16_t flags; // FLAGS from TUN
uint16_t proto; // PRPTO from TUN
unsigned char payload[]; // FRAME/PACKET
} __attribute__((packed));
const struct ip* ippacket = (struct ip*)(l3p->payload);
printf("Version is %d", ippacket->ip_v)
3。关于 PROTO 的奇怪值,如 56710,请尝试 printf("FFF: %x", ntohs(56710))
你会得到 86dd,你可以在 https://en.wikipedia.org/wiki/EtherType