解析以太网帧和数据类型

mot*_*oku 2 c sockets typedef raw-sockets

我有一个看似简单的任务,即打印有关通过特定以太网接口的帧的非常基本的信息。我有一个套接字定义为

if ((sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) return __LINE__;
strcpy(ifr.ifr_name, argv[1]);
if (ioctl(sd, SIOCGIFFLAGS, &ifr) == -1) return __LINE__;
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl(sd, SIOCSIFFLAGS, &ifr) == -1) return __LINE__;
if (ioctl(sd, SIOCGIFINDEX, &ifr) == -1) return __LINE__;
Run Code Online (Sandbox Code Playgroud)

我像这样循环输入

while (active) {
        FD_SET(sd, &fds);
        FD_SET(STDIN_FILENO, &fds);
        if ((rv = select(sd + 1, &fds, NULL, NULL, &tv)) < 0)
            active = 0;
        if (FD_ISSET(sd, &fds)) input(sd, buf);
Run Code Online (Sandbox Code Playgroud)

这就是我遇到问题的地方。我定义了每个帧都被投射到的以太网标头struct

struct ethheader {
    unsigned char       dsta[6];
    unsigned char       srca[6];
    uint16_t            type;
};
Run Code Online (Sandbox Code Playgroud)

并输出如下信息

void input(int sd, char *buf) {
    int i;
    char *p = buf;
    struct ethheader *eth = (struct ethheader*)buf;
    int len = read(sd, buf, BUF_SIZ);
    if (len < sizeof(struct ethheader)) {
        printf("smaller than an ethernet frame\n");
        return;
    } else {
        char dst[18];
        char src[18];
        for (i = 0; i < 6; i++) {
            sprintf(dst + i * 3, "%02x:", eth->dsta[i]);
            sprintf(src + i * 3, "%02x:", eth->srca[i]);
        }
        dst[17] = '\0';
        src[17] = '\0';
        printf("dst: %s src: %s ", dst, src);
        switch (eth->type) {
        case 0x0800:
            printf("IPv4\n");
            break;
        case 0x0842:
            printf("ARP\n");
            break;
        case 0x86DD:
            printf("IPv6\n");
            break;
        default:
            printf("unknown\n");
            break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我收到的输出表明我正在正确打印 MAC 地址,但我没有正确检测协议。我非常确定该错误涉及左值字节大小或字节顺序;或两者。正是在这一点上,我觉得有必要问我如何才能更好地定义我的struct价值观,以及为什么我的协议switch被破坏了?

好的,在阅读了一些评论后,我能够正确读取以太网类型:

struct ethheader {
    unsigned char       dsta[6];
    unsigned char       srca[6];
    unsigned char       type[2];
};
int type = (eth->type[0] << 8) + eth->type[1];
Run Code Online (Sandbox Code Playgroud)

我的第二个问题仍然是:我怎样才能struct用更便携的类型更好地定义这些 s ?或者我可以吗unsigned char

小智 5

如果您包括在内,<net/ethernet.h>您将拥有struct ether_header

struct ether_header
{
  u_int8_t  ether_dhost[ETH_ALEN];      /* destination eth addr */
  u_int8_t  ether_shost[ETH_ALEN];      /* source ether addr    */
  u_int16_t ether_type;                 /* packet type ID field */
} __attribute__ ((__packed__));
Run Code Online (Sandbox Code Playgroud)

您可能想要使用一些库函数,例如:

   #include <netinet/ether.h>

   char *ether_ntoa(const struct ether_addr *addr);
Run Code Online (Sandbox Code Playgroud)

您是否考虑过使用libpcap?它确实使这些事情变得容易。

(就像让一个立驹来为你做这项工作:)