IPv4 报文的接收(转发与本地传递)

我们知道,报文经过网卡驱动处理后,调用net_receive_skb传递给具体的协议处理函数,对于IPv4报文来说,其协议处理函数就是ip_rcv了,ip_rcv在进行一些健康检查等操作后,会调用ip_rcv_finish来处理报文。这也是IPv4协议对报文接收处理的开始。

我们先看下ip_rcv_finish源代码:

ip_rcv_finish:

//ip数据报文的主要处理程序(ip_rcv仅仅只是对ip数据报做一些健康性检查)//ip_rcv_finish 其实是进行路由表查询,,决定报文经过IP层处理后,是继续向上传递,还是进行转发,还是丢弃。//1.决定报文在本地传递或者转发,如果是转发还需要找到出口设备和下一跳节点//2.分析和处理一些选项static int ip_rcv_finish(struct sk_buff *skb){const struct iphdr *iph = ip_hdr(skb);struct rtable *rt;/* *Initialise the virtual path cache for the packet. It describe *how the packet travels inside Linux networking. * 刚开始没有进行路由表查询,所以还没有相应的路由表项:skb_dst(skb) == NULL。 * 则在路由表中查找ip_route_input(),关于内核的路由表 */if (skb_dst(skb) == NULL) {int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,skb->dev); //这里面进行了一些初始化操作,比较重要,与ip报文接下来的走向有关if (unlikely(err)) {if (err == -EHOSTUNREACH)IP_INC_STATS_BH(dev_net(skb->dev),IPSTATS_MIB_INADDRERRORS);else if (err == -ENETUNREACH)IP_INC_STATS_BH(dev_net(skb->dev),IPSTATS_MIB_INNOROUTES);goto drop;}}#ifdef CONFIG_NET_CLS_ROUTE//更新traffic cotrol(qos层)所使用的统计数据if (unlikely(skb_dst(skb)->tclassid)) {struct ip_rt_acct *st = per_cpu_ptr(ip_rt_acct, smp_processor_id());u32 idx = skb_dst(skb)->tclassid;st[idx&0xFF].o_packets++;st[idx&0xFF].o_bytes += skb->len;st[(idx>>16)&0xFF].i_packets++;st[(idx>>16)&0xFF].i_bytes += skb->len;}#endifif (iph->ihl > 5 && ip_rcv_options(skb))goto drop;rt = skb_rtable(skb); /* skb->dst包含路由信息。根据路由类型更新SNMP统计数据 */if (rt->rt_type == RTN_MULTICAST) {IP_UPD_PO_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INMCAST,skb->len);} else if (rt->rt_type == RTN_BROADCAST)IP_UPD_PO_STATS_BH(dev_net(rt->u.dst.dev), IPSTATS_MIB_INBCAST,skb->len);/** dst_input实际上会调用skb->dst->input(skb).input函数会根据路由信息设置为合适的* 函数指针,如果是递交到本地的则为ip_local_deliver,若是转发则为ip_forward.* 暂时仅先考虑ip_local_deliver。*/return dst_input(skb);drop:kfree_skb(skb);return NET_RX_DROP;}

ip_route_input会进行路由表查询,该函数直接或间接决定了报文之后要往何处传递。是进行本地传递还是转发。

我们可以看到如果报文没有被drop掉,那么报文最终会被dst_input(skb)处理。dst_input(skb)实际上执行的是skb->dst->input(skb)。而这里的input函数其实就是由ip_route_input决定的。

对于应该本地传递的报文,input指针会指向ip_local_deliver。对于该转发的报文,input会指向ip_forward。

本地传递

/* * Deliver IP Packets to the higher protocol layers. */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;}return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL,ip_local_deliver_finish);}我们知道,IPv4要将报文传送给上层协议(本地传递),那它需要对分段的报文进行重组,ip_defrag即完成报文重组。然后由调用Netfilter决定是否调用ip_local_deliver_finish。

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)); /* 跳过IP头部 *//* Point into the IP datagram, just past the header. *//* 设置传输层头部位置 */skb_reset_transport_header(skb);rcu_read_lock();{int protocol = ip_hdr(skb)->protocol; //取出ip头中的协议.int hash, raw;const struct net_protocol *ipprot;resubmit:// 若是raw socket发送的,需要做相应的处理,clone数据包raw = raw_local_deliver(skb, protocol); //得到raw socket, 如果不是raw socket,则返回0hash = protocol & (MAX_INET_PROTOS – 1); // 计算传输层协议处理结构在inet_protos数组hash表中的位置ipprot = rcu_dereference(inet_protos[hash]); // 获取传输层协议处理指针if (ipprot != NULL) {int ret;//主要是ipprot是否有被当前主机注册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) {if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {kfree_skb(skb);goto out;}nf_reset(skb);}//调用handler,进入相应的4层协议的处理.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_send(skb, ICMP_DEST_UNREACH,ICMP_PROT_UNREACH, 0);}} elseIP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);kfree_skb(skb);}} out:rcu_read_unlock();return 0;}

转发

报文转发有下面几个步骤完成:

1. 处理IP选项

2. 确定封包可以被转发

3.递减封包头部的TTL字段,如果TTL字段为0,则丢弃该封包

4.根据路径相关MTU,在必要时处理分段

5.把封包传送至外出设备

IPv4协议中,报文的转发从ip_forward开始:

ip_forward

只有不断找寻机会的人才会及时把握机会。

IPv4 报文的接收(转发与本地传递)

相关文章:

你感兴趣的文章:

标签云: