LWIP之IP層實現

 

 LWIP之IP層實現
2009-05-16 00:44:20
標籤:職場 休閒
原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章 原始出處 、作者信息和本聲明。否則將追究法律責任。http://bluefish.blog.51cto.com/214870/158417
2009-5-12 LWIP之IP層實現
 
       這一部分的實現都是在ip.c文件中【src\cor\ipv4】,可以看到在這個文件中主要實現了3個函數,ip_input;ip_route;ip_output以及ip_output_if。下面分別來介紹它們。
 
       這些函數可以分成兩大類:接收和發送。下面就先從發送開始,首先要說的就是ip_output函數,這個也是發送過程中最重要的一個,它是被tcp層調用的,詳細可參見以上章節。
* Simple interface to ip_output_if. It finds the outgoing network
* interface and calls upon ip_output_if to do the actual work.
err_t  ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
          u8_t ttl, u8_t tos, u8_t proto)
{
  struct netif *netif;
 
  if ((netif = ip_route(dest)) == NULL) {
    return ERR_RTE;
  }
 
  return ip_output_if(p, src, dest, ttl, tos, proto, netif);
}
可以看到該函數的實現就像註釋所說的一樣,直接調用了ip_route和ip_outputif兩個函數。根據以往的經驗,先看下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;
 
  /** IP address configuration in network byte order */
  struct ip_addr ip_addr;
  struct ip_addr netmask;
  struct ip_addr gw;
 
  /** This function is called by the network device driver
   *  to pass a packet up the TCP/IP stack. */
  err_t (* input)(struct pbuf *p, struct netif *inp);
 
  /** 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. */
  err_t (* output)(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr);
 
  /** This function is called by the ARP module when it wants
   *  to send a packet on the interface. This function outputs
   *  the pbuf as-is on the link medium. */
  err_t (* linkoutput)(struct netif *netif, struct pbuf *p);
 
#if LWIP_NETIF_STATUS_CALLBACK
  /** This function is called when the netif state is set to up or down
   */
  void (* status_callback)(struct netif *netif);
#endif /* LWIP_NETIF_STATUS_CALLBACK */
 
#if LWIP_NETIF_LINK_CALLBACK
  /** This function is called when the netif link is set to up or down
   */
  void (* link_callback)(struct netif *netif);
#endif /* LWIP_NETIF_LINK_CALLBACK */
 
  /** This field can be set by the device driver and could point
   *  to state information for the device. */
  void *state;
 
#if LWIP_DHCP
  /** the DHCP client state information for this netif */
  struct dhcp *dhcp;
#endif /* LWIP_DHCP */
 
#if LWIP_AUTOIP
  /** the AutoIP client state information for this netif */
  struct autoip *autoip;
#endif
 
#if LWIP_NETIF_HOSTNAME
  /* the hostname for this netif, NULL is a valid value */
  char*  hostname;
#endif /* LWIP_NETIF_HOSTNAME */
 
  /** number of bytes used in hwaddr */
  u8_t hwaddr_len;
 
  /** link level hardware address of this interface */
  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
 
  /** maximum transfer unit (in bytes) */
  u16_t mtu;
  /** flags (see NETIF_FLAG_ above) */
  u8_t flags;
 
  /** descriptive abbreviation */
  char name[2];
 
  /** number of this interface */
  u8_t num;
 
#if LWIP_SNMP
  /** link type (from "snmp_ifType" enum from snmp.h) */
  u8_t link_type;
  /** (estimate) link speed */
  u32_t link_speed;
  /** timestamp at last change made (up/down) */
  u32_t ts;
  /** counters */
  u32_t ifinoctets;
  u32_t ifinucastpkts;
  u32_t ifinnucastpkts;
  u32_t ifindiscards;
  u32_t ifoutoctets;
  u32_t ifoutucastpkts;
  u32_t ifoutnucastpkts;
  u32_t ifoutdiscards;
#endif /* LWIP_SNMP */
 
#if LWIP_IGMP
/* This function could be called to add or delete a entry in the multicast filter table of the ethernet MAC.*/
  err_t (*igmp_mac_filter)( struct netif *netif, struct ip_addr *group, u8_t action);
#endif /* LWIP_IGMP */
 
#if LWIP_NETIF_HWADDRHINT
  u8_t *addr_hint;
#endif /* LWIP_NETIF_HWADDRHINT */
};
該結構體實現在【src\include\lwip\netif.h】,注意到該結構體成員中有3個函數指針變量。好了,這個結構體先做一大體瞭解。用到的時候再詳細講。
       接下來先看下ip_route函數的實現:
* Finds the appropriate network interface for a given IP address. It
* searches the list of network interfaces linearly. A match is found
* if the masked IP address of the network interface equals the masked
* IP address given to the function.
struct netif * ip_route(struct ip_addr *dest)
{
  struct netif *netif;
 
  /* iterate through netifs */
  for(netif = netif_list; netif != NULL; netif = netif->next) {
    /* network mask matches? */
    if (netif_is_up(netif)) {
      if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
        /* return netif on which to forward IP packet */
        return netif;
      }
    }
  }
  if ((netif_default == NULL) || (!netif_is_up(netif_default)))
{
    snmp_inc_ipoutnoroutes();
    return NULL;
  }
  /* no matching netif found, use default netif */
  return netif_default;
}
可以說這個函數的實現很簡單,且作用也很容易看懂,就像其註釋所說的一樣。不過在這個函數中我們還是發現了一些什麼,對了,就是struct netif *netif_list;【src\core\netif.c】的使用。既然這裏都使用了這個網絡接口鏈表,那它是在哪裏被初始化的呢?
       好了,首先我們發現在netif_add函數中有對netif_list的調用,netif_add是被do_netifapi_netif_add函數調用的,而do_netifapi_netif_add是在netifapi_netif_add中通過netifapi_msg被TCPIP_NETIFAPI調用的。問題似乎很清楚,只要找到nnetifapi_netif_add是被誰調用的就好了,然而,搜遍整個工程也沒有發現這個函數的影子,除了一個聲明一個實現外。My god,又進入死衚衕了?好吧,這裏先標識一下,待解【見下面的解釋】
 
       我們接着看ip_output_if這個函數,具體函數可參考【src\core\ipv4\ip.c】。它的函數定義註釋如下:
* Sends an IP packet on a network interface. This function constructs
 * the IP header and calculates the IP header checksum. If the source
 * IP address is NULL, the IP address of the outgoing network
 * interface is filled in as source address.
 * If the destination IP address is IP_HDRINCL, p is assumed to already
 * include an IP header and p->payload points to it instead of the data.
再看最後一句:return netif->output(netif, p, dest);
嗯,看來這個netif還是關鍵啊,如果估計不錯的話,接收的時候也要用到這個結構的。那就看它在什麼地方被賦值的吧。又經過一番搜索,看來在目前的代碼中是找不到的了。查看lwip協議棧的設計與實現,特別是網絡接口層的那一節,終於明白了,原來這些是要有設備驅動來參與的:【一下爲引用】
當收到一個信息包時,設備驅動程序調用input指針指向的函數。網絡接口通過output指針連接到設備驅動。這個指針指向設備驅動中一個向物理網絡發送信息包的函數,當信息包包被髮送時由IP層調用,這個字段由設備驅動的初始設置函數填充。
       嗯,那就這樣吧,到這裏我們可以說IP層的發送流程已經走完了。
 
       接下來就是ip層的接收過程了。剛纔上面也有提到驅動設備收到包,丟給netif的input函數,這個input函數也是設備驅動層來設置的。無非有兩個可能,一個是ip_input,另外一個就是tcpip_input。因爲tcpip_input函數的處理是最終調用到了ip_input【在tcpip_thread中】。按照正常情況下應該是ip_input函數的,我們先來看下這個函數。
* This function is called by the network interface device driver when
 * an IP packet is received. The function does the basic checks of the
 * IP header such as packet size being at least larger than the header
 * size etc. If the packet was not destined for us, the packet is
 * forwarded (using ip_forward). The IP checksum is always checked.
原型:err_t ip_input(struct pbuf *p, struct netif *inp)
該函數大致的處理過程是:處理ip包頭;找到對應的netif;檢查如果是廣播或多播包,則丟掉;如果是tcp協議的話就直接調用了tcp_input函數處理數據。
 
       到此,ip層的東西大致就說完了。最後,由於tcp和ip層的東西都說完了,所以此時我們順便看下,tcpip的整體實現,這個主要是在src\api\tcpip.c文件中實現。我們知道發送過程是由socket直接調用的,所以這個文件中不涉及,說白了,這個文件主要是涉及到整個接收過程。這裏實現的函數有tcpip_input,和tcpip_thread以及tcpip_init函數。
Tcpip_init函數很簡單就是創建系統線程(sys_thread_new)tcpip_thread。
Tcpip_thread函數的註釋如下:
       * The main lwIP thread. This thread has exclusive access to lwIP core functions
      * (unless access to them is not locked). Other threads communicate with this
* thread using message boxes.
它的整個過程就是一直從mbox中取出msg,對各種msg的一個處理過程。
Tcpip_input函數,是在tcpip_thread中被調用的處理設備驅動接收到的信息包,並調用
ip_input來進一步處理。
 
整個啓動過程:
main---> vlwIPInit()
void vlwIPInit( void )
{
    /* Initialize lwIP and its interface layer. */
       sys_init();
       mem_init();                                                      
       memp_init();
       pbuf_init();
       netif_init();
       ip_init();
       sys_set_state(( signed portCHAR * ) "lwIP", lwipTCP_STACK_SIZE);
       tcpip_init( NULL, NULL );   
       sys_set_default_state();
}
 
從上面我們知道,tcpip_init創建tcpip_thread
在tcpip_thread的開始有如下代碼:
(void)arg;
 ip_init();
 
#if LWIP_UDP 
  udp_init();
#endif
 
#if LWIP_TCP
  tcp_init();
#endif
 
#if IP_REASSEMBLY
  sys_timeout(1000, ip_timer, NULL);
#endif
 
if (tcpip_init_done != NULL)
{
    tcpip_init_done(tcpip_init_done_arg);
 }
 
下面是tcp_init的實現
Void tcp_init(void)
{
  /* Clear globals. */
  tcp_listen_pcbs.listen_pcbs = NULL;
  tcp_active_pcbs = NULL;
  tcp_tw_pcbs = NULL;
  tcp_tmp_pcb = NULL;
 
  /* initialize timer */
  tcp_ticks = 0;
  tcp_timer = 0;
 
}
 
本文出自 “bluefish” 博客,請務必保留此出處http://bluefish.blog.51cto.com/214870/158417
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章