python中forward的参数_ip_forward参数对Linux内核转发影响分析
在进行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
}
其调用关系如下图:
python中forward的参数_ip_forward参数对Linux内核转发影响分析相关推荐
- ip_forward参数对Linux内核转发影响分析
在进行Linux内核转发时,需要在proc文件系统的proc/sys目录设置转发的参数,可以使用下面的方法查看该参数的值 cat /proc/sys/net/ipv4/ip_forward,该参数的默 ...
- [转载] 【python】Python中*args和**kwargs的区别(在Python中如何使用可变长参数列表)
参考链接: Python中的*args 和 **kwargs 博客已经搬家到"捕获完成": https://www.v2python.com 或者可以叫做,在Python中如何使用 ...
- Linux内核源码分析—Linux内核中的嵌入式汇编
转载请注明出处: http://blog.csdn.net/weifenghai/article/details/52794872 概述: 内核中分配文件描述符时找第一个0的位置的一个底层函数,了 ...
- python中args和kwargs_Python 函数参数*args和**kwargs
尽管*args和**kwargs参数不受重视,但这它们是Python中非常有用的特性.了解其中的潜能会让你成为更高效的开发者. *args和**kwargs参数到底有什么用呢?它们能让函数接受可选参数 ...
- Python 中的解析命令行参数
argparse argparse 是 Python 内置的一个用于命令项选项与参数解析的模块,通过在程序中定义好我们需要的参数,argparse 将会从 sys.argv 中解析出这些参数,并自动生 ...
- [转载] Python中filter筛选函数匿名参数问题
参考链接: python中的filter 最近在学习python,觉得有个地方很有意思,稍作记录,方便以后查阅. Python内建的filter()函数用于过滤序列. 简单来讲,就是针对一个序列中的每 ...
- Python 中的json模块dumps参数详解
1.什么是JSON 维基百科中的定义: JSON(JavaScript Object Notation,JavaScript对象表示法)是一种由道格拉斯·克罗克福特构想和设计.轻量级的资料交换语言,该 ...
- 第5.2节 Python中带星号的函数参数实现参数收集
函数的参数使用除了常规的位置参数和关键字参数外,还支持可变个数的函数参数,这种支持可变个数的参数方法称为参数收集,对应的参数称为收集参数. 一.参数收集的定义 Python的函数支持可变不定数量的参数 ...
- python 中的类的类方法参数self
本文转载自:python--类中的self到底有什么作用 python创建的类的函数参数中的self有什么用呢?下面创建一个关于狗的类来说一下: class Dog:# 表示狗的类def __init ...
最新文章
- 用漫画了解 Linux 内核到底长啥样!
- ubuntu安装百度云客户端
- Python学到什么程度可以面试工作?
- 一键Ghost 脱机下载不再愁
- make: Nothing to be done for `first'
- 2021年必备 Python 插件!
- 用android做体质计算器,Android入门项目(一):BMI体质指数计算器
- 【暑假训练 7.10】 codevs 2492 上帝造题的七分钟2
- mysql dns反向解析_Mysql DNS反向解析导致连接超时过程分析(skip-name-resolve)
- python怎么输入一个数字并调用_python如何直接输入上一句话,如何快速打出上一句话...
- Linux下进程隐藏的常见手法及侦测手段
- 如何清除vsphere主机提示“此主机当前没有管理网络冗余”
- Linux 平台下 Tomcat 的安装与优化
- Microsoft JScript提示‘DIRECT’未定义(2014-08-26记)
- (扫盲)WebSocket 教程
- gitlab鉴权失败
- ART-Pi 实现音乐播放器 --播放《天空之城》
- 你还记得远古时代的拨号上网么?快来了解拨号上网与宽带上网的区别
- linux中的execl函数使用
- markdown(md)编辑 全部格式