🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
以太网是有自己独立的寻址方式(MAC地址),而对于TCP/IP的上层协议(如TCP协议、IP协议),它们是以IP地址作为网络的标识,如果没有IP地址则无法进行收发数据。当数据通过网卡中接收回来的时候,LwIP内核就需要将数据进行分解,如果是IP数据报则递交给IP协议去处理,如果是ARP数据包则交由ARP协议去处理。 LwIP中数据包从网卡接收的函数是ethernetif\_input(),从第9章我们可以知道,真正让LwIP内核去处理接收到的数据包是ethernet\_input()函数,这两个函数是不一样的,名字稍微有点区别,LwIP就是在这个函数中处理不同的数据包类型,其源码具体见代码清单 10‑8。 ``` 1 err_t 2 ethernet_input(struct pbuf *p, struct netif *netif) 3 { 4 struct eth_hdr *ethhdr; 5 u16_t type; 6 7 #if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6 8 9 u16_t next_hdr_offset = SIZEOF_ETH_HDR; 10 11 #endif 12 13 LWIP_ASSERT_CORE_LOCKED(); 14 15 //校验数据长度 16 if (p->len <= SIZEOF_ETH_HDR) 17 { 18 ETHARP_STATS_INC(etharp.proterr); 19 ETHARP_STATS_INC(etharp.drop); 20 MIB2_STATS_NETIF_INC(netif, ifinerrors); 21 goto free_and_return; 22 } 23 24 if (p->if_idx == NETIF_NO_INDEX) 25 { 26 p->if_idx = netif_get_index(netif); 27 } 28 29 /* ethhdr指针指向以太网帧头部,并且强制转换成eth_hdr结构 */ 30 ethhdr = (struct eth_hdr *)p->payload; (1) 31 32 type = ethhdr->type; 33 34 if (ethhdr->dest.addr[0] & 1) 35 { 36 /* 这可能是多播或广播数据包 */ 37 if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) 38 { 39 if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) && 40 (ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) 41 { 42 /* 将pbuf标记为链路层多播 */ 43 p->flags |= PBUF_FLAG_LLMCAST; (2) 44 } 45 } 46 47 else if (eth_addr_cmp(&ethhdr->dest, &ethbroadcast)) 48 { 49 /* 将pbuf标记为链路层广播 */ 50 p->flags |= PBUF_FLAG_LLBCAST; (3) 51 } 52 } 53 54 switch (type) 55 { 56 /* 如果是IP数据报 */ 57 case PP_HTONS(ETHTYPE_IP): 58 if (!(netif->flags & NETIF_FLAG_ETHARP)) 59 { 60 goto free_and_return; 61 } 62 /* 跳过以太网首部 */ 63 if (pbuf_remove_header(p, next_hdr_offset)) (4) 64 { 65 goto free_and_return; 66 } 67 else 68 { 69 /* 传递到IP协议去处理 */ 70 ip4_input(p, netif); (5) 71 } 72 break; 73 74 //对于是ARP包 75 case PP_HTONS(ETHTYPE_ARP): 76 if (!(netif->flags & NETIF_FLAG_ETHARP)) 77 { 78 goto free_and_return; 79 } 80 /* 跳过以太网首部 */ 81 if (pbuf_remove_header(p, next_hdr_offset)) (6) 82 { 83 ETHARP_STATS_INC(etharp.lenerr); 84 ETHARP_STATS_INC(etharp.drop); 85 goto free_and_return; 86 } 87 else 88 { 89 /*传递到ARP协议处理 */ 90 etharp_input(p, netif); (7) 91 } 92 break; 93 94 //如果支持PPPOE 95 #if PPPOE_SUPPORT 96 case PP_HTONS(ETHTYPE_PPPOEDISC): 97 pppoe_disc_input(netif, p); 98 break; 99 100 case PP_HTONS(ETHTYPE_PPPOE): 101 pppoe_data_input(netif, p); 102 break; 103 #endif /* PPPOE_SUPPORT */ 104 105 //如果支持ipv6 106 #if LWIP_IPV6 107 case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */ 108 /* skip Ethernet header */ 109 if ((p->len < next_hdr_offset) || 110 pbuf_remove_header(p, next_hdr_offset)) 111 { 112 goto free_and_return; 113 } 114 else 115 { 116 /* pass to IPv6 layer */ 117 ip6_input(p, netif); 118 } 119 break; 120 #endif /* LWIP_IPV6 */ 121 122 default: 123 #ifdef LWIP_HOOK_UNKNOWN_ETH_PROTOCOL 124 if (LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(p, netif) == ERR_OK) 125 { 126 break; 127 } 128 #endif 129 ETHARP_STATS_INC(etharp.proterr); 130 ETHARP_STATS_INC(etharp.drop); 131 MIB2_STATS_NETIF_INC(netif, ifinunknownprotos); 132 goto free_and_return; 133 } 134 135 return ERR_OK; 136 137 free_and_return: 138 pbuf_free(p); 139 return ERR_OK; 140 } ``` (1):ethhdr指针指向以太网帧首部,并且强制转换成eth_hdr结构,这是为了方便对以太网帧首部进行操作。 (2):如果目标IP地址的第一个字节的bit0是1,那么有可能是多播或者是广播数据包,所以,还需要进行判断,如果是多播的,就将pbuf标记为链路层多播。 (3):如果是广播的,就将pbuf标记为链路层广播。 (4):如果数据包是ETHTYPE_IP类型,则调用pbuf_remove_header()函数跳过以太网帧首部,方便对数据进行操作。 (5):除去以太网帧首部成功,调用ip4_input()函数将数据包递交到IP协议去处理,对于IP层的处理,我们在后面的章节中讲解。 (6):跳过以太网帧首部。 (7):除去以太网帧首部成功,调用etharp_input ()函数将数据包递交到ARP协议去处理。