<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" /> 2009-5-12 LWIP IP 层实现<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

       这一部分的实现都是在 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;

 

}