IP层转发

//在inet_init时,已经使用dev_add_pack(&ip_packet_type)向ptype_base中注册二层负载
//IPv4报文处理回调,当netif_receive_skb进行二层包处理时,会遍历所有ptype_base列
//表,找到对应的三层协议,并调用回调进行处理。这里ip_rcv就是IPV4的接收处理回调
ip_rcv
//如果目地MAC不是自身,则将包丢弃,不是自身通常都是从二层桥转发。
if (skb->pkt_type == PACKET_OTHERHOST)
goto drop;

//如果skb是共享的,则先复制一份,复制失败则丢弃。
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
goto out;

//检测当前skb的头部是否有ip头大小的有效空间。
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto inhdr_error;

iph = skb->nh.iph;

//检测ip头长度是否小于最少20字节,或者不是ipv4版本
if (iph->ihl < 5 || iph->version != 4)
goto inhdr_error;

//检测当前skb的头部是否有ip整个头(可能含ip选项)的有效空间。
if (!pskb_may_pull(skb, iph->ihl*4))
goto inhdr_error;

iph = skb->nh.iph;

//进行ip校验和检测
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto inhdr_error;

//检测整个报文是否小于ip头记载的总长度,或者总长度小于IP头长度
len = ntohs(iph->tot_len);
if (skb->len < len || len < (iph->ihl*4))
goto inhdr_error;

//在二层传输时,很可能为了适应二层媒介(比如以太网帧最小必须是64字节)而
//填充无效信息。这里检测是否含有填充信息,如果含有则修正skb的tail指向有效
//数据的位置,跳过无效的填充。
if (pskb_trim_rcsum(skb, len))
IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
goto drop;

//将sky的cb成员清空。
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));

//进行netfileter的PRE_ROUTING链处理,这里不分析netfileter,假设没有被
//netfilter拦截,则会触发ip_rcv_finish调用。
NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,ip_rcv_finish);

--------------------------------------------------------------------------------------------------------------------
ip_rcv_finish
//没有目地对象,先进行路由模块查询,这里暂时先不去了解路由模块的处理机制
//仅需要知道,如果路由模块判断出当前报文是单播报文,则将设置接收、发送
//回调分别为dst.input= ip_forward
//dst.output = ip_output
if (skb->dst == NULL)
ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,skb->dev);

//如果ip头长度大于20字节,则表示有ip选项,则进行ip选项处理。
if (iph->ihl > 5 && ip_rcv_options(skb))
goto drop;

//调用skb->dst->input回调进行转发处理,这个回调就是在上面ip_route_input中
//设置的。如果回调函数的返回值为NET_XMIT_BYPASS,则循环接收处理。
dst_input(skb);
for (;;)
err = skb->dst->input(skb);

if (likely(err == 0))
return err;

if (unlikely(err != NET_XMIT_BYPASS))
return err;
----------------------------------------------------------------------------------------------------------------------
ip_forward
ip_options * opt	= &(IPCB(skb)->opt);

//进行安全策略处理,当前暂不分析
if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
goto drop;

//如果当前ip报头含有ALTER选项,同时在路由告警链中有对应的条目,则将报文
//交给该套接口进行处理,处理成功,则直接返回。
if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
return NET_RX_SUCCESS;

//目的MAC非自身,则丢弃。
if (skb->pkt_type != PACKET_HOST)
goto drop;

//标记没有进行校验和处理
skb->ip_summed = CHECKSUM_NONE;

//TTL字段值已经不能再减了,该报文不能再传递了,丢弃。
if (skb->nh.iph->ttl <= 1)
goto too_many_hops;

//安全处理,暂不分析。
if (!xfrm4_route_forward(skb))
goto drop;

rt = (struct rtable*)skb->dst;

//当前含有严格源路由选项,但当前目的地和当前出口网关不同,则丢弃。
if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
goto sr_failed;

//检测skb从head到data剩余的空间是否还能容纳下二层头加扩展头的长度,如果
//不能容下,或者该skb是共享了数据部分,则需要对该skb进行复制扩冲
if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))
goto drop;

iph = skb->nh.iph;

//递减TTL字段
ip_decrease_ttl(iph);

//如果路由模块处理时标记了有最优路由,同时ip选项中不含严格路由的选项。
//则发送重定向的ICMP报文。
if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr)
ip_rt_send_redirect(skb);

//根据报文的TOS字段设置优先级
skb->priority = rt_tos2priority(iph->tos);

//进行netfileter的FORWARD链处理,这里不分析netfileter,假设没有被
//netfilter拦截,则会触发ip_forward_finish调用。
NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev,ip_forward_finish);

----------------------------------------------------------------------------------------------------------------------
ip_forward_finish
opt	= &(IPCB(skb)->opt);

//有ip选项,则进行转发相关的ip选项处理。
if (unlikely(opt->optlen))
ip_forward_options(skb);

//将报文进行输出处理,当前output回调函数是在ip_route_input中设置。该回调函数
//为ip_output,IP层输出后面单独分析。
dst_output(skb);
skb->dst->output(skb);

你可能感兴趣的:(IP层转发)