LwIP 之二 网络接口 netif(ethernetif.c、netif.c)
??目前,網絡上多數文章所使用的LwIP版本為1.4.1。最新版本為2.0.3。從1.4.1到2.0.3(貌似從2.0.0開始),LwIP的源碼有了一定的變化,甚至于源碼的文件結構也不一樣,內部的一些實現源文件也被更新和替換了。
簡介
??LwIP使用netif來描述一個硬件網絡接口,但是由于網絡接口是直接與硬件打交道的,硬件不同則處理可能不同,必須由用戶提供最底層接口。LwIP的網絡驅動有一定的模型,/src/netif/ethernetif.c文件即為底層接口的驅動的模版,用戶為自己的網絡設備實現驅動時應參照此模塊。該文件中的函數通常為與硬件打交道的函數,當有數據接收的時候被調用,以使接收到的數據進入tcpip協議棧。
??簡單來說,netif是LwIP抽象出來的各網絡接口,協議棧可以使用多個不同的接口,而ethernetif則提供了netif訪問硬件的各接口,每個不同的接口有不同的ethernetif。
netif 結構
??在LwIP中,是通過結構體netif來描述一個硬件網絡接口的,在單網卡中,這個結構體只有一個;多網卡中可有何網卡數目相同的netif結構體,它們構成一個數據鏈。該部分的定義在文件netif.c/h中,具體如下:
/** 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 地址 */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; /* IP層調用此函數向網卡發送一包數據 */ #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; /* ARP模塊調用這個函數向網卡發送一包數據 */ #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_datavoid* 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_NETIFu16_t chksum_flags; #endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*//** maximum transfer unit (in bytes) */u16_t mtu; /* 網絡一次可以傳送的最大字節數,對于以太網一般設為 1500 *//** number of bytes used in hwaddr */u8_t hwaddr_len; /* 硬件地址長度,對于以太網就是 MAC 地址長度,為 6 各字節 *//** 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]; /* 字段用于保存每一個網絡網絡接口的名字。用兩個字符的名字來標識網絡接口使用的設備驅動的種類,名字由設備驅動來設置并且應該反映通過網絡接口表示的硬件的種類。比如藍牙設備( bluetooth)的網絡接口名字可以是 bt,而 IEEE 802.11b WLAN 設備的名字就可以是 wl,當然設置什么名字用戶是可以自由發揮的,這并不影響用戶對網絡接口的使用。當然,如果兩個網絡接口具有相同的網絡名字,我們就用 num 字段來區分相同類別的不同網絡接口。 *//** 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 multicastfilter 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 multicastfilter table of the ethernet MAC. */netif_mld_mac_filter_fn mld_mac_filter; #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ #if LWIP_NETIF_HWADDRHINTu8_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_PBUFSu16_t loop_cnt_current; #endif /* LWIP_LOOPBACK_MAX_PBUFS */ #endif /* ENABLE_LOOPBACK */ };netif鏈表
??LwIP使用鏈表(單向鏈表)來管理多個網絡接口。在netif.c中有如下全局變量struct netif *netif_list;和struct netif *netif_default;,其中前者就是網絡接口鏈表指針,后者表示默認情況下(有多網口時)使用哪個網絡接口。
??netif.c中第一部分就是添加一個會換接口(即127.0.0.1)。具體函數為void netif_init(void)。該函數在LwIP初始化時被調用(tcpip_init -> lwip_init -> netif_init)。
要使用LwIP必須調用tcpip_init完成對LwIP的初始化。
添加/刪除接口
??向網絡接口鏈表中添加新接口必須通過以下函數
struct netif * netif_add(struct netif *netif, #if LWIP_IPV4const 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);下面簡單具體分析以下添加新接口:
??網絡接口鏈表的操作非常簡單,新添加的接口在鏈表最前面!
??從網絡接口鏈表中刪除指定的接口必須通過以下函數void netif_remove(struct netif *netif);刪除也非常簡單,遍歷鏈表找到指定的接口,刪除!
netif使用
??那么具體該如何使用呢?其實使用還是非常簡單的。首先我們需要針對我們的網絡接口頂一個netif結構體變量struct netif xnetif;,接下來就是初始化。通過上面的結構不難發現,其中有非常多的函數指針,后續對于網絡接口的操作,基本全是通過各個函數指針來實現的!通常,我們會在LwIP初始化中,用以下函數添加自己的網絡接口:
/* 通過該函數,將網絡接口添加到鏈表中 */netif_add(&xnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);/* Registers the default network interface.*/netif_set_default(&xnetif);if (EthStatus == (ETH_INIT_FLAG | ETH_LINK_FLAG)){/* Set Ethernet link flag */xnetif.flags |= NETIF_FLAG_LINK_UP;/* When the netif is fully configured this function must be called.*/netif_set_up(&xnetif);}else{/* When the netif link is down this function must be called.*/netif_set_down(&xnetif);}ethernetif各函數
??上面說過,每個netif接口都需要一個底層接口文件提供訪問硬件的支持。在LwIP提供的模板ethernetif.c文件中,上來就是一個如下的結構體:
struct ethernetif {struct eth_addr *ethaddr;/* Add whatever per-interface state that is needed here. */ };??該結構用來描述底層硬件設備,該結構體唯一不可或缺的是MAC地址,它是LWIP用于相應ARP查詢的核心數據。其他如果沒有特殊需要,可以不添加其他成員數據。主要用于將底層硬件設備的一些私有數據通過netif ->state傳遞給上層(在ethernetif_init函數中賦值netif->state = ethernetif;)。一般用不到改結構。因為在netif結構中,基本包含了所有需要的信息。
??ethernetif.c文件中提供的函數主要有以下這么幾個:
- static void low_level_init(struct netif *netif);
- static struct pbuf * low_level_input(struct netif *netif);
- static err_t low_level_output(struct netif *netif, struct pbuf *p);
- void ethernetif_input( void * pvParameters )- low_level_output;
- err_t ethernetif_init(struct netif *netif);
??其中對外的接口只有ethernetif_init函數。
ethernetif_init
??該函數完成對于網絡接口的初始化工作。它是在對LwIP初始化函數中通過netif_add( &EMAC_if, &xIpAddr, &xNetMast, &xGateway, NULL, ethernetif_init, tcpip_input );來被調用的。具體見以下注釋:
err_t ethernetif_init(struct netif *netif) {LWIP_ASSERT("netif != NULL", (netif != NULL));#if LWIP_NETIF_HOSTNAMEnetif->hostname = "lwip"; /* Initialize interface hostname */#endif /* LWIP_NETIF_HOSTNAME */netif->name[0] = IFNAME0;netif->name[1] = IFNAME1;#if LWIP_IPV4#if LWIP_ARP || LWIP_ETHERNET#if LWIP_ARPnetif->output = etharp_output; /* 數據發送函數,其最終還是會調用 netif->linkoutput */#else/* The user should write ist own code in low_level_output_arp_off function */netif->output = low_level_output_arp_off;#endif /* LWIP_ARP */#endif /* LWIP_ARP || LWIP_ETHERNET */#endif /* LWIP_IPV4 */#if LWIP_IPV6 //0netif->output_ip6 = ethip6_output;#endif /* LWIP_IPV6 */netif->linkoutput = low_level_output; /* 這個才是最底層發送函數(將數據交給硬件發送)*//* initialize the hardware */low_level_init(netif);// etharp_init();// sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);return ERR_OK; }low_level_init
??該函數主要是對網卡進行一系列的初始化工作,例如:初始化MAC地址、建立接收數據的任務等。
low_level_input
??該函數負責從網卡中接收數據。正常從LwIP的緩沖池中申請一個 pbuf結構,存放數據。
ethernetif_input
??該函數即為網卡數據接收任務。LwIP中是通過一個task(ethernetif_input)輪詢檢查DMA控制器的狀態以判斷是否有數據接收到。接收的數據傳輸流程如下:
硬件接口取數 -> low_level_input -> tcpip_input(通過函數指針s_pxNetIf->input)
low_level_output
??Output過程是由應用程序以主動方式觸發的。在TCP/IP協議棧中,要發送的發送的數據最終被傳遞給了ip_output_if函數。查看代碼可知,該函數中直接調用了netif->output函數,在上面的ethernetif_init函數中有對這個變量【函數指針】賦值,它就是etharp_output。
??要發送的數據的額傳輸流程如下:
ip_output_if -> etharp_output(通過函數指針netif->output)-> ethernet_output -> low_level_output(通過函數指針netif->linkoutput) -> 硬件設備
總結
以上是生活随笔為你收集整理的LwIP 之二 网络接口 netif(ethernetif.c、netif.c)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Network 之二 Ethernet(
- 下一篇: LwIP 之三 操作系统隔离接口 sys