linux 网桥代码分析之网桥初始化Ⅱ

目录

1 初始化 br_init()

1.1 CAM 转发表初始化 br_fdb_init()

1.2 注册通知链 struct notifier_block br_device_notifier

1.3 注册用户态 ioctl 接口 br_ioctl_deviceless_stub()


1 初始化 br_init()

对于网桥初始化所需要做的功能,主要有以下几项

  1. 调用 stp_proto_register 进行 stp 协议相关的初始化
  2. 调用 br_fdb_init 进行CAM表的初始化
  3. 调用 register_pernet_subsys,为bridge模块注册网络命名空间。而 br_net_ops 的 init函数为NULL,所以调用register_pernet_subsys并没有在/proc目录下生成任何与bridge相关的目录,如果我们想在/proc下生成bridge相关的子目录或子文件,我们可以自己写init函数。
  4. 调用函数 br_netfilter_init,注册网络防火墙相关的钩子函数,主要是实现ebtables相关的功能
  5. 调用函数 register_netdevice_notifier,向通知链中注册网桥感兴趣的信息。
  6. 调用函数 br_netlink_init,进行netlink的初始化
  7. 调用 brioctl_set,设置网桥相关的 ioctl 回调函数 br_ioctl_deviceless_stub
static int __init br_init(void)
{
	int err;

	err = stp_proto_register(&br_stp_proto);
	if (err < 0) {
		pr_err("bridge: can't register sap for STP\n");
		return err;
	}

	err = br_fdb_init();
	if (err)
		goto err_out;

	err = register_pernet_subsys(&br_net_ops);
	if (err)
		goto err_out1;

	err = br_netfilter_init();
	if (err)
		goto err_out2;

	err = register_netdevice_notifier(&br_device_notifier);
	if (err)
		goto err_out3;

	err = br_netlink_init();
	if (err)
		goto err_out4;

	brioctl_set(br_ioctl_deviceless_stub);

#if IS_ENABLED(CONFIG_ATM_LANE)
	br_fdb_test_addr_hook = br_fdb_test_addr;
#endif

	return 0;
err_out4:
	unregister_netdevice_notifier(&br_device_notifier);
err_out3:
	br_netfilter_fini();
err_out2:
	unregister_pernet_subsys(&br_net_ops);
err_out1:
	br_fdb_fini();
err_out:
	stp_proto_unregister(&br_stp_proto);
	return err;
}

1.1 CAM 转发表初始化 br_fdb_init()

该函数主要是调用kmem_cache_create,获取一块slab缓存br_fdb_cache

int __init br_fdb_init(void)
{
	br_fdb_cache = kmem_cache_create("bridge_fdb_cache",
					 sizeof(struct net_bridge_fdb_entry),
					 0,
					 SLAB_HWCACHE_ALIGN, NULL);
	if (!br_fdb_cache)
		return -ENOMEM;

	get_random_bytes(&fdb_salt, sizeof(fdb_salt));
	return 0;
}

1.2 注册通知链 struct notifier_block br_device_notifier

网桥通过调用register_netdevice_notifier,向netdev_chain中注册了网桥相关的链表元素br_device_notifier,br_device_notifier的定义如下(关于通知链的详细分析可参看 linux 内核通知链)。

struct notifier_block br_device_notifier = {
	.notifier_call = br_device_event
};

进入函数br_device_event,我们可以发现网桥关心的事件有以下几个:

#define NETDEV_UP	0x0001	/* For now you can't veto a device up/down */
#define NETDEV_DOWN	0x0002 
#define NETDEV_CHANGE	0x0004	/* Notify device state change */
#define NETDEV_REGISTER 0x0005
#define NETDEV_UNREGISTER	0x0006
#define NETDEV_CHANGEMTU	0x0007
#define NETDEV_CHANGEADDR	0x0008 //需要刷新CAM表
#define NETDEV_CHANGENAME	0x000A
#define NETDEV_FEAT_CHANGE	0x000B

对于NETDEV_CHANGEADDR事件,网桥需要更新CAM表;对于NETDEV_CHANGE、NETDEV_DOWN、NETDEV_UP事件,网桥需要更改网桥端口的状态。

1.3 注册用户态 ioctl 接口 br_ioctl_deviceless_stub()

通过调用 brioctl_set,将 br_ioctl_deviceless_stub() 赋值给回调函数 br_ioctl_hook,而 br_ioctl_hook 在 sock_ioctl 中使用,这样通过在应用层调用 socket 的 ioctl 函数,就能够进行网桥的添加与删除了。

而函数 br_ioctl_deviceless_stub 能够对SIOCBRADDBR、SIOCBRDELBR、BRCTL_GET_BRIDGES、BRCTL_GET_VERSION 等操作。如果我们想增加新的ioctl,用于我们新开放的功能,就可以在该函数里增加新的case即可。

int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
{
	switch (cmd) {
	case SIOCGIFBR:
	case SIOCSIFBR:
		return old_deviceless(net, uarg);

	case SIOCBRADDBR:
	case SIOCBRDELBR:
	{
		char buf[IFNAMSIZ];

		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
			return -EPERM;

		if (copy_from_user(buf, uarg, IFNAMSIZ))
			return -EFAULT;

		buf[IFNAMSIZ-1] = 0;
		if (cmd == SIOCBRADDBR)
			return br_add_bridge(net, buf);

		return br_del_bridge(net, buf);
	}
	}
	return -EOPNOTSUPP;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值