ebtables和iptables實用工具都使用了Netfilter框架,這是它們一致的一方面,然而對于這兩者還真有一些需要聯動的地方。很多人不明白ebtales的broute表的redirect和nat表PREROUTING的redirect的區別,其實只要記住兩點即可,那就是對于相同點,它們都將數據包導向了本地的IP層;對于不同點,broute表的redirect將數據包的接收設備設置成了實際接收數據的物理網卡,而nat表將數據包的接收設備設置成了橋設備,這個可以在Linux協議棧的源代碼中看個究竟。對于broute表的redirect,可以在br_handle_frame這個handle_bridge調用的回調函數中看到以下的語句:
??? 設想一個配置,本機S的eth0的IP地址為1.1.1.254/24,其上開啟tcp的88端口,和本機直連的一臺主機H的IP地址為1.1.1.2/24,在S上配置:
意的源地址一樣,可以指定任意的目的地址,那么REDIRECT則和MASQUERADE也類似,它只是內核根據自己的策略而選擇出的一個目的地址,正如MASQUERADE也是內
核根據RFC的建議以及自己的策略選擇出的一個源地址一樣。那么如何來選擇REDIRECT的目的地址呢?看一下iptables的man手冊就知道了:
REDIRECT
This target is only valid in the nat table, in the PREROUTING and OUTPUT chains, and user-defined chains which are only called from those?
chains. It redirects the packet to the machine itself by changing the destination IP to the primary address of the incoming interface (locally
-generated packets are mapped to the 127.0.0.1 address).?
特別要注意的是“to the primary address of the incoming interface”這一句。內核中的REDIRECT規則是如何做到這點的呢?這還要看一下代碼才知道:
注:broute表的意義
switch (p->state) { case BR_STATE_FORWARDING: rhook = rcu_dereference(br_should_route_hook); if (rhook != NULL) { if (rhook(skb)) return skb; dest = eth_hdr(skb)->h_dest; } /* fall through */ case BR_STATE_LEARNING: if (!compare_ether_addr(p->br->dev->dev_addr, dest)) skb->pkt_type = PACKET_HOST; NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish); break; ...
我們看一下br_should_route_hook這個回調函數ebt_broute:static int ebt_broute(struct sk_buff *skb) { int ret; ret = ebt_do_table(NF_BR_BROUTING, skb, skb->dev, NULL, dev_net(skb->dev)->xt.broute_table); if (ret == NF_DROP) return 1; /* route it */ return 0; /* bridge it */ }
它進入了我們都熟悉xxx_do_table函數,這也就是常規的Netfilter規則查找操作,最終在找到匹配規則時,進入redirect這個target。如果沒有broute表的規則,則會進入NF_HOOK這個HOOK,其間將會遍NF_BR_BROUTING所有的規則,如果有target為redirect的規則命中,則也會進入redirect這個target,這個target是什么呢?是ebt_redirect_tg這個函數:static unsigned int ebt_redirect_tg(struct sk_buff *skb, const struct xt_target_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) //如果是NAT的PREROUTING,則將橋的MAC地址復制到數據包的目的MAC地址。 memcpy(eth_hdr(skb)->h_dest, par->in->br_port->br->dev->dev_addr, ETH_ALEN); else //如果是broute表的BROUTING,則將實際接收數據包的物理網卡的MAC地址復制到數據包的目的MAC地址。 memcpy(eth_hdr(skb)->h_dest, par->in->dev_addr, ETH_ALEN); //本機可以接收該數據包 skb->pkt_type = PACKET_HOST; //一般返回DROP return info->target; }
從br_handle_frame可以看出,一旦broute表的匹配規則返回了DROP,則handle_bridge直接返回這個skb,不再向下執行,這意味著skb將在handle_bridge返回后沿著netif_receive_skb繼續走下去,而如果沒有匹配的broute表規則,則可能在nat表的PREROUTING鏈中命中,然后在執行了ebt_redirect_tg之后會調用br_handle_frame_finish繼續下去,在br_handle_frame_finish中,由于目的MAC地址已經改成了本機網卡的MAC地址,因此會調用br_pass_frame_up將數據包向協議棧的上層發送:static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb) { struct net_device *indev, *brdev = br->dev; brdev->stats.rx_packets++; brdev->stats.rx_bytes += skb->len; indev = skb->dev; //將skb的dev修改成了brX,這樣在接下來經過LOCAL_IN之后再次調用netif_receive_skb之后,在netif_receive_skb中就不會再次進入handle_bridge的 處理邏](我家小小按下的...)輯了。 skb->dev = brdev; NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL, netif_receive_skb); }
注意,在broute表中的redrect之后,數據包的接收設備是實際的物理網卡ethX,目的MAC成了物理網卡ethX的MAC地址,而在nat的PREROUTING的redirect之后,數據包的接收設備是網橋設備,目的MAC地址成了網橋設備的MAC地址,知道了這個之后,我們再看一下一個和iptables的nat表的redirect的問題。??? 設想一個配置,本機S的eth0的IP地址為1.1.1.254/24,其上開啟tcp的88端口,和本機直連的一臺主機H的IP地址為1.1.1.2/24,在S上配置:
brctl addbr br0 brctl addif eth0 ifconfig br0 1.1.1.254/24 ifcongig eth0 0.0.0.0 #為了防止路由亂掉,因此刪除eth0的IP地址 iptables -t nat -A PREROUTING -d 2.2.2.2 -p tcp --dport 1234 -j REDIRECT --to-ports 88
在H上執行route add -host 2.2.2.2 gw 1.1.1.254 telnet 2.2.2.2 1234
結果呢?不通!連syn-ack都沒有收到,然而在S上刪除REDIRECT規則而執行以下規則則是可以的:iptables -t nat -A PREROUTING -d 2.2.2.2 -p tcp --dport 1234 -j DNAT --to-destination 1.1.1.254:88
難道DNAT和REDIRECT有什么區別嗎?如果你不明白這兩者有什么區別,那么如果你知道SNAT和MASQUERADE的區別也不錯,起碼能幫助你理解。DNAT和SNAT能指定任意的源地址一樣,可以指定任意的目的地址,那么REDIRECT則和MASQUERADE也類似,它只是內核根據自己的策略而選擇出的一個目的地址,正如MASQUERADE也是內
核根據RFC的建議以及自己的策略選擇出的一個源地址一樣。那么如何來選擇REDIRECT的目的地址呢?看一下iptables的man手冊就知道了:
REDIRECT
This target is only valid in the nat table, in the PREROUTING and OUTPUT chains, and user-defined chains which are only called from those?
chains. It redirects the packet to the machine itself by changing the destination IP to the primary address of the incoming interface (locally
-generated packets are mapped to the 127.0.0.1 address).?
特別要注意的是“to the primary address of the incoming interface”這一句。內核中的REDIRECT規則是如何做到這點的呢?這還要看一下代碼才知道:
static unsigned int redirect_tg(struct sk_buff *skb, const struct xt_target_param *par) { ... if (par->hooknum == NF_INET_LOCAL_OUT) newdst = htonl(0x7F000001); else { struct in_device *indev; struct in_ifaddr *ifa; newdst = 0; rcu_read_lock(); indev = __in_dev_get_rcu(skb->dev); //取出接收設備的IP地址 if (indev && (ifa = indev->ifa_list)) newdst = ifa->ifa_local; rcu_read_unlock(); //如果接收設備沒有IP地址,則丟棄數據包 if (!newdst) return NF_DROP; } ... return nf_nat_setup_info(ct, &newrange, IP_NAT_MANIP_DST); }
這下我們就一切都明白了,既然broute表的redirect將接收設備設置為實際的物理網卡,而此網卡的IP地址已經被刪除,那么上述函數的newdst當然不存在了,因此數據包就被DROP掉了,到此為止,問題就很清晰了。可見ebtables的redirect方式直接影響到了iptables的redirect,為了讓iptables的redirect在使用bridge時仍然隨時可行,則必須為使能broute redirect的網卡上設置IP地址,為了不使路由沖突,考慮127.0.0.2...注:broute表的意義
為何會有這樣的問題?broute是原因。所謂的broute則是bridge or router,類似早先安裝寬帶時運營商送的那種貓,能作為橋設備也能作為路由器。如果作為路由器,根本不存在橋設備這一說,因此將接收設備設置為實際的物理網卡也是理所當然的啦。
?本文轉自 dog250 51CTO博客,原文鏈接:http://blog.51cto.com/dog250/1269005