【lwip】07-鏈路層收發以太網數據幀源碼分析( 二 )

  • 多播地址 。
    • 以太網要求多播MAC地址第一個bit(最先發出)為1 。
      • IPV4對應的:01:00:5E:00:00:00 —— 01:00:5E:7F:FF:FF
      • IPV6對應的:33:33:xx:xx:xx:xx
  • 廣播地址 。
    • 以太網要求廣播地址48bit全為1 。FF:FF:FF:FF:FF:FF
  • 7.5 以太網幀報文數據結構以太網首部數據結構:struct eth_hdr
    /** Ethernet header */struct eth_hdr {#if ETH_PAD_SIZEPACK_STRUCT_FLD_8(u8_t padding[ETH_PAD_SIZE]); /* 以太網幀前的填充字段,使后面數據字段地址和系統對齊 */#endifPACK_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_tethernet_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_SETvlan_prio_vid = LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type); /* 使用鉤子函數來處理獲取VLAN幀的TCI字段 */#elif LWIP_VLAN_PCPvlan_prio_vid = -1;if (netif->hints && (netif->hints->tci >= 0)) {vlan_prio_vid = (u16_t)netif->hints->tci; /* 直接從網卡中獲取VLAN幀的TCI字段 */}#endifif (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(&ethhdr->dest, dst, ETH_HWADDR_LEN); /* 目標MAC字段 */SMEMCPY(&ethhdr->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_tethernet_input(struct pbuf *p, struct netif *netif){struct eth_hdr *ethhdr;u16_t type;#if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6u16_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_VLANif (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_CHECKif (!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_NETIFnetif = 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_IPV4if ((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_IPV6else 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(&ethhdr->dest, &ethbroadcast)) {/* FF:FF:FF:FF:FF:FF *//* 將pbuf標記為鏈路層廣播 */p->flags |= PBUF_FLAG_LLBCAST;}}switch (type) { /* 以太網幀協議類型字段處理 */#if LWIP_IPV4 && LWIP_ARPcase 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_SUPPORTcase 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_IPV6case 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_PROTOCOLif (LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(p, netif) == ERR_OK) { /* 其它協議的以太網幀 , 可用鉤子函數處理 */break; /* 是用戶需要的以太網數據幀 */}#endifETHARP_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; /* 以太網數據幀無效,丟棄這個數據包 */}

    推薦閱讀