本文分析基于Linux Kernel 1.2.13

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

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

作者:闫明

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

这里看看数据包从IP层是如何交给传输层来处理的,为了方便,这里传输层以UDP协议为例来分析。

从ip_rcv()函数中可以看到

  1. /*
  2. * Pass on the datagram to each protocol that wants it,
  3. * based on the datagram protocol.  We should really
  4. * check the protocol handler's return values here...
  5. */
  6. ipprot->handler(skb2, dev, opts_p ? &opt : 0, iph->daddr,
  7. (ntohs(iph->tot_len) - (iph->ihl * 4)),
  8. iph->saddr, 0, ipprot);

这里调用指定协议的handler函数,如果是UDP协议,该函数的定义 udp_protocol如下

  1. static struct inet_protocol udp_protocol = {
  2. udp_rcv,      /* UDP handler      */
  3. NULL,         /* Will be UDP fraglist handler */
  4. udp_err,      /* UDP error control    */
  5. &tcp_protocol,    /* next         */
  6. IPPROTO_UDP,      /* protocol ID      */
  7. 0,            /* copy         */
  8. NULL,         /* data         */
  9. "UDP"         /* name         */
  10. };

先看UDP协议数据报的报头定义如下:比较简单

  1. struct udphdr {
  2. unsigned short    source;//源端口
  3. unsigned short    dest;//目的端口
  4. unsigned short    len;//数据包长度
  5. unsigned short    check;//检验和
  6. };

下面就分析下udp_rcv()函数,流程图:

  1. /*
  2. *  All we need to do is get the socket, and then do a checksum.
  3. */
  4. int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
  5. unsigned long daddr, unsigned short len,
  6. unsigned long saddr, int redo, struct inet_protocol *protocol)
  7. {
  8. struct sock *sk;
  9. struct udphdr *uh;
  10. unsigned short ulen;
  11. int addr_type = IS_MYADDR;
  12. if(!dev || dev->pa_addr!=daddr)//检查这个数据包是不是发送给本地的数据包
  13. addr_type=ip_chk_addr(daddr);//该函数定义在devinet.c中,用于检查ip地址是否是本地或多播、广播地址
  14. /*
  15. *  Get the header.
  16. */
  17. uh = (struct udphdr *) skb->h.uh;//获得UDP数据报的报头
  18. ip_statistics.IpInDelivers++;
  19. /*
  20. *  Validate the packet and the UDP length.
  21. */
  22. ulen = ntohs(uh->len);
  23. //参数len表示ip负载长度(IP数据报的数据部分长度)= UDP数据包头+UDP数据包的数据部分+填充部分长度
  24. //ulen表示的是UDP数据报首部和负载部分的长度,所以正常情况下len>=ulen
  25. if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh))
  26. {
  27. printk("UDP: short packet: %d/%d\n", ulen, len);
  28. udp_statistics.UdpInErrors++;
  29. kfree_skb(skb, FREE_WRITE);
  30. return(0);
  31. }
  32. if (uh->check && udp_check(uh, len, saddr, daddr)) //进行UDP数据包校验
  33. {
  34. /* <mea@utu.fi> wants to know, who sent it, to
  35. go and stomp on the garbage sender... */
  36. printk("UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n",
  37. ntohl(saddr),ntohs(uh->source),
  38. ntohl(daddr),ntohs(uh->dest),
  39. ulen);
  40. udp_statistics.UdpInErrors++;
  41. kfree_skb(skb, FREE_WRITE);
  42. return(0);
  43. }
  44. len=ulen;//对len赋值为实际的UDP数据报长度
  45. #ifdef CONFIG_IP_MULTICAST//对多播情况进行处理
  46. if (addr_type!=IS_MYADDR)
  47. {
  48. /*
  49. *  Multicasts and broadcasts go to each listener.
  50. */
  51. struct sock *sknext=NULL;//next指针
  52. /*get_sock_mcast 获取在对应端口的多播套接字队列
  53. *下面函数的参数依次表示:sock结构指针,本地端口,远端地址,远端端口,本地地址
  54. */
  55. sk=get_sock_mcast(udp_prot.sock_array[ntohs(uh->dest)&(SOCK_ARRAY_SIZE-1)], uh->dest,
  56. saddr, uh->source, daddr);
  57. if(sk)
  58. {
  59. do
  60. {
  61. struct sk_buff *skb1;
  62. sknext=get_sock_mcast(sk->next, uh->dest, saddr, uh->source, daddr);//下一个满足条件的套接字
  63. if(sknext)
  64. skb1=skb_clone(skb,GFP_ATOMIC);
  65. else
  66. skb1=skb;
  67. if(skb1)
  68. udp_deliver(sk, uh, skb1, dev,saddr,daddr,len);//对满足条件的套接字调用发送函数发送
  69. sk=sknext;
  70. }
  71. while(sknext!=NULL);
  72. }
  73. else
  74. kfree_skb(skb, FREE_READ);
  75. return 0;
  76. }
  77. #endif
  78. sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);
  79. if (sk == NULL) //没有找到本地对应的套接字,则进行出错处理
  80. {
  81. udp_statistics.UdpNoPorts++;
  82. if (addr_type == IS_MYADDR)
  83. {
  84. icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);//回复ICMP出错报文,目的主机不可达
  85. }
  86. /*
  87. * Hmm.  We got an UDP broadcast to a port to which we
  88. * don't wanna listen.  Ignore it.
  89. */
  90. skb->sk = NULL;
  91. kfree_skb(skb, FREE_WRITE);
  92. return(0);
  93. }
  94. return udp_deliver(sk,uh,skb,dev, saddr, daddr, len);//调用函数发送套接字
  95. }

上面函数中调用了get_sock_mcast()函数,下面具体分析一下该函数的功能,该函数定义的位置在文件af_inet.c中

  1. /*
  2. *  Deliver a datagram to broadcast/multicast sockets.
  3. */
  4. struct sock *get_sock_mcast(struct sock *sk, //套接字指针
  5. unsigned short num,//本地端口
  6. unsigned long raddr,//远端地址
  7. unsigned short rnum,//远端端口
  8. unsigned long laddr)//本地地址
  9. {
  10. struct sock *s;
  11. unsigned short hnum;
  12. hnum = ntohs(num);
  13. /*
  14. * SOCK_ARRAY_SIZE must be a power of two.  This will work better
  15. * than a prime unless 3 or more sockets end up using the same
  16. * array entry.  This should not be a problem because most
  17. * well known sockets don't overlap that much, and for
  18. * the other ones, we can just be careful about picking our
  19. * socket number when we choose an arbitrary one.
  20. */
  21. s=sk;
  22. for(; s != NULL; s = s->next)
  23. {
  24. if (s->num != hnum) //本地端口不符合,跳过
  25. continue;
  26. if(s->dead && (s->state == TCP_CLOSE))//dead=1表示该sock结构已经处于释放状态
  27. continue;
  28. if(s->daddr && s->daddr!=raddr)//sock的远端地址不等于条件中的远端地址
  29. continue;
  30. if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0)
  31. continue;
  32. if(s->saddr  && s->saddr!=laddr)//sock的本地地址不等于条件的本地地址
  33. continue;
  34. return(s);
  35. }
  36. return(NULL);
  37. }

下面是udp_rcv调用的udp_deliver()函数

  1. static int udp_deliver(struct sock *sk,//sock结构指针
  2. struct udphdr *uh,//UDP头指针
  3. struct sk_buff *skb,//sk_buff
  4. struct device *dev,//接收的网络设备
  5. long saddr,//本地地址
  6. long daddr,//远端地址
  7. int len)//数据包的长度
  8. {
  9. //对skb结构相应字段赋值
  10. skb->sk = sk;
  11. skb->dev = dev;
  12. //skb->len = len;
  13. /*
  14. *  These are supposed to be switched.
  15. */
  16. skb->daddr = saddr;//设置目的地址为本地地址
  17. skb->saddr = daddr;//设置源地址为远端地址
  18. /*
  19. *  Charge it to the socket, dropping if the queue is full.
  20. */
  21. skb->len = len - sizeof(*uh);
  22. if (sock_queue_rcv_skb(sk,skb)<0) //调用sock_queu_rcv_skb()函数,将skb挂到sk接构中的接收队列中
  23. {
  24. udp_statistics.UdpInErrors++;
  25. ip_statistics.IpInDiscards++;
  26. ip_statistics.IpInDelivers--;
  27. skb->sk = NULL;
  28. kfree_skb(skb, FREE_WRITE);
  29. release_sock(sk);
  30. return(0);
  31. }
  32. udp_statistics.UdpInDatagrams++;
  33. release_sock(sk);
  34. return(0);
  35. }

sock_queu_rcv_skb()函数的实现如下:

    1. /*
    2. *  Queue a received datagram if it will fit. Stream and sequenced protocols
    3. *  can't normally use this as they need to fit buffers in and play with them.
    4. */
    5. int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
    6. {
    7. unsigned long flags;
    8. if(sk->rmem_alloc + skb->mem_len >= sk->rcvbuf)
    9. return -ENOMEM;
    10. save_flags(flags);
    11. cli();
    12. sk->rmem_alloc+=skb->mem_len;
    13. skb->sk=sk;
    14. restore_flags(flags);
    15. skb_queue_tail(&sk->receive_queue,skb);
    16. if(!sk->dead)
    17. sk->data_ready(sk,skb->len);
    18. return 0;
    19. }

UDP协议的更多相关文章

  1. TODO:Golang语言TCP/UDP协议重用地址端口

    TODO:Golang语言TCP/UDP协议重用地址端口 这是一个简单的包来解决重用地址的问题. go net包(据我所知)不允许设置套接字选项. 这在尝试进行TCP NAT时尤其成问题,其需要在同一 ...

  2. 闲来无事,写个基于UDP协议的Socket通讯Demo

    项目一期已经做完,二期需求还没定稿,所以最近比较闲. 上一篇写的是TCP协议,今天写一下UDP协议.TCP是有连接协议,所以发送和接收消息前客户端和服务端需要建立连接:UDP是无连接协议,所以发送消息 ...

  3. UDP协议开发

    UDP是用户数据报协议(User Datagram Protocol,UDP)的简称,其主要作用是将网络数据流量压缩成数据报形式,提供面向事务的简单信息传送服务.与TCP协议不同,UDP协议直接利用I ...

  4. 基于UDP协议模拟的一个TCP协议传输系统

    TCP协议以可靠性出名,这其中包括三次握手建立连接,流控制和拥塞控制等技术.详细介绍如下: 1. TCP协议将需要发送的数据分割成数据块.数据块大小是通过MSS(maximum segment siz ...

  5. TCP协议与UDP协议的区别

    TCP协议与UDP协议的区别(转) 首先咱们弄清楚,TCP协议和UCP协议与TCP/IP协议的联系,很多人犯糊涂了,一直都是说TCP/IP协议与UDP协议的区别,我觉得这是没有从本质上弄清楚网络通信! ...

  6. 采用UDP协议的PIC32MZ ethernet bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 经过千辛万苦,今天终于 ...

  7. 采用UDP协议实现PIC18F97J60 ethernet bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). TCP/IP Stac ...

  8. 网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程

    Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服 ...

  9. Linux内核--网络栈实现分析(九)--传输层之UDP协议(下)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7549340 更多请查看专栏,地 ...

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

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/7532512 更多请看专栏, ...

随机推荐

  1. #194 sequence(搜索+动态规划+主席树)

    考虑按顺序暴搜子序列.如果序列中的数两两不同,显然每次给上一个找到的子序列添上后缀最小值,即为下一个要找的子序列.如果不能再加了就回溯继续考虑后缀次小.第三小……值,直到找到k个子序列. 有重复的数后 ...

  2. Codeforces Round #530 (Div. 2) A,B,C,D

    A. Snowball 链接:http://codeforces.com/contest/1099/problem/A 思路:模拟 代码: #include<bits/stdc++.h> ...

  3. MongoDB常用操作命令

    查看所有数据库: > show dbs; 选定数据库: > use ECommerce; 查看当前数据库状态: > db.stats(); 查看当前数据库中所有集合: > sh ...

  4. 洛谷P2704 炮兵阵地

    本题过于经典...... 对于这种网格状压DP,套路一波刷表法DFS转移就没了. 三进制状压,0表示当前,上一个都没有.1表示当前无,上一个有.2表示当前有. 转移的条件就是上一行为0,当前不是山地, ...

  5. A1130. Infix Expression

    Given a syntax tree (binary), you are supposed to output the corresponding infix expression, with pa ...

  6. A1076. Forwards on Weibo

    Weibo is known as the Chinese version of Twitter. One user on Weibo may have many followers, and may ...

  7. python基础面试常见题

    1.为什么学习Python? Python是目前市面上,我个人认为是最简洁.最优雅.最有前途.最全能的编程语言,没有之一. 2.通过什么途径学习的Python? 通过自学,包括网上查看一些视频,购买一 ...

  8. django(八)之数据库表的一对多,多对多表-增删改查

    单表操作 表记录的添加 方式一: Book() b=Book(name="python基础",price=99,author="yuan",pub_date=& ...

  9. XTest

    腾讯优测是一个移动云测试平台,为应用.游戏.H5混合应用的研发团队提供产品质量检测与问题解决服务. 这是腾讯内部针对微信内的H5,做了一套专门的UI自动化框架.而且都是用真机来跑这些框架,在真机上模拟 ...

  10. VMware加载vmdk文件

    VMware软件文件菜单选择---映射虚拟磁盘选项,如图1所示