linux内核ip_forward参数详解

这篇具有很好参考价值的文章主要介绍了linux内核ip_forward参数详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

linux服务器经常被用来提供防火墙、路由器、NAT等功能,在这些场景下,linux内核需要将网卡上收到的报文转发给其他网络设备。linux内核提供了ip_forward参数用于开关内核的报文转发功能,只有这个开关被打开时,内核才会执行报文的转发。网上能找到不少文章介绍ip_forward参数的基本用途和配置方式,但没有什么文章具体介绍这个参数配置后具体会对内核的网络行为产生哪些影响,以及在内核中是如何实现的。而这些问题的答案对转发功能的使用方式会产生很大的影响。

本文将详细分析ip_forward参数的配置方式和影响,以及这个参数在内核中的实现逻辑。分析基于5.9.11版本的内核。

问题

  1. ip_forward配置在什么位置配置?能按什么粒度配置?
  2. ip_forward打开或关闭会对内核的网络处理流程产生哪些影响?
  3. ip_forward配置除了影响转发功能外,是否还会对其他网络配置或功能产生副作用?
  4. ip_forward的配置开关在内核中是如何实现的?

配置

ip_forward功能的配置开关有三个位置:

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

其中第一个配置是许多文章中提到的ip报文转发开关,这个配置开关是linux早期版本中定义的,它只能控制IPv4报文的转发功能,其功能和取值都等价于/proc/sys/net/ipv4/conf/all/forwarding。此外,实际上真正决定报文能否被转发的,是conf/devname/forwarding,这个配置在每个网卡设备的粒度控制这个网卡上收到的ipv4/ipv6报文能否被转发。

上面讲到,ip_forward等价于ipv4/conf/all/forwarding,而真正有效的是conf/devname/forwarding。那么conf/all/forwarding和conf/default/forwarding又是用来干嘛的?

首先,conf/default/forwarding用于控制新建设备的配置,如果在配置这个参数后创建一个新的网络设备(例如veth pair),那么新创建设备的conf/devname/forwarding值就会等于default配置。

conf/all/forwarding则可以配置当前所有设备的forwarding参数,例如将all参数配置从0修改为1,则包括default在内的所有forwarding配置都将被改成1。要注意的是all配置只有在值被修改时才有效,重复写入all当前值不会对其他forwarding配置产生任何影响。

另外要说明的是all/forwarding配置只对当前net namespace生效,每个netns有自己的独立配置。

关于conf/下的其他配置项,还有一点需要注意的是每项配置的取值逻辑是不同的,例如:forwarding的配置取的是devname/forwarding的值,而mc_forwarding的实际配置值是all/mc_forwarding&&devname/mc_forwarding,accept_local的实际配置值是all/accept_local||devname/accept_local。要知道每项配置的实际取值方式,只能通过include/linux/inetdevice.h中的定义来了解,在内核文档Documentation/networking/ip-sysctl.rst中对各项配置的含义做了介绍,但是对取值逻辑的介绍还不完善。这个接口定义方式显然非常不友好,用户不可能记得所有选项的取值逻辑,而且这些取值逻辑还会随着内核版本而变化。对用户来说,需要记得在使用这些配置前查阅对应的内核代码,来确认这些配置的使用方法。

实现

ip_forward配置是sysctl选项,也就是/proc/sys/文件系统中的虚拟文件。这类文件的操作实现是在fs/proc/proc_sysctl.c中实现的。每个sysctl配置文件都有一个对应的ctl_table数据结构,这个结构中指定的proc_handler回调函数负责实现对应文件的读写操作。

ipv4的ip_forward和forwarding配置对应的处理函数在net/ipv4/devinet.c中定义,是devinet_sysctl_forward。而ipv6的forwarding函数在net/ipv6/addrconf.c中定义,是addrconf_sysctl_forward。我们以ipv4的devinet_sysctl_forward为例分析实现。

devinet_sysctl_forward的逻辑大体如下:

  1. 通过读写ipv4_devconf.data[IPV4_DEVCONF_FORWARDING - 1],完成sysctl配置文件的基本读写操作。每个配置文件都有自己对应的ipv4_devconf.data,在ctl_table中维护。如果是读操作,那么操作过程已经结束。如果是写操作并修改了配置的值,那么还要执行后续的操作。
  2. 如果配置的是default,则跳到第5步。
  3. 如果配置的是all,则执行inet_forward_change,将default和所有设备的forwarding配置全部修改。并且会将all/accept_redirects改成与forwarding相反的值。
  4. 如果打开了ip_forward,则调用dev_disable_lro,关闭相应网络设备的LRO功能。
  5. 调用inet_netconf_notify_devconf,为每个被修改的配置项发送rtnetlink消息,通知关注forwarding配置的组件配置已经发生了变化。
  6. 调用rt_cache_flush,更新netns的路由配置序号,标记路由配置发生了变化。

可以看到上述实现和前面的配置操作是基本对应的。但是要注意到forwarding配置还会产生两个副作用:

  1. 配置all/forwarding会同步修改all/accept_redirects为相反值。accept_redirects配置决定内核是否接收ICMP redirect消息。
  2. 打开forwarding会关闭对应设备的LRO功能。LRO会造成报文被聚合,可能会让报文过大或者checksum错误从而无法被转发,因此需要在转发功能打开时关闭LRO。这个操作可能显著影响内核网络的报文接收性能。

影响

在内核协议栈中,通过IN_DEV_FORWARD(in_dev)宏来获取当前设备的forwarding配置。通过检索内核代码,可以看到有5处逻辑会通过forwarding配置决定后续流程。

  1. br_nf_pre_routing_finish函数,位于net/bridge/br_netfilter_hooks.c
    		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;
    这是网桥设备的报文处理流程,大体上是说如果在打开了forwarding的情况下ip_route_input查询路由仍然返回了EHOSTUNREACH失败,则不再继续处理skb。
  2. bpf_ipv4_fib_lookup函数,位于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;
    

    这是一个内核的bpf helper函数,用于支持ebpf程序查找转发表。如果forwarding没有打开,则查找失败,返回BPF_FIB_LKUP_RET_FWD_DISABLED。

  3. arp_process函数,位于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)))) {
                    ...
    

    这是内核处理arp报文的逻辑。如果收到了ARP请求报文,而且请求的目标不是本地IP,那么只有在forwarding打开时才有可能为其提供ARP代理应答。

  4. ip_error函数,位于net/ipv4/route.c
    	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中被设置为非本地报文的后续处理函数,在报文处理的主路径函数ip_rcv_finish中调用。这个函数会将报文skb释放,结束处理流程。函数内会根据是否启用了forwarding来决定如何处理路由失败,如果没有启用forwarding,那么这就是个错误路由到本地的包,直接丢弃即可;如果启用了forwarding,那么就是本机作为网关无法完成报文路由,会向报文发送端反馈一个ICMP_HOST_UNREACH的ICMP报文。
  5. ip_route_input_slow函数,位于net/ipv4/route.c
    	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;
    	}
    这是内核协议栈路由查找的核心逻辑。在报文的路由查找没有找到目的地址位置,或者目的地址不是本地设备地址时,就需要根据forwarding开关来确定后续的操作。如果forwarding开关没有打开,会设置路由结果fib_result.type = RTN_UNREACHABLE,rtable.dst.input= ip_error,rtable.dst.error=EHOSTUNREACH。之后执行上面介绍的ip_error函数,将报文丢弃。如果forwarding打开,就会执行ip_mkroute_input准备后续的路由转发工作,其中就会设置rtable.dst.input=ip_forward,也就是转发功能的核心函数。

总结

本文详细分析了linux内核中常用的网络配置项ip_forward的用法、作用和实现。最后看一下相关问题是否已经被解答:文章来源地址https://www.toymoban.com/news/detail-782071.html

  1. ip_forward配置在什么位置配置?能按什么粒度配置?

    ip_forward相关配置在ipv4和ipv6的sysctl配置中有独立的配置项,按网络设备粒度配置生效。真正起影响的配置项为forwarding。
  2. ip_forward打开或关闭会对内核的网络处理流程产生哪些影响?

    ip_forward配置开关会对内核网络处理的多处逻辑产生影响,其中最重要的影响是决定目的地址不是本地设备的报文能否被转发。具体影响的逻辑参见上文。
  3. ip_forward配置除了影响转发功能外,是否还会对其他网络配置或功能产生副作用?

    除了影响内核转发功能开关,ip_forward的开关还会影响accept_redirects功能和LRO功能的开关,需要注意对相关功能和网络性能的影响。此外,ipv6的forwarding开关还会影响accept_ra的功能。
  4. ip_forward的配置开关在内核中是如何实现的?

    每个sysctl配置项都有对应的处理函数,由sysctl文件系统的read/write实现函数来调用。ip_forward的处理函数为devinet_sysctl_forward,这个函数主要通过读写设备的forwarding配置项来使配置生效。

到了这里,关于linux内核ip_forward参数详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux TCP/IP内核参数调优,网络高延迟大吞吐(方案二)。

    方案一:Linux TCP/IP内核参数调优,网络高延迟大吞吐。_net.ipv4.tcp_wmem_liulilittle的博客-CSDN博客 nano /etc/sysctl.conf sysctl -p 另类设置

    2024年02月15日
    浏览(63)
  • 记Kubernetes(k8s) 节点kubeadm join报错:[ERROR FileContent--proc-sys-net-ipv4-ip_forward]

    💖The Begin💖点点关注,收藏不迷路💖 这个错误提示显示了一个预检错误,指出 /proc/sys/net/ipv4/ip_forward 的内容未设置为 1。在 Kubernetes 的预安装检查中,这是一个必要的设置,它允许数据包在节点之间进行转发。 要解决这个问题,需要设置 /proc/sys/net/ipv4/ip_forward 的值为 1。

    2024年04月14日
    浏览(70)
  • 【MATLAB数据处理实用案例详解(22)】——基于BP神经网络的PID参数整定

    基于BP神经网络的PID控制的系统结构如下图所示: 考虑仿真对象,输入为r(k)=1.0,输入层为4,隐藏层为5,输出层为3,仿真输出满足 a ( k ) = 1.2 ( 1 − 0.8 e x p ( − 0.1 k ) ) , y ( k ) = a ( k ) y − 1 1 + ( y − 1 ) 2 + u − 1 a(k)=1.2(1-0.8exp(-0.1k)),y(k)=a(k) frac{y-1}{1+(y-1)^2}+u-1 a ( k ) = 1.2 ( 1 −

    2024年02月07日
    浏览(46)
  • Linux内核--网络协议栈(五)TCP IP栈的实现原理与具体过程

    一、引言 二、Linux内核的结构 三、Linux网络子系统 四、TCP/IP协议栈 ------4.1、网络架构 ------4.2、协议无关接口 ------4.3、套接口缓存 ------4.4、重要的数据结构 五、网络信息处理流程 ------5.1、硬中断处理 ------5.2、ksoftirqd内核线程处理软中断 ------5.3、网络协议栈处理 ------5.4、

    2024年01月21日
    浏览(53)
  • Logstash数据处理服务的输出插件Output配置参数详解

    output配置字段是将收集的日志数据存输出到生存储中,一般都是elasticsearch集群。 常用字段配置: hosts ES集群每个节点的地址信息。 index :指定存储到ES的哪个索引库。 将从file日志文件中收集来的数据存储到ES索引库中。

    2023年04月09日
    浏览(46)
  • Linux 内核调优部分参数说明

    表示尽量使用内存,减少使用磁盘 swap 交换分区,内存速度明显高于磁盘一个数量级。 内存分配策略,Redis 持久化存储需设置值为1。 0:表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应

    2023年04月25日
    浏览(43)
  • Linux内核TCP参数调优全面解读

    TCP 性能的提升不仅考察 TCP 的理论知识,还考察了对于操心系统提供的内核参数的理解与应用。 TCP 协议是由操作系统实现,所以操作系统提供了不少调节 TCP 的参数。 如何正确有效的使用这些参数,来提高 TCP 性能是一个不那么简单事情。我们需要针对 TCP 每个阶段的问题来

    2024年02月11日
    浏览(43)
  • 修改linux的/sys目录下内核参数、模块...

    ① /sys/devices 该目录下是全局设备结构体系,包含所有被发现的注册在各种总线上的各种物理设备。一般来说,所有的物理设备都按其在总线上的拓扑结构来显示,但有两个例外,即platform devices和system devices。platform devices一般是挂在芯片内部的高速或者低速总线上的各种控制

    2024年02月05日
    浏览(43)
  • 【云原生】k8s 前置环境 网络配置 VMware Linux共享上网(iptables forward)

    本文介绍了如何使用 Linux iptalbes 来使其他内网主机进行上网。 解释说明:一般情况下,VMware安装好后会出现两张网卡,vmnet1 与 vmnet8 。其中vmnet1 为仅主机模式(虚拟机之间可通信,虚拟机与宿主机可通信,不能上网);vmnet8 为 NAT 模式(虚拟机之间可通信,虚拟机与宿主机

    2024年02月21日
    浏览(38)
  • Linux内核TCP/IP协议栈

    inet_init 是如何被调用的?从start_kernel到inet_init调用路径 在 Linux 内核启动过程中,inet_init 函数是通过以下路径被调用的: 1.start_kernel 函数是内核的入口点,它位于 init/main.c 文件中。 2.在 start_kernel 函数中,会调用 rest_init 函数来初始化系统的剩余部分。 3.rest_init 函数中会调

    2024年01月25日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包