前言
李柱明博客:[https://www.cnblogs.com/lizhuming/p/15487068.html](https://www.cnblogs.com/lizhuming/p/15487068.html)
3. 網絡接口
3.1 概念引入
網絡接口(以太網接口)是硬件接口(網絡接口又可以稱之為網卡)。
LWIP 是軟件那么而怎樣讓硬件和軟件無縫連接起來呢?
而且網卡又多種多樣,怎樣才能讓 LWIP 使用同樣的軟件兼容不同的硬件平台?
LWIP 中使用了一個netif
結構體來描述網卡但是網卡是直接和硬件平台打交道的:
-
用戶提供最底層接口函數。
-
LWIP 提供統一的 API。
-
舉例:
- 收:如網卡的初始化和網卡的收發數據,當 LWIP 底層得到數據之后,才會傳入到內核中去處理。
- 發:LWIP 內核需要發送數據包的時候,也需要調用網卡的發送函數。
-
LWIP 中的 etherneif.c 文件的函數通常為硬件打交道的底層函數,當有數據需要通過網卡接收或者發送數據的時候就會被調用,通過 LWIP 的協議棧的內部進行處理后,從應用層就能得到數據或者可以發送數據。
小總結:
簡單來說,netif 是 LWIP 抽象出來的網卡,LWIP 協議棧可以使用多種不同接口,而 etherneif.c 文件則提供了 netif 訪問各種不同的網卡,每個網卡有不同的實方式,每戶只需要修改 ethernetif.c 文件即可。
3.2 lwip netif 結構體
3.2.1 鏈接
源碼:((20210727163318-nobqihq))
3.2.2 字段分析
3.2.3 網卡鏈表
/** pointer to next in linked list */
struct netif *next;
LWIP 使用鏈表來統一管理同一設備的多個網卡。
netif.c 文件中定義兩個全局指針 struct netif *netif_list
和 struct netif *netif_default
netif_list
就是網卡鏈表指針,指向網卡鏈表的首節點(第一個網卡)。netif_default
默認網卡。
3.2.4 網絡 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
:網關地址。
3.2.5 接收數據函數
/** 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()
,參數為 pbuf
和 netif
類型,其中 pbuf
為接收到的數據包。
3.2.6 發送數據函數
#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 地址。
3.2.6 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()
函數調用。
3.2.7 出口回調函數
#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 被刪除時調用此函數。
3.2.8 用戶私有數據
/** 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*/
由設備驅動程序設置並指向設備的狀態信息,主要將網卡的某些私有數據傳遞給上層。
3.2.9 最大傳輸單位
/** maximum transfer unit (in bytes) */
u16_t mtu;
3.2.10 鏈路硬件地址長度&地址
/** number of bytes used in hwaddr */
u8_t hwaddr_len;
/** link level hardware address of this interface */
u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
3.2.11 網卡信息狀態標志
/** flags (@see @ref netif_flags) */
u8_t flags;
網卡狀態信息標志位,是很重要的控制字段,它包括網卡的功能使能,廣播使能,ARP 使能等重要控制位。
3.2.12 網卡名字
/** descriptive abbreviation */
char name[2];
用於保存每一個網卡的名字,用兩個字符的名字來標識。
網卡使用的設備驅動的種類,名字由設備驅動來設置並且應該反映通過網卡表示的硬件的種類。
如果兩個網卡具有相同的網絡名字,我們就用 num 字段來區分相同類別的不同網卡。
3.2.13 網卡標識
/** number of this interface */
u8_t num;
標識使用同種驅動類型的不同網卡。
3.3 netif 使用
簡要步驟:
-
定義一個
netif
作為網卡設備結構體。 -
掛載到
netif_list
鏈表中。- 使用函數:
netif_add();
- 建議參考該函數源碼。
- 使用函數:
3.4 與 netif 相關的底層函數
主要 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);
附件-源碼
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 */
};