admin管理员组文章数量:1642338
L4协议RAW IP处理
- L4协议注册
- ip_local_deliver_finish
- RAW套接字和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。
版权声明:本文标题:深入理解Linux网络技术内幕第24章 L4协议RAW IP处理 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1729332431a1196563.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论