int ip_forward(struct sk_buff *skb)
{
struct iphdr *iph; /* Our header */
struct rtable *rt; /* Route we use */
struct ip_options * opt = &(IPCB(skb)->opt);
if (skb_warn_if_lro(skb))
goto drop;
if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb)) /*xfrm(ipsec)的相关检测*/
goto drop;
/* 这也是在处理IP的选项,在IPv6中IP报头固定死了,IP选项的意义不大. */
if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
return NET_RX_SUCCESS;
/*当帧的目的地址就是本机2层地址的时候,skb->pkt_type设置为PCAKET_HOST. 所以本地广播(MAC=FF:FF...)的数据包不会被转发.*/
if (skb->pkt_type != PACKET_HOST)
goto drop;
/* 既然是转发,那么就涉及到网卡对L4层校验和的计算. 设置ip_summed为CHECKSUM_NONE,表示我们不需要让网卡对校验和做任何事情.*/
skb_forward_csum(skb);
/*
* According to the RFC, we must first decrease the TTL field. If
* that reaches zero, we must reply an ICMP control message telling
* that the packet's lifetime expired.
*/
if (ip_hdr(skb)->ttl <= 1) /*ttl小于1,此时丢掉这个包*/
goto too_many_hops;
if (!xfrm4_route_forward(skb)) /*ipSec相关检查.*/
goto drop;
rt = skb_rtable(skb);
/* 介绍了rtable结构体的每个成员变量. rt->rt_dst是路由的目的地址,在严格源路由
选项中,路由的目的地址应该是直达的,不需要经过下一跳的网关.所以有了下面的判定.
问题在于:rt->rt_dst是怎么给赋值的.在第一次解析路由选项时,如果是严格路由选项,那么会发送ICMP_PARAMETERPROB报文.
不费脑子了.
*/
if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
goto sr_failed;
/*
第一个&:如果IP报文长度大于MTU,且不允许分片, 且frag_list=NULL
第二个&:不允许本机对该skb数据分片.
综上可知,不担心IP报文发送不出去,而是担心能不能分段.
*/
if (unlikely(skb->len > dst_mtu(&rt->u.dst) && !skb_is_gso(skb) &&
(ip_hdr(skb)->frag_off & htons(IP_DF))) && !skb->local_df) {
IP_INC_STATS(dev_net(rt->u.dst.dev), IPSTATS_MIB_FRAGFAILS);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
htonl(dst_mtu(&rt->u.dst)));
goto drop;
}
/* We are about to mangle packet. Copy it! skb_cow的第二个参数可理解为MAC+IP报头(有可能和接收到的报文的报头不一样.) */
if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))
goto drop;
iph = ip_hdr(skb); //正是skb_cow才能调用iph_hdr来完整的对IP报头进行操作.
/* Decrease ttl after skb cow done */
ip_decrease_ttl(iph);
/*
* We now generate an ICMP HOST REDIRECT giving the route we calculated.
* 如果我们所找到的下一跳地址比请求的更好的话,源host现在将会收到一个ICMP REDIRESCT消息(只有当源host没有请求 source routing option时)
*/
if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr && !skb_sec_path(skb))
ip_rt_send_redirect(skb);
skb->priority = rt_tos2priority(iph->tos);
return NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, rt->u.dst.dev,
ip_forward_finish);
sr_failed:
/*
* Strict routing permits no gatewaying
*/
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);
goto drop;
too_many_hops:
/* Tell the sender its packet died... */
IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_INHDRERRORS);
icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
总体来说:好像也没做什么事情,路由信息不用管了。需要:1、检查MTU大小与报文大小是否匹配,2、TTL值处理. 3、调整线性缓冲区大小.
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
static int ip_forward_finish(struct sk_buff *skb)
{
struct ip_options * opt = &(IPCB(skb)->opt);
IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);
if (unlikely(opt->optlen))
ip_forward_options(skb); /*对IP Options中有关foreard的选项进行解析*/
/*调用输出函数.ip_output用于单播输出,ip_mc_output用于多播输出.*/
return dst_output(skb); /*调用输出函数.NO::::ip_local_out用于单播输出,ip_mc_output用于多播输出.*/
}
阅读(3049) | 评论(0) | 转发(0) |