原文地址:http://bluefish.blog.51cto.com/214870/158417
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这一部分的实现都是在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;
 
}

LWIP之IP层实现(转载)相关推荐

  1. 《TCP/IP详解卷1:协议》——第2章:链路层(转载)

    1.引言 从图1-4可以看出,在TCP/IP协议族中,链路层主要有三个目的: (1)为IP模块发送和接收IP数据报: (2)为ARP模块发送ARP请求和接收ARP应答. (3)为RARP发送RARP请 ...

  2. 用netcat,SSH构建IP层Tunnel(转载)

    2017/05/06 [关于题外话在最后] 写作本文主要基于两点,首先是因为我前段时间写了几篇关于XXN的新解,收到了很多的邮件反馈,我也思考了很多,另一个方面是因为很多人问我怎么用QQ,P2P搭建一 ...

  3. IP 层收发报文简要剖析1-ip报文的输入

    ip层数据包处理场景如下: 网络层处理数据包文时需要和路由表以及邻居系统打交道.输入数据时,提供输入接口给链路层调用,并调用传输层的输入接口将数据输入到传输层. 在输出数据时,提供输出接口给传输层,并 ...

  4. lwIP TCP/IP 协议栈笔记之十五: TCP协议

    目录 1. TCP 服务简介 2. TCP 的特性 2.1 连接机制 2.2 确认与重传 2.3 缓冲机制 2.4 全双工通信 2.5 流量控制 2.6 差错控制 2.7 拥塞控制 3. 端口号的概念 ...

  5. lwIP TCP/IP 协议栈笔记之十八: Socket接口编程

    目录 1. Socket 概述 2. LwIP 中的socket 3. Socket API 3.1 socket() 3.2 bind() 3.3 connect() 3.4 listen() 3. ...

  6. linux内核网络协议栈--ip层报文转发之ip_local_out()函数(六)

    IP层本地报文发送有两个函数ip_local_out和ip_local_out_sk,实际实现两者是等同的,因为本地发送的报文,skb必然关联着一个sock对象. 1.ip_local_out函数 s ...

  7. linux 内核网络协议栈--IP层开始直到包被处理(三)

    先看看ip头结构: struct iphdr struct iphdr {#if defined(__LITTLE_ENDIAN_BITFIELD) // 小端__u8 ihl:4, // 首部长度( ...

  8. linux 内核网络协议栈--数据从接收到IP层(二)

    此处主要讲的是从数据来到,中断到最终数据包被处理的过程. 首先来介绍一下IO端口访问问题,内核提供了这样一组函数处理: /kernel/io.c中 inb( ).inw( ).inl( )函数 分别从 ...

  9. 为什么有了MAC层还要走IP层呢?

    之前我们提到,mac地址是唯一的,那理论上,在任何两个设备之间,我应该都可以通过mac地址发送数据,为什么还需要ip地址? mac地址就好像个人的身份证号,人的身份证号和人户口所在的城市,出生的日期有 ...

最新文章

  1. mySQL Slow Query Log Rotation(慢查询日志轮循设置)
  2. 【腾讯面试题】Java集合:List、Set以及Map
  3. HTML JS正方形轮播,js,html一个页面里面多个页面轮播
  4. linux 常用命令05 常用的压缩与解压缩文件
  5. 通俗易懂告诉你CPU/GPU是什么?
  6. jffs2reset 实现分析
  7. [转]numpy 100道练习题
  8. linux内存管理源码分析 - 页框分配器
  9. java中将url下载并转换为MultipartFile文件
  10. 【BZOJ1061】【NOI2008】志愿者招募 费用流神题、单纯形裸题(代码费用流)
  11. oracle之SQL的基本函数
  12. 获益匪浅:在北京每月能白捡一万元
  13. 计算机专硕考数一英一,专硕难度升级!改考数一英一,这所近2.3万人报考的热门高校大改...
  14. 快乐地谈谈:关于RSA算法中求私钥d的欧几里得方法(辗转相除法)考试向的欸
  15. 一部一台阶,直达LTE之巅——《学好LTE》丛书简介
  16. 徐雷FrankXuLei受邀为阿里巴巴集团马云老板的恒生集团讲授《分布式高并发HA架构和全新.net core高级课程》...
  17. 【BIOS 系列 2】编写驱动库模板
  18. ipfs 云服务器_吴川ipfs存储营销,IPFS
  19. 嵌入式开发模块]AD转换芯片ADS8344驱动模块16位分辨率,程序实例
  20. 数据挖掘与机器学习课程总结

热门文章

  1. mysql分页案例_MySQL优化案例系列-mysql分页优化
  2. 将宽度赋给高度_人生应知长度、懂宽度、有高度
  3. 2.平凡之路-初识MyBatis
  4. oracle忘记密码,修改密码
  5. mysql 协议的query包及解析
  6. Sharepoint学习笔记---Sandbox Solution-- Full Trust Proxy--开发实例之(2、在Webpart中访问Full Trust Proxy)...
  7. Oracle的rownum原理和使用(整理几个达人的帖子)
  8. 3月第2周安全回顾 微软修补12个漏洞 ***盯上企业FTP
  9. OpenOffice介绍
  10. 计算机设备管理系统报告,设备管理信息化自查报告