当数据包的目的地址是本机是,Ip_rcv_finish函数就会将skb->dst->input函数指针初始化为ip_local_deliver,ip层本地发送数据包也分为两个阶段分配分别有两个处理函数:ip_local_deliver和ip_local_deliver_finish。本地转发数据包的首要任务是重组数据包,前送的数据包可以不要重组,前送可以转发每个分片数据包。
一、本地重组数据包
ip_local_deliver函数主要的任务是重组ip数据包,重组数据包调用ip_defrag函数完成,ip_defrag重组数据包完毕后返回完整数据包指针,如果还没有收到数据包的所有分片,数据包还不完整就返回NULL。重组数据包成功后调用网络过滤子系统的INPUT链上的钩子函数对数据包做过滤处理,过滤处理完成就调用ip_local_deliver_finish函数继续处理。
int ip_local_deliver(struct sk_buff *skb)
{
/*
* Reassemble IP fragments.
*/
if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
//重组数据包,如果数据包还不完整返回NULL
if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))
return 0;
}
//数据包重组完成,调用防火墙上INPUT链上的钩子处理函数
//网络过滤子系统处理完成就调用ip_local_deliver_finish继续处理
return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
ip_local_deliver_finish);
}
二、数据包从IP上传到传输层
将数据包从IP层上传给传输层的处理函数的ip_local_deliver_finish函数,它的主要任务有:
a、将数据包传给正确的上层协议处理函数
b、将数据包传给裸IP
c、对数据包进行安全策略检查
(1)、传输层协议
在IP协议头中protocol数据域占了8位,所以传输层最多支持256种协议,linux支持的传输层协议定义成枚举变量在include/linux/in.h文件中。
/* 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_RAW = 255, /* Raw IP packets */
IPPROTO_MAX
};
(2)、传输层协议处理函数结构
传输层的每个协议都定义接受网络数据包的处理函数,完成对输入数据包的处理,如分段、错误处理。传输层协议处理结构体struct net_protocol定义在include/net/protocol.h文件中,结构体如下:
/* This is used to register protocols. */
struct net_protocol {
//协议注册到内核的处理输入数据包的函数
int (*handler)(struct sk_buff *skb);
//内核收到icmp 错误消息处理函数
void (*err_handler)(struct sk_buff *skb, u32 info);
//发送检查gso
int (*gso_send_check)(struct sk_buff *skb);
struct sk_buff *(*gso_segment)(struct sk_buff *skb,
int features);
struct sk_buff **(*gro_receive)(struct sk_buff **head,
struct sk_buff *skb);
int (*gro_complete)(struct sk_buff *skb);
//是否需要ipsce策略检查,1不需要
unsigned int no_policy:1,
netns_ok:1;
};
传输层的数据包处理结构体在IP层初始化函数inet_init中调用inet_add_protocol注册到内核全局数组inet_protos中。
//内核全局数组,保存了所有传输层的处理结构体
extern const struct net_protocol *inet_protos[MAX_INET_PROTOS];
inet_add_protocol函数:
int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol)
{
int hash, ret;
//根据协议编码获取哈希值
hash = protocol & (MAX_INET_PROTOS - 1);
spin_lock_bh(&inet_proto_lock);
//将传输层协议处理结构体添加到全局数组inet_protos中
if (inet_protos[hash]) {
ret = -1;
} else {
inet_protos[hash] = prot;
ret = 0;
}
spin_unlock_bh(&inet_proto_lock);
return ret;
}
tcp协议net_protocol协议结构体实例:
static const struct net_protocol tcp_protocol = {
.handler = tcp_v4_rcv,
.err_handler = tcp_v4_err,
.gso_send_check = tcp_v4_gso_send_check,
.gso_segment = tcp_tso_segment,
.gro_receive = tcp4_gro_receive,
.gro_complete = tcp4_gro_complete,
.no_policy = 1,
.netns_ok = 1,
};
三、ip_local_deliver_finish函数
数据包通过网络过滤系统INPUT链上的钩子处理函数后调用ip_local_deliver_finish函数继续处理。ip_local_deliver函数主要任务:
a、初始化skb->data,设置传输层协议头指针skb->transplant_header,使指针指向传输层协议头的起始地址,这时IP层已经处理完成,但还是可以通过skb->network_header访问Ip协议头。
b、获取传输层协议编码iphdr-protocol,然后根据协议编码在inet_protos全局数组中找到传输层结构体,调用传输层处理函数,数据包进如传输层。
c、检查数据包是否需要ipsce检查处理
c、错误处理,返回一个imcp包表示地址不可达。
ip_local_deliver_finish函数代码:
static int ip_local_deliver_finish(struct sk_buff *skb)
{
struct net *net = dev_net(skb->dev);
//数据包偏移ip头部长度
__skb_pull(skb, ip_hdrlen(skb));
/* Point into the IP datagram, just past the header. */
//设置传输层协议头指针skb->transplant_header
skb_reset_transport_header(skb);
//加读锁
rcu_read_lock();
{
int protocol = ip_hdr(skb)->protocol;
int hash, raw;
const struct net_protocol *ipprot;
resubmit:
raw = raw_local_deliver(skb, protocol);
//求得传输层的哈希值
hash = protocol & (MAX_INET_PROTOS - 1);
//根据传输层协议号获取传输层处理函数的结构体net_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) {
//ipsec策略安全检查函数
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_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包
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;
}