我如何使用 PCAP 或其他库来解析从 TUN 收到的第 3 层帧?

How may I use PCAP or other library to parse layer 3 frame received from TUN?

这就是我从 TUN 接收第 3 层帧的方式。 基于文档编写:

  1. https://www.kernel.org/doc/Documentation/networking/tuntap.txt
  2. http://backreference.org/2010/03/26/tuntap-interface-tutorial/
  3. http://www.saminiir.com/lets-code-tcp-ip-stack-1-ethernet-arp/
  4. 解压
#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

  1. PCAP 通常不用于解析数据包。
  2. 但是您可以使用:
#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

中查找