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层的发送函数发送数据包。
原创作品,转载请标明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层的发送函数发送数据包。
相关文章推荐
- Linux内核--网络栈实现分析(九)--传输层之UDP协议(下)
- Linux内核--网络栈实现分析(九)--传输层之UDP协议(下)
- Linux内核--网络栈实现分析(五)--传输层之UDP协议(上)
- Linux内核--网络栈实现分析(五)--传输层之UDP协议(上)
- Linux内核--网络栈实现分析(五)--传输层之UDP协议(上)
- Linux内核--网络栈实现分析(五)--传输层之UDP协议(上)
- Linux内核--网络栈实现分析(五)--传输层之UDP协议(上)
- Linux内核--网络栈实现分析(五)--传输层之UDP协议(上)
- Linux内核--网络栈实现分析(九)--传输层之UDP协议(下)
- Linux内核--网络栈实现分析(九)--传输层之UDP协议(下)
- Linux内核--网络栈实现分析(九)--传输层之UDP协议(下)
- Linux内核--网络栈实现分析(十)--网络层之IP协议(下)
- Linux内核--网络栈实现分析(四)--网络层之IP协议(上)
- Linux内核--网络栈实现分析(八)--应用层发送数据(下)
- Linux内核--网络栈实现分析(四)--网络层之IP协议(上)
- Linux内核--网络栈实现分析(四)--网络层之IP协议(上)
- Linux内核--网络栈实现分析(二)--数据包的传递过程(上)
- Linux内核--网络栈实现分析(十一)--驱动程序层(下)
- Linux内核--网络栈实现分析(七)--数据包的传递过程(下)
- Linux内核--网络栈实现分析(十)--网络层之IP协议(下)