(2) LVS負載均衡:VS_TUN和VS_DR的arp問題
1. ARP協議簡介
ARP(Address Resolution Protocol)協議稱為地址解析協議,用於將主機IP地址解析為主機的MAC地址,即IP-->MAC
之間一一映射。
RARP協議相反,是將MAC地址解析為IP地址,MAC——>IP
ARP解析時分兩種情況:
- 解析目標和自己在同一網段。
A解析同網段的B,A根據自己的IP和子網掩碼判斷B和自己同網段,這時A就直接在這個網段上發一個ARP廣播包尋求B的MAC地址,所有人都收到廣播信息,但是B會將MAC地址回應給A,A緩存B的MAC地址。 - 解析目標和自己不在同一網段。
A根據自己的IP和子網掩碼判斷出B和自己不在同一個網段,這時A就向自己的網段發送一個ARP廣播包用來解析網關的MAC地址,也就是路由器的接口MAC地址,然後路由器回應,A緩存回應的MAC結果
當發送ARP請求廣播後,目標設備會進行應答,其中請求數據包和應答數據包的格式非常接近。以下是請求包和應答包數據格式的一部分,完整格式請百度或者翻閱TCP/IP協議卷(一)。
其中:
- op字段是一個1-4的值,1表示該數據幀是ARP請求包,2表示該數據幀是ARP應答包,3和4則表示RARP的請求和應答包。
- src_MAC和src_IP是數據幀中的源MAC和源IP地址。這兩個字段的值不一定是對應的,意思是src_IP不一定配置在src_MAC地址的接口上。
- dest_MAC和dest_IP則是目標MAC地址和目標IP地址。對於ARP請求包,dest_MAC值為"ff:ff:ff:ff:ff:ff",表示這是廣播包。同樣,這兩個字段的值也並非是對應的
當發送ARP請求廣播包時,op的值設置為1,目標MAC設置為廣播地址"ff:ff:ff:ff:ff:ff",然後在局域網內廣播,這是在詢問"who has DEST_IP"。每臺主機都能收到該廣播包,但只有設置了目標IP的主機才會應答:"Reply DEST_IP is-at DEST_MAC"。應答時使用單播包進行回應,會將op值改為2,表示這是應答包,同時將應答的MAC地址替換原來的"ff:ff:ff:ff:ff:ff",並將src和dest的字段位置進行調換。如下圖:
當響應者接收到請求者的ARP請求時,它會將請求包中的源MAC和源IP緩存到ARP緩存表中。
當請求者接收到響應者的應答包時,它會將應答包中的源MAC地址和源IP地址緩存到ARP緩存表中。也就是說,一次arp請求,會讓兩端主機都緩存對方的IP和MAC地址
使用ping命令或其他TCP連接時,兩端都會緩存對方的ARP條目。但為了測試,可以手動使用arping命令發送一個自定義源MAC和源IP的arp請求讓對方緩存自己的IP和MAC。
假如主機A上有eth0(192.168.100.54)和eth1兩網卡,主機B有eth0(192.168.100.70)。
下面的命令表示,在主機A上向主機B發送一個arp廣播包(如果"-c N"的N大於1,則只有第一個請求包是廣播,其他是單播),其中源MAC為eth1網卡的MAC,但源IP為eth0上的IP地址192.168.100.54。這會使得主機B緩存的arp條目為192.168.100.54<-->eth1_MAC
,但實際上這並非正確的映射關系。
arping -c 1 -I eth1 -s 192.168.100.54 192.168.100.70
有些程序可以檢測到IP地址沖突的現象。典型的如DHCP服務器準備提供IP地址給客戶端之前,會發送一個arp廣播,以便確認該IP地址是否已被其他主機使用(例如其他主機使用靜態IP時手動輸入了該IP)。如果沒有收到回應,則表示該IP地址沒有被使用,可以提供給客戶端使用,如果收到了回應,則表示該IP地址已經被使用了,DHCP會從IP池中換一個IP提供給客戶端。
arp -a(或arp -e)
命令可以顯示arp緩存表的內容,arp -d ADDR
可以刪除ARP緩存表中某條ARP記錄,這兩命令對於Windows和Linux系統都可用。
此外,
對於Windows,arp -d *
表示刪除arp緩存表中的所有記錄,
對於Linux,則使用ip neigh flush all
命令來刪除arp緩存中所有記錄。
註意,無論是arp請求還是arp應答,都帶有完整的源MAC、源IP、目標MAC和目標IP。這看似一句廢話,但不熟知arp請求的人很容易因此而陷入困惑。
2. arp_ignore和arp_announce變量的作用分析
在設置VS/TUN和VS/DR時,都需要設置到這兩個arp相關的內核變量,所以這裏先解釋解釋。
前文已經說明了arp請求包或應答包中,MAC地址可以和IP地址不對應。
這樣一來就出現問題了,
ARP請求包中,使用哪個源IP地址以及哪個源MAC地址?
ARP應答包中,使用哪個源MAC和源IP地址(註意,應答包中源IP地址並不一定是請求包中的目標IP,可能會更換為本機的其他IP地址)?
arp_ignore和arp_announce這兩個變量的作用正是設置使用哪個源IP和哪個源MAC
arp_ignore設置的是收到請求包後,在應答包中將本機的哪個IP地址和MAC地址回應給請求者以供緩存;
arp_announce設置的是發出請求包時,選擇哪個IP和MAC地址供響應者緩存,這也符合announce的字面意思:向外通告本機的哪個IP地址和MAC地址供其他主機緩存。
它們的作用如下圖所示:
再來細看arp_ignore和arp_announce的介紹。
2.1 arp_ignore
arp_ignore - INTEGER
Define different modes for sending replies in response to
received ARP requests that resolve local target IP addresses:
0 - (default): reply for any local target IP address, configured
on any interface
1 - reply only if the target IP address is local address
configured on the incoming interface
2 - reply only if the target IP address is local address
configured on the incoming interface and both with the
sender‘s IP address are part from same subnet on this interface
3 - do not reply for local addresses configured with scope host,
only resolutions for global and link addresses are replied
4-7 - reserved
8 - do not reply for all local addresses
The max value from conf/{all,interface}/arp_ignore is used
when ARP request is received on the {interface}
大致翻譯一下:
該變量接受一個整數值。定義的是當本機接收到別的主機發送的ARP請求時的不同應答模式:回應哪個IP和MAC地址給請求者。
- 0 - (default):將本機所有非lo接口的IP地址都回應出去。
- 1 - 只有當ARP請求中的目標IP地址配置在流入接口上時才響應。
- 2 - 只有當ARP請求中的目標IP地址配置在流入接口上時,且該IP地址(即發送者源地址)是接口地址上的子網地址時才響應(例如192.168.100.10/24和192.168.100.10/16的關系)。
- 3 - do not reply for local addresses configured with scope host, only resolutions for global and link addresses are replied。
- 4-7 - 保留
- 8 - 不回應任意地址。
- 當某接口接收到ARP請求時,將取conf/{all,interface}/arp_ignore中值最大的生效。
註意:這裏的是否響應,指的不是是否構建響應,而是路由決策後是否讓構建好的響應包出去。換句話說,arp響應包是一定會構建的,但是構建好的響應包根據arp_ignore設置的值不同,不一定能出的去。可見稍後arp_ignore=1的示例分析。
舉個例子來解釋值為0和1的情況。
(1)arp_ignore=0時,當主機收到arp請求時,會將本機任意可能的IP地址(lo除外)都應答給arp請求者。
例如,主機A有3個網卡,eth0(192.168.100.17/24),eth1(192.168.100.36/24)和eth2(172.16.10.10/16),主機B有網卡eth0(192.168.100.39)。當主機B發起對192.168.100.36(eth1)或17(eth0)的ping時,由於主機A回應icmp響應給主機B時,默認情況下有以下路由:
[root@xuexi ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.100.2 0.0.0.0 UG 100 0 0 eth0
0.0.0.0 192.168.100.2 0.0.0.0 UG 101 0 0 eth1
0.0.0.0 192.168.100.2 0.0.0.0 UG 102 0 0 eth2 這三條是默認路由
172.16.0.0 0.0.0.0 255.255.0.0 U 100 0 0 eth2
192.168.100.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.100.0 0.0.0.0 255.255.255.0 U 101 0 0 eth1
192.168.100.2 0.0.0.0 255.255.255.255 UH 100 0 0 eth2
這時主機A會將eth0和eth1上的IP地址都應答給主機B,且這兩個IP地址對應的MAC地址都是eth0(因為該接口是該網段的第一條路由出口)的MAC地址,因為無論請求的是哪個目標IP,構建的arp響應都能從eth0出去,而eth0的arp_ignore=0,允許任意目標的響應包出去。
以下是主機B上ping 192.168.100.36後的arp緩存表,如果結果不同,請 ip neigh flush all 清空緩存表或者多等待一段時間再測試。
[root@xuexi ~]# ip n s 192.168.100.36 dev eth0 lladdr 00:0c:29:fb:dd:04 REACHABLE 192.168.100.1 dev eth0 lladdr 00:50:56:c0:00:08 REACHABLE 192.168.100.17 dev eth0 lladdr 00:0c:29:fb:dd:04 STALE
如果將主機A上192.168.100.0 eth0的路由條目刪除,則主機B ping 192.168.100.36時,主機A將不再把eth0(192.168.100.17)響應給主機B,盡管它可以使用eth1對應的路由出去。但如果主機B ping 192.168.100.17,那麽也會將該地址響應出去,使用的源MAC地址也是eth1(因為路由出口為eth1)。因此,主機B只會緩存主機A的192.168.100.36 eth1_MAC。
由此可知,所謂響應任意可能的IP地址並不是響應所有地址,lo接口、非同一網段地址以及無第一路由的接口地址就不會主動響應出去。同樣,那些定義在接口上的別名地址也默認不會響應出去,因為它們的數據的流入流出都是通過它所依附的接口。
(2)arp_ignore=1時,當主機收到arp請求時,只有arp請求包中目標IP和流入接口上的IP相同時,才會響應該IP以及該接口的MAC。
例如,設置eth0網卡的該變量值為1。不過,為了完善測試,先將上面示例中刪除的路由條目添加回來。
route add -net 192.168.100.0/24 dev eth0
echo 1 >/proc/sys/net/ipv4/conf/eth0/arp_ignore
這時主機B上ping 192.168.100.17(eth0)時,主機A將只會響應eth0_MAC給主機B,且ping 192.168.100.36時將ping不通(這一點需要註意,在後面配置LVS的arp參數時,外界主機往往只能ping通同網段的其中一個地址)。以下是在主機B上ping 192.168.100.36(eth1)時在主機A上抓取的數據包。
[root@xuexi ~]# tcpdump -nn host 192.168.100.39 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 12:21:51.739967 ARP, Request who-has 192.168.100.36 tell 192.168.100.39, length 46 12:21:52.739741 ARP, Request who-has 192.168.100.36 tell 192.168.100.39, length 46 12:21:53.739868 ARP, Request who-has 192.168.100.36 tell 192.168.100.39, length 46 12:21:55.742680 ARP, Request who-has 192.168.100.36 tell 192.168.100.39, length 46 12:21:56.743560 ARP, Request who-has 192.168.100.36 tell 192.168.100.39, length 46 12:21:57.743086 ARP, Request who-has 192.168.100.36 tell 192.168.100.39, length 46 ^C 6 packets captured 6 packets received by filter 0 packets dropped by kernel
從結果中可以看出,主機A收到目標IP為192.168.100.36的ARP請求後,並沒有響應給主機B。因為主機B發出的ARP請求包到達主機A時的流入接口為eth1,構建arp響應包後,經過路由決策從eth0出去,而eth0上的arp_ignore=1,這個arp響應包將響應出不去,主機B也就不知道192.168.100.36的MAC地址,從而無法與A主機通信。
這裏的關鍵流入接口是eth1,但arp響應包的流出接口是eth0,eth0的arp_ignore=1意味著只有從eth0流入的arp請求,構建的arp響應包才允許出去。盡管ping的目標是eth0同網段的eth1:192.168.100.36。也就是說,只要eth0的arp_ignore=1,那麽該主機上該網段的所有地址(除了配置在eth0上的IP)都無法與外界通信。
此時,如果將eth0的路由條目刪除,或者將eth0的路由優先級設置的比eth1的路由優先級更低(例如將eth0路由的metric的值設置的比eth1的metric更大),那麽arp響應包的流出接口將是eth1,這時主機B ping主機A的eth0_IP或eth1_IP都能通,且arp緩存表中,這兩個地址對應的MAC地址都是eth1_MAC。
再繼續,如果沒有eth0的路由,或者eth0的路由優先級比eth1低,且還設置了eth1的arp_ignore=1,此時主機B將只能ping通eth1_IP,因為ping eth0_IP的arp請求從eth0而不是eth1流入,但它的響應包經過路由決策後卻要從eth1出去,eth1的arp_ignore=1不會允許這樣的arp響應包出去。
因此在arp請求過程中,目標主機路由表中路由的先後順序非常重要,它不僅決定了數據從哪流出,更深層面上還決定了流出時使用哪個MAC地址,而這直接決定是否能成功ARP請求、ARP響應以及arp緩存的 IP<-->MAC 映射結果。如果你在學習arp_ignore和arp_announce時做了很多測試,必然能體會到這一點。
2.2 arp_announce
arp_announce - INTEGER
Define different restriction levels for announcing the local
source IP address from IP packets in ARP requests sent on
interface:
0 - (default) Use any local address, configured on any interface
1 - Try to avoid local addresses that are not in the target‘s
subnet for this interface. This mode is useful when target
hosts reachable via this interface require the source IP
address in ARP requests to be part of their logical network
configured on the receiving interface. When we generate the
request we will check all our subnets that include the
target IP and will preserve the source address if it is from
such subnet. If there is no such subnet we select source
address according to the rules for level 2.
2 - Always use the best local address for this target.
In this mode we ignore the source address in the IP packet
and try to select local address that we prefer for talks with
the target host. Such local address is selected by looking
for primary IP addresses on all our subnets on the outgoing
interface that include the target IP address. If no suitable
local address is found we select the first local address
we have on the outgoing interface or on all other interfaces,
with the hope we will receive reply for our request and
even sometimes no matter the source IP address we announce.
The max value from conf/{all,interface}/arp_announce is used.
Increasing the restriction level gives more chance for
receiving answer from the resolved target while decreasing
the level announces more valid sender‘s information.
大致翻譯一下:該變量接受一個整數值。它定義的是當發送ARP請求時,在請求數據包中填入的源IP地址和源MAC地址,它們是被響應者緩存的內容。
- 0 - (default)可以使用本機上任意接口的任意地 址。
-
1 - 盡量不使用和目標IP不在同一子網的地址。當目標主機可以通過該接口達到,但要求ARP數據包中的源IP地址是邏輯網絡接口網段中的地址時,設置為該級別很有用。當生成ARP請求數據包時,將檢測所有包含目標IP的子網(自身網段或子網都可以),如果源IP地址處於該子網內,則使用該地址。如果沒有包含該源IP地址的子網,則使用級別2(arp_announce=2)來處理。
-
2 - 總是為目標地址尋找最佳本地地址作為ARP請求的源IP地址。這種模式下,將忽略源IP地址,而是嘗試選擇出能和目標IP最佳通信質量的IP地址。這個IP是通過尋找各流出接口上的主IP地址(primary IP,不能是secondary IP)得到的,它需要和目標IP地址在同網段或屬於其子網內。如果沒有選出合適的地址,將選擇第一個流出接口上的IP地址,這樣不僅可以接收到應答包,還能無視已經手動通告的源地址。
稍微解釋下:
arp_announce=0
時,向外發送ARP請求時,很可能會使用流出接口的IP地址和MAC地址,這沒有硬性限制。arp_announce=1
時,盡量使用與目標IP地址在同一子網的地址,例如目標IP地址為192.168.100.40/16,而本機有IP地址192.168.100.22/24,這個IP地址是目標IP地址子網內的一個地址,因此會盡量使用該地址作為ARP請求中的源IP地址,但是源MAC地址還是數據流出接口上的MAC。arp_announce=2
時,不管ARP請求包中指定的源IP地址是什麽(因為ARP請求包中的源IP和源MAC可以手動指定),總會在本地搜索出和目標IP最匹配的IP地址來作為源地址。它會優先選和目標IP同子網的本地IP,如果沒有則選路由表中的第一個流出接口上的IP。
例如,如果Linux主機有3個網卡:eth0(IP0)、eth1(IP1)和eth2(IP2)。如果想通過eth2接口流出源地址為IP0的ARP請求廣播包,默認情況下是行不通的。因為默認情況下,使用eth2流出ARP請求的源IP地址必須使用IP2。因此必須設置arp_announce=1或2,其實設置為1時也只是有機會流出,因為它要判斷IP0和目標IP地址是否存在子網所屬關系。只有設置arp_announce=2才必然能流出,但這時該Linux主機向外通告的IP地址將不是IP0,而是IP2。
3.設置arp_ignore和arp_announce
Linux內核2.0.xx版本中,回環接口、回環別名接口(如lo:0,lo:1)以及回環隧道接口都不會做arp回應,對於LVS集群來說,這很方便。
但從Linux 2.2.xx開始,除了回環地址(127.0.0.0/8)和廣播地址外,其他所有地址(包括回環接口上的別名接口)都會做arp回應。因此,在這樣的內核版本下配置LVS可能會出現一些問題。
從Linux內核2.2.14開始,提供了一個接口標記"hidden"用於從ARP廣播中隱藏指定接口。
這意味著對於現在的CentOS6、CentOS7來說,雖然每個接口(包括lo接口)都可以設置這兩個變量,但這兩個變量只對能arp回應的接口才生效(如eth0,eth1等對外通信的普通接口)。也就是說,在lo接口上設置arp_ignore、arp_announce等arp參數是沒有意義的。
盡管對lo接口設置arp參數沒有意義,但為了保證lo和普通網卡、隧道設置方法的統一性,以及未來的內核可能對此做出改變,本文以及網上的文章還是對它進行了同樣的設置。
例如,設置lo接口的arp_ignore=1、arp_announce=2。
echo 1 >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo 2 >/proc/sys/net/ipv4/conf/lo/arp_announce
如果在接口上設置了別名IP,例如eth0:0,由於它們仍然使用所依附的接口流入流出數據,因此在接口上設置arp_ignore和arp_announce對別名IP同樣生效。
其實,在/proc/sys/net/ipv4/conf下,除了各網卡接口的配置目錄,還有default和all兩個目錄,這兩個目錄內關於arp參數的值只影響普通網卡,不影響lo接口,也沒有意義去影響lo接口。
[root@xuexi ~]# ls -l /proc/sys/net/ipv4/conf/
total 0
dr-xr-xr-x 1 root root 0 Feb 15 2018 all
dr-xr-xr-x 1 root root 0 Feb 15 2018 default
dr-xr-xr-x 1 root root 0 Feb 14 22:56 eth0
dr-xr-xr-x 1 root root 0 Feb 14 22:56 eth1
dr-xr-xr-x 1 root root 0 Feb 14 22:56 lo
其中:
- default目錄中的變量值為普通網絡接口提供初始化值(不影響lo接口)。這個目錄其實沒什麽用,因為每次重啟操作系統,/proc/sys下的設置都會失效,而直接設置該目錄下的值又起不到提供初始化值的作用。之所以放在conf目錄內是為了提示我們可以設置default屬性的值。例如,向sysctl.conf中追加永久設置
net.ipv4.conf.default.arp_ignore=1
,這樣每次重啟系統後各網卡接口的arp_ignore級別都是1(註意:普通網卡才生效,lo接口不受影響)。 - all目錄中的變量作用範圍是所有網卡(不包括lo接口)。
對於每個網卡來說,將比較all目錄中的變量值和網卡自身的變量值,取較大值。例如:
conf/eth0/arp_ignore值為1,conf/all/arp_ignore值為0,則對於eth0接口來說,arp_ignore=1。
conf/lo/arp_announce值為0,conf/all/arp_announce值為2,則對於lo接口來說,arp_announce=0,因為all目錄不影響lo接口。但最終,lo接口上設置的arp參數值是沒有意義的。
通常,VS/TUN和VS/DR模式下,Real Server上的VIP設置在lo的別名接口上(如lo:0上),因此應該如下設置:
echo 1 >/proc/sys/net/ipv4/conf/lo/arp_ignore
echo 1 >/proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 >/proc/sys/net/ipv4/conf/lo/arp_announce
echo 2 >/proc/sys/net/ipv4/conf/all/arp_announce
其中生效的為第二條和第四條規則,第一條和第三條對lo接口的設置語句可有可無。
將conf/all/arp_ignore設置為1,可以保證無論哪個對外通信的網卡接口都只會向外響應自己接口上的IP地址(甚至可能有些同網段的接口因為路由順序排在後面而響應不出去),這樣就隱藏了設置在lo別名接口上的VIP地址。
將conf/all/arp_announce設置為2,可以保證本機只向外通告普通網卡上的IP地址,lo別名接口上的VIP不可能被通告出去。
(2) LVS負載均衡:VS_TUN和VS_DR的arp問題