如何在C中构造和ARP请求包

时间:2016-12-30 22:26:11

标签: c network-programming arp 802.11

今天我花了更多的时间来学习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请求数据包的正确方法吗?

1 个答案:

答案 0 :(得分:0)

这是正确的结构 - 除了,C编译器可以自由插入填充以确保结构成员被放置在最有效的边界。特别是,spatpa不在自然的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_tuint32_t字段使用网络字节顺序:(ntohsntohl