偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

認(rèn)真聊一次Iptables和Netfilter,簡單過下Istio Route

開發(fā) 架構(gòu)
本文會混用流量、數(shù)據(jù)包、網(wǎng)絡(luò)包這些名詞,準(zhǔn)確地說,它們指代的是內(nèi)核數(shù)據(jù)結(jié)構(gòu) Skb (sk_buffer)。

大家好,我是二哥。

上一篇文章本意是給大家一個新的視角來研究 istio route 的細(xì)節(jié)。不過后臺不少同學(xué)私信我說,一直沒有辦法理解 iptables ,也就不想細(xì)看那篇文章了。二哥一看就慌了,為了讓大家能安心地研究那篇文章,我就先來聊聊 iptables ,準(zhǔn)確地說我們需要聊的是 netfilter 。

理解 iptables 其實(shí)不難,難的是看懂 iptables 是如何配合協(xié)議棧處理流量的。本篇除了聊 iptables 之外,更重要的是二哥會帶大家一探協(xié)議棧和 iptables 密切配合過程。最后我以 istio route 為例來看看它是如何利用 iptables 將網(wǎng)絡(luò)包透明地劫持到了 Envoy 的 Outbound hanlder 15001 端口。

本文會混用流量、數(shù)據(jù)包、網(wǎng)絡(luò)包這些名詞,準(zhǔn)確地說,它們指代的是內(nèi)核數(shù)據(jù)結(jié)構(gòu) skb (sk_buffer)。

這是一篇長文,誠意滿滿,干貨滿滿,掉發(fā)也滿滿。如果你已經(jīng)對 iptables/netfilter 很熟悉了,那可以跳過前兩部分。

在開始之前,我們先區(qū)分兩個概念:

  • netfilter:內(nèi)核中對數(shù)據(jù)包進(jìn)行控制、修改和過濾(manipulation and filtering)的框架 。一個著名的實(shí)現(xiàn)是內(nèi)核模塊 ip_tables 。
  • iptables:客戶端命令行,用于操作(CRUD)各種規(guī)則來干預(yù)內(nèi)核協(xié)議棧的行為。

大家日常工作中,碰到直接上手操作 netfilter 的機(jī)會越來越少了。但這不表示 netfilter 不重要。實(shí)際上 netfilter 是 K8s 網(wǎng)絡(luò)的基礎(chǔ),即使在kubernetes v1.8 中引入了 ipvs 模式,ipvs 的著力點(diǎn)也是 netfilter 。不信你看下面的一段規(guī)則。ipvs 介入工作的前提是它得作為規(guī)則的一部分,讓 netfilter 框架在合適的時機(jī)點(diǎn)運(yùn)行。

iptables -t nat -A POSTROUTING -m ipvs --vaddr 10.10.0.1 --vport 8410 -j MASQUERADE

肯定有另一部分同學(xué)有疑惑:既然平時都不怎么用它了,為什么還要學(xué)習(xí) netfilter 呢?

不知道大家有沒有另外一個疑惑:既然整個小學(xué)都不可能用到微積分,為什么小學(xué)的數(shù)學(xué)教師需要學(xué)高等數(shù)學(xué)呢?這其實(shí)涉及到處理問題時的一個角度問題:如果解決一個問題只需要 3 分功力,你最好得具有 6 成內(nèi)功。只有這樣你才能俯視而不是仰視或者平視它,唯有俯視方得從容。

1、只看 iptables

文首提到 iptables 是客戶端命令,用于操作各種規(guī)則來干預(yù)內(nèi)核協(xié)議棧的行為。那它具體是如何使用的呢?

先來看命令長什么樣子的:

iptables [-t <table-name>] <command> <chain-name> <parameter-1> <option-1> <parameter-n> <option-n>

再來看一個使用示例:

iptables -t filter -I INPUT -s 1.2.3.4 -p tcp --dport 22 -j ACCEPT  iptables -t filter -I INPUT -p tcp --dport 22 -j DROP

(1)四表五鏈

圖片

圖 1:四表五鏈(橫鏈豎表,橫為鏈,豎為表) 圖片來源:公眾號“開發(fā)內(nèi)功修煉”

圖中橫向的四個框表示四表,對應(yīng)于 iptables 命令里面的 -t <table-name> 。如果不指定,那么默認(rèn)的 table 是 filter 。其實(shí)還有一個 security 表,用于在數(shù)據(jù)包上應(yīng)用 SELinux ,這張表并不常用,故本篇我們略過。

圖中第 1 列表示的五個框叫做五鏈,對應(yīng)于命令里面的 <chain-name> 。每個鏈像竹簽一樣串著不少肉串。這些肉串叫規(guī)則,它們的種類不同,且由不同的表提供。比如 mangle 表可能提供的是羊肉串,而 nat 表提供的是牛肉串,filter 表提供的是雞肉串。

這四個表的具體作用我就不細(xì)講了,大家可以到網(wǎng)上搜索出更詳細(xì)的答案。但下面這兩點(diǎn)是重點(diǎn)(重點(diǎn),重點(diǎn),重點(diǎn)),你一定要記得。

  • 這五個預(yù)置的鏈直接源自于 Netfilter 的鉤子,它們與四張規(guī)則表的關(guān)系是固定的。用戶即不能增加自定義表也不能修改圖 1 中已有的表與鏈的關(guān)系,但可以增加自定義的鏈(見下文)。
    新增的自定義鏈與 Netfilter 的鉤子沒有天然的對應(yīng)關(guān)系。自定義鏈不會被自動觸發(fā),只有顯式地使用 JUMP 行為(見后文),才能從默認(rèn)的五條鏈中跳轉(zhuǎn)過去執(zhí)行它們。
  • 每個命名空間都是有自己獨(dú)立的 iptables 規(guī)則,這當(dāng)然也包括四表五鏈。

表、鏈和規(guī)則之間的關(guān)系,一句話總結(jié)就是:規(guī)則是執(zhí)行的最小單元,鏈決定了規(guī)則被執(zhí)行的時機(jī),而表則限定了規(guī)則的類別。鏈的執(zhí)行時機(jī)詳見后文。

(2) command

命令中的 command 是啥?它是一些由大寫字母表示的動作。見圖 2 所示。比如 -A 用于將一個新規(guī)則插入到鏈上,嗯,就是把肉串插到竹簽上。每一次用 -A 這樣的 command 調(diào)用 iptables ,都會在對應(yīng)的鏈和表所形成的宮格里面插入一個新的規(guī)則。

圖片

圖 2:iptables command 列表

(3)自定義鏈

我們可以用 -N 來創(chuàng)建一個新鏈。如果不用 -t 來指定 table 的話,新建的 chain 默認(rèn)使用 filter table 。熟悉自定義 chain 的創(chuàng)建過程非常重要,因?yàn)楹笪奈覀円治龅?istio route 就自建了不少鏈。

二哥再強(qiáng)調(diào)一遍:自定義鏈不能被 netfilter 自動執(zhí)行,只有從五大入口鏈那里通過 -j target 才能跳轉(zhuǎn)到自定義鏈(例見后文)。

下面的例子里,自定義了一個 chain LANCE-OUTPUT ,可以看到它被放到了 table filter 里面。

# 自定義 chain LANCE-OUTPUT[root@xx usr]# iptables -N LANCE-OUTPUT
[root@xx usr]# iptables -L -t filterChain INPUT (policy ACCEPT)target prot opt source destination
Chain FORWARD (policy ACCEPT)target prot opt source destination
Chain OUTPUT (policy ACCEPT)target prot opt source destination
Chain LANCE-OUTPUT (0 references)target prot opt source destination

然后用 -A 來追加一個規(guī)則到這個自定義鏈里面。

# 追加一個規(guī)則到自定義鏈 LANCE-OUTPUT[root@xx usr]# iptables -A LANCE-OUTPUT -m comment --comment "kubernetes service portals"
[root@xx usr]# iptables -L -t filterChain INPUT (policy ACCEPT)target prot opt source destination
Chain FORWARD (policy ACCEPT)target prot opt source destination
Chain OUTPUT (policy ACCEPT)target prot opt source destination
Chain LANCE-OUTPUT (0 references)target prot opt source destination all -- anywhere anywhere /* kubernetes service portals */

(4)parameter / option

光有 command 還不行,它太粗獷了,得細(xì)膩、得精準(zhǔn)控制。這就需要通過 parameter 來實(shí)現(xiàn)。<parameter-1> <option-1> 里面填什么呢?看你喜歡,你有若干個選擇,比如文首示例里面的 -s 1.2.3.4 和 -p tcp --dport 22 。有一些 parameter 還提供了額外的以 -- 開頭的 額外 match option ,比如對 -p tcp ,你可以添加  --dport 22 這樣的額外 match option ,用以更精準(zhǔn)地控制要命中的規(guī)則。除了 tcp 外你還有  -p udp   -p icmp 可供選擇。

下面是可供使用的 parameter 列表。

圖片

圖 3:iptables parameter 列表

(5)額外的 match option

這就結(jié)束了嗎?不,還有大招沒有放。我們來看下面這個例子。例子很平淡,重點(diǎn)看 -m 。-m comment 表示這個規(guī)則需要加載 comment 模塊,從字面意思你大概能猜得出來它可以干啥。對,就是給這條規(guī)則加點(diǎn)注釋。通過 --comment xxx  這個 option ,你可以添加最多 265 個字符的注釋,前文在介紹用 -A 命令追加規(guī)則到自定義鏈時,從 iptables -L -t filter 的輸出里面你可以體驗(yàn)到這些注釋的作用。

iptables -A INPUT -i eth1 -m comment --comment "my local LAN"

通過 -m 我們可以調(diào)用包括 set / ipvs 在內(nèi)的各種擴(kuò)展模塊。有多少模塊可以選擇呢?多到?jīng)]朋友,不信你到這個鏈接里面去看:https://ipset.netfilter.org/iptables-extensions.man.html#lbAD 。我估計(jì)應(yīng)該有 60 個左右。

(6)跳轉(zhuǎn)到特點(diǎn)的目標(biāo) -j

我們設(shè)置的規(guī)則匹配上數(shù)據(jù)包后,總得干點(diǎn)啥是吧,不然不是白廢老大勁了么。當(dāng)然, -j  不是必填項(xiàng),但你非得說我就不想讓這個規(guī)則干具體的事情,也行!

我們可以給 -j 指定像 ACCEPT / DROP / QUEUE / RETURN 這樣的 netfilter 自帶的標(biāo)準(zhǔn) target ,也可以給它指定我們自定義的鏈,除此之外還有若干個像 SNAT / REDIRECT / SET 這樣的擴(kuò)展 target 可供我們使用。

比如下面這個例子中,就通過 -j KUBE-SERVICES 跳轉(zhuǎn)到自定義鏈 KUBE-SERVICES 去了。

-A INPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes service portals" -j KUBE-SERVICES

流量通過 -j 跳轉(zhuǎn)到指定 target 之后會發(fā)生什么?這取決于 target 會對流量做啥:

  • 比如對于 DROP target ,你也能猜出結(jié)局是什么:不但流量會丟棄了,它更加不會到達(dá)傳輸層(見后文)。
  • 而對于 KUBE-SERVICES 這樣的 target,netfilter 會去執(zhí)行這個鏈所定義的各種規(guī)則。

還記得前文我們說到的那默認(rèn)的五條鏈嗎?它們既是默認(rèn)的五條鏈更是 netfilter 施展拳腳的入口。從這些入口進(jìn)去,netfilter 可能會調(diào)用到若干個自定義鏈以及串在鏈上的多種多樣的規(guī)則。假如所有的規(guī)則都不會下流量下死手,那么這些規(guī)則執(zhí)行完后,就又回到入口處,也就是這五個默認(rèn)的鏈。

2、不能單看 iptables

其實(shí)讀懂和理解 iptables 規(guī)則并不難,難的是理解 netfilter 是如何和 TCP/IP 協(xié)議棧緊密集成和協(xié)作以控制流量的行為的。你們見過機(jī)場行李托運(yùn)輸送系統(tǒng)嗎?我們在值機(jī)口托運(yùn)的行李會穿過行李分揀大廳的各條分叉,兜兜轉(zhuǎn)轉(zhuǎn)才來到飛機(jī)貨艙里面。

無論是入口流量還是本地進(jìn)程產(chǎn)生的出口流量都如同我們在值機(jī)口托運(yùn)的行李,而 netfilter 和 TCP/IP 協(xié)議棧則扮演了那個行李分揀系統(tǒng)。

圖片

圖 4:四表五鏈與協(xié)議棧集成細(xì)節(jié)  圖片來源:公眾號“開發(fā)內(nèi)功修煉”

既然說到 netfilter 和 TCP/IP 協(xié)議棧則的緊密合作,那我們先看看協(xié)議棧部分。

圖中 ip_rcv() 是流量進(jìn)入 IP 層的入口,ip_forward() 是轉(zhuǎn)發(fā)流量的入口,而流量通過 ip_output() 離開 IP 層。當(dāng) IP 層決定要把流量送往傳輸層的時候,它通過 ip_local_deliver() 來完成,相對應(yīng)地,本地進(jìn)程想要把數(shù)據(jù)發(fā)送出去,需要借助 __ip_local_out() 。注意所有這些函數(shù)都在 IP 層。

協(xié)議棧在執(zhí)行這些不同的入口函數(shù)時,會有選擇地查看四表五鏈里面的鏈和相應(yīng)的規(guī)則并執(zhí)行這些規(guī)則。而規(guī)則里面所定義的 target 也反過來影響協(xié)議棧下一步的行為。

(1)過客和山海

從圖 4 我們可以看得出來,四表五鏈以及路由選擇其實(shí)是協(xié)議棧留出來給大家自由發(fā)揮的空間和口子。我們以圖中標(biāo)號 ④ 這一步的 __ip_local_out() 為例,看看內(nèi)核是如何與這些開口打交道的:

// net/ipv4/ip_output.cint __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb){  // 省略其它代碼  return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,        net, sk, skb, NULL, skb_dst(skb)->dev,        dst_output);}
//include/net/dst.hstatic inline int dst_output(struct net *net, struct sock *sk, struct sk_buff *skb){ return INDIRECT_CALL_INET(skb_dst(skb)->output, ip6_output, ip_output, net, sk, skb);}
// net/ipv4/ip_output.cint ip_output(struct net *net, struct sock *sk, struct sk_buff *skb){ struct net_device *dev = skb_dst(skb)->dev, *indev = skb->dev; // 省略其它代碼 return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, sk, skb, indev, dev, ip_finish_output, !(IPCB(skb)->flags & IPSKB_REROUTED));}

可以看到在這個函數(shù)的最后一步,協(xié)議棧就開始通過 nf_hook() 去遍歷 OUTPUT 鏈里面的規(guī)則了。這也是為什么我們說 OUTPUT 鏈?zhǔn)俏彐溨坏脑颉?/span>

nf_hook() 在遍歷完 OUTPUT 鏈之后,就調(diào)用 dst_output() 來送別網(wǎng)絡(luò)包。而網(wǎng)絡(luò)包從此則需獨(dú)自一人進(jìn)入下一段旅程,過一會兒它將會遇到 ip_output() ,從那里離開 IP 層。

我們也可以看得出來對于發(fā)送流程, OUTPUT 鏈只是一個過客,網(wǎng)絡(luò)包在這一站稍作停留后還是要繼續(xù)奔赴山海,在后面的旅途中它會碰到協(xié)議棧其它代碼和其它鏈,比如在 ip_output() 里面,它會遇到 POSTROUTING 鏈。

(2)PREROUTING 鏈

讓我們把圖 4 仔細(xì)看一遍。

對于 ① ,當(dāng)流量從外部進(jìn)入網(wǎng)卡,ip_rcv() 負(fù)責(zé)將其接入 IP 層,PREROUTING 鏈先于路由選擇介入對流量的處理流程。比如下面的例子里,每一個原本想訪問 8022 端口的流量的 dest IP 和 dest Port 全部都被改成 127.0.0.1:22 。

# iptables -t nat -A PREROUTING -p tcp -m tcp --dport 8022 -j DNAT --to-destination 127.0.0.1:22

dest IP 和 dest Port 全部都改成 127.0.0.1:22 ,你很容易就猜到:在接下來的路由選擇這一步,協(xié)議棧會把修改過之后的流量通過 ② 送往本地進(jìn)程。

如果 dest IP 和 dest Port 被改成了 39.156.66.10:443 呢?流量不會被送往本機(jī),而是通過 ③ 被 forward 離開本機(jī)。當(dāng)然前提是本機(jī) forward 功能已經(jīng)開啟了。

(3)INPUT 鏈

我們剛才說流量最終是通過 ip_local_deliver() 離開 IP 層并進(jìn)入傳輸層的。不過在這之前,還有一個 INPUT 鏈等著它。流量能否被傳輸層處理還得看 INPUT 鏈?zhǔn)欠裨试S。

比如對于下面這條規(guī)則,它存放在 INPUT 鏈的 filter 表里面,當(dāng)發(fā)現(xiàn)流量是 tcp 協(xié)議,且訪問的是本機(jī) 22 端口,就把流量丟棄掉,說白話就是不允許任何人通過 ssh 訪問本機(jī)。于是對于任何進(jìn)來的流量,② 這條路就算走到頭了。ip_local_deliver() 不會被執(zhí)行,sshd 也就沒有機(jī)會收到這個請求。

iptables -t filter -I INPUT -p tcp --dport 22 -j DROP

又或者如果一切都很正常,不出意外的話,位于傳輸層的函數(shù) tcp_v4_rcv() 會接收到這個可能已經(jīng)被修改過之后的流量,從此流量開始了它在傳輸層的旅程。

(4)FORWARD 鏈

當(dāng)路由選擇決定要把流量 forward 后,會調(diào)用 ip_forward() 開始后續(xù)的 forward 處理流程,這個流程如 ③ 所示。如果你喜歡,你可以在 FORWARD 鏈中加入你喜歡的規(guī)則來控制流量從命運(yùn)。比如下面的例子:

-A FORWARD -m comment --comment "kubernetes forwarding rules" -j KUBE-FORWARD-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT

(5)OUTPUT 鏈

① ② ③ 這三條數(shù)據(jù)流里面涉及到的三個鏈都是因網(wǎng)卡接收到了外部的流量引起的,它們都是被動被執(zhí)行。而 OUTPUT 鏈則是因?yàn)楸镜爻绦蛳蛑鲃影l(fā)送流量而觸發(fā)執(zhí)行流程。

當(dāng)網(wǎng)絡(luò)包從傳輸層通過 tcp_write_xmit() 來到 IP 層時,首先迎接它的還是路由選擇。這一步會產(chǎn)生兩個重要的決定:

  • next-hop 是誰,也即由誰來接收網(wǎng)絡(luò)包
  • 從本機(jī)哪個 interface 離開

你可能會困惑,不是由應(yīng)用程序?qū)懞玫?dest IP 來接收網(wǎng)絡(luò)包嗎?沒錯,不過那是最終接收者,在這中間還會有若干個設(shè)備會經(jīng)手并傳遞這個網(wǎng)絡(luò)包。這就好像你從南京快遞一個 iPhone 給遠(yuǎn)在北京的女友。當(dāng)然最終是你的女友負(fù)責(zé)接收、拆開這個包裹,但在她拿到包裹之前,有非常多的快遞站中轉(zhuǎn)站、快遞小哥也會觸碰到它。這里所說的 next-hop 就是負(fù)責(zé)收取快遞的第一個人。

如果這臺設(shè)備有多個網(wǎng)卡的話,得選擇其中一個網(wǎng)卡來將網(wǎng)絡(luò)包傳送出去。

ip_output() 負(fù)責(zé)將網(wǎng)絡(luò)包送離 IP 層,但且慢,看到 ④ 那里的 OUTPUT 鏈了嗎?是的,這次輪到它大顯身手了。我們可以在這里對包做一次 SNAT ,使得它離開本機(jī)的時候,源地址使用本機(jī)的 IP 地址。關(guān)于 OUTPUT 鏈具體的例子我們留到最后聊 istio route 的時候再細(xì)說。

3、通過 loopback 通信

問大家一個問題:現(xiàn)有下面這兩個網(wǎng)絡(luò)通信場景:

場景一:本機(jī)同一個 network namespace 下面的兩個進(jìn)程之間通過 loopback(后文簡稱 lo) 設(shè)備進(jìn)行網(wǎng)絡(luò)通信,如圖 5 所示。

場景二:一個局域網(wǎng)內(nèi),連接在同一個交換機(jī)上的兩臺主機(jī)上的兩個進(jìn)程相互進(jìn)行網(wǎng)絡(luò)通信,如圖 6 所示。

這兩個場景下,除去鏈路層設(shè)備的不同所帶來的二層收發(fā)數(shù)據(jù)的區(qū)別外,內(nèi)核協(xié)議棧對數(shù)據(jù)包的處理過程有本質(zhì)的不同嗎?從圖中你也可以看出來,無論是圖 5 還是圖 6,數(shù)據(jù)均需要走完如下的過程:

發(fā)端應(yīng)用層 -> 發(fā)端傳輸層 -> 發(fā)端網(wǎng)絡(luò)層 -> 發(fā)端鏈路層 ->(物理層數(shù)據(jù)收發(fā))-> 收端鏈路層 -> 收端網(wǎng)絡(luò)層 -> 收端傳輸層 -> 收端應(yīng)用層

對于圖 6,上述這個過程大家應(yīng)該沒有任何異議。重點(diǎn)是對于圖 5 所示的場景:即便通過 loopback 設(shè)備通信,網(wǎng)絡(luò)包還是要兩次完整地穿越協(xié)議棧。注意我這里的用詞:完整地。理解這點(diǎn)非常重要,因?yàn)楹竺嬉谩?/span>

圖片

圖 5:通信場景一:兩個進(jìn)程相互之間通過 loopback 設(shè)備通信

圖片

圖 6:通信場景二:LAN 通信

4、簡單過下 istio route

二哥需要強(qiáng)調(diào)一個重點(diǎn):圖 7 所示的包括四表五鏈、conntrack 表、供路由選擇的路由表、接口 eth0 和 loopback 在內(nèi)的信息都是 network namespace 的一部分。對于一個進(jìn)程來說,這些要素其實(shí)就構(gòu)成了它發(fā)起和響應(yīng)網(wǎng)絡(luò)請求的基本環(huán)境。在正常情況下,一個 Pod 里面所有的 container 都共享一個 network ns,也就共享著這個基本環(huán)境。

一個 network ns 如同一座圍城,圍住了所有的數(shù)據(jù)。

我們都知道 Linux 支持多個 network namespace,這也就意味著類似這樣的基本環(huán)境會有若干份。當(dāng)然,在每個基本環(huán)境里面,像四表五鏈、路由表之類的數(shù)據(jù)各有千秋。

我們可以將 TCP/IP 協(xié)議??闯墒浅绦虻拇a部分,而將上述的基本環(huán)境看成是程序的數(shù)據(jù)部分。很顯然 TCP/IP 棧應(yīng)該是被這個 OS 上所有人共享的,無論是進(jìn)程還是容器,甚至是基于 qemu-kvm 的虛擬機(jī)都共享著宿主機(jī)的協(xié)議棧,但 network ns 所圍起來的數(shù)據(jù)卻是各個 network ns 獨(dú)享的。

圖片

圖 7:Envoy 劫持網(wǎng)絡(luò)流量全景圖

下面我們將以 App container 想要訪問 www.baidu.com 443 端口為例來帶大家過一下 istio route 。

這個過程在圖 7 上來看,就是始于 App container internal logic 的,標(biāo)號為 ⑨ ~ ? ~ ? 的數(shù)據(jù)流。我們沿著箭頭的方向會發(fā)現(xiàn)網(wǎng)絡(luò)包被透明地劫持到了 Envoy 的 Outbound hanlder 15001 端口。我們重點(diǎn)分析 Envoy 是如何通過 iptables 來做到這一點(diǎn)的。

(1)相關(guān) iptables

首先我們來看下與這個流程相關(guān)的 iptables 。為節(jié)省篇幅,突出重點(diǎn),二哥省去了其余的部分,只保留了 OUTPUT 鏈及其會調(diào)用到的自定義鏈。

# View the details of the rule configuration in the NAT table.$ iptables -t nat -L -v
# OUTPUT chain: jumps all outbound packets to the ISTIO_OUTPUT chain.Chain OUTPUT (policy ACCEPT 79 packets, 6761 bytes)pkts bytes target prot opt in out source destination15 900 ISTIO_OUTPUT tcp -- any any anywhere anywhere
# POSTROUTING CHAIN: All packets must first enter the POSTROUTING chain when they leave the network card, and the kernel determines whether they need to be forwarded out according to the packet destination.Chain POSTROUTING (policy ACCEPT 79 packets, 6761 bytes)pkts bytes target prot opt in out source destination
# ISTIO_IN_REDIRECT chain: jumps all inbound traffic to the local 15006 port, thus successfully blocking traffic to the sidecar.Chain ISTIO_IN_REDIRECT (3 references)pkts bytes target prot opt in out source destination0 0 REDIRECT tcp -- any any anywhere anywhere redir ports 15006
# ISTIO_OUTPUT chain: see the details bellowChain ISTIO_OUTPUT (1 references)pkts bytes target prot opt in out source destination0 0 RETURN all -- any lo 127.0.0.6 anywhere0 0 ISTIO_IN_REDIRECT all -- any lo anywhere !localhost owner UID match 13370 0 RETURN all -- any lo anywhere anywhere ! owner UID match 133715 900 RETURN all -- any any anywhere anywhere owner UID match 13370 0 ISTIO_IN_REDIRECT all -- any lo anywhere !localhost owner GID match 13370 0 RETURN all -- any lo anywhere anywhere ! owner GID match 13370 0 RETURN all -- any any anywhere anywhere owner GID match 13370 0 RETURN all -- any any anywhere localhost0 0 ISTIO_REDIRECT all -- any any anywhere anywhere
# ISTIO_REDIRECT chain: redirects all traffic to Sidecar (i.e. local) port 15001.Chain ISTIO_REDIRECT (1 references)pkts bytes target prot opt in out source destination0 0 REDIRECT tcp -- any any anywhere anywhere redir ports 15001

首先我們看到這段輸出是用命令 iptables -t nat -L -v 得到的。你看到 -t nat 了嗎?這表示這些規(guī)則全部都存放在 nat 表里面。我相信大家對 NAT 有所耳聞,看到它也就大概猜得出來幾分:既然這些規(guī)則是與 NAT 表相關(guān)的,那么它們干的事情也就涉及到修改 IP 地址或端口這樣的操作。

我把這些規(guī)則之間的關(guān)系用圖 8 表示出來了。我們來看看它們是如何協(xié)作的。

圖片

圖 8:istio route 自定義鏈 ISTIO_OUTPUT 細(xì)節(jié)

(2)協(xié)作細(xì)節(jié)

首先當(dāng) App cotainer 訪問 baidu.com 時,請求從傳輸層出來后,首先需要經(jīng)過一次路由。這個時候協(xié)議棧也僅僅知道這個包的目的 IP(39.156.66.10) 和 目的端口(443),還不知道它的二層信息是什么。為什么呢?得經(jīng)過路由后,才能知道包需要從本地哪個接口離開,以及誰是 next-hop ,也只有當(dāng)知曉了這些信息后,才能填充二層頭的 src MAC 和 dest MAC。因?yàn)?IP 地址是與接口綁在一起的,所以從哪里接口離開也就決定了 src IP 是什么。

路由選擇細(xì)節(jié)就不細(xì)講了。我們先把它看作一個黑盒子,經(jīng)過它之后,協(xié)議棧做了一個決定:去 39.156.66.10 的話,得從接口 eth0 離開。再強(qiáng)調(diào)一次,eth0 位于 App cotainer 所在的 network namespace 里面。

按照前文所述的協(xié)議棧和 netfilter 配合流程,我們現(xiàn)在知道路由選擇后,緊接著需要執(zhí)行 OUTPUT 鏈里面的規(guī)則。

? OUTPUT 鏈?zhǔn)俏宕笕肟阪溨?,可在這里,它啥都沒干,直接把活外包給自定義鏈 ISTIO_OUTPUT 了。我們可以看到鏈 ISTIO_OUTPUT 上掛了 9 個規(guī)則。

圖片

圖 9:ISTIO_OUTPUT 鏈上的 9 個規(guī)則

規(guī)則 1,2,3,5,6 都不滿足條件,因?yàn)槲覀冃枰獜慕涌?eth0 離開,而不是 lo ,當(dāng)然這兩個接口都屬于這個 Pod 所使用的 network ns。

規(guī)則 4,7 對目的地的 owner UID 和 GID 做了限制,不符合我們的場景。

規(guī)則 8 的目的地是 localhost,而我們想要去 39.156.66.10。很抱歉,完美錯過。

最后就剩規(guī)則 9 了。這個規(guī)則非常粗礦,啥都行,碰到這個它,大家就只能全部乖乖地跳轉(zhuǎn)到自定義鏈 ISTIO_REDIRECT 了。

? 這一步表示了這樣的跳轉(zhuǎn)過程。

? 自定義鏈 ISTIO_REDIRECT 也是人狠話不多。只要傳輸層是 TCP 的流量,全部統(tǒng)一 REDIRECT 到 127.0.0.1:10051 。

Chain ISTIO_REDIRECT (1 references)pkts bytes target     prot opt in     out     source               destination  0     0 REDIRECT    tcp  --  any    any     anywhere             anywhere             redir ports 15001

我們來看看 target REDIRECT 會對流量干啥事。

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 localhost address, 127.0.0.1 for IPv4 and ::1 for IPv6).

說白了,它會把 dest IP 和 dest Port 改成 127.0.0.1:15001 。這好理解,畢竟只有這樣,在圖 8 ? 處路由的時候, IP 層才會把從 App container 出來的流量路由至 listen 在 15001 的 Outbound hanlder 去處理。

這一步做完后,從 ? 開始的 OUTPUT 鏈遍歷執(zhí)行過程就結(jié)束了。那結(jié)束之后下一步協(xié)議棧要干什么呢?

跟著 ? ? ? ? ? 走一遍,你會知道全部的答案。不過注意看 ? 那里所標(biāo)示的目的 IP 和目的端口,它們已經(jīng)被改掉了。既然目的 IP 已經(jīng)被改成了 127.0.0.1 了,那在 ? ? 那里發(fā)生的就是前文所述的通過 loopback 通信所涉及的技術(shù)細(xì)節(jié)了。

責(zé)任編輯:姜華 來源: 二哥聊云原生
相關(guān)推薦

2022-10-28 11:26:01

光纖終端盒光纖安裝

2011-03-15 10:00:01

NetfilterIPTables

2021-04-15 12:10:42

Go語言Go開發(fā)者

2010-12-07 09:51:43

Linux安全性netfilteriptables

2011-03-15 15:47:34

netfilteriptables

2023-03-05 18:40:39

iptables防火墻軟件

2011-03-15 12:47:11

netfilteriptables

2011-03-15 15:47:30

netfilteriptables安裝

2016-10-11 11:38:06

程序員

2018-01-10 14:13:04

測試矩陣API測試

2022-03-08 16:10:38

Redis事務(wù)機(jī)制

2011-03-15 15:47:26

netfilteriptables

2021-04-14 20:10:50

Netfileter Iptables 源碼

2011-03-15 09:10:43

iptables防火墻

2011-03-15 15:51:12

netfilteriptables

2023-10-07 08:17:40

公平鎖非公平鎖

2015-07-14 10:34:42

ViewModel代碼高效

2009-05-22 17:05:52

2010-07-14 09:48:14

IMAP設(shè)置

2011-03-15 10:48:47

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號