您的位置:首页 > 理论基础 > 计算机网络

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

2013-04-13 13:31 525 查看
本文分析基于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()函数

[cpp] view
plaincopy

static int udp_write(struct sock *sk, unsigned char *buff, int len, int noblock,

unsigned flags)

{

return(udp_sendto(sk, buff, len, noblock, flags, NULL, 0));

}

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

[cpp] view
plaincopy

struct sockaddr_in {

short int sin_family; /* Address family 地址族 */

unsigned short int sin_port; /* Port number 端口号 */

struct in_addr sin_addr; /* Internet address 网络地址 */

/* Pad to size of `struct sockaddr'. */

unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -

sizeof(unsigned short int) - sizeof(struct in_addr)];

};

#define sin_zero __pad /* for BSD UNIX comp. -FvK */

udp_sentdto()函数

[cpp] view
plaincopy

static int udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock,

unsigned flags, struct sockaddr_in *usin, int addr_len)

{

struct sockaddr_in sin;

int tmp;

/*

* Check the flags. We support no flags for UDP sending

*/

if (flags&~MSG_DONTROUTE)

return(-EINVAL);

/*

* Get and verify the address.

*/

if (usin) //如果usin不是空

{

if (addr_len < sizeof(sin))

return(-EINVAL);

memcpy(&sin,usin,sizeof(sin));

if (sin.sin_family && sin.sin_family != AF_INET)

return(-EINVAL);

if (sin.sin_port == 0)

return(-EINVAL);

}

else //usin为空

{

if (sk->state != TCP_ESTABLISHED)

return(-EINVAL);

sin.sin_family = AF_INET;//协议族

sin.sin_port = sk->dummy_th.dest;//目的端口

sin.sin_addr.s_addr = sk->daddr;//目的地址

}

/*

* BSD socket semantics. You must set SO_BROADCAST to permit

* broadcasting of data.

*/

if(sin.sin_addr.s_addr==INADDR_ANY)//目的地址是全0地址,对应当前主机

sin.sin_addr.s_addr=ip_my_addr();//将目的地址设为当前主机的网络地址

if(!sk->broadcast && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)

return -EACCES; /* Must turn broadcast on first */

sk->inuse = 1;

/* Send the packet. */

tmp = udp_send(sk, &sin, from, len, flags);//调用udp_send()真正的发送数据

/* The datagram has been sent off. Release the socket. */

release_sock(sk);

return(tmp);

}

udp_send()函数

[cpp] view
plaincopy

static int udp_send(struct sock *sk, //要发送的数据包使用的协议对用的sock结构

struct sockaddr_in *sin,//目的端的标准的网络接口地址

unsigned char *from,//要发送的数据所在地址

int len,//发送数据的长度

int rt)

{

struct sk_buff *skb;

struct device *dev;

struct udphdr *uh;

unsigned char *buff;

unsigned long saddr;

int size, tmp;

int ttl;

/*

* Allocate an sk_buff copy of the packet.

*/

size = sk->prot->max_header + len;//计算大小为UDP最长表头+ 数据长度

skb = sock_alloc_send_skb(sk, size, 0, &tmp);//根据要发送的数据分配sk_buff结构空间并对当前套接字状态检查

if (skb == NULL)

return tmp;

skb->sk = NULL; /* to avoid changing sk->saddr */

skb->free = 1;

skb->localroute = sk->localroute|(rt&MSG_DONTROUTE);

/*

* Now build the IP and MAC header.

*/

buff = skb->data;//将skb中的数据指针赋值给buff指针

saddr = sk->saddr;//本地地址

dev = NULL;

ttl = sk->ip_ttl;//生存时间

#ifdef CONFIG_IP_MULTICAST

if (MULTICAST(sin->sin_addr.s_addr))

ttl = sk->ip_mc_ttl;

#endif

tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,

&dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,ttl);//调用ip_build_header()创建IP报头和调用ip_send()创建MAC首部

skb->sk=sk; /* So memory is freed correctly */

/*

* Unable to put a header on the packet.

*/

if (tmp < 0 )

{

sk->prot->wfree(sk, skb->mem_addr, skb->mem_len);

return(tmp);

}

buff += tmp;

saddr = skb->saddr; /*dev->pa_addr;*/

skb->len = tmp + sizeof(struct udphdr) + len; /* len + UDP + IP + MAC */

skb->dev = dev;

/*

* Fill in the UDP header. 填写UDP的报头

*/

uh = (struct udphdr *) buff;

uh->len = htons(len + sizeof(struct udphdr));//数据包长度

uh->source = sk->dummy_th.source;//本地端口

uh->dest = sin->sin_port;//远端端口

buff = (unsigned char *) (uh + 1);

/*

* Copy the user data.

*/

memcpy_fromfs(buff, from, len);//复制用户数据

/*

* Set up the UDP checksum.

*/

udp_send_check(uh, saddr, sin->sin_addr.s_addr, skb->len - tmp, sk);//计算UDP报头的校验和

/*

* Send the datagram to the interface.

*/

udp_statistics.UdpOutDatagrams++;

sk->prot->queue_xmit(sk, dev, skb, 1);//调用IP层函数发送数据

return(len);

}

这样要发送的数据填充的sk_buff结构中之后再对UDP数据包添加IP数据报头和MAC帧的首部。最后调用IP层的发送函数发送数据包。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐