

【lwip】07-链路层收发以太网数据帧源码分析 - 李柱明
source link: https://www.cnblogs.com/lizhuming/p/16845412.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

参考lwip的ethernet.c
和ethernet.h
文件。
原文:李柱明博客园
7.1 链路层概述
简单概述。
相关术语:
- 结点(node):运行链路层协议的设备。如主机、路由器、交换机和WiFi接入点。
- 链路(link):沿着通信路径连接相邻结点的通信信道。
- 数据帧:链路层每一帧数据包size有限,如果上层传递下来的数据报size超出链路层每帧能接收的数据size极限,就会分片。(这里不细说)
在TCP/IP协议族中,链路层主要有三个目的:
- 为IP模块发送和接收IP数据报;
- 为ARP模块发送ARP请求和接收ARP应答;
- 为RARP发送RARP请求和接收RARP应答。
TCP/IP支持多种不同的链路层协议,这取决于网络所使用的硬件,如以太网、令牌环网、FDDI(光纤分布式数据接口)及RS-232串行线路等。
数据报从源主机传输到目标主机时,数据报必须通过端到端路径上的各段链路才能达到目标主机。
各种端到端的链路层协议由具体的端到端决定,链路层设备只需要把数据报封装在自己的链路层帧并转发到下一个端设备即可。
7.2 MAC地址的基本概念
MAC Address(Media Access Control Address),亦称为EHA(Ethernet Hardware Address)、硬件地址、物理地址(Physical Address)。
在OSI模型中,第三层网络层负责 IP地址,第二层数据链路层则负责 MAC位址。
所以一个主机会有一个IP地址,而每个网络位置会有一个专属于它的MAC位址。
MAC地址组成(48字节):前3个字节表示组织唯一标志符(OUI)+后3个字节由厂家自行分配的扩展标识符。
7.3 以太网帧结构
IEEE 802.2/802.3(RFC 1042)和以太网的封装格式(RFC 894)参考图:
7.4 以太网帧结构
IEEE 802.2/802.3(RFC 1042)和以太网的封装格式(RFC 894)参考图:
目标MAC地址(6字节):这个字段包含目标网卡的MAC地址,当一个网卡收到一个以太网数据帧。
- 如果该数据帧的目标地址是网卡自身的MAC地址或者是MAC广播地址,它都将该帧的数据字段的内容传递给网络层;
- 如果收到了其他MAC地址的帧,则将该数据帧丢弃。
源MAC地址(6字节):这个字段包含了传输该帧到局域网上的适配器的MAC地址。
类型/长度字段(2字节):类型字段允许以太网复用多种网络层协议。是表示当前以太网帧的数据区是哪个协议的数据包。
-
0x0800:表示当前数据帧中装载的数据为IPV4数据报。
-
0x0806:表示当前数据帧中装载的数据为ARP数据报。
-
0x0835:表示当前数据帧中装载的数据为RARP数据报。
-
- Ethernet II和IEEE802.3的帧格式主要的不同点在于前者定义的2字节的类型,而后者定义的是2字节的长度。类型字段和长度字段互斥,但是类型值和长度值是不相同的,以此来区别两种帧格式。
- 如果该字段值大于等于0x0600时,表示数据包中的协议类型,反之表示长度。
- 长度字段表示它后续数据的字节长度,但不包括CRC校验码。
数据字段(46~1500字节):这个字段承载了上层数据报。
- 以太网的最大传输单元(MTU)是1500字节。这意味着如果IP数据报超过了1500字节,则主机必须将该数据报分片。
- 数据字段的最小长度是46字节,如果数据报小于46字节,数据报必须被填充到46字节。当采用填充时,传递到网络层的数据包括数据报和填充部分,网络层使用IP数据报首部中的长度字段来去除填充部分。
CRC(4字节):CRC字段包含了以太网的差错校验信息。
在以太网中MAC地址可分为3类:
-
单播地址。
- 通常是对应指定网卡。
- 以太网要求单播MAC地址第一个bit(最先发出)为0。意思就是设备的MAC地址第一个bit必须是0,因为多播的第一个bit为1,这样多播地址就不会与任务网卡的mac地址冲突。
-
多播地址。
-
以太网要求多播MAC地址第一个bit(最先发出)为1。
- IPV4对应的:
01:00:5E:00:00:00
——01:00:5E:7F:FF:FF
- IPV6对应的:
33:33:xx:xx:xx:xx
- IPV4对应的:
-
-
广播地址。
- 以太网要求广播地址48bit全为1。
FF:FF:FF:FF:FF:FF
- 以太网要求广播地址48bit全为1。
7.5 以太网帧报文数据结构
以太网首部数据结构:struct eth_hdr
/** Ethernet header */ struct eth_hdr { #if ETH_PAD_SIZE PACK_STRUCT_FLD_8(u8_t padding[ETH_PAD_SIZE]); /* 以太网帧前的填充字段,使后面数据字段地址和系统对齐 */ #endif PACK_STRUCT_FLD_S(struct eth_addr dest); /* 目标MAC地址字段 */ PACK_STRUCT_FLD_S(struct eth_addr src); /* 源MAC地址字段 */ PACK_STRUCT_FIELD(u16_t type); /* 协议类型字段 */ } PACK_STRUCT_STRUCT;
7.6 发送以太网数据帧
以太网链路层发包使用ethernet_output()
函数。
主要内容:
- 填充以太网帧各个字段,如有VLAN,则VLAN也填充。
- 通过链路层发出:
netif->linkoutput(netif, p);
/** * @ingroup ethernet * Send an ethernet packet on the network using netif->linkoutput(). * The ethernet header is filled in before sending. * * @see LWIP_HOOK_VLAN_SET * * @param netif the lwIP network interface on which to send the packet * @param p the packet to send. pbuf layer must be @ref PBUF_LINK. * @param src the source MAC address to be copied into the ethernet header * @param dst the destination MAC address to be copied into the ethernet header * @param eth_type ethernet type (@ref lwip_ieee_eth_type) * @return ERR_OK if the packet was sent, any other err_t on failure */ err_t ethernet_output(struct netif * netif, struct pbuf * p, const struct eth_addr * src, const struct eth_addr * dst, u16_t eth_type) { struct eth_hdr *ethhdr; u16_t eth_type_be = lwip_htons(eth_type); #if ETHARP_SUPPORT_VLAN && (defined(LWIP_HOOK_VLAN_SET) || LWIP_VLAN_PCP) s32_t vlan_prio_vid; #ifdef LWIP_HOOK_VLAN_SET vlan_prio_vid = LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type); /* 使用钩子函数来处理获取VLAN帧的TCI字段 */ #elif LWIP_VLAN_PCP vlan_prio_vid = -1; if (netif->hints && (netif->hints->tci >= 0)) { vlan_prio_vid = (u16_t)netif->hints->tci; /* 直接从网卡中获取VLAN帧的TCI字段 */ } #endif if (vlan_prio_vid >= 0) { /* 如果需要开启VLAN标签,就需要在以太网帧中组建VLAN字段 */ struct eth_vlan_hdr *vlanhdr; LWIP_ASSERT("prio_vid must be <= 0xFFFF", vlan_prio_vid <= 0xFFFF); if (pbuf_add_header(p, SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) != 0) { /* pbuf的payload往前偏移,包含以太网首部(包含VLAN字段) */ goto pbuf_header_failed; } vlanhdr = (struct eth_vlan_hdr *)(((u8_t *)p->payload) + SIZEOF_ETH_HDR); /* 这里注意偏移。了解带VLAN标签的以太网帧报文就知道下面的操作 */ vlanhdr->tpid = eth_type_be; /* 以太网的类型字段 */ vlanhdr->prio_vid = lwip_htons((u16_t)vlan_prio_vid); /* VLAN标签的TCI字段 */ eth_type_be = PP_HTONS(ETHTYPE_VLAN); /* 这里才是VLAN的TPID字段(代码实现的手法) */ } else #endif /* ETHARP_SUPPORT_VLAN && (defined(LWIP_HOOK_VLAN_SET) || LWIP_VLAN_PCP) */ { if (pbuf_add_header(p, SIZEOF_ETH_HDR) != 0) { /* pbuf的payload往前偏移,包含以太网首部 */ goto pbuf_header_failed; } } LWIP_ASSERT_CORE_LOCKED(); /* tcpip内核上锁确认 */ ethhdr = (struct eth_hdr *)p->payload; /* 指针赋值 */ ethhdr->type = eth_type_be; /* 协议类型字段/如果开启了VLAN,这里才是VLAN的TPID字段 */ SMEMCPY(ðhdr->dest, dst, ETH_HWADDR_LEN); /* 目标MAC字段 */ SMEMCPY(ðhdr->src, src, ETH_HWADDR_LEN); /* 源MAC字段 */ LWIP_ASSERT("netif->hwaddr_len must be 6 for ethernet_output!", (netif->hwaddr_len == ETH_HWADDR_LEN)); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethernet_output: sending packet %p\n", (void *)p)); /* 通过网卡发送以太网帧 */ return netif->linkoutput(netif, p); pbuf_header_failed: LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("ethernet_output: could not allocate room for header.\n")); LINK_STATS_INC(link.lenerr); return ERR_BUF; }
7.7 接收以太网数据帧
以太网链路层收包使用ethernet_input()
函数。
该函数主要是根据以太网帧首部的类型字段,把包分发到不同的协议处理。
IP数据包丢到:ip_input()
ARP数据包丢到:etharp_input()
/** * @ingroup lwip_nosys * Process received ethernet frames. Using this function instead of directly * calling ip_input and passing ARP frames through etharp in ethernetif_input, * the ARP cache is protected from concurrent access.<br> * Don't call directly, pass to netif_add() and call netif->input(). * * @param p the received packet, p->payload pointing to the ethernet header * @param netif the network interface on which the packet was received * * @see LWIP_HOOK_UNKNOWN_ETH_PROTOCOL * @see ETHARP_SUPPORT_VLAN * @see LWIP_HOOK_VLAN_CHECK */ err_t ethernet_input(struct pbuf *p, struct netif *netif) { struct eth_hdr *ethhdr; u16_t type; #if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6 u16_t next_hdr_offset = SIZEOF_ETH_HDR; #endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ LWIP_ASSERT_CORE_LOCKED(); if (p->len <= SIZEOF_ETH_HDR) { /* 只有一个以太网报头(或更少)的数据包,不处理 */ ETHARP_STATS_INC(etharp.proterr); ETHARP_STATS_INC(etharp.drop); MIB2_STATS_NETIF_INC(netif, ifinerrors); goto free_and_return; } /* 找到以太网首部字段 */ ethhdr = (struct eth_hdr *)p->payload; LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n", (unsigned char)ethhdr->dest.addr[0], (unsigned char)ethhdr->dest.addr[1], (unsigned char)ethhdr->dest.addr[2], (unsigned char)ethhdr->dest.addr[3], (unsigned char)ethhdr->dest.addr[4], (unsigned char)ethhdr->dest.addr[5], (unsigned char)ethhdr->src.addr[0], (unsigned char)ethhdr->src.addr[1], (unsigned char)ethhdr->src.addr[2], (unsigned char)ethhdr->src.addr[3], (unsigned char)ethhdr->src.addr[4], (unsigned char)ethhdr->src.addr[5], lwip_htons(ethhdr->type))); type = ethhdr->type; #if ETHARP_SUPPORT_VLAN if (type == PP_HTONS(ETHTYPE_VLAN)) { struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr *)(((char *)ethhdr) + SIZEOF_ETH_HDR); next_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR; /* 找到下一个协议层的首部。这里就是以太网帧首部长度 */ if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) { /* 只有ethernet/vlan报头(或更少)的数据包,不处理 */ ETHARP_STATS_INC(etharp.proterr); ETHARP_STATS_INC(etharp.drop); MIB2_STATS_NETIF_INC(netif, ifinerrors); goto free_and_return; } #if defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */ #ifdef LWIP_HOOK_VLAN_CHECK if (!LWIP_HOOK_VLAN_CHECK(netif, ethhdr, vlan)) { /* 优先使用VLAN钩子函数的过滤 */ #elif defined(ETHARP_VLAN_CHECK_FN) if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) { /* ETHARP_VLAN_CHECK_FN函数的过滤 */ #elif defined(ETHARP_VLAN_CHECK) if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { /* 指定接收一个VLAN */ #endif /* 静默忽略此报文:不是我们需要的VLAN */ pbuf_free(p); return ERR_OK; } #endif /* defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */ type = vlan->tpid; /* 这个字段其实就是以太网首部的协议类型字段 */ } #endif /* ETHARP_SUPPORT_VLAN */ #if LWIP_ARP_FILTER_NETIF netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, lwip_htons(type)); /* 一个硬件映射多个IP。找到对应的netif */ #endif /* LWIP_ARP_FILTER_NETIF*/ if (p->if_idx == NETIF_NO_INDEX) { /* pbuf标记对应netif标识 */ p->if_idx = netif_get_index(netif); } if (ethhdr->dest.addr[0] & 1) { /* 目标MAC分析,首个bit为1,说明是非单播包 */ /* 多播或者广播包 */ if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) { /* 0x01 */ #if LWIP_IPV4 if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) && /* 0x00 */ (ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) { /* 0x5e */ /* 01-00-5e-开头的为IPV4链路层组播包:将pbuf标记为链路层组播 */ p->flags |= PBUF_FLAG_LLMCAST; } #endif /* LWIP_IPV4 */ } #if LWIP_IPV6 else if ((ethhdr->dest.addr[0] == LL_IP6_MULTICAST_ADDR_0) && /* 0x33 */ (ethhdr->dest.addr[1] == LL_IP6_MULTICAST_ADDR_1)) { /* 0x33 */ /* 33-33-开头的为IPV6链路层组播包:将pbuf标记为链路层组播 */ p->flags |= PBUF_FLAG_LLMCAST; } #endif /* LWIP_IPV6 */ else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) {/* FF:FF:FF:FF:FF:FF */ /* 将pbuf标记为链路层广播 */ p->flags |= PBUF_FLAG_LLBCAST; } } switch (type) { /* 以太网帧协议类型字段处理 */ #if LWIP_IPV4 && LWIP_ARP case PP_HTONS(ETHTYPE_IP): /* IPv4数据包 */ if (!(netif->flags & NETIF_FLAG_ETHARP)) { /* 如果对应的netif不是以太网设备,那当前数据包不能流入这个netif */ goto free_and_return; } /* 链路层处理完毕,pbuf数据区指向跳过以太网首部,指向以太网帧数据字段,然后递交给网络层处理 */ if (pbuf_remove_header(p, next_hdr_offset)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ethernet_input: IPv4 packet dropped, too short (%"U16_F"/%"U16_F")\n", p->tot_len, next_hdr_offset)); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet")); goto free_and_return; } else { /* 转交给ipv4模块处理 */ ip4_input(p, netif); } break; case PP_HTONS(ETHTYPE_ARP): /* ARP数据包 */ if (!(netif->flags & NETIF_FLAG_ETHARP)) { /* 如果对应的netif不是以太网设备,那当前数据包不能流入这个netif */ goto free_and_return; } /* 链路层处理完毕,pbuf数据区指向跳过以太网首部,指向以太网帧数据字段,然后递交给网络层处理 */ if (pbuf_remove_header(p, next_hdr_offset)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ethernet_input: ARP response packet dropped, too short (%"U16_F"/%"U16_F")\n", p->tot_len, next_hdr_offset)); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet")); ETHARP_STATS_INC(etharp.lenerr); ETHARP_STATS_INC(etharp.drop); goto free_and_return; } else { /* 转交给ARP模块处理 */ etharp_input(p, netif); } break; #endif /* LWIP_IPV4 && LWIP_ARP */ #if PPPOE_SUPPORT case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP以太网上发现阶段的数据包 */ /* 转交给PPP分析并处理发现阶段的数据包 */ pppoe_disc_input(netif, p); break; case PP_HTONS(ETHTYPE_PPPOE): /* PPP以太网上会话阶段的数据包 */ /* 转交给PPP分析并处理会话节点的数据包 */ pppoe_data_input(netif, p); break; #endif /* PPPOE_SUPPORT */ #if LWIP_IPV6 case PP_HTONS(ETHTYPE_IPV6): /* IPv6数据包 */ /* 链路层处理完毕,pbuf数据区指向跳过以太网首部,指向以太网帧数据字段,然后递交给网络层处理 */ if ((p->len < next_hdr_offset) || pbuf_remove_header(p, next_hdr_offset)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ethernet_input: IPv6 packet dropped, too short (%"U16_F"/%"U16_F")\n", p->tot_len, next_hdr_offset)); goto free_and_return; } else { /* 转交给IPV6模块处理 */ ip6_input(p, netif); } break; #endif /* LWIP_IPV6 */ default: #ifdef LWIP_HOOK_UNKNOWN_ETH_PROTOCOL if (LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(p, netif) == ERR_OK) { /* 其它协议的以太网帧,可用钩子函数处理 */ break; /* 是用户需要的以太网数据帧 */ } #endif ETHARP_STATS_INC(etharp.proterr); ETHARP_STATS_INC(etharp.drop); MIB2_STATS_NETIF_INC(netif, ifinunknownprotos); goto free_and_return; /* 本协议栈无法处理的以太网数据帧,直接丢弃 */ } /* 以太网数据帧有效,已经转交处理了 */ return ERR_OK; free_and_return: pbuf_free(p); return ERR_OK; /* 以太网数据帧无效,丢弃这个数据包 */ }
7.8 虚拟局域网VLAN源码分析
7.8.1 以太网标准帧和VLAN帧的区别
以太网标准数据帧报文及带VLAN标签的以太网标准数据帧报文的差异:
VLAN字段是插入在以太网首部字段中。再看代码实现,就明白了。
7.8.2 以太网发送带VLAN数据帧
VLAN字段数据结构:
- TPID和TCI字段倒置。就是为了代码简易。
/** VLAN header inserted between ethernet header and payload * if 'type' in ethernet header is ETHTYPE_VLAN. * See IEEE802.Q */ struct eth_vlan_hdr { PACK_STRUCT_FIELD(u16_t prio_vid); PACK_STRUCT_FIELD(u16_t tpid); } PACK_STRUCT_STRUCT;
组建带VLAN标签的以太网帧代码段:
- 因为只能偏移整个以太网帧首部,多偏移了类型字段的两个字节。
- 所以按上述VLAN数据结构来看,
ACK_STRUCT_FIELD(u16_t tpid);
实际指向的是以太网类型字段。 - 而为了兼容后面代码一致性,
eth_type_be = PP_HTONS(ETHTYPE_VLAN);
才是真正的VLAN的TPID字段。
vlanhdr = (struct eth_vlan_hdr *)(((u8_t *)p->payload) + SIZEOF_ETH_HDR); /* 这里注意偏移。了解带VLAN标签的以太网帧报文就知道下面的操作 */ vlanhdr->tpid = eth_type_be; /* 以太网的类型字段 */ vlanhdr->prio_vid = lwip_htons((u16_t)vlan_prio_vid); /* VLAN标签的TCI字段 */ eth_type_be = PP_HTONS(ETHTYPE_VLAN); /* 这里才是VLAN的TPID字段(代码实现的手法) */
7.8.3 以太网接收带VLAN数据帧
ethernet_input()
接收到数据帧时VLAN部分代码处理:
如果对VLAN不感兴趣,上面代码可以直接忽略VLAN部分,这样会更加便于分析。
其中检查过滤VLAN,有三种方式(仅能选其一),优先级又高到低,描述如下:
LWIP_HOOK_VLAN_CHECK
:VLAN钩子函数,检查当前数据帧是否是需要的VLAN。被ethernet_input()
函数调用。ETHARP_VLAN_CHECK_FN
:也是检查当前数据帧是否是需要的VLAN。返回1表示接受该数据帧。ETHARP_VLAN_CHECK
:指定一个VLAN ID,整个协议栈只接收该VLAN的流量。
7.8.4 开启VLAN功能ETHARP_SUPPORT_VLAN
/** * ETHARP_SUPPORT_VLAN==1: support receiving and sending ethernet packets with * VLAN header. See the description of LWIP_HOOK_VLAN_CHECK and * LWIP_HOOK_VLAN_SET hooks to check/set VLAN headers. * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check. * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted. * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted. * Alternatively, define a function/define ETHARP_VLAN_CHECK_FN(eth_hdr, vlan) * that returns 1 to accept a packet or 0 to drop a packet. */ #if !defined ETHARP_SUPPORT_VLAN || defined __DOXYGEN__ #define ETHARP_SUPPORT_VLAN 1 #endif
LWIP_HOOK_VLAN_CHECK
钩子格式说明:
/** * LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr): * Called from ethernet_input() if VLAN support is enabled * Signature:\code{.c} * int my_hook(struct netif *netif, struct eth_hdr *eth_hdr, struct eth_vlan_hdr *vlan_hdr); * \endcode * Arguments: * - netif: struct netif on which the packet has been received * - eth_hdr: struct eth_hdr of the packet * - vlan_hdr: struct eth_vlan_hdr of the packet * Return values: * - 0: Packet must be dropped. * - != 0: Packet must be accepted. */ #ifdef __DOXYGEN__ #define LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr) #endif
7.9 一个硬件映射到多个IP
一个硬件映射到多个IP的实现需要开启LWIP_ARP_FILTER_NETIF
宏并定义LWIP_ARP_FILTER_NETIF_FN()
函数。
LWIP_ARP_FILTER_NETIF_FN()
函数会在ethernet_input()
以太网接收到数据帧处理时被调用,会根据数据帧的内容更新出的netif。
该函数代码实现思路说明:
- 根据以太网数据帧协议类型区别出IP、ARP或者其它协议。
- 再根据协议分析出目标协议地址IP。
- 再遍历netif链表,匹配IP。
注意,该函数调用是在VLAN过滤后才被调用的。因为VLAN属于链路层,映射多个IP的判断字段属于网络层。
/** Define this to 1 and define LWIP_ARP_FILTER_NETIF_FN(pbuf, netif, type) * to a filter function that returns the correct netif when using multiple * netifs on one hardware interface where the netif's low-level receive * routine cannot decide for the correct netif (e.g. when mapping multiple * IP addresses to one hardware interface). */ #ifndef LWIP_ARP_FILTER_NETIF #define LWIP_ARP_FILTER_NETIF 1 #endif
__EOF__
Recommend
-
44
新浪科技讯 2月10日晚间消息,春节假期将尽,微信发布了除夕至初五的春节数据报告。在此期间,有8.23亿人收发微信红包,同比增长7.12%。微信中共发生的...
-
60
本次我们进行的是Trunk链路的配置实验及以太网通道实验实验环境:软件准备我之前文章里提到的GNS3、CRT和Wireshark首先我们进入GNS3界面,点击左侧的路由器图标,拖取两台路由器到拓扑操作区,再点击PC图标,选择VPC拖取四台PC机到拓扑操作区;同时对两台路由器进...
-
52
在网络通信中,我们可能既要通过 socket 去发送数据也要通过 socket 来收取数据。 那么一般的网络通信框架是如何收发数据的呢? 注意,这里讨论的范围是基于各种 IO 复用函数(select、poll、epoll 等)来判断 socket...
-
7
如果对硬件任务内核切换不感兴趣的同学可以跳过。 由于任务源码分析开始涉及到接口层,所以在学习源码前,先了解下posix接口层的设计。 本系列接口层会讲解两个: posix标准接口。因为本教程demo在linux上跑freertos时实际使用的就...
-
4
大家好,我是 华仔, 又跟大家见面了。今天我们主要对 Kafka 网络层收发流程进行总结下,本系列总共分...
-
8
主要分析网络接口概念、网卡数据结构、网络接口、环回接口实现等等。 6.1 概念引入 网络接口(以太网接口)是硬件接口(网络接口又可以称之为网卡)。 LWIP 是软件那么而怎样让硬件和软件无缝连接起来...
-
4
主要简述TCPIP协议族相关的。 ARP协议源码在etharp.c和etharp.h中,也是本次笔记的主要内容。 ARP源码实现的重要数据结构: ARP缓存表。 ARP报文。 原文:
-
8
默认主讲ipv4。 概念性的内容简单过一遍即可,主要还是在源码实现方面。 原文:李柱明博客:https://www.cnblogs.com/lizhuming/p/16859723.html
-
5
参考:RFC 792 原文:李柱明博客:https://www.cnblogs.com/lizhuming/p/16861945.html 10.1 ICMP简介 IP 协议是一种不可靠...
-
5
lwip_14_TCP协议之可靠传输的实现 前面章节太长了,不得不分开。 这里已源码为主,默认读者已知晓概念或原理,概念或原理可以参考前面章节,有分析。 参考:李柱明博客:
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK