int udp_rcv(struct sk_buff *skb)
{
return __udp4_lib_rcv(skb, udp_hash, IPPROTO_UDP);
}
int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[], int proto)
{
struct sock *sk;
struct udphdr *uh = udp_hdr(skb); //获取skb的UDP header的指针;
unsigned short ulen;
struct rtable *rt = (struct rtable*)skb->dst;
//routing table, 这里强制类型转换, 保证内存的分配大小为sizeof(struct rtable). 注意struct rtalbe的定义中第一个便是strcut dst_entry. 这牵扯到C语言中的多层继承, 详见http://blog.chinaunix.net/u3/114767/showart_2282422.html
__be32 saddr = ip_hdr(skb)->saddr; //源地址
__be32 daddr = ip_hdr(skb)->daddr; //目的地址
/*
* Validate the packet
*/
if (!pskb_may_pull(skb, sizeof(struct udphdr))) //判断是否有足够的数据用于pull.
goto drop; /* No space for header. */
ulen = ntohs(uh->len);
if (ulen > skb->len)
goto short_packet; //判断UDP头是否超过数据长度, AKA, 数据部分太短
if (proto == IPPROTO_UDP) {
/* UDP validates ulen. */
if (ulen <sizeof(*uh) || pskb_trim_rcsum(skb, ulen)) //判断udphdr->len是否小于udphdr长度, 或者pskb_trim_rcsum();
goto short_packet;
uh = udp_hdr(skb);
}
if (udp4_csum_init(skb, uh, proto)) //初始化udp的checksum, 成功返回0.
goto csum_error;
if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
return __udp4_lib_mcast_deliver(skb, uh, saddr, daddr, udptable); //检查是否多播和广播数据
sk = __udp4_lib_lookup(dev_net(skb->dev), saddr, uh->source, daddr, uh->dest, inet_iif(skb), udptable);//查找此UDP包对应的socket, 主要是查找源目的地址和端口号都符合的socket
if (sk != NULL) {
int ret = 0;
bh_lock_sock_nested(sk); //调用自旋锁
if (!sock_owned_by_user(sk)) //然后判断当前sock是否被用户进程占用
ret = udp_queue_rcv_skb(sk, skb); //
else
sk_add_backlog(sk, skb);
bh_ulock_sock(sk);
sock_put(sk);
if (ret > 0)
return -ret;
return 0;
}
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop;
nf_reset(skb);
/* No socket. Drop packet silently, if chechsum is wrong */
if (udp_lib_checksum_complete(skb))
goto csum_error;
UDP_INC_STATS_BH(UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
kfree_skb(skb);
return 0;
short_packet:
...
csum_error:
...
drop:
UDP_INC_STATS_BH(UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE);
kfree_skb(skb);
return 0;
}
|