二LWIP學習筆記之網絡接口管理


一、引言

  LWIP分為四個層次:鏈路層、網絡層、傳輸層和應用層。運行LWIP的嵌入式設備可以有多個網絡接口:以太網接口、串行鏈路接口、環回接口等。為了實現對所有網絡接口的有效管理,協議棧內部使用了一個名為netif的網絡接口結構來描述各種網絡設備。本章討論的內容包括:

  網絡接口管理的作用;

  網絡接口結構netif;

二、網絡接口管理

1、數據結構  

  源文件中 netif.c 和 netif.h 文件實現了與網絡接口結構管理相關的所有數據結構和函數。來看看結構 netif 是怎樣被定義的,如下代碼所示。 

————netif.h————————————————
//網絡接口最大物理地址長度,這里定義為以太網網卡 MAC 地址的長度 6
#define NETIF_MAX_HWADDR_LEN 6U
//下面幾個宏為網絡接口屬性、狀態相關的宏,主要用於描述 netif 中 flags 字段的各位
#define NETIF_FLAG_UP 0x01U //網絡接口是否已被上層使能
#define NETIF_FLAG_BROADCAST 0x02U //網絡接口是否支持廣播
#define NETIF_FLAG_POINTTOPOINT 0x04U //網絡接口是否屬於點到點連接
#define NETIF_FLAG_DHCP 0x08U //網絡接口是否支持 DHCP 功能
#define NETIF_FLAG_LINK_UP 0x10U //網絡接口的底層鏈路是否已經使能
#define NETIF_FLAG_ETHARP 0x20U //網絡接口是否支持 ARP 功能
#define NETIF_FLAG_IGMP 0x40U //網絡接口是否支持 IGMP 功能
//下面是結構 netif 的定義
struct netif 
{   
struct netif *next; //指向下一個 netif 結構,在構成鏈表 netif_list 時使用   struct ip_addr ip_addr; //網絡接口的 IP 地址   struct ip_addr netmask; //子網掩碼   struct ip_addr gw; //網關地址   //下面為三個函數指針,調用它們指向的函數就可以完成數據包的發送或接收   err_t (* input)(struct pbuf *p, struct netif *inp); //該函數向 IP 層輸入數據包   err_t (* output)(struct netif *netif, struct pbuf *p,struct ip_addr *ipaddr); //該函數發送 IP 包   err_t (* linkoutput)(struct netif *netif, struct pbuf *p); //該函數實現底層數據包發送   void *state; //該字段用戶可以自由設置,例如用於指向一些底層設備相關的信息   u16_t mtu; //該接口允許的最大數據包長度   u8_t hwaddr_len; //該接口物理地址長度   u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; //該接口的物理地址   u8_t flags; //該接口的狀態、屬性字段   char name[2//該接口的名字   u8_t num; //接口的編號   //在接口自輸入使能或者有環回接口的情況下,下面的字段   //用於描述接口發送給自己的數據包   struct pbuf *loop_first; //指向發送給自己的數據包的第一個 pbuf   struct pbuf *loop_last; //指向發送給自己的數據包的最后一個 pbuf

  next 字段是指向下一個 netif 結構的指針,前面也說過,一個設備可能會有多個網絡接口(例如路由器),LwIP 會把所有網絡接口的 netif 結構組成一個鏈表進行管理,有一個名為 netif_list 的全局變量指向該鏈表的首部。next 字段就是用於組成鏈表時用。   

  ip_addr、netmask、gw 三個字段用於描述該網絡接口的網絡地址屬性,依次稱它們為接口 IP地址、子網掩碼和網關地址。IP 地址和網絡接口必須一一對應,即設備有幾個硬件網絡接口,它就得有幾個 IP 地址;子網掩碼可以用來判斷某個目的 IP 地址與當前網絡接口 IP 地址是否處於同一子網中,IP 層會選擇與目的 IP 處於同一子網的網絡接口來發送數據包。gw 字段在數據包的發送、轉發過程中也有重要作用,如果目的 IP 地址與所有網絡接口都不屬於同一子網,LwIP 將會把數據包發送到網關處,因為它認為網關設備會對該 IP 包進行正確的轉發,此外網關也為設備提供了許多高級服務,如 DHCP、DNS 等。   

  input 字段指向一個函數,這個函數將網絡設備接收到的數據包提交給 IP 層(在以太網中,通常這個函數需要解析以太網數據幀,然后將從中得到的 IP 數據包遞交)。被指向的函數具有兩個參數:一個是 pbuf 類型,代表將要遞交的數據包;另一個為 netif 類型,代表遞交數據包的網絡設備,函數返回值是 err_t 類型。網絡設備初始化時應該向 input 字段注冊相應的輸入函數,后面將詳細討論這個問題。     

  output 字段指向一個函數,這個函數和具體網絡接口設備驅動密切相關,它用於將 IP 層數據包發送到目的地址處。在 IP 層看來,每個網絡接口都會提供一個這樣的函數供它調用,當 IP 層發送數據包時,它會遍歷 netif_list 鏈表,找出最合適的網絡接口,並調用其注冊的 output 函數發送 數據包。不同的網絡接口需要根據實際接口特性來編寫相關的發送函數,並在初始化時將 output字段指向該函數。就以太網網卡的數據包發送而言,LwIP 在源代碼 etharp.c 文件中實現了一個名為 etharp_output 的函數,該函數就可以直接完成 IP 數據包在以太網中的發送,所以在初始化以太網卡結構時,可以直接將 output 字段指向函數 etharp_output。output 指向函數的三個參數分別是 pbuf類型、netif 類型和 ip_addr 類型,返回值是 err_t 類型。其中 pbuf 代表要發送的 IP 數據包,ipaddr代表數據包的目的 IP 地址

   linkoutput 字段和上面的 output 基本上是相似的功能,但是更底層一些,這個函數主要在以太網卡通信中被 ARP 模塊調用,用來完成以太網數據幀的發送,在其他類型的網絡接口中,這個字段的值就沒啥用處了。上面說的函數 etharp_output,它一方面接收 IP 層數據包,另一方面將該數據包封裝為以太網數據幀(填寫物理地址等字段),最后便調用 linkoutput 字段注冊的函數將以太網 數 據 幀 發 送 出 去 。 

  為了實現對這些數據包的管理,在網絡接口結構 netif 中定義了兩個指針 loop_first 和loop_last,它們分別用於指向數據包 pbuf 鏈表的第一個 pbuf 和最后一個 pbuf。這里的 pbuf 鏈表比較特殊,因為所有數據包的 pbuf 都組織在同一條鏈表上,這就說明一條 pbuf 鏈表上可能存在多個 數據包,這時 pbuf 結構的 next 字段是否為空並不能成為判斷一個數據包結束與否的標志,那在這樣的一條 pbuf 鏈表中,怎樣去將各個數據包的 pbuf 划分開呢?答案在於 pbuf 結構中的 tot_len 字段和 len 字段值,當兩個字段的值相等時,就代表一個數據包的結束。 

2、函數實現  

  向系統注冊一個網絡接口設備的函數 netif_add:

————netif.c————————————————
//定義兩個全局型的 netif 指針
struct netif *netif_list; //系統的全局型 netif 鏈表
struct netif *netif_default; //記錄系統缺省(默認)網絡接口
//函數功能:向 LwIP 內核注冊一個網絡接口結構
//參數 netif:指向一個已分配好的 netif 結構體
//參數 ipaddr:網絡接口的 IP 地址
//參數 netmask:網絡接口子網掩碼
//參數 gw:網關地址
//參數 state:用戶自定義的一些數據信息
//參數 init:網絡接口的初始化函數
//參數 input:網絡接口向 IP 層提交數據包的函數
//返回值:成功注冊的網絡接口結構指針
struct netif *netif_add(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask,
struct ip_addr *gw,
void *state,
err_t (* init)(struct netif *netif),
err_t (* input)(struct pbuf *p, struct netif *netif))
{
  static u8_t netifnum = 0; //定義靜態變量 netifnum,它記錄網絡接口的編號
  //清空 netif 結構的各個字段值
  netif­>ip_addr.addr = 0;
  netif­>netmask.addr = 0;
  netif­>gw.addr = 0;
  netif­>flags = 0;
  netif­>loop_first = NULL;
  netif­>loop_last = NULL;
  //填寫結構體各個字段值
  netif­>state = state;
  netif­>num = netifnum++; //網絡接口編號
  netif­>input = input; //注冊 input 函數
  //調用函數 netif_set_addr 設置網絡接口的三個地址字段值
  netif_set_addr(netif, ipaddr, netmask, gw);
  //調用網絡接口的初始化函數,初始化網絡接口
  if (init(netif) != ERR_OK)
  {
    //初始化失敗,則返回空     return NULL;   }   //將初始化成功的 netif 結構加入 netif_list 鏈表   netif­>next = netif_list;   netif_list = netif;   //返回 netif 結構指針   return netif; }

  netif_add 函數只是簡單的初始化了 netif 結構的幾個字段,然后回調網絡接口定義的初始化函數 init 來完成網絡接口的初始化工作。 

IP4_ADDR(&gw, 192,168,1,1//初始化三個地址
IP4_ADDR(&ipaddr, 192,168,1,37);
IP4_ADDR(&netmask, 255,255,255,0//調用 netif_add 函數
netif_add(&rtl8019_netif, &ipaddr, &netmask, &gw, NULL, ethernetif_init,ethernet_input);
netif_set_default(&rtl8019_netif); //設置系統的默認網絡接口為 rtl8019_netif
netif_set_up(&rtl8019_netif); //使能網絡接口 rtl8019_netif

  在上面這段代碼中,調用 netif_add 函數時,傳遞給它的兩個函數參數是 ethernetif_init ethernet_input其中前者就是網卡初始化函數 ethernetif_init,這是源碼提供者為以太網網卡驅動程序編寫的默認初始化函數,這個函數在第 5 章中也有所提及,這里來看看它的具體實現,源代碼如下所示。ethernet_input 是 ARP 層的一個函數,它的功能是提取以太網幀中的 ARP 地址數據,並將幀中的 IP 數據遞交給 IP 層,關於這個函數在下一章中會講解。 

#define IFNAME0 'e' //定義以太網網卡的名字字符
#define IFNAME1 'n'
//定義描述網卡用戶信息的結構,該結構無實際用處,源碼作者旨在用該結構來示意
//netif 結構中的 state 字段的用法,該字段可以指向任何用戶關心的信息
struct ethernetif 
{ //該結構只包含一個簡單的指針   struct eth_addr *ethaddr; }; //函數參數 netif:網絡接口結構的指針 err_t ethernetif_init(struct netif *netif) {   struct ethernetif *ethernetif;   ethernetif = mem_malloc(sizeof(struct ethernetif)); //為用戶信息結構申請一個內存堆空間   if (ethernetif == NULL) { //申請失敗,返回內存錯誤     return ERR_MEM;    }   netif­>state = ethernetif; //將 state 字段指向 ethernetif   netif­>name[0] = IFNAME0; //設置名字字段   netif­>name[1] = IFNAME1;   netif­>output = etharp_output; //注冊 IP 數據包輸出函數,這里使用 ARP 的相關函數   netif­>linkoutput = low_level_output; //注冊以太網數據幀輸出函數   //將 ethernetif 結構賦一個用戶關心的值,這里無具體意義   ethernetif­>ethaddr = (struct eth_addr *)&(netif­>hwaddr[0]); //指向網卡的地址信息   low_level_init(netif); //調用網卡底層初始化函數,這個函數已經講過了   return ERR_OK; //返回成功 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM