自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

天天講路由,那 Linux 路由到底咋實現(xiàn)的?。?/h1>

系統(tǒng) Linux 商務辦公
容器是一種新的虛擬化技術,每一個容器都是一個邏輯上獨立的網(wǎng)絡環(huán)境。Linux 上提供了軟件虛擬出來的二層交換機 Bridge 可以解決同一個宿主機上多個容器之間互連的問題,但這是不夠的。二層交換無法解決容器和宿主機外部網(wǎng)絡的互通。

 

本文轉載自微信公眾號「開發(fā)內功修煉」,作者張彥飛allen。轉載本文請聯(lián)系開發(fā)內功修煉公眾號。

大家好,我是飛哥。

容器是一種新的虛擬化技術,每一個容器都是一個邏輯上獨立的網(wǎng)絡環(huán)境。Linux 上提供了軟件虛擬出來的二層交換機 Bridge 可以解決同一個宿主機上多個容器之間互連的問題,但這是不夠的。二層交換無法解決容器和宿主機外部網(wǎng)絡的互通。

容器肯定是需要和宿主機以外的外部網(wǎng)絡互通才具備實用價值的。比如在 Kubernets 中,就要求所有的 pod 之間都可以互通。相當于在原先物理機所組成的網(wǎng)絡之上,要再建一個互通的虛擬網(wǎng)絡出來。這就是 Overlay 網(wǎng)絡的概念,用一個簡單的示例圖表示如下。

回想在傳統(tǒng)物理物理網(wǎng)絡中,不同子網(wǎng)之間的服務器是如何互聯(lián)起來的呢,沒錯,就是在三層工作的路由器,也叫網(wǎng)關。路由器使得數(shù)據(jù)包可以從一個子網(wǎng)中傳輸?shù)搅硪粋€子網(wǎng)中,進而實現(xiàn)更大范圍的網(wǎng)絡互通。如下圖所示,一臺路由器將 192.168.0.x 和 192.168.1.x 兩個子網(wǎng)連接了起來。

在容器虛擬化網(wǎng)絡中,自然也需要這么一個角色,將容器和宿主機以外的網(wǎng)絡連接起來。其實 Linux 天生就具備路由的功能,只是在云原生時代,它的路由功能再一次找到了用武之地。在容器和外部網(wǎng)絡通信的過程中,Linux 就又承擔起路由器的角色,實現(xiàn)容器數(shù)據(jù)包的正確轉發(fā)和投遞。

在各種基于容器的云原生技術盛行的今天,再次回頭深刻理解路由工作原理顯得非常有必要,而且也非常的有價值。今天,我們就再來強化一下 Linux 上的路由知識!

一、什么時候需要路由

先來聊聊 Linux 在什么情況下需要路由過程。其實在發(fā)送數(shù)據(jù)時和接收數(shù)據(jù)時都會涉及到路由選擇,為什么?我們挨個來看。

1.1 發(fā)送數(shù)據(jù)時選路

Linux 之所以在發(fā)送數(shù)據(jù)包的時候需要進行路由選擇,這是因為服務器上是可能會有多張網(wǎng)卡設備存在的。數(shù)據(jù)包在發(fā)送的時候,一路通過用戶態(tài)、TCP 層到了 IP 層的時候,就要進行路由選擇,以決定使用哪張網(wǎng)卡設備把數(shù)據(jù)包送出去。詳細過程參見25 張圖,一萬字,拆解 Linux 網(wǎng)絡包發(fā)送過程

來大致過一下路由相關源碼源碼。網(wǎng)絡層發(fā)送的入口函數(shù)是 ip_queue_xmit。

  1. //file: net/ipv4/ip_output.c 
  2. int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl) 
  3.  // 路由選擇過程 
  4.  // 選擇完后記錄路由信息到 skb 上 
  5.  rt = (struct rtable *)__sk_dst_check(sk, 0); 
  6.  if (rt == NULL) { 
  7.   // 沒有緩存則查找路由項 
  8.   rt = ip_route_output_ports(...); 
  9.   sk_setup_caps(sk, &rt->dst); 
  10.  } 
  11.  skb_dst_set_noref(skb, &rt->dst); 
  12.  ... 
  13.  //發(fā)送 
  14.  ip_local_out(skb); 

在 ip_queue_xmit 里我們開頭就看到了路由項查找, ip_route_output_ports 這個函數(shù)中完成路由選擇。路由選擇就是到路由表中進行匹配,然后決定使用哪個網(wǎng)卡發(fā)送出去。

Linux 中最多可以有 255 張路由表,其中默認情況下有 local 和 main 兩張。使用 ip 命令可以查看路由表的具體配置。拿 local 路由表來舉例。

  1. #ip route list table local 
  2. local 10.143.x.y dev eth0 proto kernel scope host src 10.143.x.y 
  3. local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 

1.2 接收數(shù)據(jù)時選路

沒錯,接收數(shù)據(jù)包的時候也需要進行路由選擇。這是因為 Linux 可能會像路由器一樣工作,將收到的數(shù)據(jù)包通過合適的網(wǎng)卡將其轉發(fā)出去。

Linux 在 IP 層的接收入口 ip_rcv 執(zhí)行后調用到 ip_rcv_finish。在這里展開路由選擇。如果發(fā)現(xiàn)確實就是本設備的網(wǎng)絡包,那么就通過 ip_local_deliver 送到更上層的 TCP 層進行處理。

如果路由后發(fā)現(xiàn)非本設備的網(wǎng)絡包,那就進入到 ip_forward 進行轉發(fā),最后通過 ip_output 發(fā)送出去。

具體的代碼如下。

  1. //file: net/ipv4/ip_input.c 
  2. static int ip_rcv_finish(struct sk_buff *skb){ 
  3.     ... 
  4.     if (!skb_dst(skb)) { 
  5.         int err = ip_route_input_noref(skb, iph->daddr, iph->saddr, 
  6.                            iph->tos, skb->dev); 
  7.         ... 
  8.     } 
  9.     ... 
  10.     return dst_input(skb); 

其中 ip_route_input_noref 就是在進行路由查找。

  1. //file: net/ipv4/route.c 
  2. int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr, 
  3.     u8 tos, struct net_device *dev) 
  4.  ... 
  5.  res = ip_route_input_slow(skb, daddr, saddr, tos, dev); 
  6.  return res; 

這里記著 ip_route_input_slow 就行了,后面我們再看。

1.3 linux 路由小結

路由在內核協(xié)議棧中的位置可以用如下一張圖來表示。

網(wǎng)絡包在發(fā)送的時候,需要從本機的多個網(wǎng)卡設備中選擇一個合適的發(fā)送出去。網(wǎng)絡包在接收的時候,也需要進行路由選擇,如果是屬于本設備的包就往上層送到網(wǎng)絡層、傳輸層直到 socket 的接收緩存區(qū)中。如果不是本設備上的包,就選擇合適的設備將其轉發(fā)出去。

二、Linux 的路由實現(xiàn)

2.1 路由表

路由表(routing table)在內核源碼中的另外一個叫法是轉發(fā)信息庫(Forwarding Information Base,F(xiàn)IB)。所以你在源碼中看到的 fib 開頭的定義基本上就是和路由表相關的功能。

其中路由表本身是用 struct fib_table 來表示的。

  1. struct fib_table { 
  2.  struct hlist_node tb_hlist; 
  3.  u32   tb_id; 
  4.  int   tb_default; 
  5.  int   tb_num_default; 
  6.  unsigned long  tb_data[0]; 
  7. }; 

所有的路由表都通過一個 hash - fib_table_hash 來組織和管理。它是放在網(wǎng)絡命名空間 net 下的。這也就說明每個命名空間都有自己獨立的路由表。

  1. //file:include/net/net_namespace.h 
  2. struct net { 
  3.  struct netns_ipv4 ipv4; 
  4.  ... 
  5.  
  6. //file: include/net/netns/ipv4.h 
  7. struct netns_ipv4 { 
  8.  // 所有路由表  
  9.  struct hlist_head *fib_table_hash; 
  10.  
  11.  // netfilter 
  12.  ... 

在默認情況下,Linux 只有 local 和 main 兩個路由表。如果內核編譯時支持策略路由,那么管理員最多可以配置 255 個獨立的路由表。

如果你的服務器上創(chuàng)建了多個網(wǎng)絡命名空間的話,那么就會存在多套路由表。以除了默認命名網(wǎng)絡空間外,又創(chuàng)了了一個新網(wǎng)絡命名空間的情況為例,路由表在整個內核數(shù)據(jù)結構中的關聯(lián)關系總結如下圖所示。

2.2 路由查找

在上面的小節(jié)中我們看到,發(fā)送過程調用 ip_route_output_ports 來查找路由,接收過程調用 ip_route_input_slow 來查找。但其實這兩個函數(shù)都又最終會調用到 fib_lookup 這個核心函數(shù),源碼如下。

  1. //file: net/ipv4/route.c 
  2. struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4) 
  3.  ... 
  4.  // 進入 fib_lookup 
  5.  if (fib_lookup(net, fl4, &res)) { 
  6.  } 
  7.  
  8. //file: net/ipv4/route.c 
  9. static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, 
  10.           u8 tos, struct net_device *dev) 
  11.  ... 
  12.  // 進入 fib_lookup 
  13.  err = fib_lookup(net, &fl4, &res); 

我們來看下 fib_loopup 都干了啥。為了容易理解,我們只看一下不支持多路由表版本的 fib_lookup。

  1. //file: include/net/ip_fib.h 
  2. static inline int fib_lookup(struct net *net, const struct flowi4 *flp, 
  3.         struct fib_result *res) 
  4.  struct fib_table *table
  5.  
  6.  table = fib_get_table(net, RT_TABLE_LOCAL); 
  7.  if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF)) 
  8.   return 0; 
  9.  
  10.  table = fib_get_table(net, RT_TABLE_MAIN); 
  11.  if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF)) 
  12.   return 0; 
  13.  return -ENETUNREACH; 

這個函數(shù)就是依次到 local 和 main 表中進行匹配,匹配到后就返回,不會繼續(xù)往下匹配。從上面可以看到 local 表的優(yōu)先級要高于 main 表,如果 local 表中找到了規(guī)則,則路由過程就結束了。

這也就是很多同學說為什么 ping 本機的時候在 eth0 上抓不到包的根本原因。所有命中 local 表的包都會被送往 loopback 設置,不會過 eth0。

三、路由的使用方法

3.1 開啟轉發(fā)路由

在默認情況下,Linux 上的轉發(fā)功能是關閉的,這時候 Linux 發(fā)現(xiàn)收到的網(wǎng)絡包不屬于自己就會將其丟棄。

但在某些場景下,例如對于容器網(wǎng)絡來說,Linux 需要轉發(fā)本機上其它網(wǎng)絡命名空間中過來的數(shù)據(jù)包,需要手工開啟轉發(fā)。如下這兩種方法都可以。

  1. # sysctl -w net.ipv4.ip_forward=1 
  2. # sysctl net.ipv4.conf.all.forwarding=1 

開啟后,Linux 就能像路由器一樣對不屬于本機(嚴格地說是本網(wǎng)絡命名空間)的 IP 數(shù)據(jù)包進行路由轉發(fā)了。

3.2 查看路由表

在默認情況下,Linux 只有 local 和 main 兩個路由表。如果內核編譯時支持策略路由,那么管理員最多可以配置 255 個獨立的路由表。在 centos 上可以通過以下方式查看是否開啟了 CONFIG_IP_MULTIPLE_TABLES 多路由表支持。

  1. # cat /boot/config-3.10.0-693.el7.x86_64  
  2. CONFIG_IP_MULTIPLE_TABLES=y 
  3. ... 

所有的路由表按照從 0 - 255 進行編號,每個編號都有一個別名。編號和別名的對應關系在 /etc/iproute2/rt_tables 這個文件里可以查到。

  1. # cat /etc/iproute2/rt_tables 
  2. 255     local 
  3. 254     main 
  4. 253     default 
  5. 0       unspec 
  6. 200     eth0_table 

查看某個路由表的配置,通過使用 ip route list table {表名} 來查看。

  1. #ip route list table local 
  2. local 10.143.x.y dev eth0 proto kernel scope host src 10.143.x.y 
  3. local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 

如果是查看 main 路由表,也可以直接使用 route 命令

  1. # route -n 
  2. Kernel IP routing table 
  3. Destination     Gateway         Genmask         Flags Metric Ref    Use Iface 
  4. 10.0.0.0        10.*.*.254      255.0.0.0       UG    0      0        0 eth0 
  5. 10.*.*.0        0.0.0.0         255.255.248.0   U     0      0        0 eth0 

上面字段中的含義如下

  • Destination:目的地址,可以是一個具體的 IP,也可以是一個網(wǎng)段,和 Genmask 一起表示。
  • Gateway:網(wǎng)關地址,如果是 0.0.0.0 表示不需要經(jīng)過網(wǎng)關。
  • Flags: U 表示有效,G 表示連接路由,H 這條規(guī)則是主機路由,而不是網(wǎng)絡路由。
  • Iface:網(wǎng)卡設備,使用哪個網(wǎng)卡將包送過去。

上述結果中輸出的第一條路由規(guī)則表示這臺機器下,確切地說這個網(wǎng)絡環(huán)境下,所有目標為 10.0.0.0/8(Genmask 255.0.0.0 表示前 8 位為子網(wǎng)掩碼) 網(wǎng)段的網(wǎng)絡包都要通過 eth0 設備送到 10...254 這個網(wǎng)關,由它再幫助轉發(fā)。

第二條路由規(guī)則表示,如果目的地址是 10...0/21(Genmask 255.255.248.0 表示前 21 位為子網(wǎng)掩碼)則直接通過 eth0 發(fā)出即可,不需要經(jīng)過網(wǎng)關就可通信。

3.3 修改路由表

默認的 local 路由表是內核根據(jù)當前機器的網(wǎng)卡設備配置自動生成的,不需要手工維護。對于main 的路由表配置我們一般只需要使用 route add 命令就可以了,刪除使用 route del。

修改主機路由

  1. # route add -host 192.168.0.100 dev eth0 //直連不用網(wǎng)關 
  2. # route add -host 192.168.1.100 dev eth0 gw 192.168.0.254 //下一跳網(wǎng)關 

修改網(wǎng)絡路由

  1. # route add -net 192.168.1.0/24 dev eth0 //直連不用網(wǎng)關 
  2. # route add -net 192.168.1.0/24 dev eth0 gw 10.162.132.110 //下一跳網(wǎng)關 

也可以指定一條默認規(guī)則,不命中其它規(guī)則的時候會執(zhí)行到這條。

  1. # route add default gw 192.168.0.1 eth0 

對于其它編號的路由表想要修改的話,就需要使用 ip route 命令了。這里不過多展開,只用 main 表舉一個例子,有更多使用需求的同學請自行搜索。

  1. # ip route add 192.168.5.0/24 via 10.*.*.110 dev eth0 table main 

3.4 路由規(guī)則測試

在配置了一系列路由規(guī)則后,為了快速校驗是否符合預期,可以通過 ip route get 命令來確認。

  1. # ip route get 192.168.2.25 
  2. 192.168.2.25 via 10.*.*.110 dev eth0 src 10.*.*.161 
  3.     cache 

本文總結

在現(xiàn)如今各種網(wǎng)絡虛擬化技術里,到處都能看著對路由功能的靈活應用。所以我們今天專門深入研究了一下 Linux 路由工作原理。

在 Linux 內核中,對于發(fā)送過程和接收過程都會涉及路由選擇,其中接收過程的路由選擇是為了判斷是該本地接收還是將它轉發(fā)出去。

 

默認有 local 和 main 兩個路由表,不過如果安裝的 linux 開啟了 CONFIG_IP_MULTIPLE_TABLES 選項的話,最多能支持 255 張路由表。

路由選擇過程其實不復雜,就是根據(jù)各個路由表的配置找到合適的網(wǎng)卡設備,以及下一跳的地址,然后把包轉發(fā)出去就算是完事。

通過合適地配置路由規(guī)則,容器中的網(wǎng)絡環(huán)境和外部的通信不再是難事。通過大量地干預路由規(guī)則就可以實現(xiàn)虛擬網(wǎng)絡互通。

 

好了,今天的分享就到這里了,期待你的點贊、再看和轉發(fā)~~

 

責任編輯:武曉燕 來源: 開發(fā)內功修煉
相關推薦

2010-08-05 13:11:52

2018-06-04 16:20:56

Linux動態(tài)路由Quagga

2011-09-05 14:07:07

linux系統(tǒng)QOS限速路由

2011-04-01 12:37:19

路由

2011-04-01 13:28:37

2010-06-10 16:20:37

BGP路由協(xié)議

2009-12-02 12:52:00

華為路由器命令

2011-04-01 13:55:58

路由路由器路由表

2013-12-16 14:41:45

2011-04-01 13:32:27

路由路由器

2011-04-01 13:01:57

路由表路由器

2009-11-18 09:22:43

linux路由功能

2014-06-16 16:37:36

linux路由表

2011-04-01 14:15:41

路由路由器路由接口

2019-09-10 13:58:57

動態(tài)路由路由器網(wǎng)絡

2010-08-13 09:08:11

CISCO配置

2009-11-11 16:56:46

VRRP路由技術

2011-08-29 14:45:15

路由器ADSL貓雙路由連接

2010-01-13 11:05:24

2009-10-27 14:31:57

linux靜態(tài)路由
點贊
收藏

51CTO技術棧公眾號