- 论坛徽章:
- 1
|
最近在做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,被退信了,写给代码的作者又没有回复,
大家讨论一下吧,到底怎么回事,会不会是我有什么地方理解错误了????
|
|