Linux内核--网络栈实现分析(四)--网络层之IP协议(上)

本文分析基于Linux Kernel 1.2.13

原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7514017

更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html

作者:闫明

注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析、”(下)“表示分析是从上向下分析。

简单分析了链路层之后,上升到网络层来分析,看看链路层是如何为其上层--网络层服务的。其实在驱动程序层和网络层直接还有一层是接口层,叫做驱动程序接口层,用来整合不同的网络设备。接口层的内容会在上下层中提及。这里我们分析网络IP协议的实现原理。

其实现的文件主要是net/inet/ip.c文件中

我们首先分析下ip_init()初始化函数

这个函数是如何被调用的呢?

下面是调用的过程:

首先是在系统启动过程main.c中调用了sock_init()函数

[cpp] view plaincopy

  1. void sock_init(void)//网络栈初始化
  2. {
  3. int i;
  4. printk("Swansea University Computer Society NET3.019\n");
  5. /*
  6. *  Initialize all address (protocol) families.
  7. */
  8. for (i = 0; i < NPROTO; ++i) pops[i] = NULL;
  9. /*
  10. *  Initialize the protocols module.
  11. */
  12. proto_init();
  13. #ifdef CONFIG_NET
  14. /*
  15. *  Initialize the DEV module.
  16. */
  17. dev_init();
  18. /*
  19. *  And the bottom half handler
  20. */
  21. bh_base[NET_BH].routine= net_bh;//设置NET 下半部分的处理函数为net_bh
  22. enable_bh(NET_BH);
  23. #endif
  24. }

然后调用了proto_init()函数

[cpp] view plaincopy

  1. void proto_init(void)
  2. {
  3. extern struct net_proto protocols[];    /* Network protocols 全局变量,定义在protocols.c中*/
  4. struct net_proto *pro;
  5. /* Kick all configured protocols. */
  6. pro = protocols;
  7. while (pro->name != NULL) //对所有的定义的域进行初始化
  8. {
  9. (*pro->init_func)(pro);
  10. pro++;
  11. }
  12. /* We‘re all done... */
  13. }

而protocols全局变量协议向量表的定义中对INET域中协议的初始化函数设置为inet_proto_init()

[cpp] view plaincopy

  1. /*
  2. *  Protocol Table
  3. */
  4. struct net_proto protocols[] = {
  5. #ifdef  CONFIG_UNIX
  6. { "UNIX", unix_proto_init },
  7. #endif
  8. #if defined(CONFIG_IPX)||defined(CONFIG_ATALK)
  9. { "802.2",    p8022_proto_init },
  10. { "SNAP", snap_proto_init },
  11. #endif
  12. #ifdef CONFIG_AX25
  13. { "AX.25",    ax25_proto_init },
  14. #endif
  15. #ifdef  CONFIG_INET
  16. { "INET", inet_proto_init },
  17. #endif
  18. #ifdef  CONFIG_IPX
  19. { "IPX",  ipx_proto_init },
  20. #endif
  21. #ifdef CONFIG_ATALK
  22. { "DDP",  atalk_proto_init },
  23. #endif
  24. { NULL,   NULL        }
  25. };

看到在inet_proto_init()函数中调用了ip_init()对IP层进行了初始化。

[cpp] view plaincopy

  1. void inet_proto_init(struct net_proto *pro)//INET域协议初始化函数
  2. {
  3. struct inet_protocol *p;
  4. int i;
  5. printk("Swansea University Computer Society TCP/IP for NET3.019\n");
  6. /*
  7. *  Tell SOCKET that we are alive...
  8. */
  9. (void) sock_register(inet_proto_ops.family, &inet_proto_ops);
  10. ...........................................
  11. printk("IP Protocols: ");
  12. for(p = inet_protocol_base; p != NULL;) //将inet_protocol_base指向的一个inet_protocol结构体加入数组inet_protos中
  13. {
  14. struct inet_protocol *tmp = (struct inet_protocol *) p->next;
  15. inet_add_protocol(p);
  16. printk("%s%s",p->name,tmp?", ":"\n");
  17. p = tmp;
  18. }
  19. /*
  20. *  Set the ARP module up
  21. */
  22. arp_init();//对地址解析层进行初始化
  23. /*
  24. *  Set the IP module up
  25. */
  26. ip_init();//对IP层进行初始化
  27. }

代码中inet_protocol_base指向的链表为&igmp_protocol-->&icmp_protocol-->&udp_protocol-->&tcp_protocol-->NULL(定义在protocol.c中)

分析ip_init()函数需要先要知道packet_type结构,这个结构体是网络层协议的结构体,网络层协议与该结构体一一对应。

[cpp] view plaincopy

  1. /*该结构用于表示网络层协议,网络层协议与packt_type一一对应*/
  2. struct packet_type {
  3. unsigned short    type;   /* This is really htons(ether_type). ,对应的网络层协议编号*/
  4. struct device *   dev;
  5. int           (*func) (struct sk_buff *, struct device *,
  6. struct packet_type *);
  7. void          *data;
  8. struct packet_type    *next;
  9. };

第一个字段的网络层协议编号定义在include/linux/if_ether.h中

[cpp] view plaincopy

  1. /* These are the defined Ethernet Protocol ID‘s. */
  2. #define ETH_P_LOOP  0x0060      /* Ethernet Loopback packet */
  3. #define ETH_P_ECHO  0x0200      /* Ethernet Echo packet     */
  4. #define ETH_P_PUP   0x0400      /* Xerox PUP packet     */
  5. #define ETH_P_IP    0x0800      /* Internet Protocol packet */
  6. #define ETH_P_ARP   0x0806      /* Address Resolution packet    */
  7. #define ETH_P_RARP      0x8035      /* Reverse Addr Res packet  */
  8. #define ETH_P_X25   0x0805      /* CCITT X.25           */
  9. #define ETH_P_ATALK 0x809B      /* Appletalk DDP        */
  10. #define ETH_P_IPX   0x8137      /* IPX over DIX         */
  11. #define ETH_P_802_3 0x0001      /* Dummy type for 802.3 frames  */
  12. #define ETH_P_AX25  0x0002      /* Dummy protocol id for AX.25  */
  13. #define ETH_P_ALL   0x0003      /* Every packet (be careful!!!) */
  14. #define ETH_P_802_2 0x0004      /* 802.2 frames         */
  15. #define ETH_P_SNAP  0x0005      /* Internal only        */

第二个字段表示处理包的网络接口设备,一般初始化为NULL。

第三个字段为相应网络协议的处理函数。

第四个字段是一个void指针。

第五个字段是next指针域,用于将该结构连接成链表。

下面是ip_init()函数

[cpp] view plaincopy

  1. /*
  2. *  IP registers the packet type and then calls the subprotocol initialisers
  3. */
  4. void ip_init(void)//该函数在af_inet.c文件中
  5. {
  6. ip_packet_type.type=htons(ETH_P_IP);
  7. dev_add_pack(&ip_packet_type);//将网络协议插入IP协议链表,头插法
  8. /* So we flush routes when a device is downed */
  9. register_netdevice_notifier(&ip_rt_notifier);//将其插入通知链表
  10. /*  ip_raw_init();
  11. ip_packet_init();
  12. ip_tcp_init();
  13. ip_udp_init();*/
  14. }

这里需要说明的是系统采用主动通知的方式,其实现是有赖于notifier_block结构,其定义在notifier.h中

[cpp] view plaincopy

  1. struct notifier_block
  2. {
  3. int (*notifier_call)(unsigned long, void *);
  4. struct notifier_block *next;
  5. int priority;
  6. };

对于网卡设备而言,网卡设备的启动和关闭是事件,内核需要得到通知从而采取相应的措施。其原理是:当事件发生时,事件通知者便利某个队列,对队列中感兴趣(符合条件)的被通知者调用被通知者注册是定义的通知处理函数,从而达到让内核做出相应的操作。

当硬件缓冲区数据填满后,会执行中断处理程序,以NE 8390网卡为例,由于在ne.c文件中注册中断时的中断处理函数设置如下:

[cpp] view plaincopy

  1. request_irq (dev->irq, ei_interrupt, 0, wordlength==2 ? "ne2000":"ne1000");

中断处理函数为ei_interrupt()。执行ei_interrupt()函数时会调用函数ei_recieve(),而ei_recieve()函数会调用netif_rx()函数将以skb_buf的形式发送给上层。

当然netif_rx()函数的特点前面分析过,即Bottom Half技术,使得中断处理过程有效的缩短,提高系统的效率。在下半段该函数会调用dev_transmit()函数,而它会调用函数dev_tint()函数,dev_tinit()会调用函数dev_queue_xmit(),这个函数会调用dev->hard_start_xmit函数,该函数指针在ethdev_init()函数中赋值了:

[cpp] view plaincopy

  1. dev->hard_start_xmit = &ei_start_xmit;//设备的发送函数,定义在8390.c中

最后调用ei_start_xmit()函数将数据包从硬件设备中读出放在skb中,即存放到内核空间中。

中断返回后系统会执行下半段,即执行net_bh()函数,该函数会扫描网络协议队列,调用相应的协议的接收函数,IP协议就会调用ip_rcv()

[cpp] view plaincopy

  1. /*
  2. *  When we are called the queue is ready to grab, the interrupts are
  3. *  on and hardware can interrupt and queue to the receive queue a we
  4. *  run with no problems.
  5. *  This is run as a bottom half after an interrupt handler that does
  6. *  mark_bh(NET_BH);
  7. */
  8. void net_bh(void *tmp)
  9. {
  10. ...................................
  11. while((skb=skb_dequeue(&backlog))!=NULL)//出队直到队列为空
  12. {
  13. ...............................
  14. /*
  15. *   Fetch the packet protocol ID.  This is also quite ugly, as
  16. *   it depends on the protocol driver (the interface itself) to
  17. *   know what the type is, or where to get it from.  The Ethernet
  18. *   interfaces fetch the ID from the two bytes in the Ethernet MAC
  19. *   header (the h_proto field in struct ethhdr), but other drivers
  20. *   may either use the ethernet ID‘s or extra ones that do not
  21. *   clash (eg ETH_P_AX25). We could set this before we queue the
  22. *   frame. In fact I may change this when I have time.
  23. */
  24. type = skb->dev->type_trans(skb, skb->dev);//取出该数据包所属的协议类型
  25. /*
  26. *  We got a packet ID.  Now loop over the "known protocols"
  27. *  table (which is actually a linked list, but this will
  28. *  change soon if I get my way- FvK), and forward the packet
  29. *  to anyone who wants it.
  30. *
  31. *  [FvK didn‘t get his way but he is right this ought to be
  32. *  hashed so we typically get a single hit. The speed cost
  33. *  here is minimal but no doubt adds up at the 4,000+ pkts/second
  34. *  rate we can hit flat out]
  35. */
  36. pt_prev = NULL;
  37. for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) //遍历ptype_base所指向的网络协议队列
  38. {
  39. //判断协议号是否匹配
  40. if ((ptype->type == type || ptype->type == htons(ETH_P_ALL)) && (!ptype->dev || ptype->dev==skb->dev))
  41. {
  42. /*
  43. *  We already have a match queued. Deliver
  44. *  to it and then remember the new match
  45. */
  46. if(pt_prev)
  47. {
  48. struct sk_buff *skb2;
  49. skb2=skb_clone(skb, GFP_ATOMIC);//复制数据包结构
  50. /*
  51. *  Kick the protocol handler. This should be fast
  52. *  and efficient code.
  53. */
  54. if(skb2)
  55. pt_prev->func(skb2, skb->dev, pt_prev);//调用相应协议的处理函数,
  56. //这里和网络协议的种类有关系
  57. //如IP 协议的处理函数就是ip_rcv
  58. }
  59. /* Remember the current last to do */
  60. pt_prev=ptype;
  61. }
  62. } /* End of protocol list loop */
  63. ...........................................
  64. }

IP数据包类型的初始化设置在ip.c中

[cpp] view plaincopy

  1. /*
  2. *  IP protocol layer initialiser
  3. */
  4. static struct packet_type ip_packet_type =
  5. {
  6. 0,  /* MUTTER ntohs(ETH_P_IP),*/
  7. NULL,   /* All devices */
  8. ip_rcv,
  9. NULL,
  10. NULL,
  11. };

接下来分析ip_rcv()函数,这是IP层的接收函数,接收来自链路层的数据。

这里首先了解一下IP数据包的首部结构,结构示意图如下:

标志位三位,分别是:保留,DF(可以分片),MF(还有后续分片)。

内核中对应的结构体定义如下(include/linux/ip.h):

[cpp] view plaincopy

  1. /*IP数据包首部结构体*/
  2. struct iphdr {
  3. #if defined(LITTLE_ENDIAN_BITFIELD)
  4. __u8    ihl:4,
  5. version:4;
  6. #elif defined (BIG_ENDIAN_BITFIELD)
  7. __u8    version:4,
  8. ihl:4;
  9. #else
  10. #error  "Please fix <asm/byteorder.h>"
  11. #endif
  12. __u8    tos;//服务类型
  13. __u16   tot_len;//总长度
  14. __u16   id;//标示
  15. __u16   frag_off;//标志和片偏移
  16. __u8    ttl;//生存时间
  17. __u8    protocol;//协议
  18. __u16   check;//头部校验和
  19. __u32   saddr;//源地址
  20. __u32   daddr;//目的地址
  21. /*The options start here. */
  22. };

内核中用于封装网络数据的最重要的数据结构sk_buff定义在include/linux/skbuff.h中:

[cpp] view plaincopy

  1. struct sk_buff {
  2. struct sk_buff        * volatile next;//指针域,指向后继
  3. struct sk_buff        * volatile prev;//指针域,指向前驱
  4. #if CONFIG_SKB_CHECK
  5. int               magic_debug_cookie;
  6. #endif
  7. struct sk_buff        * volatile link3;//该指针域用于TCP协议,指向数据包重发队列
  8. struct sock           *sk;//该数据指向的套接字
  9. volatile unsigned long    when;   /* used to compute rtt‘s    用于计算往返时间*/
  10. struct timeval        stamp;
  11. struct device         *dev;//标示发送或接收该数据包的接口设备
  12. struct sk_buff        *mem_addr;//指向该sk_buff结构指向的内存基地址,用于该数据结构的内存释放
  13. union {//该联合结构用于实现通用
  14. struct tcphdr   *th;
  15. struct ethhdr   *eth;//链路层有效
  16. struct iphdr    *iph;//网络层有效
  17. struct udphdr   *uh;
  18. unsigned char   *raw;
  19. unsigned long   seq;//TCP协议有效,表示该数据包的ACK值
  20. } h;
  21. struct iphdr      *ip_hdr;        /* For IPPROTO_RAW ,指向IP首部指针,用于RAW套接字*/
  22. unsigned long         mem_len;//表示该结构的大小和数据帧的总大小
  23. unsigned long         len;//表示数据帧大小
  24. unsigned long         fraglen;//表示分片个数
  25. struct sk_buff        *fraglist;  /* Fragment list ,分片数据包队列*/
  26. unsigned long         truesize;//==mem_len
  27. unsigned long         saddr;//源地址
  28. unsigned long         daddr;//目的地址
  29. unsigned long         raddr;//数据包的下一站地址
  30. volatile char         acked,//==1表示该数据包已经得到确认
  31. used,//==1表示该数据包已经被数据包用完,可以释放
  32. free,//==1表示该数据包用完后直接释放,不用缓存
  33. arp;//==1表示MAC数据帧首部完成,否则表示MAC首部目的硬件地址尚不知晓,需使用ARP协议询问
  34. unsigned char         tries,//表示数据包已得到试发送
  35. lock,//表示是否被其他程序使用
  36. localroute,//表示是局域网路由还是广域网路由
  37. pkt_type;//表示数据包的类型,分为,发往本机、广播、多播、发往其他主机
  38. #define PACKET_HOST     0       /* To us */
  39. #define PACKET_BROADCAST    1
  40. #define PACKET_MULTICAST    2
  41. #define PACKET_OTHERHOST    3       /* Unmatched promiscuous */
  42. unsigned short        users;      /* User count - see datagram.c (and soon seqpacket.c/stream.c) 使用该数据包的程序数目*/
  43. unsigned short        pkt_class;  /* For drivers that need to cache the packet type with the skbuff (new PPP) */
  44. #ifdef CONFIG_SLAVE_BALANCING
  45. unsigned short        in_dev_queue;//表示数据包是否在设备缓冲队列
  46. #endif
  47. unsigned long         padding[0];//填充
  48. unsigned char         data[0];//指向数据部分
  49. };

ip_rcv()函数流程图:

[cpp] view plaincopy

  1. /*
  2. *  This function receives all incoming IP datagrams.
  3. */
  4. int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
  5. {
  6. struct iphdr *iph = skb->h.iph;
  7. struct sock *raw_sk=NULL;
  8. unsigned char hash;
  9. unsigned char flag = 0;
  10. unsigned char opts_p = 0;   /* Set iff the packet has options. */
  11. struct inet_protocol *ipprot;//每个传输层协议对应一个inet_protocol,用于调用传输层的服务函数
  12. static struct options opt; /* since we don‘t use these yet, and they
  13. take up stack space. */
  14. int brd=IS_MYADDR;
  15. int is_frag=0;
  16. #ifdef CONFIG_IP_FIREWALL
  17. int err;
  18. #endif
  19. ip_statistics.IpInReceives++;
  20. /*
  21. *  Tag the ip header of this packet so we can find it
  22. */
  23. skb->ip_hdr = iph;//设置IP首部指针
  24. /*
  25. *  Is the datagram acceptable?
  26. *
  27. *  1.  Length at least the size of an ip header
  28. *  2.  Version of 4
  29. *  3.  Checksums correctly. [Speed optimisation for later, skip loopback checksums]
  30. *  (4. We ought to check for IP multicast addresses and undefined types.. does this matter ?)
  31. */
  32. //IP数据包合法性检查
  33. if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 ||
  34. skb->len<ntohs(iph->tot_len) || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0)
  35. {
  36. ip_statistics.IpInHdrErrors++;
  37. kfree_skb(skb, FREE_WRITE);
  38. return(0);
  39. }
  40. /*
  41. *  See if the firewall wants to dispose of the packet.
  42. */
  43. #ifdef  CONFIG_IP_FIREWALL
  44. //检查防火墙是否阻止该数据包,过滤数据包
  45. if ((err=ip_fw_chk(iph,dev,ip_fw_blk_chain,ip_fw_blk_policy, 0))!=1)
  46. {
  47. if(err==-1)
  48. icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
  49. kfree_skb(skb, FREE_WRITE);
  50. return 0;
  51. }
  52. #endif
  53. /*
  54. *  Our transport medium may have padded the buffer out. Now we know it
  55. *  is IP we can trim to the true length of the frame.
  56. */
  57. skb->len=ntohs(iph->tot_len);
  58. /*
  59. *  Next analyse the packet for options. Studies show under one packet in
  60. *  a thousand have options....
  61. */
  62. if (iph->ihl != 5)//IP数据报首部存在选项字段
  63. {   /* Fast path for the typical optionless IP packet. */
  64. memset((char *) &opt, 0, sizeof(opt));
  65. if (do_options(iph, &opt) != 0)
  66. return 0;
  67. opts_p = 1;
  68. }
  69. /*
  70. *  Remember if the frame is fragmented.
  71. */
  72. //看该数据包是否含有分片,MF和偏移同时为0,则表示无分片,否则是分片,此处有BUG
  73. /*
  74. 分片的条件
  75. 第一个分片MF=1,offset=0
  76. 中间分片MF=1,offset!=0
  77. 最后分片MF=1,offset!=0
  78. */
  79. if(iph->frag_off)
  80. {
  81. if (iph->frag_off & 0x0020)
  82. is_frag|=1;
  83. /*
  84. *  Last fragment ?
  85. */
  86. if (ntohs(iph->frag_off) & 0x1fff)
  87. is_frag|=2;
  88. }
  89. /*
  90. *  Do any IP forwarding required.  chk_addr() is expensive -- avoid it someday.
  91. *
  92. *  This is inefficient. While finding out if it is for us we could also compute
  93. *  the routing table entry. This is where the great unified cache theory comes
  94. *  in as and when someone implements it
  95. *
  96. *  For most hosts over 99% of packets match the first conditional
  97. *  and don‘t go via ip_chk_addr. Note: brd is set to IS_MYADDR at
  98. *  function entry.
  99. */
  100. if ( iph->daddr != skb->dev->pa_addr && (brd = ip_chk_addr(iph->daddr)) == 0)
  101. {
  102. /*
  103. *  Don‘t forward multicast or broadcast frames.广播的数据报不转发
  104. */
  105. if(skb->pkt_type!=PACKET_HOST || brd==IS_BROADCAST)
  106. {
  107. kfree_skb(skb,FREE_WRITE);
  108. return 0;
  109. }
  110. /*
  111. *  The packet is for another target. Forward the frame
  112. */
  113. #ifdef CONFIG_IP_FORWARD
  114. ip_forward(skb, dev, is_frag);//转发数据报
  115. #else
  116. /*      printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n",
  117. iph->saddr,iph->daddr);*/
  118. ip_statistics.IpInAddrErrors++;
  119. #endif
  120. /*
  121. *  The forwarder is inefficient and copies the packet. We
  122. *  free the original now.
  123. */
  124. kfree_skb(skb, FREE_WRITE);
  125. return(0);
  126. }
  127. #ifdef CONFIG_IP_MULTICAST
  128. //多播
  129. if(brd==IS_MULTICAST && iph->daddr!=IGMP_ALL_HOSTS && !(dev->flags&IFF_LOOPBACK))
  130. {
  131. /*
  132. *  Check it is for one of our groups
  133. */
  134. struct ip_mc_list *ip_mc=dev->ip_mc_list;
  135. do
  136. {
  137. if(ip_mc==NULL)
  138. {
  139. kfree_skb(skb, FREE_WRITE);
  140. return 0;
  141. }
  142. if(ip_mc->multiaddr==iph->daddr)
  143. break;
  144. ip_mc=ip_mc->next;
  145. }
  146. while(1);
  147. }
  148. #endif
  149. /*
  150. *  Account for the packet
  151. */
  152. #ifdef CONFIG_IP_ACCT
  153. ip_acct_cnt(iph,dev, ip_acct_chain);
  154. #endif
  155. /*
  156. * Reassemble IP fragments.
  157. */
  158. if(is_frag)//该数据报是一个分片,进行合并
  159. {
  160. /* Defragment. Obtain the complete packet if there is one */
  161. /*该函数的作用是梳理分片的数据报,如果接收当前分片后,所有分片均已到达
  162. *该函数会调用ip_glue()函数进行IP数据报的重组,否则将该IP数据报放到ipq中fragment
  163. *字段指向的队列中
  164. *
  165. */
  166. skb=ip_defrag(iph,skb,dev);
  167. if(skb==NULL)
  168. return 0;
  169. skb->dev = dev;
  170. iph=skb->h.iph;
  171. }
  172. /*
  173. *  Point into the IP datagram, just past the header.
  174. */
  175. skb->ip_hdr = iph;
  176. skb->h.raw += iph->ihl*4;//sk_buff中union类型的h字段永远指向当前正在处理的协议的首部,这里使其指向传输层的首部,用于传输层的处理
  177. /*
  178. *  Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies.
  179. */
  180. hash = iph->protocol & (SOCK_ARRAY_SIZE-1);
  181. /* If there maybe a raw socket we must check - if not we don‘t care less */
  182. //处理RAW类型的套接字
  183. if((raw_sk=raw_prot.sock_array[hash])!=NULL)
  184. {
  185. struct sock *sknext=NULL;
  186. struct sk_buff *skb1;
  187. raw_sk=get_sock_raw(raw_sk, hash,  iph->saddr, iph->daddr);
  188. if(raw_sk)  /* Any raw sockets */
  189. {
  190. do
  191. {
  192. /* Find the next */
  193. sknext=get_sock_raw(raw_sk->next, hash, iph->saddr, iph->daddr);
  194. if(sknext)
  195. skb1=skb_clone(skb, GFP_ATOMIC);
  196. else
  197. break;  /* One pending raw socket left */
  198. if(skb1)
  199. raw_rcv(raw_sk, skb1, dev, iph->saddr,iph->daddr);//RAW类型套接字的接收函数
  200. raw_sk=sknext;
  201. }
  202. while(raw_sk!=NULL);
  203. /* Here either raw_sk is the last raw socket, or NULL if none */
  204. /* We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy */
  205. }
  206. }
  207. /*
  208. *  skb->h.raw now points at the protocol beyond the IP header.
  209. */
  210. hash = iph->protocol & (MAX_INET_PROTOS -1);
  211. //对所有使用IP协议的上层协议套接字处理
  212. for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
  213. {
  214. struct sk_buff *skb2;
  215. if (ipprot->protocol != iph->protocol)
  216. continue;
  217. /*
  218. *   See if we need to make a copy of it.  This will
  219. *   only be set if more than one protocol wants it.
  220. *   and then not for the last one. If there is a pending
  221. *   raw delivery wait for that
  222. */
  223. if (ipprot->copy || raw_sk)
  224. {
  225. skb2 = skb_clone(skb, GFP_ATOMIC);
  226. if(skb2==NULL)
  227. continue;
  228. }
  229. else
  230. {
  231. skb2 = skb;
  232. }
  233. flag = 1;
  234. /*
  235. * Pass on the datagram to each protocol that wants it,
  236. * based on the datagram protocol.  We should really
  237. * check the protocol handler‘s return values here...
  238. */
  239. ipprot->handler(skb2, dev, opts_p ? &opt : 0, iph->daddr,
  240. (ntohs(iph->tot_len) - (iph->ihl * 4)),
  241. iph->saddr, 0, ipprot);
  242. }
  243. /*
  244. * All protocols checked.
  245. * If this packet was a broadcast, we may *not* reply to it, since that
  246. * causes (proven, grin) ARP storms and a leakage of memory (i.e. all
  247. * ICMP reply messages get queued up for transmission...)
  248. */
  249. if(raw_sk!=NULL)    /* Shift to last raw user */
  250. raw_rcv(raw_sk, skb, dev, iph->saddr, iph->daddr);
  251. else if (!flag)     /* Free and report errors */
  252. {
  253. if (brd != IS_BROADCAST && brd!=IS_MULTICAST)
  254. icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev);
  255. kfree_skb(skb, FREE_WRITE);
  256. }
  257. return(0);
  258. }

这里会进一步调用raw_rcv()或者相应协议的ipprot->handler来调用传输层服务函数。下篇会进行简单分析。

时间: 2024-10-15 11:02:22

Linux内核--网络栈实现分析(四)--网络层之IP协议(上)的相关文章

Linux内核--网络栈实现分析(七)--数据包的传递过程(下)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7545855 更多请查看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 在博文Linux内核--网络栈

Linux内核--网络栈实现分析(十一)--驱动程序层(下)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7555870 更多请查看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 在博文Linux内核--网络栈

Linux内核--网络栈实现分析(一)--网络栈初始化

本文分析基于内核Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7488828 更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 以后的系列博文将深入分析Linux内核的网络栈实现原理,这里看到曹桂平博士的分析后,也决定选择Linux内核1.2.13版本进行分析. 原因如下: 1.功能和网络栈层次

Linux内核--网络栈实现分析(三)--驱动程序层+链路层(上)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7497260 更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 经过前面两篇博文的分析,已经对L

Linux内核--网络栈实现分析(六)--应用层获取数据包(上)

本文分析基于内核Linux 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7541907 更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 上篇博文分析了传输层从网络层获取数据包后将

Linux内核--网络栈实现分析(二)--数据包的传递过程(上)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7492423 更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 上一篇博文中我们从宏观上分析了L

Linux内核--网络栈实现分析(十)--网络层之IP协议(下)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7552455 更多请查看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 上篇博文分析传输层最终会调用函

Linux内核--网络栈实现分析(五)--传输层之UDP协议(上)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/7532512 更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 这里看看数据包从IP层是如何

Linux内核--网络栈实现分析(八)--应用层发送数据(下)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7547826 更多请查看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 下面是发送数据的流程: 应用层