全部博文(252)
分类: LINUX
2014-07-13 14:55:17
当上联路由设备配置为桥模式时,下挂的PC通过PPPOE拨号连接互联网时,这时PPPOE发送的数据包是添加了PPPOE封装的数据包,当经过路由的桥模式进行透传时,在桥的转发处理流程中对PPPOE封装的数据包进行了特殊的处理,这里的Linux内核版本为Linux2.6.32。
首先,需要先了解一下数据包在桥模式下的转发流程,在桥的br_nf_pre_routing函数中对PPPOE和VLAN的处理如下:
点击(此处)折叠或打开
这里对数据包进行了判断,这里重点看一下对VLAN和PPPOE封装的处理
点击(此处)折叠或打开
这里就是判断是不是带VLAN的数据包 和PPPOE封装的数据报文,但在IS_PPPOE_IP函数中有一个变量brnf_filter_pppoe_tagged,这Linux系统中默认的值为0.
点击(此处)折叠或打开
所以在br_nf_pre_routing函数中如果过来的是PPPOE封装的数据包,该宏IS_PPPOE_IP的判断关键是看brnf_filter_pppoe_tagged变量的值,我们可以通过在proc系统中修改该值。
/proc/sys/net/bridge # cat bridge-nf-filter-pppoe-tagged
0
可以使用下面的方式修改该值:
echo 1 >/proc/sys/net/bridge/bridge-nf-filter-pppoe-tagged
如果在br_nf_pre_routing函数中直接return NF_ACCEPT;后,下面的函数nf_bridge_alloc就不会被调用,这样造成了skb->nf_bridge;为空,这样在br_nf_post_routing函数中,下面的判断会直接返回。
if (!nf_bridge)
return NF_ACCEPT;
HOOK点函数NF_HOOK_THRESH(pf, NF_INET_POST_ROUTING, skb, NULL, realoutdev,
br_nf_dev_queue_xmit, NF_IP_PRI_NAT_SRC + 1);
无法调用,导致了NF_INET_POST_ROUTING挂载的函数都无法执行,也就是在__nf_conntrack_confirm函数中做的相应处理对于桥发包都是不生效的。
当bridge-nf-filter-pppoe-tagged值为真时,还有另外一种情况也会导致通过桥的数据包不会建立会话,当桥下面的PC使用PPPOE拨号上网时,这里假设数据包是带着VLAN的PPPOE数据包,带的VLAN tag为200,在桥的处理函数中br_nf_pre_routing(),我们再看一下上面提到的对PPPOE数据包的判断,如果skb->protocol不等于IP协议,也不是VLAN的0x8100,也不是PPPOE的0x8864,在这里就会直接返回NF_ACCEPT接收该数据包,
点击(此处)折叠或打开
导致下面的两个函数不会被调用
点击(此处)折叠或打开
所以skb->nf_bridge为NULL,在后续的函数中对skb->nf_bridge进行判断时,都是不成立的。导致刚进入br_nf_post_routing函数,判断nf_bridge为空时,直接返回NF_ACCEPT。
if (!nf_bridge)
return NF_ACCEPT;
因此,经过桥上的数据包就不会建立会话, 通过抓包可以看到其发送的报文数据格式:
0000 04 7d 7b 91 0b 69 d8 49 0b b6 8c cb 81 00 00 c8 .}{..i.I........
0010 88 64 11 00 79 08 00 61 00 21 45 00 00 5f be 17 .d..y..a.!E.._..
0020 00 00 3d 11 70 c2 ca 6a 2e 97 72 f9 e2 b9 00 35 ..=.p..j..r....5
0030 ee bb 00 4b ef d3 67 33 81 80 00 01 00 02 00 00 ...K..g3........
通过上面的打印可以看出,8100为VLAN协议,00C8为VLAN TAG 200,8864 为
#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */
00 21 为#define PPP_IP 0x21 /* Internet Protocol */
结合pppoe_proto()函数的实现,看看该函数是怎么获取协议的,从MAC层开始+14个字节+6个字节的PPPOE头部信息,应该获取到0x0021,如果没有VLAN TAG的4个字节,这样应该是正确,但是如果存在VLAN的4个字节,这样就无法取得0x0021的协议字段。而获取到是0x7908。这样就造成了前面提到的if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb) && !IS_PPPOE_IP(skb))的判读为真,直接return NF_ACCEPT。
点击(此处)折叠或打开