今天我花了更多的时间来学习ARP数据包。为了理解它的结构,我尝试使用libpcap
在C中构建一个。我构建了一个简单的ARP请求数据包,并使用pcap_inject
函数发送数据包。此函数返回发送的字节数。
当我调试我的代码时,我看到我的数据包长度为42个字节。我在网上搜索了一下,无法找到答案,告诉我这是否是ARP请求的合适大小。即使是wikipedia entry也让我感到困惑。我发现了这篇文章。从用户提供的答案:
- 如果要在未标记的帧中发送ARP消息,则帧开销本身为18字节。这将导致一个框架 28 + 18 = 46个字节,没有填充。额外的18个字节的填充 在这种情况下,必须将帧膨胀为64字节长度。
- 如果要在802.1Q标记的帧中发送ARP消息,则帧开销为22字节,导致总帧大小为 28 + 22 = 50个字节。在这种情况下,填充需要14个字节长。
- 如果要在双标记帧中发送ARP消息,则帧开销为26字节,导致总帧大小为54 字节。在这种情况下,填充需要10个字节长。
我的问题是在这种情况下我该怎么做。我是否必须使用填充?
Bellow我发布了我的数据包的结构。
#define ETH_P_ARP 0x0806 /* Address Resolution packet */
#define ARP_HTYPE_ETHER 1 /* Ethernet ARP type */
#define ARP_PTYPE_IPv4 0x0800 /* Internet Protocol packet */
/* Ethernet frame header */
typedef struct {
uint8_t dest_addr[ETH_ALEN]; /* Destination hardware address */
uint8_t src_addr[ETH_ALEN]; /* Source hardware address */
uint16_t frame_type; /* Ethernet frame type */
} ether_hdr;
/* Ethernet ARP packet from RFC 826 */
typedef struct {
uint16_t htype; /* Format of hardware address */
uint16_t ptype; /* Format of protocol address */
uint8_t hlen; /* Length of hardware address */
uint8_t plen; /* Length of protocol address */
uint16_t op; /* ARP opcode (command) */
uint8_t sha[ETH_ALEN]; /* Sender hardware address */
uint32_t spa; /* Sender IP address */
uint8_t tha[ETH_ALEN]; /* Target hardware address */
uint32_t tpa; /* Target IP address */
} arp_ether_ipv4;
最后,我只是按照以下顺序复制每个结构成员并发送数据包:
void packageARP(unsigned char *buffer, ether_hdr *frameHeader, arp_ether_ipv4 *arp_packet, size_t *bufferSize) {
unsigned char *cp;
size_t packet_size;
cp = buffer;
packet_size = sizeof(frameHeader->dest_addr)
+ sizeof(frameHeader->src_addr)
+ sizeof(frameHeader->frame_type)
+ sizeof(arp_packet->htype)
+ sizeof(arp_packet->ptype)
+ sizeof(arp_packet->hlen)
+ sizeof(arp_packet->plen)
+ sizeof(arp_packet->op)
+ sizeof(arp_packet->sha)
+ sizeof(arp_packet->spa)
+ sizeof(arp_packet->tha)
+ sizeof(arp_packet->tpa);
/*
* Copy the Ethernet frame header to the buffer.
*/
memcpy(cp, &(frameHeader->dest_addr), sizeof(frameHeader->dest_addr));
cp += sizeof(frameHeader->dest_addr);
memcpy(cp, &(frameHeader->src_addr), sizeof(frameHeader->src_addr));
cp += sizeof(frameHeader->src_addr);
/* Normal Ethernet-II framing */
memcpy(cp, &(frameHeader->frame_type), sizeof(frameHeader->frame_type));
cp += sizeof(frameHeader->frame_type);
/*
* Add the ARP data.
*/
memcpy(cp, &(arp_packet->htype), sizeof(arp_packet->htype));
cp += sizeof(arp_packet->htype);
memcpy(cp, &(arp_packet->ptype), sizeof(arp_packet->ptype));
cp += sizeof(arp_packet->ptype);
memcpy(cp, &(arp_packet->hlen), sizeof(arp_packet->hlen));
cp += sizeof(arp_packet->hlen);
memcpy(cp, &(arp_packet->plen), sizeof(arp_packet->plen));
cp += sizeof(arp_packet->plen);
memcpy(cp, &(arp_packet->op), sizeof(arp_packet->op));
cp += sizeof(arp_packet->op);
memcpy(cp, &(arp_packet->sha), sizeof(arp_packet->sha));
cp += sizeof(arp_packet->sha);
memcpy(cp, &(arp_packet->spa), sizeof(arp_packet->spa));
cp += sizeof(arp_packet->spa);
memcpy(cp, &(arp_packet->tha), sizeof(arp_packet->tha));
cp += sizeof(arp_packet->tha);
memcpy(cp, &(arp_packet->tpa), sizeof(arp_packet->tpa));
cp += sizeof(arp_packet->tpa);
*bufferSize = packet_size;
}
这是构建ARP请求数据包的正确方法吗?
答案 0 :(得分:0)
这是正确的结构 - 除了,C编译器可以自由插入填充以确保结构成员被放置在最有效的边界。特别是,spa
和tpa
不在自然的32位边界(由于前面的6字节MAC地址字段),因此编译器可能希望在每个之前插入两个填充字节。
如果您使用的是gcc,则可以确保__attribute__((packed))
不会发生这种情况:
struct {
[fields]
} __attribute__((packed)) arp_ether_ipv4;
其他编译器可能有不同但等效的机制(例如#pragma
指令)。
ARP有效负载应为28个字节。添加14字节以太网头,总共42个字节。正如您所说,802.1Q(VLAN)标头插入额外的4个字节,“双标记”帧(在Internet服务提供商之外不常见)将添加2 X 4 = 8个字节。如果您使用的是普通端点计算机,则通常不会添加这些标头。 IT部门会根据需要将您的交换机配置为自动插入/删除这些标头。
网络驱动程序将自动将 填充到64字节(以太网最小数据包大小)。 64实际上是60 + 4字节以太网FCS [帧校验和]。 (你引用的帖子显然在他们的计算中包括了4字节的FCS,这就是为什么他们的数字看起来很糟糕。)
另外,不要忘记对所有uint16_t
和uint32_t
字段使用网络字节顺序:(ntohs
和ntohl
)