在进行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内核转发影响分析相关推荐

  1. ip_forward参数对Linux内核转发影响分析

    在进行Linux内核转发时,需要在proc文件系统的proc/sys目录设置转发的参数,可以使用下面的方法查看该参数的值 cat /proc/sys/net/ipv4/ip_forward,该参数的默 ...

  2. [转载] 【python】Python中*args和**kwargs的区别(在Python中如何使用可变长参数列表)

    参考链接: Python中的*args 和 **kwargs 博客已经搬家到"捕获完成": https://www.v2python.com 或者可以叫做,在Python中如何使用 ...

  3. Linux内核源码分析—Linux内核中的嵌入式汇编

    转载请注明出处: http://blog.csdn.net/weifenghai/article/details/52794872   概述: 内核中分配文件描述符时找第一个0的位置的一个底层函数,了 ...

  4. python中args和kwargs_Python 函数参数*args和**kwargs

    尽管*args和**kwargs参数不受重视,但这它们是Python中非常有用的特性.了解其中的潜能会让你成为更高效的开发者. *args和**kwargs参数到底有什么用呢?它们能让函数接受可选参数 ...

  5. Python 中的解析命令行参数

    argparse argparse 是 Python 内置的一个用于命令项选项与参数解析的模块,通过在程序中定义好我们需要的参数,argparse 将会从 sys.argv 中解析出这些参数,并自动生 ...

  6. [转载] Python中filter筛选函数匿名参数问题

    参考链接: python中的filter 最近在学习python,觉得有个地方很有意思,稍作记录,方便以后查阅. Python内建的filter()函数用于过滤序列. 简单来讲,就是针对一个序列中的每 ...

  7. Python 中的json模块dumps参数详解

    1.什么是JSON 维基百科中的定义: JSON(JavaScript Object Notation,JavaScript对象表示法)是一种由道格拉斯·克罗克福特构想和设计.轻量级的资料交换语言,该 ...

  8. 第5.2节 Python中带星号的函数参数实现参数收集

    函数的参数使用除了常规的位置参数和关键字参数外,还支持可变个数的函数参数,这种支持可变个数的参数方法称为参数收集,对应的参数称为收集参数. 一.参数收集的定义 Python的函数支持可变不定数量的参数 ...

  9. python 中的类的类方法参数self

    本文转载自:python--类中的self到底有什么作用 python创建的类的函数参数中的self有什么用呢?下面创建一个关于狗的类来说一下: class Dog:# 表示狗的类def __init ...

最新文章

  1. 用漫画了解 Linux 内核到底长啥样!
  2. ubuntu安装百度云客户端
  3. Python学到什么程度可以面试工作?
  4. 一键Ghost 脱机下载不再愁
  5. make: Nothing to be done for `first'
  6. 2021年必备 Python 插件!
  7. 用android做体质计算器,Android入门项目(一):BMI体质指数计算器
  8. 【暑假训练 7.10】 codevs 2492 上帝造题的七分钟2
  9. mysql dns反向解析_Mysql DNS反向解析导致连接超时过程分析(skip-name-resolve)
  10. python怎么输入一个数字并调用_python如何直接输入上一句话,如何快速打出上一句话...
  11. Linux下进程隐藏的常见手法及侦测手段
  12. 如何清除vsphere主机提示“此主机当前没有管理网络冗余”
  13. Linux 平台下 Tomcat 的安装与优化
  14. Microsoft JScript提示‘DIRECT’未定义(2014-08-26记)
  15. (扫盲)WebSocket 教程
  16. gitlab鉴权失败
  17. ART-Pi 实现音乐播放器 --播放《天空之城》
  18. 你还记得远古时代的拨号上网么?快来了解拨号上网与宽带上网的区别
  19. linux中的execl函数使用
  20. markdown(md)编辑 全部格式

热门文章

  1. Python数据分析与展示——Pandas基本操作
  2. 计算机开机怎么设置网络连接,电脑怎么设置开机自动连接宽带
  3. MySQL 最新版行政区划
  4. 利用PyQt5制作本地音乐播放器
  5. 微信视频号自助下单刷平台
  6. 高等数学笔记-苏德矿-第九章-重积分(Ⅱ)-三重积分
  7. k8s源码分析 pdf_我是怎么阅读kubernetes源代码的?
  8. 第十五章 - 垃圾回收相关算法
  9. 28岁程序员身家过亿退休,追寻诗和远方:去日本!
  10. java 中报错 ~[classes/:na]