盒子设备接口收发包的思考1

  目前在处理盒子产品时,发现wan口和lan口收发报文时还在走内核路由逻辑,因为从wan口进来的包如果转发只能从lan口转发出去,所以此时路由查找是个多余动作!!此处应该是一个可以优化点,来试一试吧!!mark,也不想不通为啥一个产品这么多年都没有人去思考这些!!!

  工作中还是要多想一想为什么?不要随波逐流的接受!! 思考内在的原因,说不定这些看起来正确的东西实际上是错误!最起码在目前场景下是错误的!!

收包:

 

 

 

 

 

 

 

 

 

 

//从这里进入L4传输层
/*
 * ip_local_deliver_finish()将输入数据包从网络层传递
 * 到传输层。过程如下:
 * 1)首先,在数据包传递给传输层之前,去掉IP首部
 * 2)接着,如果是RAW套接字接收数据包,则需要
 * 复制一份副本,输入到接收该数据包的套接字。
 * 3)最后,通过传输层的接收例程,将数据包传递
 * 到传输层,由传输层进行处理。
 */
 /* 
 ip 层处理报文过程中,回复制一份报文到raw_socket中去;有的是IPPROTO_TCP/IPPROTO_RAW
 当 socket(AF_INET, SOCK_RAW, IPPROTO_RAW)时,它会接收所有协议的数据包,并且
IP_HDRINCL 是默认打开的,即是说应用层要提供 L3 和 L4 层的头。再如,如果是
IPPROTO_TCP 时,它只接收到 TCP 包。而 IP_HDRINCL 是默认不打开的,即系统会处理 L3
的头部
*/
static int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
/*
     * 在数据包传递给传输层之前,先去掉
     * IP首部。
     */
    __skb_pull(skb, skb_network_header_len(skb));

    rcu_read_lock();
    {
        int protocol = ip_hdr(skb)->protocol;
        const struct net_protocol *ipprot;
        int raw;
/*
         * 处理RAW套接字,先根据传输层协议号
         * 得到哈希值,然后查看raw_v4_htable散列表
         * 中以该值为关键字的哈希桶是否为空,
         * 如果不为空,则说明创建了RAW套接字,
         * 复制该数据包的副本输入到注册到
         * 该桶中的所有套接字。
         */
         /*
ip_local_deliver_finish函数会先检查哈希表raw_v4_htable。
因为在创建 socket时,inet_create会把协议号IPPROTO_ICMP的值赋给socket的成员num,
并以num为键值,把socket存入哈 项表raw_v4_htable?瑀aw_v4_htable[IPPROTO_ICMP&(MAX_INET_PROTOS-1)]上即存放了 这个socket,实际上是一个socket的链表,
如果其它还有socket要处理这个回显应答,也会被放到这里,组成一个链 表,
ip_local_deliver_finish收到数据报后,取出这个socket链表(目前实际上只有一项),
调用raw_v4_input,把 skb交给每一个socket进行处理。
然后,还需要把数据报交给inet_protos[IPPROTO_ICMP& (MAX_INET_PROTOS-1)],即icmp_rcv处理,
因为对于icmp报文,每一个都是需要经过协议栈处理的,
但对回显应 答,icmp_rcv只是简单丢弃,并未实际处理。
*/
    resubmit:
    //之前开巨帧的时候,icmp不通就是在这里面的函数中sock_queue_rcv_skb丢的
        raw = raw_local_deliver(skb, protocol);
//如果是raw套接字,则则该函数里面会复制一份skb,然后送到  ,例如用ping 1.2.2.2的时候,会走这里面,不会走icmp_recv*/
        ipprot = rcu_dereference(inet_protos[protocol]);
        if (ipprot) {
            int ret;
/*
         * 通过查找inet_portos数组,确定是否
         * 注册了与IP首部中传输层协议号
         * 一致的传输层协议。若查找命中,
         * 则执行对应的传输层协议例程。
         */
            if (!ipprot->no_policy) {
                if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
                    kfree_skb(skb);
                    goto out;
                }
                nf_reset(skb);
            }
            ret = ipprot->handler(skb);//这里面会进入udp tcp传输层
            if (ret < 0) {
                protocol = -ret;
                goto resubmit;
            }
            __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
        } else {
            if (!raw) {
                /*
             * 如果没有响应的协议传输层接收该数据包,
             * 则释放该数据包。在释放前,如果是RAW
             * 套接字没有接收或接收异常,则还需产生
             * 一个目的不可达ICMP报文给发送方。表示该包raw没有接收并且inet_protos中没有注册该协议
             */
                if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
                    __IP_INC_STATS(net, IPSTATS_MIB_INUNKNOWNPROTOS);
                    icmp_send(skb, ICMP_DEST_UNREACH,
                          ICMP_PROT_UNREACH, 0);
                }
                kfree_skb(skb);
            } else {
                __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
                consume_skb(skb);
            }
        }
    }
 out:
    rcu_read_unlock();

    return 0;
}

 

可以看到:ip_rcv_finish ip_local_deliver_finish 涉及到路由的逻辑主要是:

  • 检测是否可以使用early_demux
  • 检测skb的路由缓存是否存在,没有则通过路由来查找
  • 根据查找的路由是否deliver_to_stack 还是forward_out

对于此时产品下:可以判定的是报文肯定会上tcp/ip协议栈,所以对transparent 流量,我们可以直接简单的忽略掉路由系统,直接上传报文到协议栈!!!

对于tcp L4来说 不会涉及到路由功能;

所以:可知对于tproxy 功能收包时 可以不需要路由逻辑,即可以去掉相关冗余逻辑!!!

 

posted @ 2021-03-05 14:04  codestacklinuxer  阅读(200)  评论(1编辑  收藏  举报