UDP之数据报接收过程(一)


UDP数据报的接收过程要分两部分来看:

  1. 网络层将数据报递交给UDP后,UDP的处理过程。该过程中,UDP需要接收数据包并对其进行校验,校验成功后将其放入接收队列中等待用户空间程序来读取;
  2. 用户空间程序调用read()等系统调用读取已经放入接收队列中的数据。

这篇笔记先来介绍第一部分。

从IP层接收数据包: udp_rcv()

在AF_INET协议族初始化时,由UDP注册给网络层的接收回调函数。

int udp_rcv(struct sk_buff *skb)
{
   
	return __udp4_lib_rcv(skb, &udp_table, IPPROTO_UDP);
}

@skb: 输入数据包
@udptable:已绑定端口的UDP传输控制块哈希表,将从该哈希表查找该skb属于哪个套接字
@proto:L4协议号,到这里可能是IPPROTO_UDP或者IPPROTO_UDPLITE
int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, int proto)
{
   
	struct sock *sk;
	struct udphdr *uh;
	unsigned short ulen;
	struct rtable *rt = skb_rtable(skb);
	__be32 saddr, daddr;
	struct net *net = dev_net(skb->dev);

	// 保证skb的数据区至少包含udp首部
	if (!pskb_may_pull(skb, sizeof(struct udphdr)))
		goto drop; /* No space for header. */
	uh = udp_hdr(skb);
	ulen = ntohs(uh->len);
	// skb中的数据长度不能小于UDP首部指示的数据包长度,该检查确保数据包是完整的
	if (ulen > skb->len)
		goto short_packet;

	if (proto == IPPROTO_UDP) {
   
		// 1. UDP数据包长度必须大于首部长度
		// 2. pskb_trim_rcum()会去掉可能的填充(UDP数据包过小,IP可能会填充),然后重新计算校验和
		if (ulen < sizeof(*uh) || pskb_trim_rcsum(skb, ulen))
			goto short_packet;
		uh = udp_hdr(skb);
	}
	// 校验和检查
	if (udp4_csum_init(skb, uh, proto))
		goto csum_error;

	// 获取数据包中的源IP和目的IP地址
	saddr = ip_hdr(skb)->saddr;
	daddr = ip_hdr(skb)->daddr;
	// 对于多播或者广播报文的处理
	if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
		return __udp4_lib_mcast_deliver(net, skb, uh, saddr, daddr, udptable);

	// 根据报文的源端口号和目的端口号查询udptable,寻找应该接收该数据包的传输控制块
	sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
    if (sk != NULL) {
   
        // 找到了处理该数据包的传输控制块,调用udp_queue_rcv_skb()接收数据包
		int ret = udp_queue_rcv_skb(sk, skb);
		sock_put(sk);

		/* a return value > 0 means to resubmit the input, but
		 * it wants the return to be -protocol, or 0
		 */
		if (ret > 0)
			return -ret;
		return 0;
	}

	// 到这里,说明没有传输控制块接收该数据包,做些统计然后丢弃该数据包

	// IPSec相关
	if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
		goto drop;
	nf_reset(skb);

	/* No socket. Drop packet silently, if checksum is wrong */
	if (udp_lib_checksum_complete(skb))
		goto csum_error;
	// 累计输入数据包错误统计值,并且回复端口不可达ICMP报文
	UDP_INC_STATS_BH(net, UDP_MIB_NOPORTS, proto == IPPROTO_UDPLITE);
	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(<
  • 0
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值