實現乙個做雙向NAT的虛擬網絡卡

2021-06-22 20:11:07 字數 3055 閱讀 3561

還是老問題,linux系統中通過iptables配置的nat無法在雙向通訊環境中使用,你無法配置一條nat規則實現對兩個方向主動發起的流量做nat,解決這個問題的方案有好幾種:

iptables的nat配置本身就是先match再執行乙個target,因此一條規則只能表示一種轉換策略,要想實現「來自x的資料報的源位址轉換為y,去往y的資料報的目標位址轉為x」這樣的邏輯,必須使用兩條規則。那麼為何不使用兩條規則呢?因為iptables的nat配置是基於資料流的,它只對乙個建立ip_conntrack結構體的那個資料報進行規則查詢,因此在乙個流已經建立並在資料傳輸的時候,加入一條nat配置是無效的。

xtables-addons中有乙個rawnat,不再基於ip_conntrack了,也就是它是基於資料報而不是資料流的nat,即時生效問題解決了,但是由於它還是乙個match-target規則,因此要想實現雙向的nat,還是要配置兩條規則。

編寫乙個netfilter hook模組不是什麼難事,我自己寫過好幾個,但是,netfilter框架是在協議棧的處理路徑上攔截資料報進行檢查-匹配/動作的,它對每乙個經過協議棧的資料報都要進行檢查,也就是說每乙個資料報都要經過hook函式的過濾,在netfilter hook過多的時候,大大降低了效率。

這是一種全新的理念,實現乙個虛擬網絡卡,其xmit函式是這樣的:

static netdev_tx_t sdnat_net_xmit(struct sk_buff *skb, struct net_device *dev)

if (flags & snat) else if (flags & dnat)

// 此時skb的dst為將資料報導入nat裝置的dst_entry,

// 為了防止迴圈路由,將其drop,nat已經完成,已經沒有用了

skb_dst_drop(skb);

// 清除mark,因為一般通過mark策略路由將資料報導入nat裝置

// 這也是為了防止迴圈路由

skb->mark = 0;

xmit:

netif_rx_ni(skb);

drop:

kfree_skb(skb);

return netdev_tx_ok;

}

do_trans_src/dst完全可以通過乙個函式實現,此處是為了使介面更加清晰。具體的轉換就不多說了,很簡單,修改掉ip報頭的源位址或者目標位址,然後重新計算l3,l4的校驗碼。

關鍵是如何組織nat規則。我使用乙個nat_entry來儲存每一條規則:

struct nat_entry ;

hash的計算如下:

static u32 keys_get_hash(__be32 key)

模組載入的時候,會建立兩個虛擬網絡卡,乙個負責snat,乙個負責dnat,同時系統中也會有兩個sdnat_struct結構體,乙個負責snat,乙個負責dnat:

struct sdnat_struct ;

linux上要配置就是兩條策略路由:

a.從內網口進入往外發的資料報匯入到snat網絡卡裝置進行sant;

b.從外網口進入到內網口的資料報匯入到dnat網絡卡裝置進行dnat。

這樣就可以雙向自動轉換了,不管資料是從哪個首先發起的,實現了「來自x的資料報的源位址轉換為y,去往y的資料報的目標位址轉為x」。是不是和cisco的static nat有些類似呢?定義出入裝置而不是靠iptables的match來過濾資料報。我比較喜歡使用procfs作為使用者介面,因為它方便shell操作:

echo +192.168.1.1 9.24.100.1 >/proc/net/nat

上面的命令執行後,將會在兩塊網絡卡共享的hash表中新增乙個nat_entry,key1為192.168.1.1,key2為9.24.100.1,在snat網絡卡裝置中,將會用skb的iph->saddr做hash後查表匹配其key1,取出key2作為要轉換的ip位址,在dnat網絡卡裝置中,將會用skb的iph->daddr做hash後查表匹配key2,取出key1作為要轉換到的ip位址。如果想刪除一條規則,那麼就執行:

echo -192.168.1.1 9.24.100.1 >/proc/net/nat

策略路由規則如下:

ip rule add iif $內網口 table snat

ip rule add iif $外網口 table dnat

ip route add 0.0.0.0/0 dev snat0 table snat

ip route add 0.0.0.0/0 dev dnat0 table dnat

依靠路由來做是否要進行nat的判斷,是不是更加高效些呢?而不再需要通過netfilter模組去匹配每乙個資料報了,也不需要折騰低效率的ip_conntrack了!值得注意的是,sdnat裝置的xmit函式最終執行了乙個netif_rx_ni這相當於將資料報重新注入其本身,此時資料報的iif將不再是內網口或者外網口了,而是實實在在的sdant虛擬網絡卡裝置,因此資料報再次到達路由模組的時候將不會再次進入sdnat裝置。

除了netfilter框架之外,我們也可以使用linux的網絡卡裝置模型來構建另一套資料報過濾系統,是的,其思想就是上面展示的。我曾經寫過幾篇關於在路由項中儲存資訊,然後通過查路由表的方式獲取資訊的技巧,其中使用了自己定義的「路由表」,查詢方式依然是最長字首匹配法,只是路由項中儲存的東西變了。在本文中,我給出的是使用linux原生的路由表(不是自己定義的)+自定義的虛擬網絡卡裝置實現資料報過濾的思想,按照這種思想,iptables的每乙個target就是乙個虛擬網絡卡裝置,每一系列的matches就是一條路由,該路由的路由項就是將資料報導入對應的虛擬網絡卡裝置,路由的方式來匹配資料報將比netfilter的方式高效,因為它使用了hash/trie這類高效的資料結構,而不是像netfilter那樣遍歷好幾層的鍊錶。

事實上,這種思想很新嗎?不!路由項不是有unreachable或者blackhole嗎?它們不正是iptables的reject和drop麼?

實現乙個做雙向NAT的虛擬網絡卡

還是老問題。linux系統中通過iptables配置的nat無法在雙向通訊環境中使用,你無法配置一條nat規則實現對兩個方向主動發起的流量做nat,解決問題的方案有好幾種 iptables的nat配置本身就是先match再執行乙個target,因此一條規則僅僅能表示一種轉換策略,要想實現 來自x的資...

linux增加乙個虛擬網絡卡

linux虛擬網絡卡配置 作用 一塊真實網絡卡配置多個ip引數,可同時和多個網路通訊 實現步驟 1.cd etc sysconfig network scripts 進入網絡卡配置檔案所在目錄 2.cp ifcfg eth0 ifcfg eth0 0 複製真實網絡卡配置檔案為第一塊虛擬網絡卡配置檔案...

linux增加乙個虛擬網絡卡

linux虛擬網絡卡配置 作用 一塊真實網絡卡配置多個ip引數,可同時和多個網路通訊 實現步驟 1.cd etc sysconfig network scripts 進入網絡卡配置檔案所在目錄 2.cp ifcfg eth0 ifcfg eth0 0 複製真實網絡卡配置檔案為第一塊虛擬網絡卡配置檔案...