udp的收发包的思考

  在测试radius 性能时,想到一个问题,以前tcp报文在ip层处理时,涉及到路由查找,

对于tcp协议报文;skb中没有路由缓存,没有关联的sock;且非分片报文;ip_early_demux设置为true;则调用early_demux函数提前在IP层做established状态的sock查找,并负责将sock结构体成员sk_rx_dst的路由缓存赋值给skb成员_skb_refdst;

TCP入口路由缓存设置时间点如下:

a)作为服务端,三次握手完成时,在函数tcp_v4_syn_recv_sock中创建子sock时赋值;
b)作为客户端在函数tcp_finish_connect;
c)在函数tcp_rcv_established中sock处于established状态时,更新其值--->在tcp_rcv_established函数中已经将输入路由缓存在了sk_rx_dst中,所以early_demux可以通过一次sock查找,同时找到缓存的路由项;

复制代码
复制代码
struct sock *tcp_v4_syn_recv_sock()
{
    newsk = tcp_create_openreq_child(sk, req, skb);
    inet_sk_rx_dst_set(newsk, skb);
}
void tcp_finish_connect()
{
    //IPv4v6两个回调函数inet_sk_rx_dst_set与inet6_sk_rx_dst_set
    if (skb)
        icsk->icsk_af_ops->sk_rx_dst_set(sk, skb);
}
void tcp_rcv_established()
{
    if (unlikely(!sk->sk_rx_dst))
        inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb);
}
复制代码

对于UDP协议,对于先查找 对应目标的 sk,先关联skb 和sk,在判断DST_NOCACHE标志,如果成立,增加dst引用计数,设置skb的dst;否则,调用skb_dst_set_noref直接进行设置。

参考之前文章

目前radius 主机主要是处理本机到设备之间的udp报文。那是不是可以针对udp 1812 udp1813 做个特殊的定制???

udp_v4_early_demux 处理中提取针对udp 1812  1813的逻辑处理?其余的udp报文就走正常逻辑了!!

查看代码
 int udp_v4_early_demux(struct sk_buff *skb)
{
	struct net *net = dev_net(skb->dev);
	struct in_device *in_dev = NULL;
	const struct iphdr *iph;
	const struct udphdr *uh;
	struct sock *sk = NULL;
	struct dst_entry *dst;
	int dif = skb->dev->ifindex;
	int sdif = inet_sdif(skb);
	int ours;

	/* validate the packet */
	if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct udphdr)))
		return 0;

	iph = ip_hdr(skb);
	uh = udp_hdr(skb);

	if (skb->pkt_type == PACKET_MULTICAST) {
		in_dev = __in_dev_get_rcu(skb->dev);

		if (!in_dev)
			return 0;

		ours = ip_check_mc_rcu(in_dev, iph->daddr, iph->saddr,
				       iph->protocol);
		if (!ours)
			return 0;

		sk = __udp4_lib_mcast_demux_lookup(net, uh->dest, iph->daddr,
						   uh->source, iph->saddr,
						   dif, sdif);
	} else if (skb->pkt_type == PACKET_HOST) {
		sk = __udp4_lib_demux_lookup(net, uh->dest, iph->daddr,
					     uh->source, iph->saddr, dif, sdif);
	}

	if (!sk || !refcount_inc_not_zero(&sk->sk_refcnt))
		return 0;

	skb->sk = sk;
	skb->destructor = sock_efree;
	dst = READ_ONCE(sk->sk_rx_dst);

	if (dst)
		dst = dst_check(dst, 0);
	if (dst) {
		u32 itag = 0;

		/* set noref for now.
		 * any place which wants to hold dst has to call
		 * dst_hold_safe()
		 */
		skb_dst_set_noref(skb, dst);

		/* for unconnected multicast sockets we need to validate
		 * the source on each packet
		 */
		if (!inet_sk(sk)->inet_daddr && in_dev)
			return ip_mc_validate_source(skb, iph->daddr,
						     iph->saddr, iph->tos,
						     skb->dev, in_dev, &itag);
	}
	return 0;
}

 

代码分析:

ipprot->early_demux(skb);   和edemux = READ_ONCE(ipprot->early_demux);edemux(skb); 对比

也就是防止编译器优化READ_ONCE  防止编译器优化

为什么需要sysctl_ip_early_demux来控制呢?

对于没有connected的 udp,  disable UDP early demux   性能更好,其实这个一般对tcp更好!所以一般都是tcp打开,udp关闭。

同时涉及到cpu架构以及cache,即使udp hash table在cache里面,性能影响也不会有什么区别

参考之前文章 udp bind      udp_connect

 

if (net->ipv4.sysctl_ip_early_demux &&
	    !skb_dst(skb) &&
	    !skb->sk &&
	    !ip_is_fragment(iph)) {
		const struct net_protocol *ipprot;
		int protocol = iph->protocol;

		ipprot = rcu_dereference(inet_protos[protocol]);
		if (ipprot && (edemux = READ_ONCE(ipprot->early_demux))) {
			err = INDIRECT_CALL_2(edemux, tcp_v4_early_demux,
					      udp_v4_early_demux, skb);
			if (unlikely(err))
				goto drop_error;
			/* must reload iph, skb->head might have changed */
			iph = ip_hdr(skb);
		}
	}

 

posted @   codestacklinuxer  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
历史上的今天:
2020-05-31 都知道的copy_from_user
点击右上角即可分享
微信分享提示