数据包接收系列 — IP协议处理流程(二)
本文主要内容:在接收数据包时,IP协议的处理流程。
内核版本:2.6.37
Author:zhangskd @ csdn blog
我们接着来看数据包如何发往本地的四层协议。
ip_local_deliver
在ip_local_deliver()中,如果发现数据报有被分片,则进行组装。
然后调用NF_INET_LOCAL_IN处的钩子函数,如果数据包被钩子函数放行,
则调用ip_local_deliver_finish()继续处理。
/* Deliver IP Packets to the higher protocol layers. */ int ip_local_deliver(struct sk_buff *skb) { /* * Reassemble IP fragments. * 如果IP数据报有被分片,则在这里进行组装还原。 */ if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) { if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER)) return 0; } /* 调用netfilter的NF_INET_LOCAL_IN的钩子函数,如果此数据包被钩子函数放行,则调用 * ip_local_deliver_finish()继续处理。 */ return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL, ip_local_deliver_finish); }
ip_local_deliver_finish
ip_local_deliver_finish()主要做了:
处理RAW IP,如果有配置安全策略,则进行IPsec安全检查。
根据IP报头的protocol字段,找到对应的L4协议(net_protocol),调用该协议的接收函数net_protocol->handler()。
对于TCP协议,net_protocol实例为tcp_protocol,协议处理函数为tcp_v4_rcv()。
接下来就进入四层协议的处理流程了,TCP协议的入口函数为tcp_v4_rcv()。
static int ip_local_deliver_finish(struct sk_buff *skb) { struct net *net = dev_net(skb->dev); /* 把skb->data指向L4协议头,更新skb->len */ __skb_pull(skb, ip_hdrlen(skb)); /* 赋值skb->transport_header */ skb_reset_transport_header(skb); rcu_read_lock(); { int protocol = ip_hdr(skb)->protocol; /* L4协议号 */ int hash, raw; const struct net_protocol *ipprot; resubmit: /* 处理RAW IP */ raw = raw_local_deliver(skb, protocol); hash = protocol & (MAX_INET_PROTOS - 1); /* 作为数组索引 */ /* 从inet_protos数组中取出对应的net_protocol元素,TCP的为tcp_protocol */ ipprot = rcu_dereference(inet_protos[hash]); if (ipprot != NULL) { int ret; if (! net_eq(net, &init_net) && ! ipprot->netns_ok) { if (net_ratelimit()) printk("%s: proto %d isn't netns-ready\n", __func__, protocol); kfree_skb(skb); goto out; } /* 如果需要检查IPsec安全策略 */ if (! ipprot->no_policy) { if (! xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { kfree_skb(skb); goto out; } nf_reset(skb); } /* 调用L4协议的处理函数,对于TCP,调用tcp_protocol->handler,为tcp_v4_rcv() */ ret = ipprot->handler(skb); if (ret < 0) { protocol = - ret; goto resubmit; } IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS); } else { if (! raw) { if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { IP_INC_STATS_BH(net, IPSTATS_MIB_INUNKNOWNPROTOS); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0); } else IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS); kfree_skb(skb); } } out: rcu_read_unlock(); return 0; }
L4协议处理函数
数据包从L2传递到L3时,通过packet_type结构来找到L3协议的处理函数。
同理,数据包从L3传递到L4时,通过net_protocol结构来找到L4协议的处理函数。
/* This is used to register protocols. */ struct net_protocol { int (*handler) (struct sk_buff *skb); /* L4协议的接收函数 */ void (*error_handler) (struct sk_buff *skb, u32 info); /* ICMP用的 */ ... unsigned int no_policy:1, /* 不需要检查IPsec策略 */ netns_ok:1; }; #define MAX_INET_PROTOS 256 /* MUST be a power of 2 */
把协议号作为索引,可以从数组找到对应协议的net_protocol元素。
const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS];
enum { ... IPPROTO_ICMP = 1, /* Internet Control Message Protocol */ ... IPPROTO_TCP = 6, /* Transmission Control Protocol */ ... IPPROTO_UDP = 17, /* User Datagram Protocol */ ... IPPROTO_RAW = 255, /* Raw IP packets */ IPPROTO_MAX }; /* TCP协议的net_protocol */ static const struct net_protocol tcp_protocol = { .handler = tcp_v4_rcv, .err_handler = tcp_v4_err, ... .no_policy = 1, .netns_ok = 1, }; /* UDP协议的net_protocol */ static const struct net_protocol udp_protocol = { .handler = udp_rcv, .err_handler = upd_err, ... .no_policy = 1, .netns_ok = 1, }; /* ICMP协议的net_protocl */ static const struct net_protocol icmp_protocol = { .handler = icmp_rcv, .no_policy = 1, .netns_ok = 1, };
初始化
static int __init inet_init(void) { ... /* Add all the base protocols */ if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0) printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n"); if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0) printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n"); if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0) printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n"); ... }
RAW IP
struct raw_hashinfo { rwlock_t lock; struct hlist_head ht[RAW_HTABLE_SIZE]; }; static struct raw_hashinfo raw_v4_hashinfo = { .lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock); }; #define RAW_HTABLE_SIZE MAX_INET_PROTOS
RAW IP报的处理函数为raw_v4_input()。
int raw_local_deliver(struct sk_buff *skb, int protocol) { int hash; struct sock *raw_sk; hash = protocol & (RAW_HTABLE_SIZE - 1); /* L4协议号作为key */ raw_sk = sk_head(&raw_v4_hashinfo.ht[hash]); /* 取出哈希桶的第一个sock */ /* If there maybe a raw socket we must check - if not we don't care less. * raw_v4_input(): IP input processing comes here for RAW socket delivery. * 如果该协议上有连接,那么调用raw_v4_input()来处理。 */ if (raw_sk && ! raw_v4_input(skb, ip_hdr(skb), hash)) raw_sk = NULL; return raw_sk != NULL; }