本节主要分析网桥初始化相关的代码
对于网桥初始化所需要做的功能,主要有以下几项
1、CAM表的初始化
2、注册网桥相关的网络防火墙钩子函数
3、向通知链表中注册网桥的回调函数,处理网桥感兴趣的一些事件
4、设置网桥的ioctl,以便处理应用层添加网桥、删除网桥的需求
5、注册网桥处理回调函数,在接收封包处理函数netif_receive_skb中用来处理网桥设备
而网桥相关的初始化,主要是由函数br_init来完成的。
下面是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函数。
关于register_pernet_subsys函数的详细处理流程可看我以前的文档register_pernet_subsys相关学习
static structpernet_operations br_net_ops = {
.exit = br_net_exit,
};
4、调用函数br_netfilter_init,注册网络防火墙相关的钩子函数,主要是实现ebtables相关的功能
5、调用函数register_netdevice_notifier,向通知链中注册网桥感兴趣的信息。
6、调用函数br_netlink_init,进行netlink的初始化
7、调用brioctl_set,设置网桥相关的ioctl回调函数br_ioctl_deviceless_stub,
8、设置br_handle_frame_hook的回调函数
static int __init br_init(void)
{
int err;
err =stp_proto_register(&br_stp_proto);
if (err < 0) {
printk(KERN_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);
br_handle_frame_hook =br_handle_frame;
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
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;
}
下面继续对一些比较重要的初始化函数进行分析:
a) br_fdb_init
该函数主要是调用kmem_cache_create,获取一块slab缓存br_fdb_cache
该函数定义在br_fdb.c中,这次仅分析这一个函数,对于br_fdb.c中的其他函数,我打算专门作为一小节来分析
b) 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,我们可以发现网桥关心的事件有以下几个:
NETDEV_CHANGEMTU、NETDEV_CHANGEADDR、NETDEV_CHANGE、NETDEV_FEAT_CHANGE、NETDEV_DOWN、NETDEV_UP
对于NETDEV_CHANGEADDR事件,网桥需要更新CAM表;对于NETDEV_CHANGE、NETDEV_DOWN、NETDEV_UP事件,网桥需要更改网桥端口的状态。对于网桥通知链相关的代码,在文件br_notify.c中。
c)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:
returnold_deviceless(net, uarg);
case SIOCBRADDBR:
case SIOCBRDELBR:
{
charbuf[IFNAMSIZ];
if(!capable(CAP_NET_ADMIN))
return -EPERM;
if(copy_from_user(buf, uarg, IFNAMSIZ))
return -EFAULT;
buf[IFNAMSIZ-1] =0;
if (cmd ==SIOCBRADDBR)
returnbr_add_bridge(net, buf);
returnbr_del_bridge(net, buf);
}
}
return -EOPNOTSUPP;
}
d) br_handle_frame
通过把该函数赋值给回调函数br_handle_frame_hook,就可以在接收函数netif_receive_skb中增加对桥接代码的处理了,该函数是处理网桥入口流量的关键函数。
以上就是网桥初始化有关的代码