免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 4442 | 回复: 14
打印 上一主题 下一主题

[网络子系统] ebtables 的redirect target 不能把带vlan 信息的skb的传到三层???? [复制链接]

论坛徽章:
1
2017金鸡报晓
日期:2017-01-10 15:13:29
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-08-14 11:49 |只看该作者 |倒序浏览
最近在做linux 网桥的相关配置测试,发现ebtables的redirect target 不能把带vlan的信息的skb传到三层,其分析过程如下

环境:
linux 内核版本:3.14.31
机器有两个网卡分别是eth2,eth3,两个网卡作为网桥br0的端口,
而且这个两个端口接着都是交换机的trunk口,就是通过br0的帧都打了vlan tag, 可以带不同的vlan id

ebtables的规则如下
-A BROUTING -p 802_1Q --vlan-encap IPv4 -j redirect  --redirect-target DROP

当带着vlan 信息的ip包过来的时候,碰到上面的ebtables规则,数据包经过的路径如下

netif_receive_skb( )->skb_vlan_untag( )->br_handle_frame( )->ebt_broute( )->ebt_do_table( )->ebt_redirect_tg( )->netif_receive_skb( )->ip_rcv( )

skb_vlan_untag( ) 会把802.1q的vlan tag的信息,放到 skb->vlan_tci 和skb->vlan_proto 这两个值中,这时候, vlan_tx_tag_present(skb) 就会为真
ebt_redirect_tg( )  这个函数会修改eth_hdr(skb)->h_dest 和skb->pkt_type=PACKET_HOST,让其在网桥中做bridged 的判断的时候,数据包能上到上层 ;
ebt_broute ( ) 如果返回 NF_DROP,就是规则中的标准target ,br_handle_frame 就会返回RX_HANDLER_PASS 给 netif_receive_skb()这个函数

netif_receive_skb 的主要实现代码在__netif_receive_skb_core 这个函数,所以把这个函数的代码的几个关键点调出来分析了

static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc){
                   ..........................
another_round:
        skb->skb_iif = skb->dev->ifindex;

        __this_cpu_inc(softnet_data.processed);

        if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
            skb->protocol == cpu_to_be16(ETH_P_8021AD)) {
                skb = skb_vlan_untag(skb);
                if (unlikely(!skb))
                        goto unlock;
        }   
                  ............................................
        if (vlan_tx_tag_present(skb)) {
                if (pt_prev) {
                        ret = deliver_skb(skb, pt_prev, orig_dev);
                        pt_prev = NULL;
                }
                if (vlan_do_receive(&skb))
                        goto another_round;
                else if (unlikely(!skb))
                        goto unlock;
        }
        rx_handler = rcu_dereference(skb->dev->rx_handler);
        if (rx_handler) {
                if (pt_prev) {
                        ret = deliver_skb(skb, pt_prev, orig_dev);
                        pt_prev = NULL;
                }
                switch (rx_handler(&skb)) {    ------------->这里调用的实际函数就是br_handle_frame( )
                case RX_HANDLER_CONSUMED:
                        ret = NET_RX_SUCCESS;
                        goto unlock;
                case RX_HANDLER_ANOTHER:
                        goto another_round;
                case RX_HANDLER_EXACT:
                        deliver_exact = true;
                case RX_HANDLER_PASS:---->当broute链返回NF_DROP的时候br_handle_frame 就返回RX_HANDLER_PASS
                        break;
                default:
                        BUG();
                }
                if (unlikely(vlan_tx_tag_present(skb))) {      -----> 当从网桥的相关代码返回的时候,就会走到这里,如果在网桥的代码里面没有对skb->vlan_tci 做任何处理的话
                                                                                ------->这个判断就会是真 ,下面的那个判断也是一样 ,因此skb->pkt_type就会被改成PACKET_OTHERHOST,
                        if (vlan_tx_tag_get_id(skb))                ----->但是这个在ebt_redirect_tg()才刚把这把skb->pkt_type改为PACKET_HOST
                            skb->pkt_type = PACKET_OTHERHOST;  
                /* Note: we might in the future use prio bits
                 * and set skb->priority like in vlan_do_receive()
                 * For the time being, just ignore Priority Code Point
                 */
                        skb->vlan_tci = 0;
        }

       .....................................
}

如果 skb->pkt_type =PACKET_OTHERHOST, 当skb传到 ip_rcv , 这个skb会因为skb->pkt_type 不是PACKET_HOST,而被ip_rcv 丢掉,

上面就是整个分析过程,后来我想想,会不会有什么参数可以让网桥在skb上传到上层协议栈的时候清空skb->vlan_tci ,找了个遍,都没有找到,
我就在ebt_redirect_tg()中做了个修改,验证上面的分析,结果,概率之后skb就真的能上传到三层了,修改如下


static unsigned int  ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
        const struct ebt_redirect_info *info = par->targinfo;
        
        if (!skb_make_writable(skb, 0))
                return EBT_DROP;

        if (par->hooknum != NF_BR_BROUTING)
                /* rcu_read_lock()ed by nf_hook_slow */
                memcpy(eth_hdr(skb)->h_dest,
                       br_port_get_rcu(par->in)->br->dev->dev_addr, ETH_ALEN);
        else
                memcpy(eth_hdr(skb)->h_dest, par->in->dev_addr, ETH_ALEN);
        skb->pkt_type = PACKET_HOST;
   
       if (unlikely(vlan_tx_tag_present(skb))) { --->加了这个判断和清空的操作
            skb->vlan_tci = 0;
        }

        return info->target;
}

有点想不明白的,为什么在__netif_receive_skb_core 中,rx_handle 返回后,要根据skb是否带有vlan 信息把skb->pkt_type修改为PACKET_OTHERHOST呢?
难道linux 协议栈认为,都要到三层了,skb中的vlan 信息就没用啦,所以传给三层的skb就应该带vlan 信息的了??如果是这样的话,那么ebt_redirect_tg()这个函数
是不是应该考虑一下skb带vlan信息的情况,然后做处理呢??

另外,我试过在ebtables的nat表中的PREROUTING 添加一条同样效果的规则,一样存在同样的问题。
后来查了ebtables 中dnat的代码,也是一样的,没有考虑skb带vlan的情况的


发email到netfilter的maillist,被退信了,写给代码的作者又没有回复,

大家讨论一下吧,到底怎么回事,会不会是我有什么地方理解错误了????



论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
2 [报告]
发表于 2015-08-14 12:06 |只看该作者
回复 1# philarlala


skb_vlan_untag 这个函数的首先看一下,感觉他应该是调整 skb 中的一些指针。

报文进入三层的话 ,skb->data 应该就指向三层了。但是 vlan 的信息应该都在的,自己可以考虑通过 offset 的方式获取过。 我说的是通常的做法,不一定保证完全有效。需要你验证一下。

   

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
3 [报告]
发表于 2015-08-14 12:43 |只看该作者
本帖最后由 nswcfd 于 2015-08-14 14:01 编辑

br_netfilter/ebtables本身就是一个hack机制,有支持不了的场景也不是什么新鲜事情。

在网桥br_handle_frame被调用之前,先经过了一轮vlan处理,如果本地有对应的vlan接口,则goto another_round,进入vlan口的本地接收逻辑。
能够进入网桥逻辑,说明前面的vlan处理失败,即本地没有vlan接口,所以在第二次vlan处理的时候,置位OTHER_HOST。

综合起来就是,带tag的报文,如果本地可以处理(有相关的vlan子接口),就修改为PACKET_HOST;
否则(即便rx_hanlder为空,不走网桥逻辑),还带着tag的话,就认为是OTHERHOST。

现在的处理流程,跟2.6时候的不太一样,那里netif_receiveskb里面没有专门针对vlan的处理逻辑,所以没有问题(被etable_redirect修改的packet_type不会再次被覆盖)。

论坛徽章:
1
2017金鸡报晓
日期:2017-01-10 15:13:29
4 [报告]
发表于 2015-08-14 13:28 |只看该作者
在skb_vlan_untag( )中是有处理指针的,这个函数主要是把vlan头的信息放到skb中放vlan 信息的成员skb->vlan_tci 和skb->vlan_proto 中,而在skb中就不会再有vlan头的信息,这个3.14的内核的处理和2.6.x的处理是不一样的,在2.6.x中把skb整个打印出来,是可以看到在前面是vlan头相关的信息的,在3.14中中打印整个skb是没有vlan头这些信息的回复 2# Godbach


   

论坛徽章:
1
2017金鸡报晓
日期:2017-01-10 15:13:29
5 [报告]
发表于 2015-08-14 13:30 |只看该作者
对啊,我就觉得好有点神奇啊,这个写内核的都是大神,不应该考虑的这么草率才对的回复 3# nswcfd


   

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
6 [报告]
发表于 2015-08-14 13:48 |只看该作者
回复 4# philarlala

你怎么打印的?

是从二层头部开始打印吗?


   

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
7 [报告]
发表于 2015-08-14 13:53 |只看该作者
本帖最后由 nswcfd 于 2015-08-14 14:05 编辑

2.6时代,vlan针对8021q注册了一个packet_type,tag处理是在netif_receive之后完成的(不考虑硬件加速的场景),不像新内核,把tag处理提前了netif_receive里面。

2.6: netif_receive_skb -> vlan_skb_recv -> untag(取决于vlan_flag) -> netif_rx -> ip_rcv
3.x: netif_receive_skb -> untag -> goto another_round(内部循环) -> ip_rcv

以上是针对vlan虚拟子接口的情况,即本地终结vlan tag。

这跟楼主谈到的vlan报文交换(bridge逻辑)还不太一样。bridge的转发是不关心vlan tag的(这跟绝大多数交换机的行为不太一样)。
ebtable/broute提供了一个提前终止bridge并回退route的机制,但这机制跟vlan报文的功能组合关系恐怕没在代码作者的考量范围之内(或者没有同步更新吧)。

楼主有没有进一步想一想,就算3.x没有问题,packet_type是host,报文送到ip_rcv,又该怎么处理?
查找路由表,找出接口,应该是eth2、eth3、bri0的哪一个?发出去的报文,tag又是怎么被打上的?
skb上可能带着tag的信息,但如果做了DNAT,把目的IP改成localhost,那本地发出去的应答报文是新的skb,它的tag又是怎么指定的呢?

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
8 [报告]
发表于 2015-08-14 14:00 |只看该作者
难道linux 协议栈认为,都要到三层了,skb中的vlan 信息就没用啦

应该是这么个意思,让vlan tag终结在本地三层。就算是需要转发到其它口,也未必就是进来方向的tag。
典型的应用场景,跨vlan的路由。本来不同vlan的报文是二层相互隔离的,要想互通,只能通过三层路由来互连。

论坛徽章:
1
2017金鸡报晓
日期:2017-01-10 15:13:29
9 [报告]
发表于 2015-08-14 14:44 |只看该作者
从skb->head 开始打印回复 6# Godbach


   

论坛徽章:
1
2017金鸡报晓
日期:2017-01-10 15:13:29
10 [报告]
发表于 2015-08-14 14:50 |只看该作者
ebtable/broute提供了一个提前终止bridge并回退route的机制,但这机制跟vlan报文的功能组合关系恐怕没在代码作者的考量范围之内(或者没有同步更新吧)

我也觉得ebtables和vlan的组合有点混乱的,看到4.x的内核代码,这一部分的内容好像都是这样的回复 7# nswcfd


   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP