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


    本文分析基于Linux Kernel 1.2.13

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

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

    作者:闫明

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


    上篇分析了应用层经过BSD socket层到INET socket层的函数调用关系和数据的处理流程,INET层会调用具体的传输层协议,还是以UDP协议为例

    udp_write()函数

    1. static int udp_write(struct sock *sk, unsigned char *buff, int len, int noblock,  
    2.       unsigned flags)  
    3. {  
    4.     return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));  
    5. }  

    在分析udp_sendto()函数之前,先了解一下sockaddr_in结构,这是标准的网络接口地址结构的定义

    1. struct sockaddr_in {  
    2.   short int     sin_family; /* Address family   地址族 */  
    3.   unsigned short int    sin_port;   /* Port number  端口号     */  
    4.   struct in_addr    sin_addr;   /* Internet address 网络地址    */  
    5.   
    6.   /* Pad to size of `struct sockaddr'. */  
    7.   unsigned char     __pad[__SOCK_SIZE__ - sizeof(short int) -  
    8.             sizeof(unsigned short int) - sizeof(struct in_addr)];  
    9. };  
    10. #define sin_zero    __pad       /* for BSD UNIX comp. -FvK  */  

    udp_sentdto()函数

    1. static int udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock,  
    2.        unsigned flags, struct sockaddr_in *usin, int addr_len)  
    3. {  
    4.     struct sockaddr_in sin;  
    5.     int tmp;  
    6.   
    7.     /*  
    8.      *  Check the flags. We support no flags for UDP sending 
    9.      */  
    10.     if (flags&~MSG_DONTROUTE)   
    11.         return(-EINVAL);  
    12.     /* 
    13.      *  Get and verify the address.  
    14.      */  
    15.        
    16.     if (usin) //如果usin不是空  
    17.     {  
    18.         if (addr_len < sizeof(sin))   
    19.             return(-EINVAL);  
    20.         memcpy(&sin,usin,sizeof(sin));  
    21.         if (sin.sin_family && sin.sin_family != AF_INET)  
    22.             return(-EINVAL);  
    23.         if (sin.sin_port == 0)   
    24.             return(-EINVAL);  
    25.     }   
    26.     else //usin为空  
    27.     {  
    28.         if (sk->state != TCP_ESTABLISHED)   
    29.             return(-EINVAL);  
    30.         sin.sin_family = AF_INET;//协议族  
    31.         sin.sin_port = sk->dummy_th.dest;//目的端口  
    32.         sin.sin_addr.s_addr = sk->daddr;//目的地址  
    33.     }  
    34.     
    35.     /* 
    36.      *  BSD socket semantics. You must set SO_BROADCAST to permit 
    37.      *  broadcasting of data. 
    38.      */  
    39.        
    40.     if(sin.sin_addr.s_addr==INADDR_ANY)//目的地址是全0地址,对应当前主机  
    41.         sin.sin_addr.s_addr=ip_my_addr();//将目的地址设为当前主机的网络地址  
    42.           
    43.     if(!sk->broadcast && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)  
    44.             return -EACCES;         /* Must turn broadcast on first */  
    45.   
    46.     sk->inuse = 1;  
    47.   
    48.     /* Send the packet. */  
    49.     tmp = udp_send(sk, &sin, from, len, flags);//调用udp_send()真正的发送数据  
    50.   
    51.     /* The datagram has been sent off.  Release the socket. */  
    52.     release_sock(sk);  
    53.     return(tmp);  
    54. }  

    udp_send()函数

    1. static int udp_send(struct sock *sk, //要发送的数据包使用的协议对用的sock结构  
    2.                        struct sockaddr_in *sin,//目的端的标准的网络接口地址  
    3.                        unsigned char *from,//要发送的数据所在地址  
    4.                        int len,//发送数据的长度  
    5.                        int rt)  
    6. {  
    7.     struct sk_buff *skb;  
    8.     struct device *dev;  
    9.     struct udphdr *uh;  
    10.     unsigned char *buff;  
    11.     unsigned long saddr;  
    12.     int size, tmp;  
    13.     int ttl;  
    14.     
    15.     /*  
    16.      *  Allocate an sk_buff copy of the packet. 
    17.      */  
    18.        
    19.     size = sk->prot->max_header + len;//计算大小为UDP最长表头+ 数据长度  
    20.     skb = sock_alloc_send_skb(sk, size, 0, &tmp);//根据要发送的数据分配sk_buff结构空间并对当前套接字状态检查  
    21.   
    22.   
    23.     if (skb == NULL)   
    24.         return tmp;  
    25.   
    26.     skb->sk       = NULL;    /* to avoid changing sk->saddr */  
    27.     skb->free     = 1;  
    28.     skb->localroute = sk->localroute|(rt&MSG_DONTROUTE);  
    29.   
    30.     /* 
    31.      *  Now build the IP and MAC header.  
    32.      */  
    33.        
    34.     buff = skb->data;//将skb中的数据指针赋值给buff指针  
    35.     saddr = sk->saddr;//本地地址  
    36.     dev = NULL;  
    37.     ttl = sk->ip_ttl;//生存时间  
    38. #ifdef CONFIG_IP_MULTICAST  
    39.     if (MULTICAST(sin->sin_addr.s_addr))  
    40.         ttl = sk->ip_mc_ttl;  
    41. #endif  
    42.     tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,  
    43.             &dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,ttl);//调用ip_build_header()创建IP报头和调用ip_send()创建MAC首部  
    44.   
    45.     skb->sk=sk;  /* So memory is freed correctly */  
    46.       
    47.     /* 
    48.      *  Unable to put a header on the packet. 
    49.      */  
    50.                   
    51.     if (tmp < 0 )   
    52.     {  
    53.         sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);  
    54.         return(tmp);  
    55.     }  
    56.       
    57.     buff += tmp;  
    58.     saddr = skb->saddr; /*dev->pa_addr;*/  
    59.     skb->len = tmp + sizeof(struct udphdr) + len;    /* len + UDP + IP + MAC */  
    60.     skb->dev = dev;  
    61.       
    62.     /* 
    63.      *  Fill in the UDP header. 填写UDP的报头 
    64.      */  
    65.        
    66.     uh = (struct udphdr *) buff;  
    67.     uh->len = htons(len + sizeof(struct udphdr));//数据包长度  
    68.     uh->source = sk->dummy_th.source;//本地端口  
    69.     uh->dest = sin->sin_port;//远端端口  
    70.     buff = (unsigned char *) (uh + 1);  
    71.   
    72.     /* 
    73.      *  Copy the user data.  
    74.      */  
    75.        
    76.     memcpy_fromfs(buff, from, len);//复制用户数据  
    77.   
    78.     /* 
    79.      *  Set up the UDP checksum.  
    80.      */  
    81.        
    82.     udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);//计算UDP报头的校验和  
    83.   
    84.     /*  
    85.      *  Send the datagram to the interface.  
    86.      */  
    87.        
    88.     udp_statistics.UdpOutDatagrams++;  
    89.        
    90.     sk->prot->queue_xmit(sk, dev, skb, 1);//调用IP层函数发送数据  
    91.     return(len);  
    92. }  

    这样要发送的数据填充的sk_buff结构中之后再对UDP数据包添加IP数据报头和MAC帧的首部。最后调用IP层的发送函数发送数据包。
  • 相关阅读:
    ADexplorer
    Ldap登陆AD(Active Directory)进行认证的Java示例
    通过LDAP验证Active Directory服务
    APACHE + LDAP 的权限认证配置方法
    How to authenticate a user by uid and password?
    js汉字与拼音互转终极方案,附简单的JS拼音输入法【转】
    给MySQL增加mysql-udf-http和mysql-udf-json自定义函数,让MySQL有调用http接口和查询直接回JSON的能力
    CentOS6.7安装RabbitMQ3.6.5
    CentOS利用inotify+rsync实现文件同步
    CentOS两台服务器利用scp拷贝文件
  • 原文地址:https://www.cnblogs.com/wangfengju/p/6173198.html
Copyright © 2020-2023  润新知