• 【lwip】06网络接口层分析



    前言

    主要分析网络接口概念、网卡数据结构、网络接口、环回接口实现等等。

    参考:

    6.1 概念引入

    网络接口(以太网接口)是硬件接口(网络接口又可以称之为网卡)。

    LWIP 是软件那么而怎样让硬件和软件无缝连接起来呢?

    而且网卡又多种多样,怎样才能让 LWIP 使用同样的软件兼容不同的硬件平台?

    LWIP 中使用了一个netif结构体来描述网卡但是网卡是直接和硬件平台打交道的:

    • 用户提供最底层接口函数。

    • LWIP 提供统一的 API。

    • 举例:

      • 收:如网卡的初始化和网卡的收发数据,当 LWIP 底层得到数据之后,才会传入到内核中去处理。
      • 发:LWIP 内核需要发送数据包的时候,也需要调用网卡的发送函数。
    • LWIP 中的 etherneif.c 文件的函数通常为硬件打交道的底层函数,当有数据需要通过网卡接收或者发送数据的时候就会被调用,通过 LWIP 的协议栈的内部进行处理后,从应用层就能得到数据或者可以发送数据。

    小总结:

    简单来说,netif 是 LWIP 抽象出来的网卡,LWIP 协议栈可以使用多种不同接口,而 etherneif.c 文件则提供了以太网网卡 netif 的抽象,每个网卡有不同的实现方式,每户只需要修改 ethernetif.c 文件即可。

    小笔记:

    • 网卡就是一个水管接口,把上层tcpip协议栈和底层数据链路对接起来,使其数据流通。

      • 收:底层数据链路的数据,经过网卡,网卡把这些数据解析好,然后格式化为上层协议栈需要的数据格式,再把这些数据交给上层协议栈。
      • 发:上层协议栈的数据,经过网卡,网卡把这些数据解析好,然后格式化为底层数据链路需要的数据格式,再把这些数据交给底层让其发出去。
    • 网卡底层自由

      • 由于网络接口需要对接的上层包含了链路层的以太网帧处理ethnet_input()ethnet_output()
      • 所以网络接口底层对接的可以直接是自由数据,包括UART、SPI、以太网设备、GPRS、其它线程接过来的数据等等任何数据流。

    6.2 网络接口层数据概念流图


    图源自李柱明

    6.3 网卡收包程序流图


    图源自李柱明

    6.4 网卡数据结构

    6.4.1 struct netif源码

    /** Generic data structure used for all lwIP network interfaces.
     *  The following fields should be filled in by the initialization
     *  function for the device driver: hwaddr_len, hwaddr[], mtu, flags */
    struct netif {
    #if !LWIP_SINGLE_NETIF
      /** pointer to next in linked list */
      struct netif *next;
    #endif
    
    #if LWIP_IPV4
      /** IP address configuration in network byte order */
      ip_addr_t ip_addr;
      ip_addr_t netmask;
      ip_addr_t gw;
    #endif /* LWIP_IPV4 */
    #if LWIP_IPV6
      /** Array of IPv6 addresses for this netif. */
      ip_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES];
      /** The state of each IPv6 address (Tentative, Preferred, etc).
       * @see ip6_addr.h */
      u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES];
    #if LWIP_IPV6_ADDRESS_LIFETIMES
      /** Remaining valid and preferred lifetime of each IPv6 address, in seconds.
       * For valid lifetimes, the special value of IP6_ADDR_LIFE_STATIC (0)
       * indicates the address is static and has no lifetimes. */
      u32_t ip6_addr_valid_life[LWIP_IPV6_NUM_ADDRESSES];
      u32_t ip6_addr_pref_life[LWIP_IPV6_NUM_ADDRESSES];
    #endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
    #endif /* LWIP_IPV6 */
      /** This function is called by the network device driver
       *  to pass a packet up the TCP/IP stack. */
      netif_input_fn input;
    #if LWIP_IPV4
      /** This function is called by the IP module when it wants
       *  to send a packet on the interface. This function typically
       *  first resolves the hardware address, then sends the packet.
       *  For ethernet physical layer, this is usually etharp_output() */
      netif_output_fn output;
    #endif /* LWIP_IPV4 */
      /** This function is called by ethernet_output() when it wants
       *  to send a packet on the interface. This function outputs
       *  the pbuf as-is on the link medium. */
      netif_linkoutput_fn linkoutput;
    #if LWIP_IPV6
      /** This function is called by the IPv6 module when it wants
       *  to send a packet on the interface. This function typically
       *  first resolves the hardware address, then sends the packet.
       *  For ethernet physical layer, this is usually ethip6_output() */
      netif_output_ip6_fn output_ip6;
    #endif /* LWIP_IPV6 */
    #if LWIP_NETIF_STATUS_CALLBACK
      /** This function is called when the netif state is set to up or down
       */
      netif_status_callback_fn status_callback;
    #endif /* LWIP_NETIF_STATUS_CALLBACK */
    #if LWIP_NETIF_LINK_CALLBACK
      /** This function is called when the netif link is set to up or down
       */
      netif_status_callback_fn link_callback;
    #endif /* LWIP_NETIF_LINK_CALLBACK */
    #if LWIP_NETIF_REMOVE_CALLBACK
      /** This function is called when the netif has been removed */
      netif_status_callback_fn remove_callback;
    #endif /* LWIP_NETIF_REMOVE_CALLBACK */
      /** This field can be set by the device driver and could point
       *  to state information for the device. */
      void *state;
    #ifdef netif_get_client_data
      void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];
    #endif
    #if LWIP_NETIF_HOSTNAME
      /* the hostname for this netif, NULL is a valid value */
      const char*  hostname;
    #endif /* LWIP_NETIF_HOSTNAME */
    #if LWIP_CHECKSUM_CTRL_PER_NETIF
      u16_t chksum_flags;
    #endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
      /** maximum transfer unit (in bytes) */
      u16_t mtu;
    #if LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES
      /** maximum transfer unit (in bytes), updated by RA */
      u16_t mtu6;
    #endif /* LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES */
      /** link level hardware address of this interface */
      u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
      /** number of bytes used in hwaddr */
      u8_t hwaddr_len;
      /** flags (@see @ref netif_flags) */
      u8_t flags;
      /** descriptive abbreviation */
      char name[2];
      /** number of this interface. Used for @ref if_api and @ref netifapi_netif,
       * as well as for IPv6 zones */
      u8_t num;
    #if LWIP_IPV6_AUTOCONFIG
      /** is this netif enabled for IPv6 autoconfiguration */
      u8_t ip6_autoconfig_enabled;
    #endif /* LWIP_IPV6_AUTOCONFIG */
    #if LWIP_IPV6_SEND_ROUTER_SOLICIT
      /** Number of Router Solicitation messages that remain to be sent. */
      u8_t rs_count;
    #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
    #if MIB2_STATS
      /** link type (from "snmp_ifType" enum from snmp_mib2.h) */
      u8_t link_type;
      /** (estimate) link speed */
      u32_t link_speed;
      /** timestamp at last change made (up/down) */
      u32_t ts;
      /** counters */
      struct stats_mib2_netif_ctrs mib2_counters;
    #endif /* MIB2_STATS */
    #if LWIP_IPV4 && LWIP_IGMP
      /** This function could be called to add or delete an entry in the multicast
          filter table of the ethernet MAC.*/
      netif_igmp_mac_filter_fn igmp_mac_filter;
    #endif /* LWIP_IPV4 && LWIP_IGMP */
    #if LWIP_IPV6 && LWIP_IPV6_MLD
      /** This function could be called to add or delete an entry in the IPv6 multicast
          filter table of the ethernet MAC. */
      netif_mld_mac_filter_fn mld_mac_filter;
    #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
    #if LWIP_ACD
      struct acd *acd_list;
    #endif /* LWIP_ACD */
    #if LWIP_NETIF_USE_HINTS
      struct netif_hint *hints;
    #endif /* LWIP_NETIF_USE_HINTS */
    #if ENABLE_LOOPBACK
      /* List of packets to be queued for ourselves. */
      struct pbuf *loop_first; /* 环回数据包缓存的首个pbuf地址 */
      struct pbuf *loop_last; /* 环回数据包缓存的最后一个pbuf地址,用于后面继续有pbuf入队时,从尾部插入。(因为pbuf链表是单向非循环链表) */
    #if LWIP_LOOPBACK_MAX_PBUFS
      u16_t loop_cnt_current; /* 当前有多少个环回数据包未读 */
    #endif /* LWIP_LOOPBACK_MAX_PBUFS */
    #if LWIP_NETIF_LOOPBACK_MULTITHREADING
      /* Used if the original scheduling failed. */
      /* 是否需要把netif_poll()API重新转发到lwip内核线程去跑。在转发失败时,标记下次要重新转发 */
      u8_t reschedule_poll;
    #endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
    #endif /* ENABLE_LOOPBACK */
    };
    

    6.4.2 字段分析

    6.4.2.1 网卡链表

    /** pointer to next in linked list */
      struct netif *next;
    

    LWIP 使用链表来统一管理同一设备的多个网卡。

    netif.c 文件中定义两个全局指针 struct netif *netif_liststruct netif *netif_default

    • netif_list 就是网卡链表指针,指向网卡链表的首节点(第一个网卡)。
    • netif_default 默认网卡。

    6.4.2.2 网络 IP

    #if LWIP_IPV4
      /** IP address configuration in network byte order */
      ip_addr_t ip_addr;
      ip_addr_t netmask;
      ip_addr_t gw;
    #endif /* LWIP_IPV4 */
    

    ip_addr:网络中的 IP 地址。

    netmask:子网掩码。

    gw:网关地址。

    6.4.2.3 接收数据函数input()

    /** This function is called by the network device driver
       *  to pass a packet up the TCP/IP stack. */
      netif_input_fn input;
    

    该函数为网卡北向出口函数,是把底层数据包往协议栈送的,一般是ethernet_input(),送入arp协议处理,再往上送。

    6.4.2.4 网络IP层发送数据函数output()

    #if LWIP_IPV4
      /** This function is called by the IP module when it wants
       *  to send a packet on the interface. This function typically
       *  first resolves the hardware address, then sends the packet.
       *  For ethernet physical layer, this is usually etharp_output() */
      netif_output_fn output;
    #endif /* LWIP_IPV4 */
    

    函数由 IP 层调用,在接口上发送数据包。用户需要编写该函数并使 output 指向它。

    通这个函数的处理步骤是首先解析硬件地址,然后发送数据包。

    对于以太网物理层,该函数通常是 etharp_output(),参数是 pbuf,netif,和 ip_addr 类型。

    注意:其中 ipaddr 代表要将数据包发送到的地址,但不一定数据包最终到达的 ip 地址。比如要发送 ip 数据报到一个并不在本网络的主机上,该数据包要被发送到一个路由器上,这里的 ipaddr 就是路由器的 ip 地址。

    6.4.2.5 链路层发送函数linkoutput()

    /** This function is called by ethernet_output() when it wants
       *  to send a packet on the interface. This function outputs
       *  the pbuf as-is on the link medium. */
      netif_linkoutput_fn linkoutput;
    

    该函数和 output 类似,也需要用户自己实现一个函数,但是只有两个参数,一般是自定义函数 low_level_output()

    当需要在网卡上发送一个数据包时,该函数会被 ethernet_output() 函数调用,在底层发送数据。

    6.4.2.6 出口回调函数

    #if LWIP_NETIF_REMOVE_CALLBACK
      /** This function is called when the netif has been removed */
      netif_status_callback_fn remove_callback;
    #endif /* LWIP_NETIF_REMOVE_CALLBACK */
    

    当 netif 被删除时调用此函数。

    6.4.2.7 用户私有数据

    /** This field can be set by the device driver and could point
       *  to state information for the device. */
      void *state;
    #ifdef netif_get_client_data
      void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];
    #endif
    #if LWIP_IPV6_AUTOCONFIG
      /** is this netif enabled for IPv6 autoconfiguration */
      u8_t ip6_autoconfig_enabled;
    #endif /* LWIP_IPV6_AUTOCONFIG */
    #if LWIP_IPV6_SEND_ROUTER_SOLICIT
      /** Number of Router Solicitation messages that remain to be sent. */
      u8_t rs_count;
    #endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
    #if LWIP_NETIF_HOSTNAME
      /* the hostname for this netif, NULL is a valid value */
      const char*  hostname;
    #endif /* LWIP_NETIF_HOSTNAME */
    #if LWIP_CHECKSUM_CTRL_PER_NETIF
      u16_t chksum_flags;
    #endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
    

    由设备驱动程序设置并指向设备的状态信息,主要将网卡的某些私有数据传递给上层。

    6.4.2.8 最大传输单位

    /** maximum transfer unit (in bytes) */
      u16_t mtu;
    

    6.4.2.9 链路硬件地址长度&地址

    /** number of bytes used in hwaddr */
      u8_t hwaddr_len;
      /** link level hardware address of this interface */
      u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
    

    6.4.2.10 网卡信息状态标志

    /** flags (@see @ref netif_flags) */
      u8_t flags;
    

    网卡状态信息标志位,是很重要的控制字段,它包括网卡的功能使能,广播使能,ARP 使能等重要控制位。

    6.4.2.11 网卡名字

    /** descriptive abbreviation */
      char name[2];
    

    用于保存每一个网卡的名字,用两个字符的名字来标识。

    网卡使用的设备驱动的种类,名字由设备驱动来设置并且应该反映通过网卡表示的硬件的种类。

    如果两个网卡具有相同的网络名字,我们就用 num 字段来区分相同类别的不同网卡。

    6.4.2.12 网卡标识

    /** number of this interface */
      u8_t num;
    

    标识使用同种驱动类型的不同网卡。

    6.4.2.13 网卡提示

    #if LWIP_NETIF_USE_HINTS
      struct netif_hint *hints;
    #endif /* LWIP_NETIF_USE_HINTS */
    

    网卡提示,有些网卡相关的数据保存空间。

    其数据结构:

    struct netif_hint {
    #if LWIP_NETIF_HWADDRHINT
       u8_t addr_hint;
    #endif
    #if LWIP_VLAN_PCP
      /** VLAN hader is set if this is >= 0 (but must be <= 0xFFFF) */
      s32_t tci;
    #endif
     };
    

    比如addr_hint在ARP协议中的用途就是,当当前网卡发送了一包数据时用到了ARP缓存表某条arp entry时,就会保存这个arp entry的索引到addr_hint中,下次这个网卡发送数据时,通过addr_hint获取索引后,直接从这条索引开始遍历ARP缓存表,加速组包时间。

    tci是用于VLAN组包中的字段值。

    6.5 netif 快速使用

    6.5.1 简要步骤

    1. 定义一个 netif 作为网卡设备结构体。

    2. 挂载到 netif_list 链表中:netif_add();

      1. 需要提供网卡初始化函数和网卡协议栈入口函数作为netif_add()的参数传入。
    3. 使能网卡底层(链路层):netif_set_link_up()

    4. 使能网卡上层(协议栈):netif_set_up()

    6.5.2 与 netif 相关的底层函数

    low_level_output()low_level_input()函数是网卡的南向直接操作函数,是对网卡设备的写、读处理。

    相当于网卡设备的驱动范畴的函数。

    主要 API:

    /* 网卡初始化函数 */
    static void low_level_init(struct netif *netif);
    
    /* 网卡的发送函数,
     * 将内核的数据包发送出去,数据包采用pbuf数据结构进行描述 */
    static err_t low_level_output(struct netif *netif, struct pbuf *p);
    
    /* 网卡的数据接收函数,
     * 该函数必须将接收的数据封装成pbuf的形式 */
    static struct pbuf * low_level_input(struct netif *netif);
    

    相关 API:(如以太网)

    /* 上层管理网卡netif的到时候会被调用的函数,
     * 最终还是调用 low_level_init() 函数 */
    err_t ethernetif_init(struct netif *netif);
    
    /* 主要作用就是调用low_level_input()函数从网卡中读取一个数据包,
     * 然后解析该数据包的类型是属于ARP数据包还是IP数据包,
     * 再将包递交给上层。
     * 无操作系统中使用:可以直接使用的函数,因为内核会周期性去处理该接收函数。
     * 有操作系统中系统:一般会将其改写成一个线程的形式,可以周期性去调用low_level_input()网卡接收函数。*/
    void ethernetif_input(void *pParams);
    

    6.6 网卡信息状态标志

    在网卡netif->flags;成员中体现。

    • NETIF_FLAG_UP

      • 网络接口上层是否被使能。
      • 属于一个软件(协议栈)就绪标志,表示lwip协议栈已经合法获取到该IP,lwip协议栈已经准备好接收处理这个网卡的数据了。
      • 相当于一个lwip协议栈内部与外部南向通信的开关阀。
    • NETIF_FLAG_BROADCAST:网络接口是否支持广播。

    • NETIF_FLAG_LINK_UP

      • 网络接口的底层链路是否被使能。
      • 属于一个硬件(链路层)就绪标志,表示当前网卡南向接口使用的数据链路硬件就绪。
    • NETIF_FLAG_ETHARP:网络接口是否支持ARP功能。

    • NETIF_FLAG_ETHERNET:网络接口是否是以太网设备。

    • NETIF_FLAG_IGMP:网络接口是否支持IGMP协议功能。

    • NETIF_FLAG_MLD6:网络接口是否支持MLD6功能。

    /**
     * @defgroup netif_flags Flags
     * @ingroup netif
     * @{
     */
    
    /** Whether the network interface is 'up'. This is
     * a software flag used to control whether this network
     * interface is enabled and processes traffic.
     * It must be set by the startup code before this netif can be used
     * (also for dhcp/autoip).
     */
    #define NETIF_FLAG_UP           0x01U
    /** If set, the netif has broadcast capability.
     * Set by the netif driver in its init function. */
    #define NETIF_FLAG_BROADCAST    0x02U
    /** If set, the interface has an active link
     *  (set by the network interface driver).
     * Either set by the netif driver in its init function (if the link
     * is up at that time) or at a later point once the link comes up
     * (if link detection is supported by the hardware). */
    #define NETIF_FLAG_LINK_UP      0x04U
    /** If set, the netif is an ethernet device using ARP.
     * Set by the netif driver in its init function.
     * Used to check input packet types and use of DHCP. */
    #define NETIF_FLAG_ETHARP       0x08U
    /** If set, the netif is an ethernet device. It might not use
     * ARP or TCP/IP if it is used for PPPoE only.
     */
    #define NETIF_FLAG_ETHERNET     0x10U
    /** If set, the netif has IGMP capability.
     * Set by the netif driver in its init function. */
    #define NETIF_FLAG_IGMP         0x20U
    /** If set, the netif has MLD6 capability.
     * Set by the netif driver in its init function. */
    #define NETIF_FLAG_MLD6         0x40U
    

    6.7 添加网卡netif_add()

    主要内容是:

    • 网卡数据结构内容配置;
    • 把这个网卡插入到网卡链表netif_list

    参数:

    • netif:网卡数据结构资源。
    • ipaddr:网卡IP地址。
    • netmask:网卡子网掩码。
    • gw:网卡网关。
    • state:用户自定义的一些数据。
    • init:网卡初始化函数。各种网卡的初始化不完全一样,所以又用户自己实现。
    • input:网卡北方北向接口。是把数据传入TCPIP协议栈的接口。
    /**
     * @ingroup netif
     * Add a network interface to the list of lwIP netifs.
     *
     * @param netif a pre-allocated netif structure
     * @param ipaddr IP address for the new netif
     * @param netmask network mask for the new netif
     * @param gw default gateway IP address for the new netif
     * @param state opaque data passed to the new netif
     * @param init callback function that initializes the interface
     * @param input callback function that is called to pass
     * ingress packets up in the protocol layer stack.<br>
     * It is recommended to use a function that passes the input directly
     * to the stack (netif_input(), NO_SYS=1 mode) or via sending a
     * message to TCPIP thread (tcpip_input(), NO_SYS=0 mode).<br>
     * These functions use netif flags NETIF_FLAG_ETHARP and NETIF_FLAG_ETHERNET
     * to decide whether to forward to ethernet_input() or ip_input().
     * In other words, the functions only work when the netif
     * driver is implemented correctly!<br>
     * Most members of struct netif should be be initialized by the
     * netif init function = netif driver (init parameter of this function).<br>
     * IPv6: Don't forget to call netif_create_ip6_linklocal_address() after
     * setting the MAC address in struct netif.hwaddr
     * (IPv6 requires a link-local address).
     *
     * @return netif, or NULL if failed.
     */
    struct netif *
    netif_add(struct netif *netif,
    #if LWIP_IPV4
              const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
    #endif /* LWIP_IPV4 */
              void *state, netif_init_fn init, netif_input_fn input)
    {
    #if LWIP_IPV6
      s8_t i;
    #endif
    
      LWIP_ASSERT_CORE_LOCKED();
    
    #if LWIP_SINGLE_NETIF
      if (netif_default != NULL) { /* 单网卡功能监测 */
        LWIP_ASSERT("single netif already set", 0);
        return NULL;
      }
    #endif
    
      /* 网卡数据结构内存资源检验 */
      LWIP_ERROR("netif_add: invalid netif", netif != NULL, return NULL);
      /* 网卡初始化钩子函数校验 */
      LWIP_ERROR("netif_add: No init function given", init != NULL, return NULL);
    
    #if LWIP_IPV4
      if (ipaddr == NULL) {
        ipaddr = ip_2_ip4(IP4_ADDR_ANY); /* 获取IPV4地址 */
      }
      if (netmask == NULL) {
        netmask = ip_2_ip4(IP4_ADDR_ANY); /* 获取子网掩码 */
      }
      if (gw == NULL) {
        gw = ip_2_ip4(IP4_ADDR_ANY); /* 获取网关 */
      }
    
      /* 重置网卡配置状态 */
      ip_addr_set_zero_ip4(&netif->ip_addr);
      ip_addr_set_zero_ip4(&netif->netmask);
      ip_addr_set_zero_ip4(&netif->gw);
      netif->output = netif_null_output_ip4;
    #endif /* LWIP_IPV4 */
    #if LWIP_IPV6
      for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { /* 遍历当前网卡的各个IPV6地址空间 */
        ip_addr_set_zero_ip6(&netif->ip6_addr[i]); /* 重置这个IPV6 */
        netif->ip6_addr_state[i] = IP6_ADDR_INVALID; /* 标记为无效态 */
    #if LWIP_IPV6_ADDRESS_LIFETIMES /* ipv6生存期字段 */
        netif->ip6_addr_valid_life[i] = IP6_ADDR_LIFE_STATIC; /* 静态地址,没有生存期 */
        netif->ip6_addr_pref_life[i] = IP6_ADDR_LIFE_STATIC; /* 静态地址,没有生存期 */
    #endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
      }
      netif->output_ip6 = netif_null_output_ip6; /* 初始化北方南向接口为一个虚拟接口 */
    #endif /* LWIP_IPV6 */
      NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL);
      netif->mtu = 0; /* 重置MTU */
      netif->flags = 0; /* 重置网卡状态 */
    #ifdef netif_get_client_data
      memset(netif->client_data, 0, sizeof(netif->client_data)); /* 清空网卡数据结构用户数据空间 */
    #endif /* LWIP_NUM_NETIF_CLIENT_DATA */
    #if LWIP_IPV6
    #if LWIP_IPV6_AUTOCONFIG
      /* 缺省情况下,IPv6地址自动配置功能处于开启状态 */
      netif->ip6_autoconfig_enabled = 1;
    #endif /* LWIP_IPV6_AUTOCONFIG */
      nd6_restart_netif(netif); /* 使能ipv6网卡,配置路由请求RS次数 */
    #endif /* LWIP_IPV6 */
    #if LWIP_NETIF_STATUS_CALLBACK
      netif->status_callback = NULL; /* 重置网卡上层(协议栈)状态回调 */
    #endif /* LWIP_NETIF_STATUS_CALLBACK */
    #if LWIP_NETIF_LINK_CALLBACK
      netif->link_callback = NULL; /* 重置底层(数据链路)状态回调 */
    #endif /* LWIP_NETIF_LINK_CALLBACK */
    #if LWIP_IGMP
      netif->igmp_mac_filter = NULL; /* 重置(ipv4)IGMP协议以太网MAC过滤表函数 */
    #endif /* LWIP_IGMP */
    #if LWIP_IPV6 && LWIP_IPV6_MLD
      netif->mld_mac_filter = NULL; /* 重置(ipv6)MLD协议以太网MAC过滤表函数 */
    #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
    
      netif->state = state; /* 配置网卡状态 */
      netif->num = netif_num; /* 配置网卡标识 */
      netif->input = input; /* 配置网卡数据到TCPIP协议栈入口函数 */
    
    #if LWIP_ACD
      netif->acd_list = NULL; /* 重置ACD模块链表 */
    #endif /* LWIP_ACD */
      NETIF_RESET_HINTS(netif); /* 重置网卡hints字段 */
    #if ENABLE_LOOPBACK
      netif->loop_first = NULL; /* 重置发送给自己的第一个数据包指针 */
      netif->loop_last = NULL; /* 重置发送给自己的最后一个数据包指针 */
    #if LWIP_LOOPBACK_MAX_PBUFS
      netif->loop_cnt_current = 0; /* 重置发送给自己的pbuf最大缓存包数限制 */
    #endif /* LWIP_LOOPBACK_MAX_PBUFS */
    #if LWIP_NETIF_LOOPBACK_MULTITHREADING
      netif->reschedule_poll = 0; /* 是否在多线程环境下 */
    #endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
    #endif /* ENABLE_LOOPBACK */
    
    #if LWIP_IPV4
      netif_set_addr(netif, ipaddr, netmask, gw); /* 设置网卡IP、子网掩码、网关 */
    #endif /* LWIP_IPV4 */
    
      /* 为netif调用用户指定的初始化函数 */
      if (init(netif) != ERR_OK) { /* 不同的网卡有不同的初始化,所以又用户提供 */
        return NULL;
      }
    #if LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES
      /* 初始化IPv6的MTU为netif驱动设置的MTU。这可以稍后由RA进行更新。 */
      netif->mtu6 = netif->mtu;
    #endif /* LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES */
    
    #if !LWIP_SINGLE_NETIF
      /* 网卡标识唯一性校验。其算法先在历史累加获得,溢出再复位。 */
      {
        struct netif *netif2;
        int num_netifs;
        do {
          if (netif->num == 255) {
            netif->num = 0;
          }
          num_netifs = 0;
          for (netif2 = netif_list; netif2 != NULL; netif2 = netif2->next) {
            LWIP_ASSERT("netif already added", netif2 != netif);
            num_netifs++;
            LWIP_ASSERT("too many netifs, max. supported number is 255", num_netifs <= 255);
            if (netif2->num == netif->num) { /* 已经溢出过了,标识+1直接用 */
              netif->num++;
              break;
            }
          }
        } while (netif2 != NULL);
      }
      if (netif->num == 254) { /* 网卡标识溢出,归零 */
        netif_num = 0;
      } else { /*  */
        netif_num = (u8_t)(netif->num + 1); /* 更新网卡标识全局记录值 */
      }
    
      /* 添加网卡到网卡链表头部 */
      netif->next = netif_list;
      netif_list = netif;
    #endif /* "LWIP_SINGLE_NETIF */
      mib2_netif_added(netif);
    
    #if LWIP_IGMP
      if (netif->flags & NETIF_FLAG_IGMP) { /* 如果网卡支持IGMP功能 */
        igmp_start(netif); /* 开始IGMP处理 */
      }
    #endif /* LWIP_IGMP */
    
      LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP",
                                netif->name[0], netif->name[1]));
    #if LWIP_IPV4
      LWIP_DEBUGF(NETIF_DEBUG, (" addr "));
      ip4_addr_debug_print(NETIF_DEBUG, ipaddr);
      LWIP_DEBUGF(NETIF_DEBUG, (" netmask "));
      ip4_addr_debug_print(NETIF_DEBUG, netmask);
      LWIP_DEBUGF(NETIF_DEBUG, (" gw "));
      ip4_addr_debug_print(NETIF_DEBUG, gw);
    #endif /* LWIP_IPV4 */
      LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
      
      /* 网卡添加完毕,把这个网卡数据结构地址及状态通过回调链表ext_callback,通知到各个地方 */
      netif_invoke_ext_callback(netif, LWIP_NSC_NETIF_ADDED, NULL);
    
      return netif;
    }
    

    6.8 网卡上层协议栈使能netif_set_up()

    netif_set_up()函数用于使能网卡上层协议栈。

    相当于打开网卡北向接口的开关阀,让网卡和协议栈的数据能够流通。

    对应标志位:NETIF_FLAG_UP。表示上层协议栈准备好了,也打通了网卡与协议栈的通道。

    /**
     * @ingroup netif
     * Bring an interface up, available for processing
     * traffic.
     */
    void
    netif_set_up(struct netif *netif)
    {
      LWIP_ASSERT_CORE_LOCKED();
    
      LWIP_ERROR("netif_set_up: invalid netif", netif != NULL, return);
    
      if (!(netif->flags & NETIF_FLAG_UP)) { /* 协议栈还没使能 */
        netif_set_flags(netif, NETIF_FLAG_UP); /* 标记使能协议栈 */
      
        /* 记录网卡协议栈使能时间 */
        MIB2_COPY_SYSUPTIME_TO(&netif->ts);
        /* 网卡状态回调 */
        NETIF_STATUS_CALLBACK(netif);
    
    #if LWIP_NETIF_EXT_STATUS_CALLBACK
        {
          netif_ext_callback_args_t args;
          args.status_changed.state = 1;
          /* 网卡状态静态回调 */
          netif_invoke_ext_callback(netif, LWIP_NSC_STATUS_CHANGED, &args);
        }
    #endif
        /* 发送ARP/IGMP/MLD/RS事件,例如:linup / netifup或addr-change */
        /* 协议栈和数据链路都使能了才生效 */
        netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4 | NETIF_REPORT_TYPE_IPV6);
    #if LWIP_IPV6
        /* 重置ipv6网卡,设置路由请求RS次数(see RFC 4861, ch. 6.3.7) */
        nd6_restart_netif(netif);
    #endif /* LWIP_IPV6 */
      }
    }
    

    netif_set_link_up()函数用于使能网卡底层数据链路。

    相当于打开网卡南向接口的开关阀,让网卡和底层数据链路的数据能够流通。

    对应标志位:NETIF_FLAG_LINK_UP。表示底层数据链路准备好了,也打通了网卡与底层数据链路的通道。

    因为在数据链路层新增了一个设备节点,所以需要在链路层做一些处理,通告下。

    比如发起ARP请求,宣告当前IP被我使用,给为同僚ARP缓存表有空间就存一下吧。

    链路层使能后,还需要通过回调通知其它业务,表明当前网卡状态更新了。

    /**
     * @ingroup netif
     * Called by a driver when its link goes up
     */
    void
    netif_set_link_up(struct netif *netif)
    {
      LWIP_ASSERT_CORE_LOCKED();
    
      LWIP_ERROR("netif_set_link_up: invalid netif", netif != NULL, return);
    
      if (!(netif->flags & NETIF_FLAG_LINK_UP)) { /* 数据链路还没使能 */
        netif_set_flags(netif, NETIF_FLAG_LINK_UP); /* 标记使能数据链路 */
    
        /* 链路层新增了一个设备节点,需要在链路层通告处理下 */
    
    #if LWIP_DHCP /* dhcp协议相关 */
        dhcp_network_changed_link_up(netif);
    #endif /* LWIP_DHCP */
    
    #if LWIP_AUTOIP /* AUTOIP相关 */
        autoip_network_changed_link_up(netif);
    #endif /* LWIP_AUTOIP */
        /* 发送ARP/IGMP/MLD/RS事件,例如:linup / netifup或addr-change */
        /* 协议栈和数据链路都使能了才生效 */
        netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4 | NETIF_REPORT_TYPE_IPV6);
    #if LWIP_IPV6
        /* 重置ipv6网卡,设置路由请求RS次数(see RFC 4861, ch. 6.3.7) */
        nd6_restart_netif(netif);
    #endif /* LWIP_IPV6 */
    
        /* 数据链路状态回调 */
        NETIF_LINK_CALLBACK(netif);
    #if LWIP_NETIF_EXT_STATUS_CALLBACK
        {
          netif_ext_callback_args_t args;
          args.link_changed.state = 1;
          /* 网卡状态静态回调 */
          netif_invoke_ext_callback(netif, LWIP_NSC_LINK_CHANGED, &args);
        }
    #endif
      }
    }
    

    6.10 以太网网卡伪代码分析

    参考lwip官方提供的ethernetif.c文件。

    6.10.1 相关API说明

    用户需要封装单个网卡底层数据链路相关的函数:

    • low_level_init()

      • 该函数用于初始化底层数据链路硬件设备,使其具备底层通信能力。
    • low_level_output()

      • 该函数用于底层数据链路发送数据,网卡的南向出口。
      • 收到的上层数据包时pbuf类型的。
    • low_level_input()

      • 该函数用于底层数据链路接收数据,网卡的南向入口。
      • 需要组装为pbuf类型再转交给上层。

    ethernetif_init():网卡初始化函数。封装这个接口是给netif_add()添加网卡时初始化当前网卡的。

    ethernetif_input():数据链路接收数据并传入TCPIP协议栈。

    发送的数据是由上层一层一层传下来的,相关接口也是层层调用即可。

    但是接收数据不一样,我们代码不知道数据什么时候来,所以需要创建一条线程,专门用于检索接收到的数据包,然后传入网卡,让网卡一层一层上传处理。

    lwip提供的参考,这个线程就是ethernetif_input()

    6.10.2 ethernetif_init()

    伪代码参考:

    /**
     * Should be called at the beginning of the program to set up the
     * network interface. It calls the function low_level_init() to do the
     * actual setup of the hardware.
     *
     * This function should be passed as a parameter to netif_add().
     *
     * @param netif the lwip network interface structure for this ethernetif
     * @return ERR_OK if the loopif is initialized
     *         ERR_MEM if private data couldn't be allocated
     *         any other err_t on error
     */
    err_t
    ethernetif_init(struct netif *netif)
    {
      struct ethernetif *ethernetif;
    
      LWIP_ASSERT("netif != NULL", (netif != NULL));
    
      ethernetif = mem_malloc(sizeof(struct ethernetif));
      if (ethernetif == NULL) {
        LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
        return ERR_MEM;
      }
    
    #if LWIP_NETIF_HOSTNAME
      /* Initialize interface hostname */
      netif->hostname = "lwip";
    #endif /* LWIP_NETIF_HOSTNAME */
    
      /* 在结构netif中初始化snmp变量和计数器。最后一个参数应该替换为链接速度,单位为比特/秒 */
      MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
    
      /* 网卡信息配置 */
      netif->state = ethernetif;
      netif->name[0] = IFNAME0;
      netif->name[1] = IFNAME1;
      /* 我们在这里直接使用etharp_output()来保存函数调用。
        如果你必须在发送之前做一些检查(例如,如果链接可用…),你可以声明自己的函数为调用etharp_output()。 */
    #if LWIP_IPV4
      netif->output = etharp_output; /* ARP协议的北向入口,用户可以二次封装这个接口。收到的数据时网络层的IP数据 */
    #endif /* LWIP_IPV4 */
    #if LWIP_IPV6
      netif->output_ip6 = ethip6_output;
    #endif /* LWIP_IPV6 */
      netif->linkoutput = low_level_output; /* 网卡南向出口。把数据发往底层数据链路。 */
    
      ethernetif->ethaddr = (struct eth_addr *) & (netif->hwaddr[0]); /* 配置硬件地址,MAC */
    
      /* 初始化网卡硬件设备 */
      low_level_init(netif);
    
      return ERR_OK;
    }
    

    6.10.3 ethernetif_input()

    该函数为应该独立线程,用于检测和接收底层数据链路数据,打包好发往TCPIP协议栈。

    /**
     * This function should be called when a packet is ready to be read
     * from the interface. It uses the function low_level_input() that
     * should handle the actual reception of bytes from the network
     * interface. Then the type of the received packet is determined and
     * the appropriate input function is called.
     *
     * @param netif the lwip network interface structure for this ethernetif
     */
    static void
    ethernetif_input(struct netif *netif)
    {
      struct ethernetif *ethernetif;
      struct eth_hdr *ethhdr;
      struct pbuf *p;
    
      ethernetif = netif->state; /* 用户私人信息拿出来 */
    
      /* 接收一个数据包,并封装成pbuf类型 */
      p = low_level_input(netif);
      /* 如果无法读取数据包,则静默地忽略它 */
      if (p != NULL) {
        /* 将所有数据包传递给ethernet_input,由ethernet_input决定支持哪些数据包 */
        if (netif->input(p, netif) != ERR_OK) {
          LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
          pbuf_free(p); /* 上传失败,丢弃该数据包 */
          p = NULL;
        }
      }
    }
    
    

    6.10.4 low_level_init()

    /**
     * In this function, the hardware should be initialized.
     * Called from ethernetif_init().
     *
     * @param netif the already initialized lwip network interface structure
     *        for this ethernetif
     */
    static void
    low_level_init(struct netif *netif)
    {
      struct ethernetif *ethernetif = netif->state; /* 获取用户私人信息 */
    
      /* 设置MAC硬件地址长度 */
      netif->hwaddr_len = ETHARP_HWADDR_LEN;
    
      /* 设置MAC地址 */
      netif->hwaddr[0] = ;
      /* ... */
      netif->hwaddr[5] = ;
    
      /* 设置MTU */
      netif->mtu = 1500;
    
      /* 设备性能标记 */
      /* 根据实际设备性能而配置 */
      netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
    
    #if LWIP_IPV6 && LWIP_IPV6_MLD
      /*
       * 用于实现MAC过滤的硬件/网络。
       * 默认情况下,所有节点的链路本地是被处理的,因此我们必须让硬件知道允许多播包进入。
       * 应该在前面设置mld_mac_filter。 */
      if (netif->mld_mac_filter != NULL) {
        ip6_addr_t ip6_allnodes_ll;
        ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll);
        netif->mld_mac_filter(netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER);
      }
    #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
    
      /* 执行初始化接口所需的其他操作。 */
    }
    

    6.11环回接口

    6.11.1 环回地址

    ipv4的127 网段的所有地址都称为环回地址,主要用来测试网络协议是否工作正常的作用。

    常用:

    IPv4:127.0.0.1

    IPv6:::1

    默认记录IPv4。

    环回地址表示“我自己”的意思。

    6.11.2 环回数据包流图


    图源自李柱明

    6.11.3 相关宏及API

    ENABLE_LOOPBACK:开启环回功能。

    LWIP_NETIF_LOOPBACK:每个网卡都有环回功能。

    LWIP_HAVE_LOOPIF:可创建独立的环回网卡。

    netif_init():lwip网卡模块初始化。如果开启了LWIP_NETIF_LOOPBACK,则会在该函数里面添加一个环回网卡loop_netif

    netif_loopif_init():环回网卡初始化。

    • 相当于以太网网卡的low_level_init()

    netif_loop_output():环回输出接口。

    • 如果数据包的目标IP和当前网卡的IP一致,则调用内部会调用该函数,而不是netif->output()

    • 如果是环回网卡,netif->output()也是该函数。

      • 所以如果目标IP和环回网卡一致,则直接调用netif_loop_output()
      • 如果不一致,则调用netif->output(),其实也是netif_loop_output()

    netif_poll():环回处理接口。

    • 单线程环境下:在应用程序中循环调用。
    • 多线程环境下:内部会通过消息,把netif_poll()转交给wlip内核线程去跑。
    • 目的是把环回数据包环回发到对应网卡的IP层处理。netif_loop_output()输入的数据传到netif->input()

    6.11.4 环回网卡初始化

    lwip网卡模块初始化调用netif_init()函数,只是在lwip协议栈初始化是调用,如果开启了环回LWIP_HAVE_LOOPIF功能,才会有操作,就是创建一个环回IP的网卡,仅此而已。

    对应到每一个实际网卡的初始化,在netif_add()会讲解。

    void
    netif_init(void)
    {
    #if LWIP_HAVE_LOOPIF
    #if LWIP_IPV4
    #define LOOPIF_ADDRINIT &loop_ipaddr, &loop_netmask, &loop_gw,
      ip4_addr_t loop_ipaddr, loop_netmask, loop_gw;
      IP4_ADDR(&loop_gw, 127, 0, 0, 1); /* 设置回环网卡的网关 */
      IP4_ADDR(&loop_ipaddr, 127, 0, 0, 1); /* 设置回环网卡的IP地址 */
      IP4_ADDR(&loop_netmask, 255, 0, 0, 0); /* 设置回环网卡的子网掩码 */
    #else /* LWIP_IPV4 */
    #define LOOPIF_ADDRINIT
    #endif /* LWIP_IPV4 */
    
    #if NO_SYS
      netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, ip_input);
    #else  /* NO_SYS */
      netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, tcpip_input); /* 添加网卡 */
    #endif /* NO_SYS */
    
    #if LWIP_IPV6
      IP_ADDR6_HOST(loop_netif.ip6_addr, 0, 0, 0, 0x00000001UL); /* ipv6的回环地址为::1 */
      loop_netif.ip6_addr_state[0] = IP6_ADDR_VALID; /* 标记地址有效 */
    #endif /* LWIP_IPV6 */
    
      netif_set_link_up(&loop_netif); /* 添加新网卡,告知链路层,如ARP宣告使用次IP,都让回环的没有。 */
      netif_set_up(&loop_netif); /* 协议栈启用此网卡 */
    #endif /* LWIP_HAVE_LOOPIF */
    }
    

    回环网卡初始化:netif_loopif_init()

    /**
     * Initialize a lwip network interface structure for a loopback interface
     *
     * @param netif the lwip network interface structure for this loopif
     * @return ERR_OK if the loopif is initialized
     *         ERR_MEM if private data couldn't be allocated
     */
    static err_t
    netif_loopif_init(struct netif *netif)
    {
      LWIP_ASSERT("netif_loopif_init: invalid netif", netif != NULL);
    
      /* 初始化结构netif中的SNMP变量和计数器
       * ifSpeed:不能做任何假设!
       */
      MIB2_INIT_NETIF(netif, snmp_ifType_softwareLoopback, 0);
    
      /* 网卡名字 */
      netif->name[0] = 'l';
      netif->name[1] = 'o';
    #if LWIP_IPV4
      netif->output = netif_loop_output_ipv4; /* 回环输出接口 */
    #endif
    #if LWIP_IPV6
      netif->output_ip6 = netif_loop_output_ipv6; /* 回环输出接口 */
    #endif
    #if LWIP_LOOPIF_MULTICAST
      netif_set_flags(netif, NETIF_FLAG_IGMP); /* 支持IGMP协议 */
    #endif
      NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_DISABLE_ALL); /* checksum校验功能相关 */
      return ERR_OK;
    }
    #endif /* LWIP_HAVE_LOOPIF */
    

    6.11.5 环回输出接口netif_loop_output()

    传入的pbuf是被以复制的显示缓存到对应网卡的loop_first链表中:

    • 先申请新的pbuf r,用于存储拷贝传入的pbuf p的数据。
    • 预测检查pbuf节点数是否超出限制LWIP_LOOPBACK_MAX_PBUFS
    • 拷贝pbuf数据。
    • 拼接到对应网卡的环回pbuf链表netif->loop_first中。
    • 如果网卡的环回pbuf链表还有数据,说明上次已经触发过netif_poll(),正常按理是不用再触发的。但是如果上次触发失败,这次还是要触发。
    • 如果网卡大的环回链表没有数据,说明当前netif_poll()没有在跑。需要触发netif_poll()
    • 如果netif_poll()触发失败,需要标记下,下次再发环回数据时补回触发。
    /**
     * @ingroup netif
     * Send an IP packet to be received on the same netif (loopif-like).
     * The pbuf is copied and added to an internal queue which is fed to 
     * netif->input by netif_poll().
     * In multithreaded mode, the call to netif_poll() is queued to be done on the
     * TCP/IP thread.
     * In callback mode, the user has the responsibility to call netif_poll() in 
     * the main loop of their application.
     *
     * @param netif the lwip network interface structure
     * @param p the (IP) packet to 'send'
     * @return ERR_OK if the packet has been sent
     *         ERR_MEM if the pbuf used to copy the packet couldn't be allocated
     */
    err_t
    netif_loop_output(struct netif *netif, struct pbuf *p)
    {
      struct pbuf *r;
      err_t err;
      struct pbuf *last;
    #if LWIP_LOOPBACK_MAX_PBUFS
      u16_t clen = 0;
    #endif /* LWIP_LOOPBACK_MAX_PBUFS */
      /* 如果我们有一个loop, SNMP计数器会为此进行调整,
       * 如果不是,则调整为'netif'。 */
    #if MIB2_STATS
    #if LWIP_HAVE_LOOPIF
      struct netif *stats_if = &loop_netif; /* 相关状态信息记录到loop_netif(不管数据包的目标是否是该网卡) */
    #else /* LWIP_HAVE_LOOPIF */
      struct netif *stats_if = netif; /* 相关状态信息记录到对应网卡 */
    #endif /* LWIP_HAVE_LOOPIF */
    #endif /* MIB2_STATS */
    #if LWIP_NETIF_LOOPBACK_MULTITHREADING
      u8_t schedule_poll = 0; /* 是否需要转发netif_poll()到lwip内核线程执行 */
    #endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
      SYS_ARCH_DECL_PROTECT(lev);
    
      LWIP_ASSERT("netif_loop_output: invalid netif", netif != NULL);
      LWIP_ASSERT("netif_loop_output: invalid pbuf", p != NULL);
    
      /* 申请新的pbuf资源 */
      r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
      if (r == NULL) {
        /* 申请失败。状态信息记录。 */
        LINK_STATS_INC(link.memerr);
        LINK_STATS_INC(link.drop);
        MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
        return ERR_MEM;
      }
    #if LWIP_LOOPBACK_MAX_PBUFS
      clen = pbuf_clen(r); /* 查看申请到的pbuf有多长(查看多少个pbuf节点组成,如果MEM基于POOL时才有意义) */
      /* 检查队列上是否有溢出或太多的pbuf */
      if (((netif->loop_cnt_current + clen) < netif->loop_cnt_current) ||
          ((netif->loop_cnt_current + clen) > LWIP_MIN(LWIP_LOOPBACK_MAX_PBUFS, 0xFFFF))) {
        pbuf_free(r); /* 过多,全部丢弃 */
        /* 输出失败。记录状态信息。 */
        LINK_STATS_INC(link.memerr);
        LINK_STATS_INC(link.drop);
        MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
        return ERR_MEM;
      }
      /* 资源申请成功,需要记录pbuf链表有是由多少个节点组成的,用于出队检查和包数限制。 */
      /* 笔者认为,在这里就更新,未免太早了。如果pbuf拷贝失败,会导致这个值虚大。 */
      netif->loop_cnt_current = (u16_t)(netif->loop_cnt_current + clen);
    #endif /* LWIP_LOOPBACK_MAX_PBUFS */
    
      /* 将整个pbuf队列p复制到单个pbuf r中 */
      if ((err = pbuf_copy(r, p)) != ERR_OK) {
        pbuf_free(r);
        /* 拷贝失败。记录状态信息。 */
        LINK_STATS_INC(link.memerr);
        LINK_STATS_INC(link.drop);
        MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
        /* 笔者添加:netif->loop_cnt_current = (u16_t)(netif->loop_cnt_current - clen); */
        return err;
      }
    
      /* 把包放在一个链表上,通过调用netif_poll()进行出队. */
    
      /* last指向链r中的最后一个pbuf */
      for (last = r; last->next != NULL; last = last->next) {
        /* nothing to do here, just get to the last pbuf */
      }
    
      SYS_ARCH_PROTECT(lev); /* 进入临界 */
      if (netif->loop_first != NULL) { /* 环回链表中已有缓存数据 */
        LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL);
        netif->loop_last->next = r; /* 拼接pbuf链表 */
        netif->loop_last = last; /* 保存最后一个pbuf的指针。用于还没其它pbuf插入时,直接从尾部插入。 */
    #if LWIP_NETIF_LOOPBACK_MULTITHREADING
        if (netif->reschedule_poll) { /* 需要重新把netif_pool()转发到lwip内核线程执行(因为上次转发失败) */
          schedule_poll = 1; /* 标记需要外包 */
          netif->reschedule_poll = 0; /* 恢复标志位 */
        }
    #endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
      } else { /* 环回链表中没有缓存数据 */
        netif->loop_first = r; /* 首个pbuf */
        netif->loop_last = last; /* 保存最后一个pbuf的指针 */
    #if LWIP_NETIF_LOOPBACK_MULTITHREADING
        /* 环回队列中没有数据,说明netif_poll()已经处理完毕,需要重新外包netif_poll() */
        schedule_poll = 1;
    #endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
      }
      SYS_ARCH_UNPROTECT(lev); /* 退出临界 */
    
      /* 记录状态信息。 */
      LINK_STATS_INC(link.xmit);
      MIB2_STATS_NETIF_ADD(stats_if, ifoutoctets, p->tot_len);
      MIB2_STATS_NETIF_INC(stats_if, ifoutucastpkts);
    
    #if LWIP_NETIF_LOOPBACK_MULTITHREADING
      /* 多线程环境下 */
      if (schedule_poll) {
        /* 打包消息,转发netif_poll()函数到lwip内核线程去跑 */
        if (tcpip_try_callback((tcpip_callback_fn)netif_poll, netif) != ERR_OK) {
          SYS_ARCH_PROTECT(lev);
          netif->reschedule_poll = 1;
          SYS_ARCH_UNPROTECT(lev);
        }
      }
    #endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
    
      return ERR_OK;
    }
    

    6.11.6 环回处理接口netif_poll()

    非可重入函数。

    • pbuf数据从网卡环回pbuf链表netif->loop_first中出队,把数据转交给IP模块入口ip_input()处理。
    /**
     * Call netif_poll() in the main loop of your application. This is to prevent
     * reentering non-reentrant functions like tcp_input(). Packets passed to
     * netif_loop_output() are put on a list that is passed to netif->input() by
     * netif_poll().
     */
    void
    netif_poll(struct netif *netif)
    {
      /* 如果我们有一个loop, SNMP计数器会为此进行调整,
       * 如果不是,则调整为'netif'。 */
    #if MIB2_STATS
    #if LWIP_HAVE_LOOPIF
      struct netif *stats_if = &loop_netif; /* 相关状态信息记录到loop_netif(不管数据包的目标是否是该网卡) */
    #else /* LWIP_HAVE_LOOPIF */
      struct netif *stats_if = netif; /* 相关状态信息记录到对应网卡 */
    #endif /* LWIP_HAVE_LOOPIF */
    #endif /* MIB2_STATS */
      SYS_ARCH_DECL_PROTECT(lev);
    
      LWIP_ASSERT("netif_poll: invalid netif", netif != NULL);
    
      SYS_ARCH_PROTECT(lev); /* 进入临界 */
      while (netif->loop_first != NULL) { /* 直到环回链表都处理完毕 */
        struct pbuf *in, *in_end;
    #if LWIP_LOOPBACK_MAX_PBUFS
        u8_t clen = 1;
    #endif /* LWIP_LOOPBACK_MAX_PBUFS */
    
        in = in_end = netif->loop_first;
        while (in_end->len != in_end->tot_len) { /* 遍历当前pbuf链表数据包最后一个有效的pbuf */
          LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
          in_end = in_end->next; /* 遍历下一个 */
    #if LWIP_LOOPBACK_MAX_PBUFS
          clen++; /* 记录当前pbuf链表的pbuf成员个数 */
    #endif /* LWIP_LOOPBACK_MAX_PBUFS */
        }
    #if LWIP_LOOPBACK_MAX_PBUFS
        /* 调整队列上的pbuf数量 */
        LWIP_ASSERT("netif->loop_cnt_current underflow",
                    ((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
        netif->loop_cnt_current = (u16_t)(netif->loop_cnt_current - clen);
    #endif /* LWIP_LOOPBACK_MAX_PBUFS */
    
        /* 'in_end'现在指向'in'的最后一个pbuf */
        if (in_end == netif->loop_last) {
          /* 队列上的pbuf已经遍历完毕了,后续没有其它pbuf。更新相关字段 */
          netif->loop_first = netif->loop_last = NULL;
        } else { /* 环回队列还要其它pbuf */
          /* 从列表中弹出pbuf */
          netif->loop_first = in_end->next;
          LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
        }
        /* 弹出队列 */
        in_end->next = NULL;
        SYS_ARCH_UNPROTECT(lev);
        /* 获取这个环回pbuf数据包的源网卡,也是目标网卡 */
        in->if_idx = netif_get_index(netif);
        /* 状态信息记录。 */
        LINK_STATS_INC(link.recv);
        MIB2_STATS_NETIF_ADD(stats_if, ifinoctets, in->tot_len);
        MIB2_STATS_NETIF_INC(stats_if, ifinucastpkts);
        /* loopback报文始终是IP报文! */
        if (ip_input(in, netif) != ERR_OK) { /* 转入对应网卡的IP模块 */
          pbuf_free(in); /* 转入失败后需要释放这个pbuf数据包内存资源 */
        }
        SYS_ARCH_PROTECT(lev);
      }
      SYS_ARCH_UNPROTECT(lev);
    }
    
  • 相关阅读:
    java中的基本数据类型以及对应的包装类
    JQuery 基础
    题库一
    String、StringBuffer和StringBuilder的区别
    ideal控制台输出乱码
    python进阶
    python基础
    请求一个网页时,web服务做了什么?[以php为例]
    go defer看到你头晕的操作
    计算机基础-数据结构-栈
  • 原文地址:https://www.cnblogs.com/lizhuming/p/16642648.html
Copyright © 2020-2023  润新知