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

2021-09-07 05:53:38 字數 3102 閱讀 7242

還是老問題。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全然能夠通過乙個函式實現,此處是為了使介面更加清晰。詳細的轉換就不多說了,非常easy,改動掉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 複製真實網絡卡配置檔案為第一塊虛擬網絡卡配置檔案...