【lwip】lwip源碼基礎


前言

本筆記記錄 lwip 源碼基礎內容

李柱明部落格://www.cnblogs.com/lizhuming/p/15487094.html

概念&作用

網路介面

概念引入

網路介面(乙太網介面)是硬體介面(網路介面又可以稱之為網卡)。

LWIP 是軟體那麼而怎樣讓硬體和軟體無縫連接起來呢?

而且網卡又多種多樣,怎樣才能讓 LWIP 使用同樣的軟體兼容不同的硬體平台?

LWIP 中使用了一個netif結構體來描述網卡但是網卡是直接和硬體平台打交道的:

  • 用戶提供最底層介面函數。

  • LWIP 提供統一的 API。

  • 舉例:

    • 收:如網卡的初始化和網卡的收發數據,當 LWIP 底層得到數據之後,才會傳入到內核中去處理。
    • 發:LWIP 內核需要發送數據包的時候,也需要調用網卡的發送函數。
  • LWIP 中的 etherneif.c 文件的函數通常為硬體打交道的底層函數,當有數據需要通過網卡接收或者發送數據的時候就會被調用,通過 LWIP 的協議棧的內部進行處理後,從應用層就能得到數據或者可以發送數據。

總結

簡單來說,netif 是 LWIP 抽象出來的網卡,LWIP 協議棧可以使用多種不同介面,而 etherneif.c 文件則提供了 netif 訪問各種不同的網卡,每個網卡有不同的實方式,每戶只需要修改 ethernetif.c 文件即可。

lwip netif 結構體

鏈接

參考:LWIP中netif結構體的介紹

源碼:((20210727163318-nobqihq))

欄位分析

網卡鏈表

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

LWIP 使用鏈表來統一管理同一設備的多個網卡。

netif.c 文件中定義兩個全局指針 struct netif *netif_liststruct netif *netif_default

  • netif_list 就是網卡鏈表指針,指向網卡鏈表的首節點(第一個網卡)。
  • netif_default 默認網卡。

網路 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:網關地址。

接收數據函數

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

該函數由網路設備驅動程式調用,將數據包傳遞到 TCP/IP 協議棧(IP 層),這通常是 ethernet_input(),參數為 pbufnetif 類型,其中 pbuf 為接收到的數據包。

發送數據函數

#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 地址。

ARP 模組調用的發送函數

/** 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 類似,也需要用戶自己實現一個函數,但是只有兩個參數,它是由 ARP 模組調用的,一般是自定義函數 low_level_output()

當需要在網卡上發送一個數據包時,該函數會被 ethernet_output() 函數調用。

出口回調函數

#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 被刪除時調用此函數。

用戶私有數據

/** 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*/

由設備驅動程式設置並指向設備的狀態資訊,主要將網卡的某些私有數據傳遞給上層。

最大傳輸單位

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

鏈路硬體地址長度&地址

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

網卡資訊狀態標誌

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

網卡狀態資訊標誌位,是很重要的控制欄位,它包括網卡的功能使能,廣播使能,ARP 使能等重要控制位。

網卡名字

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

用於保存每一個網卡的名字,用兩個字元的名字來標識。

網卡使用的設備驅動的種類,名字由設備驅動來設置並且應該反映通過網卡表示的硬體的種類。

如果兩個網卡具有相同的網路名字,我們就用 num 欄位來區分相同類別的不同網卡。

網卡標識

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

標識使用同種驅動類型的不同網卡。

netif 使用

簡要步驟:

  1. 定義一個 netif 作為網卡設備結構體。

  2. 掛載到 netif_list 鏈表中。

    1. 使用函數:netif_add();
    2. 建議參考該函數源碼。

源碼

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 {
  /** pointer to next in linked list */
  struct netif *next;

#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];
#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_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*/
  /** maximum transfer unit (in bytes) */
  u16_t mtu;
  /** number of bytes used in hwaddr */
  u8_t hwaddr_len;
  /** link level hardware address of this interface */
  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
  /** flags (@see @ref netif_flags) */
  u8_t flags;
  /** descriptive abbreviation */
  char name[2];
  /** number of this interface */
  u8_t num;
#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_NETIF_HWADDRHINT
  u8_t *addr_hint;
#endif /* LWIP_NETIF_HWADDRHINT */
#if ENABLE_LOOPBACK
  /* List of packets to be queued for ourselves. */
  struct pbuf *loop_first;
  struct pbuf *loop_last;
#if LWIP_LOOPBACK_MAX_PBUFS
  u16_t loop_cnt_current;
#endif /* LWIP_LOOPBACK_MAX_PBUFS */
#endif /* ENABLE_LOOPBACK */
};