`
网络接口
  • 浏览: 44930 次
文章分类
社区版块
存档分类
最新评论

ip_forward参数对Linux内核转发的影响

 
阅读更多

转自:http://10495372.blog.51cto.com/10485372/1671453

在进行Linux内核转发时,需要在proc文件系统的proc/sys目录设置转发的参数,可以使用下面的方法查看该参数的值 cat /proc/sys/net/ipv4/ip_forward,该参数的默认值为0,可以使用下面的方法进行修改该值,使能Linux内核的IP层的数据抓发,但是下面的方法在系统重启后不再生效。
echo 1 > /proc/sys/net/ipv4/ip_forward
在Linux系统中也提供了一个系统的配置工具sysctl,使用它可以读取和配置Linux内核的一些参数。但是该方法和proc文件系统相关,使用该工具Linux内核需要支持proc文件系统。下面是使用sysctl配置内核的转发参数。
# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0
/ #  sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
/ # sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
/ #
注意,参数 net.ipv4.ip_forward 实际是对应的 proc 目录/proc/sys/net/ipv4/ip_forward,选项 -w 表示配置该内核配置参数,没有选项表示读内核配置参数,不加任何选项信息,就表示读取操作。
通过上面的方法我们可以设置和读取IP转发的参数。但是本文重点不是讲该参数如何配置,而是在配置完成后,在内核的转发过程中如何生效的,以及如何配置到内核中。既然,该参数是配置使能IP层的转发,那应该在Linux内核的转发部分对该参数进行了判断,该参数的判断实际上是在查找路由时进行判断的

在查路由的过程中,如果是转发的数据包调用下面的宏判断转发的参数是否开启。在函数ip_route_input_slow。
if (!IN_DEV_FORWARD(in_dev))
   goto e_hostunreach;
看一下该宏是如何进行定义的,下面的宏定义在include/linux/inetdevice.h文件中。
#define IN_DEV_FORWARD(in_dev)  IN_DEV_CONF_GET((in_dev), FORWARDING)
在把IN_DEV_CONF_GET宏进一步展开了看:
#define IN_DEV_CONF_GET(in_dev, attr) \
ipv4_devconf_get((in_dev), NET_IPV4_CONF_ ## attr)//这里的##表示连接两个字符串。
下面是ipv4_devconf_get函数的定义:
static inline int ipv4_devconf_get(struct in_device *in_dev, int index)
{
index--;//这里的index相当于NET_IPV4_CONF_FORWARDING
return in_dev->cnf.data[index];// init_net->ipv4.devconf_dfl.data[0]
}
(1)对于宏NET_IPV4_CONF_ FORWARDING,定义在include/linux/sysctl.h文件中,是一个枚举类型的。
enum
{
NET_IPV4_CONF_FORWARDING=1,
NET_IPV4_CONF_MC_FORWARDING=2,
NET_IPV4_CONF_PROXY_ARP=3,
NET_IPV4_CONF_ACCEPT_REDIRECTS=4,
NET_IPV4_CONF_SECURE_REDIRECTS=5,
NET_IPV4_CONF_SEND_REDIRECTS=6,
NET_IPV4_CONF_SHARED_MEDIA=7,
NET_IPV4_CONF_RP_FILTER=8,
NET_IPV4_CONF_ACCEPT_SOURCE_ROUTE=9,
NET_IPV4_CONF_BOOTP_RELAY=10,
NET_IPV4_CONF_LOG_MARTIANS=11,
NET_IPV4_CONF_TAG=12,
NET_IPV4_CONF_ARPFILTER=13,
NET_IPV4_CONF_MEDIUM_ID=14,
NET_IPV4_CONF_NOXFRM=15,
NET_IPV4_CONF_NOPOLICY=16,
NET_IPV4_CONF_FORCE_IGMP_VERSION=17,
NET_IPV4_CONF_ARP_ANNOUNCE=18,
NET_IPV4_CONF_ARP_IGNORE=19,
NET_IPV4_CONF_PROMOTE_SECONDARIES=20,
NET_IPV4_CONF_ARP_ACCEPT=21,
NET_IPV4_CONF_ARP_NOTIFY=22,
NET_IPV4_CONF_SRC_VMARK=24,
__NET_IPV4_CONF_MAX
};
(2)对于return in_dev->cnf.data[index];返回的相当于in_dev->cnf.data[0],那下面我们看一下该初始值是如何产生的。
首先,in_dev是怎么获取到的,在ip_route_input_slow函数中通过struct in_device *in_dev = in_dev_get(dev);函数获取,在in_dev_get函数中调用__in_dev_get_rcu,通过下面的赋值语句进行赋值struct in_device *in_dev = dev->ip_ptr;
static inline struct in_device *__in_dev_get_rcu(const struct net_device *dev)
{
struct in_device *in_dev = dev->ip_ptr;
if (in_dev)
 in_dev = rcu_dereference(in_dev);
return in_dev;
}
 
static __inline__ struct in_device *
in_dev_get(const struct net_device *dev)
{
struct in_device *in_dev;
 
rcu_read_lock();
in_dev = __in_dev_get_rcu(dev);
if (in_dev)
 atomic_inc(&in_dev->refcnt);
rcu_read_unlock();
return in_dev;
}
dev->ip_ptr;又是什么时候赋值呢?答案是在net_device注册初始化函数inetdev_init中,
static struct in_device *inetdev_init(struct net_device *dev)
{
struct in_device *in_dev;
 
ASSERT_RTNL();
 
in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
if (!in_dev)
 goto out;
memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
 sizeof(in_dev->cnf));//这里对in_dev->cnt进行初始化操作,---(1)
in_dev->cnf.sysctl = NULL;
in_dev->dev = dev;
if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL)
 goto out_kfree;
if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
 dev_disable_lro(dev);
/* Reference in_dev->dev */
dev_hold(dev);
/* Account for reference dev->ip_ptr (below) */
in_dev_hold(in_dev);
 
devinet_sysctl_register(in_dev);
ip_mc_init_dev(in_dev);
if (dev->flags & IFF_UP)
 ip_mc_up(in_dev);
 
/* we can receive as soon as ip_ptr is set -- do this last */
rcu_assign_pointer(dev->ip_ptr, in_dev);//使用RCU保护锁机制对dev->ip_ptr进行赋值
out:
return in_dev;
out_kfree:
kfree(in_dev);
in_dev = NULL;
goto out;
}
(1)dev_net(dev)->ipv4.devconf_dfl 也就相当于init_net->ipv4.devconf_dfl,而devconf_dfl的初始化时在/net/ipv4/devinet.c文件中,devinet_init_net函数中,
static struct ipv4_devconf ipv4_devconf_dflt = {
.data = {
 [NET_IPV4_CONF_ACCEPT_REDIRECTS - 1] = 1,
 [NET_IPV4_CONF_SEND_REDIRECTS - 1] = 1,
 [NET_IPV4_CONF_SECURE_REDIRECTS - 1] = 1,
 [NET_IPV4_CONF_SHARED_MEDIA - 1] = 1,
 [NET_IPV4_CONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
},
};//这里并没有对FORWARDING进行赋值操作
static __net_init int devinet_init_net(struct net *net)
{
int err;
struct ipv4_devconf *all, *dflt;
#ifdef CONFIG_SYSCTL
struct ctl_table *tbl = ctl_forward_entry;
struct ctl_table_header *forw_hdr;
#endif
 
err = -ENOMEM;
all = &ipv4_devconf; //----------------------------进行初始化操作
dflt = &ipv4_devconf_dflt;
 
if (net != &init_net) {
 all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
 if (all == NULL)
 goto err_alloc_all;
 
 dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
 if (dflt == NULL)
 goto err_alloc_dflt;
 
#ifdef CONFIG_SYSCTL
 tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
 if (tbl == NULL)
 goto err_alloc_ctl;
 
 tbl[0].data = &all->data[NET_IPV4_CONF_FORWARDING - 1];
 tbl[0].extra1 = all;
 tbl[0].extra2 = net;
#endif
}
 
#ifdef CONFIG_SYSCTL
err = __devinet_sysctl_register(net, "all",
 NET_PROTO_CONF_ALL, all);
if (err < 0)
 goto err_reg_all;
 
err = __devinet_sysctl_register(net, "default",
 NET_PROTO_CONF_DEFAULT, dflt);
if (err < 0)
 goto err_reg_dflt;
 
err = -ENOMEM;
forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl);
if (forw_hdr == NULL)
 goto err_reg_ctl;
net->ipv4.forw_hdr = forw_hdr;
#endif
 
net->ipv4.devconf_all = all;//这里对net->ipv4_devconfi_all进行了初始化
net->ipv4.devconf_dflt = dflt;// //这里对net->devconf_dflt进行了初始化
return 0;
………………………….
}
上面的函数对net相关功能的初始化,在devinet.c文件中还有一个和ipv4_devconf_dflt类似的变量ipv4_devconf,但是IN_DEV_FORWARD(in_dev)宏读取的是结构体ipv4_devconf_dflt中变量的值,所以,如果要在Linux内核中修改转发的参数时,需要在ipv4_devconf_dflt中添加才能生效。
static struct ipv4_devconf ipv4_devconf = {
.data = {
 [NET_IPV4_CONF_ACCEPT_REDIRECTS - 1] = 1,
 [NET_IPV4_CONF_SEND_REDIRECTS - 1] = 1,
 [NET_IPV4_CONF_SECURE_REDIRECTS - 1] = 1,
 [NET_IPV4_CONF_SHARED_MEDIA - 1] = 1,
 [NET_IPV4_CONF_FORCE_IGMP_VERSION-1]=2,
},
};
(3)下面看一下使用echo 1 > /proc/sys/net/ipv4/ip_forward配置语句如何是Linux内核IP转发生效的。
在上面的devinet_init_net()函数中,有下面的两段代码
struct ctl_table *tbl = ctl_forward_entry;
forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl);
其中ctl_forward_entry定义为下面的结构,
static struct ctl_table ctl_forward_entry[] = {
{
 .ctl_name= NET_IPV4_FORWARD,//一个ID,
 .procname  = "ip_forward",//字符串,包含在proc/sys下目录项,实际为proc/sys目录下的文件名
 .data= &ipv4_devconf.data[
  NET_IPV4_CONF_FORWARDING - 1],//回调函数设置的值
 .maxlen    = sizeof(int),//设置值的最大长度
 .mode = 0644,//文件的权限,也就是ip_forward文件的权限
 .proc_handler  = devinet_sysctl_forward,// 对/proc/sys下面的文件修改的时候调用该回调函数。
 .strategy = devinet_conf_sysctl,// 用sysctl读写系统参数时候调用该回调函数
 .extra1     = &ipv4_devconf,
 .extra2     = &init_net,
},
{ },
};
forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl);用于动态注册系统控制功能,其中net_ipv4_path定义为下面的形式。也就是proc/sys/下的目录名,tbl就是上面的ctl_forward_entry[]结构体。
static __net_initdata struct ctl_path net_ipv4_path[] = {
{ .procname = "net", .ctl_name = CTL_NET, },
{ .procname = "ipv4", .ctl_name = NET_IPV4, },
{ },
};
使用echo 1 > /proc/sys/net/ipv4/ip_forward调用devinet_sysctl_forward函数进行处理,下面是该函数的定义实现。其中参数write为1表示写配置,为0表示读取配置值,buffer是要写的值,lenp为buffer的大小,ppos为位置。这里的__user是告诉不应该解除该指针的引用,因为在当前地址空间中它是没有意义的,所以对于这种变量,在kernel中使用要用到copy_to_user和copy_from_user
static int devinet_sysctl_forward(ctl_table *ctl, int write,
   void __user *buffer,
   size_t *lenp, loff_t *ppos)
{
int *valp = ctl->data;//获取&ipv4_devconf.data地址
int val = *valp;
loff_t pos = *ppos;
int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);//该函数处理传进来的int型,proc_dostring处理传过来的字符串。
/* ctl->data change  echo "0" >/proc/sys/net/ipv4/ip_forward  write = 1 *valp = 0 val = 1 */
if (write && *valp != val) {
 struct net *net = ctl->extra2;
 
 if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
 if (!rtnl_trylock()) {
 /* Restore the original values before restarting */
 *valp = val;
 *ppos = pos;
 return restart_syscall();
 }
 if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
 inet_forward_change(net);//调用该函数进行配置in_dev->cnf.data
 } else if (*valp) {
 struct ipv4_devconf *cnf = ctl->extra1;
 struct in_device *idev =
  container_of(cnf, struct in_device, cnf);
 dev_disable_lro(idev->dev);
 }
 rtnl_unlock();
 rt_cache_flush(net, 0);
 }
}
 
return ret;
}
下面是这个函数就是修改forward参数,
static void inet_forward_change(struct net *net)
{
struct net_device *dev;
int on = IPV4_DEVCONF_ALL(net, FORWARDING);//获取配置的值
 
IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
IPV4_DEVCONF_DFLT(net, FORWARDING) = on;//设置ipv4_devconf_dflt结构体,
 
read_lock(&dev_base_lock);
for_each_netdev(net, dev) {
 struct in_device *in_dev;
 if (on)
 dev_disable_lro(dev);
 rcu_read_lock();
 in_dev = __in_dev_get_rcu(dev);
 if (in_dev)
 IN_DEV_CONF_SET(in_dev, FORWARDING, on);//调用该宏设置in_dev->cnf.data
 rcu_read_unlock();
}
read_unlock(&dev_base_lock);
}
 
static inline void ipv4_devconf_set(struct in_device *in_dev, int index,
     int val)
{
index--;
set_bit(index, in_dev->cnf.state);
in_dev->cnf.data[index] = val;//设置in_dev的data,这里的Index为NET_IPV4_CONF_FORWARDING
}

分享到:
评论

相关推荐

    Linux 内核网络参数配置资料

    在深入探讨Linux内核网络参数配置之前,我们先来了解一下这些参数的基本作用及其对系统的影响。Linux作为一种强大的开源操作系统,其内核提供了丰富的网络参数配置选项,使得用户可以根据具体的应用场景进行灵活调整...

    Buildroot 是Linux平台上一个构建嵌入式Linux系统的框架.zip

    首先在命令行输入:cat /proc/sys/net/ipv4/ip_forwad,检查Linux内核是不是开启IP转发功能。如果结果为1,表明路由转发功能已经开启;如果结果为0,表明没有开启。出于安全考虑,Linux内核默认是禁止数据包路由转发...

    跨平台UI框架,支持Windows、Mac、Linux,支持国产Linux.zip

    首先在命令行输入:cat /proc/sys/net/ipv4/ip_forwad,检查Linux内核是不是开启IP转发功能。如果结果为1,表明路由转发功能已经开启;如果结果为0,表明没有开启。出于安全考虑,Linux内核默认是禁止数据包路由转发...

    Baetyl 是 Linux Foundation Edge 旗下的边缘计算项目.zip

    首先在命令行输入:cat /proc/sys/net/ipv4/ip_forwad,检查Linux内核是不是开启IP转发功能。如果结果为1,表明路由转发功能已经开启;如果结果为0,表明没有开启。出于安全考虑,Linux内核默认是禁止数据包路由转发...

    一个以桌面环境为主的Linux操作系统.zip

    首先在命令行输入:cat /proc/sys/net/ipv4/ip_forwad,检查Linux内核是不是开启IP转发功能。如果结果为1,表明路由转发功能已经开启;如果结果为0,表明没有开启。出于安全考虑,Linux内核默认是禁止数据包路由转发...

    Linux 平台下基于 Rust + GTK 开发的音乐播放器.zip

    首先在命令行输入:cat /proc/sys/net/ipv4/ip_forwad,检查Linux内核是不是开启IP转发功能。如果结果为1,表明路由转发功能已经开启;如果结果为0,表明没有开启。出于安全考虑,Linux内核默认是禁止数据包路由转发...

    Tailmon-EDR是一款Linux服务器安全防护软件.zip

    首先在命令行输入:cat /proc/sys/net/ipv4/ip_forwad,检查Linux内核是不是开启IP转发功能。如果结果为1,表明路由转发功能已经开启;如果结果为0,表明没有开启。出于安全考虑,Linux内核默认是禁止数据包路由转发...

    基于开源Linux优化的强实时操作系统,可以支持智能汽车.zip

    首先在命令行输入:cat /proc/sys/net/ipv4/ip_forwad,检查Linux内核是不是开启IP转发功能。如果结果为1,表明路由转发功能已经开启;如果结果为0,表明没有开启。出于安全考虑,Linux内核默认是禁止数据包路由转发...

    XRobot是一套适用于微控制器、x86 Linux和模拟器的机器人开发框架.zip

    首先在命令行输入:cat /proc/sys/net/ipv4/ip_forwad,检查Linux内核是不是开启IP转发功能。如果结果为1,表明路由转发功能已经开启;如果结果为0,表明没有开启。出于安全考虑,Linux内核默认是禁止数据包路由转发...

    Java代码实现调用RapidOCR(基于PaddleOCR),适配Mac、Win、Linux,支持最新PP-OCRv4.zip

    首先在命令行输入:cat /proc/sys/net/ipv4/ip_forwad,检查Linux内核是不是开启IP转发功能。如果结果为1,表明路由转发功能已经开启;如果结果为0,表明没有开启。出于安全考虑,Linux内核默认是禁止数据包路由转发...

    以简单、易用、高性能为目标、开源的时序数据库,支持Linux及Windows, Time Series Database.zip

    首先在命令行输入:cat /proc/sys/net/ipv4/ip_forwad,检查Linux内核是不是开启IP转发功能。如果结果为1,表明路由转发功能已经开启;如果结果为0,表明没有开启。出于安全考虑,Linux内核默认是禁止数据包路由转发...

    linux forward的实现 Linux IP数据流 FIB

    Linux Forwarding,也称为IP数据流转发,是Linux内核中的一个重要功能,它使得Linux系统可以作为路由器,将网络数据包从一个接口转发到另一个接口。这个过程涉及到多个层次的处理,包括网络接口层、IP层以及路由选择...

    是一款为热门数据库系统打造的管理客户端.zip

    首先在命令行输入:cat /proc/sys/net/ipv4/ip_forwad,检查Linux内核是不是开启IP转发功能。如果结果为1,表明路由转发功能已经开启;如果结果为0,表明没有开启。出于安全考虑,Linux内核默认是禁止数据包路由转发...

    使用proc系统访问linux内核

    ### 使用proc系统访问Linux内核 #### 一、引言 在Linux系统中,`/proc` 文件系统是一个特殊的虚拟文件系统,它充当了一个连接内核空间与用户空间的桥梁。通过这一机制,用户可以方便地获取系统状态信息、监控进程...

    setr.rar_linux_linux 路由_linux 路由器_路由器_路由器设置

    这需要对Linux内核的网络子系统有深入的理解,包括网络套接字编程、路由表结构和路由选择算法等。 总的来说,将Linux服务器作为路由器是一种经济且灵活的解决方案,尤其适用于需要高度定制和自动化管理的网络环境。...

    深入Linux内核网络堆栈

    - **NF_IP_FORWARD**:当数据包需要转发到另一个接口时调用。 - **NF_IP_LOCAL_OUT**:当数据包是由本地进程生成并准备发送出去时调用。 - **NF_IP_POST_ROUTING**:在数据包最终离开系统前调用。 ##### 2. 钩子...

    基于Linux的HGU设备接入公网用户数量限制的内核实现新方法.pdf

    文章指出,针对HGU的用户数量限制,可以在Netfilter的NF_IP_FORWARD Hook点注册钩子处理函数,因为这个点位于数据包在被转发之前,适合用于控制数据包的流向,从而实现对用户数量的限制。 Netfilter在网络层有五个...

    Linux系统的路由实践.pdf

    首先在命令行输入:cat /proc/sys/net/ipv4/ip_forward,检查Linux内核是否开启IP转发功能。如果结果为1,表明路由转发功能已经开启;如果结果为0,表明没有开启。 Linux系统的路由实践可以通过静态路由和动态路由...

    nat.rar_linux nat_nat_nat 实现_数据包转发

    `netfilter`是内核中的一个钩子系统,允许用户空间程序对进出的数据包进行操作。而`iptables`是控制这些钩子的命令行工具,可以设置规则来转发、拒绝或接受特定的数据包。 要实现NAT数据包转发,你需要执行以下步骤...

    linux ip隧道配置手册

    4. **设置IP转发**:在Linux系统中启用IP转发功能,以便系统可以转发经过GRE隧道的数据包。这通常通过向`/proc/sys/net/ipv4/ip_forward`写入数字“1”来实现。 5. **清理旧规则**:使用`iptables -F`清空所有现有的...

Global site tag (gtag.js) - Google Analytics