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

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 的防火墙技术研究

    _IP_FORWARD 根据FORWARD 链(在filter 表中)中的规则对数据包进行检查,如果规则匹配,则回 调函数按规则所指定的动作来处理该包,否则丢弃该包。2)如果数据包的目的地址是本机,则系统将数 据包发往input 关键点。...

    linux共享上网设置实例详解

    1、打开内核ip转发 vi /etc/sysctl.conf net.ipv4.ip_forward = 1 执行sysctrl -p生效  2、如果主机未启用防火墙,那么如下设置iptables [root@Web-Lnmp02 ~]# iptables -F [root@Web-Lnmp02 ~]# iptables -P ...

    Linux防火墙.pdf

     2.3.7 Linux内核IGMP攻击 32  2.4 网络层回应 33  2.4.1 网络层过滤回应 33  2.4.2 网络层阈值回应 33  2.4.3 结合多层的回应 34  第3章 传输层的攻击与防御 35  3.1 使用iptables记录传输层首部 35 ...

    wg-manager:易于使用的WireGuard仪表板和管理工具

    同行创建/删除/修改带宽使用情况统计通过QRCode,文本导出通过API密钥进行身份验证以实现自动化(在GUI中创建) 使用Docker自动设置一般的修改管理员用户创建和管理API密钥依存关系Linux内核&gt; = 5.6 (或者:...

    lvs的ipvsadm

    负载均衡的一个模块,LVS(Linux Virtual Server)其实就是针对高可伸缩、高可用网络服务的需求,给出了基于IP层和基于内容请求分发的负载平衡调度解决方法,并在Linux内核中实现了这些方法,将一组服务器构成一个...

    linux.chm文档

    uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS / DMI) hdparm -i /dev/hda 罗列一个磁盘的架构特性 hdparm -tT /dev/sda 在磁盘上执行测试性读取操作 cat /proc/cpuinfo 显示CPU ...

    构筑Linux防火墙之IPtables的概念与用法

    实际的操作分为以下几类: &lt;br&gt;◆ DNAT &lt;br&gt;◆ SNAT &lt;br&gt;◆ MASQUERADE &lt;br&gt;DNAT操作主要用在这样一种情况,你有一个合法的IP地址,要把对防火墙的访问 重定向到其他的机子上(比如DMZ)。也就是说,...

    sysctl:K4YT3X的强化系统配置

    防止读取内核指针禁用所有程序的Ptrace禁止通过SUID / GUID程序进行核心转储禁用IPv4 / IPv6路由启用BBR TCP拥塞控制启用S​​YN cookie以减轻SYN泛洪攻击启用IP反向路径过滤以进行源验证...在应用配置文件之前,请...

    minimart-netstack

    现在,从其他计算机(例如192.168.1.99)ping 192.168.1.222将导致本地内核接收ping,然后将其转发到192.168.1.222 ,由于免费的ARP公告,它知道自己运行以太网MAC地址。 这会导致ping请求不断重复,每次使用一个较...

    oracle asm rac

    eth1作为对对内访问使用 配置IP地址 使用setup命令修改IP 分别配置eth0和eth1(我们只演示node1的配置,node2也配置成相应的IP) 保存退出 重启使配置生效 3、关闭没必要的服务 chkconfig autofs off ...

    Iptables 中文指南

    2.2. 内核配置 2.3. 编译与安装 2.3.1. 编译 2.3.2. 在Red Hat 7.1上安装 3. 表和链 3.1. 概述 3.2. mangle 表 3.3. nat 表 3.4. Filter 表 4. 状态机制 4.1. 概述 4.2. conntrack记录 4.3. 数据包在用户...

Global site tag (gtag.js) - Google Analytics