解析以太网帧和数据类型
Parsing Ethernet Frames and Data Types
我有一个看似简单的任务,即打印关于通过特定以太网接口的帧的基本信息。我有一个定义为
的套接字
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__;
我这样循环输入
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);
这是我遇到问题的地方。我用 struct
定义了每个帧投射到的以太网头
struct ethheader {
unsigned char dsta[6];
unsigned char srca[6];
uint16_t type;
};
并输出类似
的信息
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] = '[=13=]';
src[17] = '[=13=]';
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;
}
}
}
我收到的输出表明我正在正确打印 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];
我的第二个问题仍然存在:我怎样才能更好地定义这些 struct
更便携的类型;或者我可以接受 unsigned char
吗?
如果你包括 <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__));
您可能想要使用以下库函数:
#include <netinet/ether.h>
char *ether_ntoa(const struct ether_addr *addr);
您是否考虑过使用 libpcap?它真的让这些事情变得简单。
(就像让 tachikoma 为您完成工作:)
我有一个看似简单的任务,即打印关于通过特定以太网接口的帧的基本信息。我有一个定义为
的套接字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__;
我这样循环输入
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);
这是我遇到问题的地方。我用 struct
struct ethheader {
unsigned char dsta[6];
unsigned char srca[6];
uint16_t type;
};
并输出类似
的信息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] = '[=13=]';
src[17] = '[=13=]';
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;
}
}
}
我收到的输出表明我正在正确打印 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];
我的第二个问题仍然存在:我怎样才能更好地定义这些 struct
更便携的类型;或者我可以接受 unsigned char
吗?
如果你包括 <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__));
您可能想要使用以下库函数:
#include <netinet/ether.h>
char *ether_ntoa(const struct ether_addr *addr);
您是否考虑过使用 libpcap?它真的让这些事情变得简单。
(就像让 tachikoma 为您完成工作:)