原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章 原始出處 、作者信息和本聲明。否則將追究法律責任。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