0
Linux TCP/IP协议栈之Socket的实现分析(六 数据包的接收)
原文地址:http://www.skynet.org.cn/viewthread.php?tid=304&extra=page%3D1
作者:kendo
前面了解过sk有一个接收队列,用于存储接收到的skb,对于socket层面上来讲,数据接收,就是要把数据从这个队列中取出来,交给上层用户态。这里涉及到出队操作,但是,要了解如何出队,就得了解传输层协议如何入队。前面一直用tcp协议来分析,现在还没有把整个tcp栈分析出来,要再继续用tcp协议来分析,就有点问题了,所以,数据的接收和发送,都将以udp协议来分析。虽然它很简单,但同样也反应了socket层数据与接收的全部核心内容与思路。我以希望,下一步拿下tcp协议后,再把这部份的tcp实现补上来。
一、udp层的数据接收
udp层的数据接收,对于socket而言,就是接收队列的入队操作。在ip层中,如果是本地数据,则会交由ip_local_deliver_finish()函数处理,它会根据传输层协议的类型,交由相应的处理函数,对于udp协议而言,就是udp_rcv():
/*
* All we need to do is get the socket, and then do a checksum.
*/
int udp_rcv(struct sk_buff *skb)
{
struct sock *sk;
struct udphdr *uh;
unsigned short ulen;
struct rtable *rt = (struct rtable*)skb->dst;
u32 saddr = skb->nh.iph->saddr;
u32 daddr = skb->nh.iph->daddr;
int len = skb->len;
/*
* 数据包至少应有UDP首部长度.
*/
if (!pskb_may_pull(skb, sizeof(struct udphdr)))
goto no_header;
/*获取udp首部指针*/
uh = skb->h.uh;
/* 数据长度,含首部长度 */
ulen = ntohs(uh->len);
/* 数据包长度不够:UDP长度比skb长度小,意味着数据的丢失,而udp长度比udp首部还要小,好像这个不太可能,除非封包出错 ^o^*/
if (ulen > len || ulen < sizeof(*uh))
goto short_packet;
/* 截去udp报文后面的多余报文 */
if (pskb_trim(skb, ulen))
goto short_packet;
/* 开始udp校验和计算,主要查依赖于skb的ip_summumed字段的设置来决定是否需要进行校验和计算 */
if (udp_checksum_init(skb, uh, ulen, saddr, daddr) < 0)
goto csum_error;
/* 转换多播或广播处理例程 */
if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
/* 查找数据段对应的socket结构的sk */
sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex);
if (sk != NULL) {
/* 找到了,数据包进入UDP的socket的接收队列*/
int ret = udp_queue_rcv_skb(sk, skb);
sock_put(sk);
/* a return value > 0 means to resubmit the input, but
* it it wants the return to be -protocol, or 0
*/
if (ret > 0)
return -ret;
return 0;
}
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop;
/* 没有对应的socket. 如果校验和错误,则丢弃它 */
if (udp_checksum_complete(skb))
goto csum_error;
/* 发送一个目的不可达报文 */
UDP_INC_STATS_BH(UDP_MIB_NOPORTS);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
/*
* Hmm. We got an UDP packet to a port to which we
* don't wanna listen. Ignore it.
*/
kfree_skb(skb);
return(0);
short_packet:
NETDEBUG(if (net_ratelimit())
printk(KERN_DEBUG "UDP: short packet: From %u.%u.%u.%u:%u %d/%d to %u.%u.%u.%u:%u\n",
NIPQUAD(saddr),
ntohs(uh->source),
ulen,
len,
NIPQUAD(daddr),
ntohs(uh->dest)));
no_header:
UDP_INC_STATS_BH(UDP_MIB_INERRORS);
kfree_skb(skb);
return(0);
csum_error:
/*
* RFC1122: OK. Discards the bad packet silently (as far as
* the network is concerned, anyway) as per 4.1.3.4 (MUST).
*/
NETDEBUG(if (net_ratelimit())
printk(KERN_DEBUG "UDP: bad checksum. From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d ulen %d\n",
NIPQUAD(saddr),
ntohs(uh->source),
NIPQUAD(daddr),
ntohs(uh->dest),
ulen));
drop:
UDP_INC_STATS_BH(UDP_MIB_INERRORS);
kfree_skb(skb);
return(0);
}
函数的核心思想,是根据skb,查找到与之对应的sk,调用udp_v4_lookup()函数实现——udp与tcp一样,socket有一个hash表,这个查找,就是查找hash表的过程。
原文地址:http://www.skynet.org.cn/viewthread.php?tid=304&extra=page%3D1
作者:kendo
前面了解过sk有一个接收队列,用于存储接收到的skb,对于socket层面上来讲,数据接收,就是要把数据从这个队列中取出来,交给上层用户态。这里涉及到出队操作,但是,要了解如何出队,就得了解传输层协议如何入队。前面一直用tcp协议来分析,现在还没有把整个tcp栈分析出来,要再继续用tcp协议来分析,就有点问题了,所以,数据的接收和发送,都将以udp协议来分析。虽然它很简单,但同样也反应了socket层数据与接收的全部核心内容与思路。我以希望,下一步拿下tcp协议后,再把这部份的tcp实现补上来。
一、udp层的数据接收
udp层的数据接收,对于socket而言,就是接收队列的入队操作。在ip层中,如果是本地数据,则会交由ip_local_deliver_finish()函数处理,它会根据传输层协议的类型,交由相应的处理函数,对于udp协议而言,就是udp_rcv():
/*
* All we need to do is get the socket, and then do a checksum.
*/
int udp_rcv(struct sk_buff *skb)
{
struct sock *sk;
struct udphdr *uh;
unsigned short ulen;
struct rtable *rt = (struct rtable*)skb->dst;
u32 saddr = skb->nh.iph->saddr;
u32 daddr = skb->nh.iph->daddr;
int len = skb->len;
/*
* 数据包至少应有UDP首部长度.
*/
if (!pskb_may_pull(skb, sizeof(struct udphdr)))
goto no_header;
/*获取udp首部指针*/
uh = skb->h.uh;
/* 数据长度,含首部长度 */
ulen = ntohs(uh->len);
/* 数据包长度不够:UDP长度比skb长度小,意味着数据的丢失,而udp长度比udp首部还要小,好像这个不太可能,除非封包出错 ^o^*/
if (ulen > len || ulen < sizeof(*uh))
goto short_packet;
/* 截去udp报文后面的多余报文 */
if (pskb_trim(skb, ulen))
goto short_packet;
/* 开始udp校验和计算,主要查依赖于skb的ip_summumed字段的设置来决定是否需要进行校验和计算 */
if (udp_checksum_init(skb, uh, ulen, saddr, daddr) < 0)
goto csum_error;
/* 转换多播或广播处理例程 */
if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
/* 查找数据段对应的socket结构的sk */
sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex);
if (sk != NULL) {
/* 找到了,数据包进入UDP的socket的接收队列*/
int ret = udp_queue_rcv_skb(sk, skb);
sock_put(sk);
/* a return value > 0 means to resubmit the input, but
* it it wants the return to be -protocol, or 0
*/
if (ret > 0)
return -ret;
return 0;
}
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop;
/* 没有对应的socket. 如果校验和错误,则丢弃它 */
if (udp_checksum_complete(skb))
goto csum_error;
/* 发送一个目的不可达报文 */
UDP_INC_STATS_BH(UDP_MIB_NOPORTS);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
/*
* Hmm. We got an UDP packet to a port to which we
* don't wanna listen. Ignore it.
*/
kfree_skb(skb);
return(0);
short_packet:
NETDEBUG(if (net_ratelimit())
printk(KERN_DEBUG "UDP: short packet: From %u.%u.%u.%u:%u %d/%d to %u.%u.%u.%u:%u\n",
NIPQUAD(saddr),
ntohs(uh->source),
ulen,
len,
NIPQUAD(daddr),
ntohs(uh->dest)));
no_header:
UDP_INC_STATS_BH(UDP_MIB_INERRORS);
kfree_skb(skb);
return(0);
csum_error:
/*
* RFC1122: OK. Discards the bad packet silently (as far as
* the network is concerned, anyway) as per 4.1.3.4 (MUST).
*/
NETDEBUG(if (net_ratelimit())
printk(KERN_DEBUG "UDP: bad checksum. From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d ulen %d\n",
NIPQUAD(saddr),
ntohs(uh->source),
NIPQUAD(daddr),
ntohs(uh->dest),
ulen));
drop:
UDP_INC_STATS_BH(UDP_MIB_INERRORS);
kfree_skb(skb);
return(0);
}
函数的核心思想,是根据skb,查找到与之对应的sk,调用udp_v4_lookup()函数实现——udp与tcp一样,socket有一个hash表,这个查找,就是查找hash表的过程。
网卡驱动和队列层中的数据包接收==>Linux TCP/IP协议栈笔记[zz 节选]
继续服务器开发设计书籍的编写


2008/04/13
19:37
4781

内文分页: [1] 

