深入理解Linux网络技术内幕 第24章 L4协议RAW IP处理

L4协议注册

L4协议由struct net_protocol描述,其中的handler作为处理报文的函数,err_handler由ICMP协议处理函数所用的函数,用于通知L4协议收到ICMP UNREACHABLE消息。

/* This is used to register protocols. */
struct net_protocol {
	int			(*early_demux)(struct sk_buff *skb);
	int			(*early_demux_handler)(struct sk_buff *skb);
	int			(*handler)(struct sk_buff *skb);
	void			(*err_handler)(struct sk_buff *skb, u32 info);
	unsigned int		no_policy:1,
				netns_ok:1,
				/* does the protocol do more stringent
				 * icmp tag validation than simple
				 * socket lookup?
				 */
				icmp_strict_tag_validation:1;
};

inet_add_protocol函数用于注册L4协议。注册函数将struct net_protocol插入到inet_protos数组中,协议号就是数组的下标。

int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol)
{
	if (!prot->netns_ok) {
		pr_err("Protocol %u is not namespace aware, cannot register.\n",
			protocol);
		return -EINVAL;
	}

	return !cmpxchg((const struct net_protocol **)&inet_protos[protocol],
			NULL, prot) ? 0 : -1;
}

在函数inet_init中会注册常用的协议比如TCP UDP ICMP等。

	/*
	 *	Add all the base protocols.
	 */

	if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
		pr_crit("%s: Cannot add ICMP protocol\n", __func__);
	if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
		pr_crit("%s: Cannot add UDP protocol\n", __func__);
	if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
		pr_crit("%s: Cannot add TCP protocol\n", __func__);
#ifdef CONFIG_IP_MULTICAST
	if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
		pr_crit("%s: Cannot add IGMP protocol\n", __func__);
#endif

ip_local_deliver_finish

前面的章节曾提到过ip_local_deliver函数,该函数在处理完分片重组后,如果IP报文没有分片或者分片重组完成,就会调用ip_local_deliver_finish函数。
ip_local_deliver_finish函数主要工作是根据IP报文头中的协议号找到正确的协议处理函数进行后续的处理。
如果IP报文头中协议号没有注册协议处理函数且没有RAW套接字对该报文感兴趣,则该报文被丢弃,且返回一条ICMP 协议不可达的信息。
除了内核中的注册函数可以处理L4报文外应用程序也可以通过注册RAW套接字处理自己关心的协议报文。

该函数首先使用__skb_pull调整sk_buff->data指针使其指向L4报文头达到去除IP报文头的目的。
接下来取出协议号。

	__skb_pull(skb, skb_network_header_len(skb));

	rcu_read_lock();
	{
		int protocol = ip_hdr(skb)->protocol;
		const struct net_protocol *ipprot;
		int raw;

下面要处理的是,首先在raw_local_deliver中检查有没有RAW套接字关心该协议如果有的话将该报文clone一份并将数据传递给相应RAW套接字对应的处理函数。
然后获取inet_protos表中内核注册的协议处理函数,如果有的话调用ipprot->handler(skb)处理。

	resubmit:
		raw = raw_local_deliver(skb, protocol);

		ipprot = rcu_dereference(inet_protos[protocol]);
		if (ipprot) {
			int ret;

			if (!ipprot->no_policy) {
				if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
					kfree_skb(skb);
					goto out;
				}
				nf_reset(skb);
			}
			ret = ipprot->handler(skb);
			if (ret < 0) {
				protocol = -ret;
				goto resubmit;
			}
			__IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
		} else {

RAW套接字和RAW IP

应用程序可以使用RAW套接字跳过内核在用户空间中处理L4协议。比如OSPF协议完全在用户空间实现。

  • TCP协议时完全在内核中实现的。
  • OSPF协议时在用户空间中实现的,发送报文时需要将L4报文头传递给内核。
  • ping程序时,请求可以再用户空间中发送,而相应在内核空间中实现。
  • traceroute程序L4和L4报文头都由应用程序处理,应用程序将L4协议设置为RAW IP,然后将套接字设定IP_HDRINCL选项。

把报文传递给数个应用程序需要额外的开销,因为所有为该协议注册的套接字都要拷贝一份该报文的副本,且应用程序需要把不想要的报文筛选掉。因此如果协议需要繁重的多路复用时,通常不会在用户空间使用RAW IP实现。
内核使用struct raw_hashinfo存储RAW套接字处理函数表,最后将skb克隆一份并调用raw_rcv。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值