linux下ip协议(V4)的实现(二)

这次主要介绍下forward和local delivery。

上次我们提到当ip_rcv_finish完成后后调用相关的发送函数ip_forward或者ip_local_deliver.这次就主要介绍这两个函数。

先来看forward。

forward一般由下面几部组成:

1 执行ip option
2 确定这个包能被forward
3 减小ttl,当ttl为0时,丢掉这个包
4 如果需要,则将这个包切片
5 发送包到输出网络设备

这里要注意,如果包由于一些原因,不能被forward,则必须发送ICMP消息到发送主机。

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);
///gso相关设置
if (skb_warn_if_lro(skb))
goto drop;
///xfrm(ipsec)的相关检测
if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
goto drop;
///判断是否有Router_alter option(也就是保存发送端的ip),如果有的话,调用ip_call_ra_chain处理,当空间已满,则返回false,并继续处理。
if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
return NET_RX_SUCCESS;

///判断这个包是否是由本地主机的2层进行接受的。在2层设置帧的类型,当帧的目的地址就是本机2层地址的时候,skb->pkt_type设置为PCAKET_HOST.
if (skb->pkt_type != PACKET_HOST)
goto drop;

///由于是forward,因此我们不需要在意4层的校验。设置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.
*/
///ttl小于1,此时丢掉这个包
if (ip_hdr(skb)->ttl <= 1)
goto too_many_hops;

///ipsec的检测
if (!xfrm4_route_forward(skb))
goto drop;

///得到路由表
rt = skb->rtable;

///判断是否是Strict源路由option。如果是的话,看源路由option所制定的路由能否和rt_gateway(下一跳)匹配。
if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
goto sr_failed;

///检测一些相关域。如果出错,则发送icmp,并丢弃这个包
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;
}

///由于我们将要修改这个skb的一些东西(在下面的ip_forward_finish中),因此我们需要复制一个拷贝(主要是防止skb共享)
if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))
goto drop;
iph = ip_hdr(skb);

///减少ttl
ip_decrease_ttl(iph);

///如果我们所找到的下一跳地址比请求的更好的话,源host现在将会收到一个ICMP REDIRESCT消息(只有当源host没有请求 source routing option时)
if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr && !skb->sp)
ip_rt_send_redirect(skb);

///QOS的优先级设置
skb->priority = rt_tos2priority(iph->tos);

///最终返回netfilter的hook,这里我们还是暂时忽略netfilter,只关注ip_forward_finish.
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->dev), IPSTATS_MIB_INHDRERRORS);
icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
drop:
kfree_skb(skb);
return NET_RX_DROP;
}


下来看ip_forward_finish方法,它主要是用来处理一些剩下的options,并且ip_forward_options还会重新计算ip checksum,因为它会update一些ip头的域:

static int ip_forward_finish(struct sk_buff *skb)
{
struct ip_options * opt = &(IPCB(skb)->opt);

IP_INC_STATS_BH(dev_net(skb->dst->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);

if (unlikely(opt->optlen))
ip_forward_options(skb);
///最终返回dst_output,这个虚函数最终调用skb->dst_output,如果是单播则是ip_output,如果是多播则是ip_mc_output.而且切片(如果有需要)也会在这个函数进行).这里还有一个neighboring subsystem的概念,我们后面会讲到。
return dst_output(skb);
}


来看ip_local_deliver函数:

int ip_local_deliver(struct sk_buff *skb)
{
/*
* Reassemble IP fragments.
*/
///如果有切片,则开始组包。
if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))
return 0;
}
///返回netfilter hook,最终会调用ip_local_deliver_finish.它最终会将数据包发送往4层。下一次我们会详细介绍这个函数。
return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
ip_local_deliver_finish);
}


这里我们可以看到一个对比,那就是forward时,不需要组包,这是因为local delivery必须把完整的包发送往4层,而forward,不需要经过4层,就直接把帧发送出去。

最后我们来看下ip_local_deliver_finish函数片段:


static int ip_local_deliver_finish(struct sk_buff *skb)
{
struct net *net = dev_net(skb->dev);

__skb_pull(skb, ip_hdrlen(skb));

/* Point into the IP datagram, just past the header. */
skb_reset_transport_header(skb);

rcu_read_lock();
{
int protocol = ip_hdr(skb)->protocol;
int hash, raw;
struct net_protocol *ipprot;

resubmit:
///对raw socket进行处理。
raw = raw_local_deliver(skb, protocol);

hash = protocol & (MAX_INET_PROTOS - 1);
ipprot = rcu_dereference(inet_protos[hash]);
if (ipprot != NULL) {
int ret;

...................................................
///将数据包交给已注册的高层协议的处理函数。
ret = ipprot->handler(skb);
if (ret < 0) {
protocol = -ret;
goto resubmit;
}
IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);
}
...................................................
}
out:
rcu_read_unlock();

return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值