在底层接收到数据后,通过底层网卡驱动处理完成后,会调用函数netif_receive_skb进行二层mac子层进行处理,对于需要本机处理的三层数据包,是如何调用各三层处理函数的呢?
对于ip子层处理完以后,需要发送到本地上层继续处理的数据包,又是如何调用相应四层处理函数的呢?
三层数据接收处理相关的链表及处理流程
1、 三层的协议有arp、ipv4、ipv6、ipx等,我比较熟悉且用到的是arp、ip这两个协议的数据包接收处理函数,下面我们看一下三层协议接收处理函数相关的数据结构
三层数据包协议处理
structpacket_type {
__be16 type; /*三层协议类型*/
struct net_device *dev; /* 与该协议相关联的设备,可以对不同的设备绑定不同的三层协议,我们一般将该值设置为NULL,即匹配所有设备*/
/*协议接收处理函数,处理接收的三层数据包的协议处理函数*/
int (*func)(struct sk_buff *,
struct net_device *,
struct packet_type *,
struct net_device *);
/*gso相关的处理函数,对gso不熟悉*/
struct sk_buff *(*gso_segment)(structsk_buff *skb,
intfeatures);
int (*gso_send_check)(structsk_buff *skb);
struct sk_buff **(*gro_receive)(structsk_buff **head,
struct sk_buff *skb);
int (*gro_complete)(structsk_buff *skb);
void *af_packet_priv;/*用于PF_SOCKET类型的socket。它指向相关的sock数据结构*/
struct list_head list;/*将该三层协议处理结构添加到三层协议处理链表ptype_base 中*/
};
static struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
思考:二层协议处理函数为netif_receive_skb,在二层数据处理完以后,对于需要发送到本机的三层数据包,该如何发送到相应的协议层呢?三层协议不止一个,应该怎样处理呢?
相对来说,使用一个链表,对于需要发送到本机的三层数据包,遍历链表ptype_base,找到与数据包的三层协议对应的三层协议packet_type,然后就调用packet_type->(*func),由相应的三层处理函数进行后续处理。对于ipv4,即为ip_rcv;对于arp即为arp_rcv;对于ipv6,即为ipv6_rcv。
将三层协议packet_type添加到链表ptype_base中的函数为
void dev_add_pack(structpacket_type *pt)
{
int hash;
spin_lock_bh(&ptype_lock);
if (pt->type == htons(ETH_P_ALL))
list_add_rcu(&pt->list,&ptype_all);
else {
hash = ntohs(pt->type) &PTYPE_HASH_MASK;
list_add_rcu(&pt->list,&ptype_base[hash]);
}
spin_unlock_bh(&ptype_lock);
}
将三层协议packet_type添加到hash链表ptype_base,就是调用链表的处理函数,还是比较简单的。遍历hash链表ptype_base,可以使用list_for_each_entry_rcu实现。
四层数据接收处理相关的链表及处理流程
1、 对于ipv4、ipv6的三层处理函数ip_rcv、ipv6_rcv处理完以后,需要由四层协议处理的函数,其处理流程与三层协议链表ptype类似。下面分析四层数据接收协议相关的数据结构
/*四层协议注册相关的数据结构 */
struct net_protocol {
int (*handler)(struct sk_buff*skb);/*四层协议相关的函数*/
void (*err_handler)(struct sk_buff*skb, u32 info);/*错误处理*/
/*gso相关*/
int (*gso_send_check)(structsk_buff *skb);
structsk_buff *(*gso_segment)(struct sk_buff *skb,
int features);
structsk_buff **(*gro_receive)(struct sk_buff **head,
struct sk_buff *skb);
int (*gro_complete)(struct sk_buff*skb);
unsignedint no_policy:1,
netns_ok:1;
};
与三层协议相关的数据结构packet_type相比,该数据结构中并没有标记协议的类型,那四层协议数据结构是如何查找的呢?
因为四层协议相关的数据结构,主要是链接到hash链表structnet_protocol *inet_protos[MAX_INET_PROTOS]。虽说是hash数组,但是该数组的最大值为256,而三、四层协议相关的定义如下,而hash数组的最大值即为256,即退化为了数组指针。所以每一个协议均对应一个net_protocol指针,此时只要从ip头部周获取到相应的四层的类型,即可从数组中取出相应的四层协议结构。而在三层结构,有可能出现同一个链表存在多个三层协议,此时就需要在三层协议中增加一个协议类型成员了。
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, /* IPIPtunnels (older KA9Q tunnels use 94) */
IPPROTO_TCP = 6, /*Transmission Control Protocol */
IPPROTO_EGP = 8, /*Exterior Gateway Protocol */
IPPROTO_PUP = 12, /* PUPprotocol */
IPPROTO_UDP = 17, /*User Datagram Protocol */
IPPROTO_IDP = 22, /* XNS IDPprotocol */
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_PIM = 103, /* Protocol Independent Multicast */
IPPROTO_COMP = 108, /* Compression Header protocol*/
IPPROTO_SCTP = 132, /* Stream Control TransportProtocol */
IPPROTO_RAW = 255, /* Raw IP packets */
IPPROTO_MAX
};
四层协议添加到v4数组net_protocol和从数组中删除的函数为,主要是将四层协议结构添加到数组中去。
int inet_add_protocol(const structnet_protocol *prot, unsigned char protocol)
{
inthash, ret;
hash= protocol & (MAX_INET_PROTOS - 1);
spin_lock_bh(&inet_proto_lock);
if(inet_protos[hash]) {
ret= -1;
}else {
inet_protos[hash]= prot;
ret= 0;
}
spin_unlock_bh(&inet_proto_lock);
returnret;
}
/*
* Remove a protocol from the hash tables.
*/
int inet_del_protocol(const structnet_protocol *prot, unsigned char protocol)
{
inthash, ret;
hash= protocol & (MAX_INET_PROTOS - 1);
spin_lock_bh(&inet_proto_lock);
if(inet_protos[hash] == prot) {
inet_protos[hash]= NULL;
ret= 0;
}else {
ret= -1;
}
spin_unlock_bh(&inet_proto_lock);
synchronize_net();
returnret;
}
在ip_local_deliver_finish中根据从三层协议头中获取的四层协议类型,从数组inet_protos中取出相应的四层协议net_protocol,执行net_protocol->handler,即将数据包交由四层协议处理。对于udp为udp_rcv;对于tcp为tcp_v4_rcv;对于icmp为icmp_rcv;对于igmp为igmp_rcv。这样即实现了将三层数据包交由上层继续处理。