🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
发送ARP请求包的时候,需要填充已知的目标IP地址、源MAC地址、源IP地址等,并且需要该ARP包进行广播出去,所以以太网首部的目标MAC地址为FF-FF-FF-FF-FF-FF,源码具体见代码清单 10‑7。 ``` 1 /*发送原始ARP数据包(操作码和所有地址都可以修改) 2 * @param netif用于发送ARP数据包的lwip网络接口 3 * @param ethsrc_addr以太网头的源MAC地址 4 * @param ethdst_addr以太网头的目标MAC地址 5 * @param hwsrc_addr ARP协议头的源MAC地址 6 * @param ipsrc_addr ARP协议头的源IP地址 7 * @param hwdst_addr ARP协议头的目标MAC地址 8 * @param ipdst_addr ARP协议头的目标IP地址 9 * @param操作编码ARP数据包的类型 10 * @return ERR_OK如果已发送ARP数据包 11 * 如果无法分配ARP数据包,则为ERR_MEM 12 */ 13 static err_t 14 etharp_raw(struct netif *netif, //用于发送ARP数据包的lwip网络接口 15 const struct eth_addr *ethsrc_addr,//以太网头的源MAC地址 16 const struct eth_addr *ethdst_addr,//以太网头的目标MAC地址 17 const struct eth_addr *hwsrc_addr,//ARP协议头的源MAC地址 18 const ip4_addr_t *ipsrc_addr,//ARP协议头的源IP地址 19 const struct eth_addr *hwdst_addr, //ARP协议头的目标MAC地址 20 const ip4_addr_t *ipdst_addr,// ARP协议头的目标IP地址 21 const u16_t opcode)//操作编码ARP数据包的类型(op字段) 22 { 23 struct pbuf *p; 24 err_t result = ERR_OK; 25 struct etharp_hdr *hdr; 26 27 //申请ARP报文的内存空间 28 p = pbuf_alloc(PBUF_LINK, SIZEOF_ETHARP_HDR, PBUF_RAM); 29 30 if (p == NULL) 31 { 32 ETHARP_STATS_INC(etharp.memerr);//内存申请失败,返回错误代码 33 return ERR_MEM; 34 } 35 36 //ARP报文的数据区域,并且强制将起始地址转化成ARP报文首部 37 hdr = (struct etharp_hdr *)p->payload; 38 39 hdr->opcode = lwip_htons(opcode); //填写ARP数据包的op字段 40 41 //填写源MAC地址 42 SMEMCPY(&hdr->shwaddr, hwsrc_addr, ETH_HWADDR_LEN); 43 //填写目标MAC地址 44 SMEMCPY(&hdr->dhwaddr, hwdst_addr, ETH_HWADDR_LEN); 45 46 //以太网首部源MAC地址 47 IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->sipaddr, ipsrc_addr); 48 / 49 //以太网首部目标MAC地址 50 IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->dipaddr, ipdst_addr); 51 52 //填写ARP首部硬件类型 53 hdr->hwtype = PP_HTONS(LWIP_IANA_HWTYPE_ETHERNET); 54 //填写ARP首部协议类型 55 hdr->proto = PP_HTONS(ETHTYPE_IP); 56 57 //填写ARP数据包硬件地址长度 58 hdr->hwlen = ETH_HWADDR_LEN; 59 //填写ARP数据包协议地址长度 60 hdr->protolen = sizeof(ip4_addr_t); 61 62 //调用底层发送函数将以太网数据帧发送出去 63 ethernet_output(netif, p, ethsrc_addr, ethdst_addr, ETHTYPE_ARP); 64 65 ETHARP_STATS_INC(etharp.xmit); 66 67 pbuf_free(p);//发送完成释放内存 68 p = NULL; 69 70 return result; //返回结果 71 } 72 73 //FF-FF-FF-FF-FF-FF 74 const struct eth_addr ethbroadcast = 75 {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; 76 //00-00-00-00-00-00 77 const struct eth_addr ethzero = {{0, 0, 0, 0, 0, 0}}; 78 79 //发送ARP请求包,指定目标MAC地址 80 static err_t 81 etharp_request_dst(struct netif *netif, 82 const ip4_addr_t *ipaddr, 83 const struct eth_addr *hw_dst_addr) 84 { 85 return etharp_raw(netif, 86 (struct eth_addr *)netif->hwaddr, 87 hw_dst_addr, 88 (struct eth_addr *)netif->hwaddr, 89 netif_ip4_addr(netif), &ethzero, 90 ipaddr, ARP_REQUEST); 91 } 92 93 //发送ARP请求包,目标MAC地址为 ethbroadcast 94 err_t 95 etharp_request(struct netif *netif, const ip4_addr_t *ipaddr) 96 { 97 return etharp_request_dst(netif, ipaddr, &ethbroadcast); 98 } ``` 总的来说就是先调用etharp\_request()函数进行发送ARP请求包,在etharp\_request()函数中会调用etharp\_request\_dst()函数进行发送,此时指定的目标MAC地址是ethbroadcast,而在etharp\_request\_dst()函数中会调用etharp\_raw()进行发送ARP请求包,层层调用,并且每层的参数都是越来越多的,这样子封装对于上层程序来说更加好处理,在etharp\_raw()函数中,会对ARP数据包进行封装,然后再封装到以太网数据帧中,最终调用以太网底层发送函数进行将以太网数据帧发送出去。