c - 如何用C语言构造ARP请求包

标签 c network-programming arp wifi

今天我花了更多的时间来学习 ARP 数据包。为了理解它的结构,我尝试使用 libpcap 在 C 语言中自己构建一个。我构造了一个简单的 ARP 请求数据包,并使用 pcap_inject 函数发送数据包。该函数返回发送的字节数。

当我调试代码时,我发现我的数据包有 42 字节长。我在互联网上搜索了一下,但找不到答案来告诉我这是否适合 ARP 请求的大小。甚至是wikipedia entry我有点困惑。我发现了这篇文章。从用户提供的答案来看:

  • If the ARP message is to be sent in an untagged frame then the frame overhead itself is 18 bytes. That would result in a frame of 28+18=46 bytes without padding. Additional 18 bytes of padding are necessary in this case to bloat the frame to the 64 byte length.
  • If the ARP message is to be sent in an 802.1Q-tagged frame then the frame overhead is 22 bytes, resulting in the total frame size of 28+22=50 bytes. In this case, the padding needs to be 14 bytes long.
  • If the ARP message is to be sent in a double-tagged frame then the frame overhead is 26 bytes, resulting in the total frame size of 54 bytes. In this case, the padding needs to be 10 bytes long.

我的问题是在这种情况下我必须做什么。我是否必须使用填充?

下面我发布了我的数据包的结构。

#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 请求数据包的正确方法吗?

最佳答案

这是正确的结构 - 除了,C 编译器可以自由插入填充以确保结构成员放置在最有效的边界处。特别是spatpa不在自然的 32 位边界(由于前面的 6 字节 MAC 地址字段),因此编译器可能希望在每个字节之前插入两个字节的填充。

如果您使用的是 gcc,则可以使用 __attribute__((packed)) 确保不会发生这种情况。 :

struct {
       [fields]
}  __attribute__((packed)) arp_ether_ipv4;

其他编译器可能具有不同但等效的机制(例如 #pragma 指令)。

ARP 有效负载应为 28 字节。添加 14 字节以太网 header ,总共 42 个字节。正如您引用的内容所述,802.1Q (VLAN) header 会插入额外的 4 个字节,而“双标记”帧(在互联网服务提供商之外并不常见)将添加 2 X 4 = 8 个字节。如果您使用的是普通端点计算机,则通常不会添加这些 header 。 IT 部门将配置您的交换机以根据需要自动插入/删除这些 header 。

您的网络驱动程序将自动将 42 字节填充为 64 字节(以太网最小数据包大小)。 64 实际上是 60 + 4 字节以太网 FCS [帧校验和]。 (您引用的帖子显然在他们的计算中包含了 4 字节 FCS,这就是为什么他们的数字看起来很奇怪。)

另外,不要忘记对所有uint16_t使用网络字节顺序。和uint32_t字段:( ntohsntohl )

关于c - 如何用C语言构造ARP请求包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41403445/

相关文章:

c - GLib:通过 UI 传播警告错误

c - 套接字无法在基本客户端/服务器之间连接

C 网络编程错误,因为\n

tcp - 如何减少 MODBUS TCP 的 ARP 刷新导致的 TCP 延迟

windows - 使用 ARP 从已知 MAC 地址获取未知 IP 地址?

c - 使用目标MAC的ARP数据包

c - ARM M4 每周期指令 (IPC) 计数器

c - gtk_container_get_children

c++ - 我应该使用 AcceptEx() 还是 WSAAccept()?

python - 获取与给定 CIDR 匹配的子网列表