就像题目所说的那样,接收流程简单分析,是对数据包接收流程的分析,不涉及route,还没那功力。从netif_receive_skb函数我们可以看到如果一个IP数据包到来,最终会送给注册的ip_rcv函数来处理,从ip_rcv函数说起吧:
1. if (skb->pkt_type == PACKET_OTHERHOST)
goto drop;
看数据包的MAC地址是不是本机的MAC地址,如果不是直接丢掉。这个 skb->pkt_type 是在调用netif_receive_skb函数之前赋值的,我们来看一下:
skb->protocol = eth_type_trans(skb, netdev);
-
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
-
{
-
struct ethhdr *eth;
-
unsigned char *rawp;
-
-
skb->mac.raw = skb->data;
-
skb_pull(skb, ETH_HLEN);
-
eth = eth_hdr(skb);
-
-
if (is_multicast_ether_addr(eth->h_dest)) {
-
if (!compare_ether_addr(eth->h_dest, dev->broadcast))
-
skb->pkt_type = PACKET_BROADCAST; //广播包
-
else
-
skb->pkt_type = PACKET_MULTICAST; //组播包
-
}
-
-
/*
-
* This ALLMULTI check should be redundant by 1.4
-
* so don't forget to remove it.
-
*
-
* Seems, you forgot to remove it. All silly devices
-
* seems to set IFF_PROMISC.
-
*/
-
-
else if (1 /*dev->flags&IFF_PROMISC */ ) {
-
if (unlikely(compare_ether_addr(eth->h_dest, dev->dev_addr))) //在这里进行了MAC地址的比较
-
skb->pkt_type = PACKET_OTHERHOST;
-
}
-
-
if (ntohs(eth->h_proto) >= 1536) //0x0800 IP
-
return eth->h_proto;
-
-
rawp = skb->data;
-
-
/*
-
* This is a magic hack to spot IPX packets. Older Novell breaks
-
* the protocol design and runs IPX over 802.3 without an 802.2 LLC
-
* layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
-
* won't work for fault tolerant netware but does for the rest.
-
*/
-
if (*(unsigned short *)rawp == 0xFFFF)
-
return htons(ETH_P_802_3);
-
-
/*
-
* Real 802.2 LLC
-
*/
-
return htons(ETH_P_802_2);
-
}
接下来就看数据包是否共享,共享的话要clone一份
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
goto out;
}
之后是对IP头的检查,最好调用ip_rcv_finish函数
return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
ip_rcv_finish);
在这里使用了netfilter框架,在IP层有5个钩子点,从NF_IP_PRE_ROUTING 可以看出,这个是在路由之前的钩子点。
-
/* IP Hooks */
-
/* After promisc drops, checksum checks. */
-
#define NF_IP_PRE_ROUTING 0
-
/* If the packet is destined for this box. */
-
#define NF_IP_LOCAL_IN 1
-
/* If the packet is destined for another interface. */
-
#define NF_IP_FORWARD 2
-
/* Packets coming from a local process. */
-
#define NF_IP_LOCAL_OUT 3
-
/* Packets about to hit the wire. */
-
#define NF_IP_POST_ROUTING 4
-
#define NF_IP_NUMHOOKS 5
2. ip_rcv_finish函数
这个函数是涉及到了路由的地方,我们只要知道数据包经过路由后知道是否要转发,还是向上传递给L4 ,最终调用的是 skb->dst->input(skb); 这个input函数指针在路由时被挂上了钩子函数
ip_local_deliver或者是ip_forward,我们看ip_local_deliver这个函数
3. ip_local_deliver这个函数
这个函数从代码量上看也很简单,看是否分片,是分片的话在这里重组,然后调用ip_local_deliver_finish函数,这个函数是在NF_IP_LOCAL_IN Hook点上挂的函数
-
int ip_local_deliver(struct sk_buff *skb)
-
{
-
/*
-
* Reassemble IP fragments.
-
*/
-
-
if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
-
skb = ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER);
-
if (!skb)
-
return 0;
-
}
-
-
return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
-
ip_local_deliver_finish);
-
}
4. ip_local_deliver_finish 函数
在这个函数中把数据包送到了L4,L3工作算是完成了 。这里说句体外话,当然也没出去。这个函数用inline去修饰有作用吗?
-
static inline int ip_local_deliver_finish(struct sk_buff *skb)
-
{
-
int ihl = skb->nh.iph->ihl*4;
-
-
__skb_pull(skb, ihl); //用不到IP头了,剥掉
-
-
/* Point into the IP datagram, just past the header. */
-
skb->h.raw = skb->data;
-
-
rcu_read_lock();
-
{
-
/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
-
int protocol = skb->nh.iph->protocol; //看数据包中L4 是UDP,还是TCP,还是Raw
-
int hash;
-
struct sock *raw_sk;
-
struct net_protocol *ipprot;
-
-
resubmit:
-
hash = protocol & (MAX_INET_PROTOS - 1);
-
raw_sk = sk_head(&raw_v4_htable[hash]);
-
-
/* If there maybe a raw socket we must check - if not we
-
* don't care less
-
*/
-
if (raw_sk && !raw_v4_input(skb, skb->nh.iph, hash)) //这里是对Raw的处理
-
raw_sk = NULL;
-
-
if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL) { //从初始化的inet_protos取得相应的struct net_protocol 结构,在这个结构中挂了L4处理函数
-
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); //调用L4处理函数来处理
-
if (ret < 0) {
-
protocol = -ret;
-
goto resubmit;
-
}
-
IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
-
} else {
-
if (!raw_sk) {
-
if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
-
IP_INC_STATS_BH(IPSTATS_MIB_INUNKNOWNPROTOS);
-
icmp_send(skb, ICMP_DEST_UNREACH,
-
ICMP_PROT_UNREACH, 0);
-
}
-
} else
-
IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
-
kfree_skb(skb);
-
}
-
}
-
out:
-
rcu_read_unlock();
-
-
return 0;
-
}
从代码中我们看到了L3到L4的过程,所以我们要分析一下L4如何挂入的,也就是inet_protos数组的初始化过程,这个过程在inet_init函数中完成的
5. inet_protos数组的初始化以及L4处理函数的挂入
#define MAX_INET_PROTOS 256 /* Must be a power of 2 */
struct net_protocol *inet_protos[MAX_INET_PROTOS];
/* Standard well-defined IP protocols. */
enum {
IPPROTO_IP = 0, /* Dummy protocol for TCP */
IPPROTO_ICMP = 1, /* Internet Control Message Protocol */
IPPROTO_IGMP = 2, /* Internet Group Management Protocol */
IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */
IPPROTO_TCP = 6, /* Transmission Control Protocol */
IPPROTO_EGP = 8, /* Exterior Gateway Protocol */
IPPROTO_PUP = 12, /* PUP protocol */
IPPROTO_UDP = 17, /* User Datagram Protocol */
IPPROTO_IDP = 22, /* XNS IDP protocol */
IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol */
IPPROTO_RSVP = 46, /* RSVP protocol */
IPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702) */
IPPROTO_IPV6 = 41, /* IPv6-in-IPv4 tunnelling */
IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */
IPPROTO_AH = 51, /* Authentication Header protocol */
IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */
IPPROTO_PIM = 103, /* Protocol Independent Multicast */
IPPROTO_COMP = 108, /* Compression Header protocol */
IPPROTO_SCTP = 132, /* Stream Control Transport Protocol */
IPPROTO_UDPLITE = 136, /* UDP-Lite (RFC 3828) */
IPPROTO_RAW = 255, /* Raw IP packets */
IPPROTO_MAX
};
我们看到L3到L4有这么多条路径可以走, 其实最长走的也就4个IPPROTO_ICMP IPPROTO_UDP IPPROTO_TCP IPPROTO_IGMP, 在inet_init函数中也就挂入了这四个
-
/*
-
* 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");
-
#ifdef CONFIG_IP_MULTICAST
-
if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
-
printk(KERN_CRIT "inet_init: Cannot add IGMP protocol\n");
-
#endif
这个挂入过程和L2到L3的挂入过程一样,以协议类型为索引做hash,然后挂入。来看一下udp_protocol吧
static struct net_protocol udp_protocol = {
.handler = udp_rcv,
.err_handler = udp_err,
.no_policy = 1,
};
我们看到了udp_rcv,进入L4了......