概念
- RCU机制:Linux的同步机制,读-拷贝修改。原理是,RCU保护的共享数据结构,读者不需要锁,写者要写的时候先拷贝一份副本,修改副本,等到没有读者读原数据之后,将指向原数据的指针改为指向副本。允许多个读者同时访问被保护数据,是否允许多个写者并行访问取决于写者间的同步机制。
- RTNL互斥锁:内核的接口很多都显式的要求在rtnl lock的保护下,它是一个mutex锁,用来保护netdevice等相关的数据,在内核、用户配置间的一致性。
PS:内核代码版本为4.18.0
模块初始化
网桥
网桥的作用:
1、起初,当网桥没有记录mac和端口对应关系时,会将报文在其所有端口广播发出,类似HUB。
2、每转发一个报文,都学习源MAC与端口的对应关系,建立地址-端口的对照表(CAM表)。
3、每转发一个报文,都根据目的MAC地址,从CAM表中查找对应发送数据的端口。
(net/bridge/br_private.h文件定义了桥相关的结构体)
struct net_device: 网络设备结构
struct sk_buff: 应用层的数据包结构
struct net_bridge: 桥的结构体
struct net_bridge_port: 桥的端口成员结构体
struct net_bridge_fdb_entry: 桥的CAM转发表项结构体,记录端口和mac地址信息
struct net_bridge_mdb_htable: 组播组转发表结构体
struct net_bridge_mdb_entry: 组播组转发表项结构体
struct net_port_vlans: 端口vlan结构体
struct mac_addr: MAC地址结构体
网桥初始化
//net/bridge/br.c
static int __init br_init(void)
{
err = stp_proto_register(&br_stp_proto); //生成树协议
err = br_fdb_init(); //kmem_cache_create创建struct net_bridge_fdb_entry高速缓存,用来记录网桥端口中mac地址信息
err = register_pernet_subsys(&br_net_ops); //在一个网络命名空间里注册子系统
err = br_nf_core_init(); //路由相关,初始化struct dst_ops fake_dst_ops的percpu_counter计数器,用于统计与加锁(net/bridge/br_nf_core.c/include/net/dst_ops.h)
err = register_netdevice_notifier(&br_device_notifier); //struct notifier_block br_device_notifier定义通知调用函数br_device_event,处理网桥相关的事件
err = register_switchdev_notifier(&br_switchdev_notifier); //struct notifier_block br_switchdev_notifier定义通知调用函数br_switchdev_event,学习MAC地址
err = br_netlink_init(); //初始化netlink,用来与用户程序通信,用到rtnl(net/bridge/br_netlink.c)
brioctl_set(br_ioctl_deviceless_stub); //注册一个函数,在添加新的br时使用
}
//net/bridge/br_netfilter_hooks.c
//注册bridge的hook函数
static int __init br_netfilter_init(void)
{
ret = register_pernet_subsys(&brnf_net_ops); //注册子系统
ret = register_netdevice_notifier(&brnf_notifier); //struct brnf_device_event定义了通知调用函数brnf_device_event,调用nf_register_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops))注册hook函数
#ifdef CONFIG_SYSCTL
brnf_sysctl_header = register_net_sysctl(&init_net, "net/bridge", brnf_table); //使应用程序空间用户能够方便修改内核参数
#endif
RCU_INIT_POINTER(nf_br_ops, &br_ops); //创建nf_br_ops的RCU指针
printk(KERN_NOTICE "Bridge firewalling registered\n");
return 0;
}
- br_add_bridge(net/bridge/br_if.c)添加网桥时,调用br_dev_setup(net/bridge/br_device.c)初始化一些网桥的信息,eth_hw_addr_random硬件MAC地址,ether_setup以太网信息,SET_NETDEV_DEVTYPE,br_netfilter_rtable_init(net/bridge/br_nf_core.c)路由表等。
- br_add_if(net/bridge/br_if.c)添加网桥端口,调用netdev_rx_handler_register(dev, br_handle_frame, p)注册handler,dev为设备,br_handle_frame为接收处理函数,p为端口数据(net/core/dev.c)。
//net/core/dev.c
int netdev_rx_handler_register(struct net_device *dev,
rx_handler_func_t *rx_handler,
void *rx_handler_data)
{
/* Note: rx_handler_data must be set before rx_handler */
rcu_assign_pointer(dev->rx_handler_data, rx_handler_data); //struct net_bridge_port *p
rcu_assign_pointer(dev->rx_handler, rx_handler); //br_handle_frame
return 0;
}
Netfilter初始化
从初始化可以看出,netfilter的钩子是一个二维链表,不同协议下分别是各hook点的链表,以优先级为顺序。
static int __init sock_init(void)
{
int err;
/*
* Initialize the network sysctl infrastructure.
*/
err = net_sysctl_init();
if (err)
goto out;
/*
* Initialize skbuff SLAB cache
*/
skb_init();
/*
* Initialize the protocols module.
*/
init_inodecache();
err = register_filesystem(&sock_fs_type);
if (err)
goto out_fs;
sock_mnt = kern_mount(&sock_fs_type);
if (IS_ERR(sock_mnt)) {
err = PTR_ERR(sock_mnt);
goto out_mount;
}
/* The real protocol initialization is performed in later initcalls.
*/
#ifdef CONFIG_NETFILTER
err = netfilter_init(); //初始化netfilter(net/netfilter/core.c),netfilter_net_ops定义初始化函数netfilter_net_init
if (err)
goto out;
#endif
ptp_classifier_init();
out:
return err;
out_mount:
unregister_filesystem(&sock_fs_type);
out_fs:
goto out;
}
core_initcall(sock_init); /* early initcall */
//net/netfilter/core.c
static int __net_init netfilter_net_init(struct net *net)
{
//区分协议
__netfilter_net_init(net->nf.hooks_ipv4, ARRAY_SIZE(net->nf.hooks_ipv4));
__netfilter_net_init(net->nf.hooks_ipv6, ARRAY_SIZE(net->nf.hooks_ipv6));
#ifdef CONFIG_NETFILTER_FAMILY_ARP
__netfilter_net_init(net->nf.hooks_arp, ARRAY_SIZE(net->nf.hooks_arp));
#endif
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
__netfilter_net_init(net->nf.hooks_bridge, ARRAY_SIZE(net->nf.hooks_bridge));
#endif
#if IS_ENABLED(CONFIG_DECNET)
__netfilter_net_init(net->nf.hooks_decnet, ARRAY_SIZE(net->nf.hooks_decnet));
#endif
#ifdef CONFIG_PROC_FS
net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter",
net->proc_net);
if (!net->nf.proc_netfilter) {
if (!net_eq(net, &init_net))
pr_err("cannot create netfilter proc entry");
return -ENOMEM;
}
#endif
return 0;
}
static void __net_init
__netfilter_net_init(struct nf_hook_entries __rcu **e, int max)
{
int h;
for (h = 0; h < max; h++)
RCU_INIT_POINTER(e[h], NULL);
}
HOOK
钩子函数声明
//include/linux/netfilter.h
static inline int
NF_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct sk_buff *skb,
struct net_device *in, struct net_device *out,
int (*okfn)(struct net *, struct sock *, struct sk_buff *))
{
int ret = nf_hook(pf, hook,