网络设备初始化
函数alloc_netdev_mqs分配一个网络设备结构体net_device,in_device结构体由inetdev_init分配和初始化。
struct net_device *alloc_netdev_mqs(...)
{
struct net_device *p;
p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
}
static struct in_device *inetdev_init(struct net_device *dev)
{
struct in_device *in_dev;
in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
}
两者的关联
net_device结构主要用于内核自身(设备驱动、上层协议等)对网络设备的操作;in_device主要是保存用户态对此设备的配置信息,比如IP地址的配置,其保存在in_device的成员ifa_list中。两个结构体通过指针互指联系在一起。
新创建的网络设备net_device在注册的时候,会调用netdevice通知链发送NETDEV_REGISTER事件。在此链上,有devinet_init注册的处理函数inetdev_event。其判断net_device是否已有相应的in_device结构,没有的话调用inetdev_init分配。inetdev_init函数最后,将初始化后的in_device结构赋值给net_device的ip_ptr指针。
新创建的网络设备net_device在注册的时候,会调用netdevice通知链发送NETDEV_REGISTER事件。在此链上,有devinet_init注册的处理函数inetdev_event。其判断net_device是否已有相应的in_device结构,没有的话调用inetdev_init分配。inetdev_init函数最后,将初始化后的in_device结构赋值给net_device的ip_ptr指针。
int register_netdevice(struct net_device *dev)
{
ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);
}
static struct in_device *inetdev_init(struct net_device *dev)
{
in_dev->dev = dev; //net_device赋值到in_device结构体的成员dev
rcu_assign_pointer(dev->ip_ptr, in_dev); //in_device赋值到net_device的成员ip_ptr。
}
static int inetdev_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct in_device *in_dev = __in_dev_get_rtnl(dev);
if (!in_dev) {
if (event == NETDEV_REGISTER) {
in_dev = inetdev_init(dev); //net_device注册事件,分配in_device。
}
}
}
static struct notifier_block ip_netdev_notifier = {
.notifier_call = inetdev_event,
};
void __init devinet_init(void)
{
register_netdevice_notifier(&ip_netdev_notifier); //注册netdevice的通知链
}
net_device结构体
net_device结构体,定义的相关成员包括:网卡硬件类信息、统计类信息、上层协议处理接口、流控接口等。
struct net_device {
int irq;
struct list_head ptype_all; //监听此设备上所有报文的处理函数链表
int ifindex; //接口索引值
struct net_device_stats stats; //收发报文的统计信息
const struct net_device_ops *netdev_ops;
const struct ethtool_ops *ethtool_ops;
struct in_device __rcu *ip_ptr;
struct inet6_dev __rcu *ip6_ptr;
rx_handler_func_t __rcu *rx_handler;
void __rcu *rx_handler_data;
struct netdev_queue __rcu *ingress_queue;
struct Qdisc *qdisc; //流控
};
in_device结构体
in_device结构体,其中两个重要成员:保存邻居表配置的arp_parms和保存IPv4配置的cnf,二者保存的参数都可通过相应的proc文件修改。struct neigh_parms {
void *sysctl_table; //neigh_sysctl_table实现proc文件操作data中配置
int data[NEIGH_VAR_DATA_MAX];
DECLARE_BITMAP(data_state, NEIGH_VAR_DATA_MAX);
};
struct ipv4_devconf {
void *sysctl; //devinet_sysctl_table实现proc文件操作data中配置
int data[IPV4_DEVCONF_MAX];
DECLARE_BITMAP(state, IPV4_DEVCONF_MAX);
};
struct in_device {
struct net_device *dev;
struct in_ifaddr *ifa_list; //IP地址链
... //多播相关结构
struct neigh_parms *arp_parms; //邻居表(arp)参数配置
struct ipv4_devconf cnf; //IPv4相关device配置
};
arp_parms和cnf的初始化
static struct in_device *inetdev_init(struct net_device *dev)
{
struct in_device *in_dev;
memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, //cnf取默认值dev_net(dev)->ipv4.devconf_dflt
sizeof(in_dev->cnf));
in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); //arp_parms取默认值arp_tbl->parms
err = devinet_sysctl_register(in_dev); //注册sysctl处理函数
}
arp_parms结构体
struct neigh_table arp_tbl = {
.parms = {
.tbl = &arp_tbl,
.reachable_time = 30 * HZ,
.data = {
[NEIGH_VAR_RETRANS_TIME] = 1 * HZ,
[NEIGH_VAR_BASE_REACHABLE_TIME] = 30 * HZ,
...
}
}
}
可通过proc文件修改in_device设备(eth0)的arp参数:
echo 200 > /proc/sys/net/ipv4/neigh/eth0/retrans_time
echo 3010 > /proc/sys/net/ipv4/neigh/eth0/base_reachable_time
echo 3010 > /proc/sys/net/ipv4/neigh/eth0/base_reachable_time
邻居表可配置值的定义在文件include/net/neighbour.h中。
enum {
NEIGH_VAR_RETRANS_TIME,
NEIGH_VAR_BASE_REACHABLE_TIME,
...
}
内核定了了两个配置项操作函数,NEIGH_VAR与NEIGH_VAR_SET:
static inline void neigh_var_set(struct neigh_parms *p, int index, int val)
{
set_bit(index, p->data_state);
p->data[index] = val;
}
#define NEIGH_VAR(p, attr) ((p)->data[NEIGH_VAR_ ## attr]) //获取attr配置的值,如NEIGH_VAR(p, BASE_REACHABLE_TIME)
#define NEIGH_VAR_SET(p, attr, val) neigh_var_set(p, NEIGH_VAR_ ## attr, val) //设置attr的值
cnf结构体
in_dev的cnf默认取值于全局变量ipv4_devconf_dflt:
static struct ipv4_devconf ipv4_devconf_dflt = {
.data = {
[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
...
},
};
可配置值定义在include/uapi/linux/ip.h文件:
enum
{
IPV4_DEVCONF_FORWARDING=1,
IPV4_DEVCONF_MC_FORWARDING,
...
}
可通过proc文件修改设备(eth0)的配置参数:
echo 1 > /proc/sys/net/ipv4/conf/eth0/mc_forwarding 内核定义的配置项操作函数,IN_DEV_CONF_GET与IN_DEV_CONF_SET,位于文件include/linux/inetdevice.h。
static inline int ipv4_devconf_get(struct in_device *in_dev, int index)
{
index--;
return in_dev->cnf.data[index];
}
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;
}
#define IN_DEV_CONF_GET(in_dev, attr) \
ipv4_devconf_get((in_dev), IPV4_DEVCONF_ ## attr)
#define IN_DEV_CONF_SET(in_dev, attr, val) \
ipv4_devconf_set((in_dev), IPV4_DEVCONF_ ## attr, (val))
网络命名空间ipv4_devconf
另外要注意,系统还有一组基于net namespace的ipv4_devconf配置参数,其初值定义如下:
static struct ipv4_devconf ipv4_devconf = {
.data = {
[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
...
},
};
也可通过proc文件修改其配置:
echo 1 > /proc/sys/net/ipv4/forwarding
echo 1 > /proc/sys/net/ipv4/mc_forwarding
在内核代码中,可通过宏定义IPV4_DEVCONF_ALL获取其中attr的值:
#define IPV4_DEVCONF(cnf, attr) ((cnf).data[IPV4_DEVCONF_ ## attr - 1])
#define IPV4_DEVCONF_ALL(net, attr) \
IPV4_DEVCONF((*(net)->ipv4.devconf_all), attr)
由于存在两套ipv4_devconf参数值(基于in_device的值和基于net namespace的值),内核定义了如下的判断宏,两者and/or/max:
#define IN_DEV_ANDCONF(in_dev, attr) \
(IPV4_DEVCONF_ALL(dev_net(in_dev->dev), attr) && \
IN_DEV_CONF_GET((in_dev), attr))
#define IN_DEV_NET_ORCONF(in_dev, net, attr) \
(IPV4_DEVCONF_ALL(net, attr) || \
IN_DEV_CONF_GET((in_dev), attr))
#define IN_DEV_ORCONF(in_dev, attr) \
IN_DEV_NET_ORCONF(in_dev, dev_net(in_dev->dev), attr)
#define IN_DEV_MAXCONF(in_dev, attr) \
(max(IPV4_DEVCONF_ALL(dev_net(in_dev->dev), attr), \
IN_DEV_CONF_GET((in_dev), attr)))
内核版本
linux-4.14.4