linux服務(wù)器經(jīng)常被用來提供防火墻、路由器、NAT等功能,在這些場(chǎng)景下,linux內(nèi)核需要將網(wǎng)卡上收到的報(bào)文轉(zhuǎn)發(fā)給其他網(wǎng)絡(luò)設(shè)備。linux內(nèi)核提供了ip_forward參數(shù)用于開關(guān)內(nèi)核的報(bào)文轉(zhuǎn)發(fā)功能,只有這個(gè)開關(guān)被打開時(shí),內(nèi)核才會(huì)執(zhí)行報(bào)文的轉(zhuǎn)發(fā)。網(wǎng)上能找到不少文章介紹ip_forward參數(shù)的基本用途和配置方式,但沒有什么文章具體介紹這個(gè)參數(shù)配置后具體會(huì)對(duì)內(nèi)核的網(wǎng)絡(luò)行為產(chǎn)生哪些影響,以及在內(nèi)核中是如何實(shí)現(xiàn)的。而這些問題的答案對(duì)轉(zhuǎn)發(fā)功能的使用方式會(huì)產(chǎn)生很大的影響。
本文將詳細(xì)分析ip_forward參數(shù)的配置方式和影響,以及這個(gè)參數(shù)在內(nèi)核中的實(shí)現(xiàn)邏輯。分析基于5.9.11版本的內(nèi)核。
問題
- ip_forward配置在什么位置配置?能按什么粒度配置?
- ip_forward打開或關(guān)閉會(huì)對(duì)內(nèi)核的網(wǎng)絡(luò)處理流程產(chǎn)生哪些影響?
- ip_forward配置除了影響轉(zhuǎn)發(fā)功能外,是否還會(huì)對(duì)其他網(wǎng)絡(luò)配置或功能產(chǎn)生副作用?
- ip_forward的配置開關(guān)在內(nèi)核中是如何實(shí)現(xiàn)的?
配置
ip_forward功能的配置開關(guān)有三個(gè)位置:
1. /proc/sys/net/ipv4/ip_forward
2. /proc/sys/net/ipv4/conf/{all/default/devname}/forwarding
3. /proc/sys/net/ipv6/conf/{all/default/devname}/forwarding
其中第一個(gè)配置是許多文章中提到的ip報(bào)文轉(zhuǎn)發(fā)開關(guān),這個(gè)配置開關(guān)是linux早期版本中定義的,它只能控制IPv4報(bào)文的轉(zhuǎn)發(fā)功能,其功能和取值都等價(jià)于/proc/sys/net/ipv4/conf/all/forwarding。此外,實(shí)際上真正決定報(bào)文能否被轉(zhuǎn)發(fā)的,是conf/devname/forwarding,這個(gè)配置在每個(gè)網(wǎng)卡設(shè)備的粒度控制這個(gè)網(wǎng)卡上收到的ipv4/ipv6報(bào)文能否被轉(zhuǎn)發(fā)。
上面講到,ip_forward等價(jià)于ipv4/conf/all/forwarding,而真正有效的是conf/devname/forwarding。那么conf/all/forwarding和conf/default/forwarding又是用來干嘛的?
首先,conf/default/forwarding用于控制新建設(shè)備的配置,如果在配置這個(gè)參數(shù)后創(chuàng)建一個(gè)新的網(wǎng)絡(luò)設(shè)備(例如veth pair),那么新創(chuàng)建設(shè)備的conf/devname/forwarding值就會(huì)等于default配置。
conf/all/forwarding則可以配置當(dāng)前所有設(shè)備的forwarding參數(shù),例如將all參數(shù)配置從0修改為1,則包括default在內(nèi)的所有forwarding配置都將被改成1。要注意的是all配置只有在值被修改時(shí)才有效,重復(fù)寫入all當(dāng)前值不會(huì)對(duì)其他forwarding配置產(chǎn)生任何影響。
另外要說明的是all/forwarding配置只對(duì)當(dāng)前net namespace生效,每個(gè)netns有自己的獨(dú)立配置。
關(guān)于conf/下的其他配置項(xiàng),還有一點(diǎn)需要注意的是每項(xiàng)配置的取值邏輯是不同的,例如:forwarding的配置取的是devname/forwarding的值,而mc_forwarding的實(shí)際配置值是all/mc_forwarding&&devname/mc_forwarding,accept_local的實(shí)際配置值是all/accept_local||devname/accept_local。要知道每項(xiàng)配置的實(shí)際取值方式,只能通過include/linux/inetdevice.h中的定義來了解,在內(nèi)核文檔Documentation/networking/ip-sysctl.rst中對(duì)各項(xiàng)配置的含義做了介紹,但是對(duì)取值邏輯的介紹還不完善。這個(gè)接口定義方式顯然非常不友好,用戶不可能記得所有選項(xiàng)的取值邏輯,而且這些取值邏輯還會(huì)隨著內(nèi)核版本而變化。對(duì)用戶來說,需要記得在使用這些配置前查閱對(duì)應(yīng)的內(nèi)核代碼,來確認(rèn)這些配置的使用方法。
實(shí)現(xiàn)
ip_forward配置是sysctl選項(xiàng),也就是/proc/sys/文件系統(tǒng)中的虛擬文件。這類文件的操作實(shí)現(xiàn)是在fs/proc/proc_sysctl.c中實(shí)現(xiàn)的。每個(gè)sysctl配置文件都有一個(gè)對(duì)應(yīng)的ctl_table數(shù)據(jù)結(jié)構(gòu),這個(gè)結(jié)構(gòu)中指定的proc_handler回調(diào)函數(shù)負(fù)責(zé)實(shí)現(xiàn)對(duì)應(yīng)文件的讀寫操作。
ipv4的ip_forward和forwarding配置對(duì)應(yīng)的處理函數(shù)在net/ipv4/devinet.c中定義,是devinet_sysctl_forward。而ipv6的forwarding函數(shù)在net/ipv6/addrconf.c中定義,是addrconf_sysctl_forward。我們以ipv4的devinet_sysctl_forward為例分析實(shí)現(xiàn)。
devinet_sysctl_forward的邏輯大體如下:
- 通過讀寫ipv4_devconf.data[IPV4_DEVCONF_FORWARDING - 1],完成sysctl配置文件的基本讀寫操作。每個(gè)配置文件都有自己對(duì)應(yīng)的ipv4_devconf.data,在ctl_table中維護(hù)。如果是讀操作,那么操作過程已經(jīng)結(jié)束。如果是寫操作并修改了配置的值,那么還要執(zhí)行后續(xù)的操作。
- 如果配置的是default,則跳到第5步。
- 如果配置的是all,則執(zhí)行inet_forward_change,將default和所有設(shè)備的forwarding配置全部修改。并且會(huì)將all/accept_redirects改成與forwarding相反的值。
- 如果打開了ip_forward,則調(diào)用dev_disable_lro,關(guān)閉相應(yīng)網(wǎng)絡(luò)設(shè)備的LRO功能。
- 調(diào)用inet_netconf_notify_devconf,為每個(gè)被修改的配置項(xiàng)發(fā)送rtnetlink消息,通知關(guān)注forwarding配置的組件配置已經(jīng)發(fā)生了變化。
- 調(diào)用rt_cache_flush,更新netns的路由配置序號(hào),標(biāo)記路由配置發(fā)生了變化。
可以看到上述實(shí)現(xiàn)和前面的配置操作是基本對(duì)應(yīng)的。但是要注意到forwarding配置還會(huì)產(chǎn)生兩個(gè)副作用:
- 配置all/forwarding會(huì)同步修改all/accept_redirects為相反值。accept_redirects配置決定內(nèi)核是否接收ICMP redirect消息。
- 打開forwarding會(huì)關(guān)閉對(duì)應(yīng)設(shè)備的LRO功能。LRO會(huì)造成報(bào)文被聚合,可能會(huì)讓報(bào)文過大或者checksum錯(cuò)誤從而無法被轉(zhuǎn)發(fā),因此需要在轉(zhuǎn)發(fā)功能打開時(shí)關(guān)閉LRO。這個(gè)操作可能顯著影響內(nèi)核網(wǎng)絡(luò)的報(bào)文接收性能。
影響
在內(nèi)核協(xié)議棧中,通過IN_DEV_FORWARD(in_dev)宏來獲取當(dāng)前設(shè)備的forwarding配置。通過檢索內(nèi)核代碼,可以看到有5處邏輯會(huì)通過forwarding配置決定后續(xù)流程。
- br_nf_pre_routing_finish函數(shù),位于net/bridge/br_netfilter_hooks.c
這是網(wǎng)橋設(shè)備的報(bào)文處理流程,大體上是說如果在打開了forwarding的情況下ip_route_input查詢路由仍然返回了EHOSTUNREACH失敗,則不再繼續(xù)處理skb。if ((err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))) { struct in_device *in_dev = __in_dev_get_rcu(dev); /* If err equals -EHOSTUNREACH the error is due to a * martian destination or due to the fact that * forwarding is disabled. For most martian packets, * ip_route_output_key() will fail. It won't fail for 2 types of * martian destinations: loopback destinations and destination * 0.0.0.0. In both cases the packet will be dropped because the * destination is the loopback device and not the bridge. */ if (err != -EHOSTUNREACH || !in_dev || IN_DEV_FORWARD(in_dev)) goto free_skb; rt = ip_route_output(net, iph->daddr, 0, RT_TOS(iph->tos), 0); if (!IS_ERR(rt)) { /* - Bridged-and-DNAT'ed traffic doesn't * require ip_forwarding. */ if (rt->dst.dev == dev) { skb_dst_set(skb, &rt->dst); goto bridged_dnat; } ip_rt_put(rt); } free_skb: kfree_skb(skb); return 0;
- bpf_ipv4_fib_lookup函數(shù),位于net/core/filter.c
dev = dev_get_by_index_rcu(net, params->ifindex); if (unlikely(!dev)) return -ENODEV; /* verify forwarding is enabled on this interface */ in_dev = __in_dev_get_rcu(dev); if (unlikely(!in_dev || !IN_DEV_FORWARD(in_dev))) return BPF_FIB_LKUP_RET_FWD_DISABLED;
這是一個(gè)內(nèi)核的bpf helper函數(shù),用于支持ebpf程序查找轉(zhuǎn)發(fā)表。如果forwarding沒有打開,則查找失敗,返回BPF_FIB_LKUP_RET_FWD_DISABLED。
- arp_process函數(shù),位于net/ipv4/arp.c
if (addr_type == RTN_LOCAL) { ... } else if (IN_DEV_FORWARD(in_dev)) { if (addr_type == RTN_UNICAST && (arp_fwd_proxy(in_dev, dev, rt) || arp_fwd_pvlan(in_dev, dev, rt, sip, tip) || (rt->dst.dev != dev && pneigh_lookup(&arp_tbl, net, &tip, dev, 0)))) { ...
這是內(nèi)核處理arp報(bào)文的邏輯。如果收到了ARP請(qǐng)求報(bào)文,而且請(qǐng)求的目標(biāo)不是本地IP,那么只有在forwarding打開時(shí)才有可能為其提供ARP代理應(yīng)答。文章來源:http://www.zghlxwxcb.cn/news/detail-782071.html
- ip_error函數(shù),位于net/ipv4/route.c
這是路由查找失敗后的錯(cuò)誤處理函數(shù),會(huì)在ip_route_input_slow中被設(shè)置為非本地報(bào)文的后續(xù)處理函數(shù),在報(bào)文處理的主路徑函數(shù)ip_rcv_finish中調(diào)用。這個(gè)函數(shù)會(huì)將報(bào)文skb釋放,結(jié)束處理流程。函數(shù)內(nèi)會(huì)根據(jù)是否啟用了forwarding來決定如何處理路由失敗,如果沒有啟用forwarding,那么這就是個(gè)錯(cuò)誤路由到本地的包,直接丟棄即可;如果啟用了forwarding,那么就是本機(jī)作為網(wǎng)關(guān)無法完成報(bào)文路由,會(huì)向報(bào)文發(fā)送端反饋一個(gè)ICMP_HOST_UNREACH的ICMP報(bào)文。net = dev_net(rt->dst.dev); if (!IN_DEV_FORWARD(in_dev)) { switch (rt->dst.error) { case EHOSTUNREACH: __IP_INC_STATS(net, IPSTATS_MIB_INADDRERRORS); break; case ENETUNREACH: __IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES); break; } goto out; }
- ip_route_input_slow函數(shù),位于net/ipv4/route.c
這是內(nèi)核協(xié)議棧路由查找的核心邏輯。在報(bào)文的路由查找沒有找到目的地址位置,或者目的地址不是本地設(shè)備地址時(shí),就需要根據(jù)forwarding開關(guān)來確定后續(xù)的操作。如果forwarding開關(guān)沒有打開,會(huì)設(shè)置路由結(jié)果fib_result.type = RTN_UNREACHABLE,rtable.dst.input= ip_error,rtable.dst.error=EHOSTUNREACH。之后執(zhí)行上面介紹的ip_error函數(shù),將報(bào)文丟棄。如果forwarding打開,就會(huì)執(zhí)行ip_mkroute_input準(zhǔn)備后續(xù)的路由轉(zhuǎn)發(fā)工作,其中就會(huì)設(shè)置rtable.dst.input=ip_forward,也就是轉(zhuǎn)發(fā)功能的核心函數(shù)。err = fib_lookup(net, &fl4, res, 0); if (err != 0) { if (!IN_DEV_FORWARD(in_dev)) err = -EHOSTUNREACH; goto no_route; } if (res->type == RTN_BROADCAST) { if (IN_DEV_BFORWARD(in_dev)) goto make_route; /* not do cache if bc_forwarding is enabled */ if (IPV4_DEVCONF_ALL(net, BC_FORWARDING)) do_cache = false; goto brd_input; } if (res->type == RTN_LOCAL) { err = fib_validate_source(skb, saddr, daddr, tos, 0, dev, in_dev, &itag); if (err < 0) goto martian_source; goto local_input; } if (!IN_DEV_FORWARD(in_dev)) { err = -EHOSTUNREACH; goto no_route; }
總結(jié)
本文詳細(xì)分析了linux內(nèi)核中常用的網(wǎng)絡(luò)配置項(xiàng)ip_forward的用法、作用和實(shí)現(xiàn)。最后看一下相關(guān)問題是否已經(jīng)被解答:文章來源地址http://www.zghlxwxcb.cn/news/detail-782071.html
- ip_forward配置在什么位置配置?能按什么粒度配置?
ip_forward相關(guān)配置在ipv4和ipv6的sysctl配置中有獨(dú)立的配置項(xiàng),按網(wǎng)絡(luò)設(shè)備粒度配置生效。真正起影響的配置項(xiàng)為forwarding。 - ip_forward打開或關(guān)閉會(huì)對(duì)內(nèi)核的網(wǎng)絡(luò)處理流程產(chǎn)生哪些影響?
ip_forward配置開關(guān)會(huì)對(duì)內(nèi)核網(wǎng)絡(luò)處理的多處邏輯產(chǎn)生影響,其中最重要的影響是決定目的地址不是本地設(shè)備的報(bào)文能否被轉(zhuǎn)發(fā)。具體影響的邏輯參見上文。 - ip_forward配置除了影響轉(zhuǎn)發(fā)功能外,是否還會(huì)對(duì)其他網(wǎng)絡(luò)配置或功能產(chǎn)生副作用?
除了影響內(nèi)核轉(zhuǎn)發(fā)功能開關(guān),ip_forward的開關(guān)還會(huì)影響accept_redirects功能和LRO功能的開關(guān),需要注意對(duì)相關(guān)功能和網(wǎng)絡(luò)性能的影響。此外,ipv6的forwarding開關(guān)還會(huì)影響accept_ra的功能。 - ip_forward的配置開關(guān)在內(nèi)核中是如何實(shí)現(xiàn)的?
每個(gè)sysctl配置項(xiàng)都有對(duì)應(yīng)的處理函數(shù),由sysctl文件系統(tǒng)的read/write實(shí)現(xiàn)函數(shù)來調(diào)用。ip_forward的處理函數(shù)為devinet_sysctl_forward,這個(gè)函數(shù)主要通過讀寫設(shè)備的forwarding配置項(xiàng)來使配置生效。
到了這里,關(guān)于linux內(nèi)核ip_forward參數(shù)詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!