日期:2014-05-16 浏览次数:21020 次
本文主要讲解了Linux内核IP层的整体架构和对从网卡接受的报文处理流程,使用的内核的版本是2.6.32.27
为了方便理解,本文采用整体流程图加伪代码的方式对Linxu内核中IP整体实现架构和对网卡报文的处理流程进行了讲解,希望可以对大家有所帮助。阅读本文章假设大家对C语言有了一定的了解
IP层的整体实现架构

IP层接受底层数据报文的处理流程
/*
* 在NET_RX_SOFTIRQ软中后,由ETH_P_IP触发的ipv4协议入口函数
*/
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
/*
* 过滤掉送往其他主机的数据包(这时网卡正在处于混杂模式)
*/
if (skb->pkt_type == PACKET_OTHERHOST)
goto drop;
iph = ip_hdr(skb);
/*头的长度是否至少是IP头长度(5); 是否是IPV4报文*/
if (iph->ihl < 5 || iph->version != 4)
goto inhdr_error;
/*IP头长度是否正确,不是伪造的长度*/
if (!pskb_may_pull(skb, iph->ihl*4))
goto inhdr_error;
iph = ip_hdr(skb);
/*检查校验和*/
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto inhdr_error;
len = ntohs(iph->tot_len);
if (skb->len < len) {
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);
goto drop;
} else if (len < (iph->ihl*4))
goto inhdr_error;
/*实际尺寸不匹配套接字缓冲(skb->len)中维护的信息,则调用skb_trim调整数据包的长度*/
if (pskb_trim_rcsum(skb, len)) {
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
goto drop;
}
/*调用IP_PRE_ROUTING(NF_INET_PRE_ROUTING)上注册的钩子,
*在调用钩子处理完之后,调用钩子处理完成之后,调用ip_rcv_finish
* 后面讲防火墙的时候,我们会仔细梳理*/
return NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish);
}
/* NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish)*/
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, INT_MIN)
{
int __ret; \
if ((__ret=nf_hook_thresh(pf, hook, (skb), indev, outdev, okfn, thresh, 1)) == 1)\
__ret = (okfn)(skb); \
__ret;
}
static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
struct sk_buff *skb,
struct net_device *indev,
struct net_device *outdev,
int (*okfn)(struct sk_buff *), int thresh,
int cond)
{
/*逐个调用注册的防火墙钩子*/
return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh);
}
/*
* 接收完数据包后的后续处理函数
*/
static int ip_rcv_finish(struct sk_buff *skb)
{
const struct iphdr *iph = ip_hdr(skb);
struct rtable *rt;
/*
* 激活ip_route_input,确定报文的路由,如果ip_route_input无法从FIB中找到路由
* 则丢弃数据报文,ip_route_input将在IP路由中的专题中进行讲解
*/
if (skb_dst(skb) == NULL) {
int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev);
if (unlikely(err)) {
goto drop;
}
}
/*检查IP报头里面是否含有选项,如果含有建立ip_options*/
if (iph->ihl > 5 &am