上一节,讲述桥模式初始化时需要做的一些事情,这一节,我们一起来看看如何添加一个网桥设备。
我们先来看一个命令:
**brctl addbr br1**
上节我们提到一个用来处理ioctl命令的函数br_ioctl_deviceless_stub通过调用brioctl_set,
将br_ioctl_deviceless_stub赋值给回调函数br_ioctl_hook,而br_ioctl_hook在sock_ioctl中使用。
这样通过在应用层调用socket的ioctl函数,就能够进行网桥的添加与删除了。
如果我们想增加新的ioctl,用于我们新开放的功能,就可以在该函数里增加新的case即可。
当我们输入上面命令时,就会触发br_ioctl_deviceless_stub函数来响应br_add_bridge函数,当命令执行完成以后,
使用brctl show命令就可以看见我们添加的br1这个网桥设备已经生成。
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;
}
好!知道br_ioctl_deviceless_stub的功能后,我们来看看br_add_bridge,这个函数添加桥设备需要初始化哪些东西:
int br_add_bridge(struct net *net, const char *name)
{
struct net_device *dev;
int res;
/*为设备分配一块内存,并且调用br_dev_setup函数初始化*/
dev = alloc_netdev(sizeof(struct net_bridge), name, NET_NAME_UNKNOWN,
br_dev_setup);
if (!dev)
return -ENOMEM;
dev_net_set(dev, net);
/*初始化dev的netlink链接通知操作函数*/
dev->rtnl_link_ops = &br_link_ops;
/*注册一个网络设备*/
res = register_netdev(dev);
if (res)
free_netdev(dev);
return res;
}
br_dev_setup函数用来初始化桥设备所需要的基本数据,尤其是在br_netdev_ops,中指定了很多的设备函数,
包括启用,关闭网桥,修改mtu以及设备ioctl函数,添加或删除,桥下设备等等一系列函数,这些函数的具体作用我们会在后续的章节中继续讨论。
void br_dev_setup(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
eth_hw_addr_random(dev);
ether_setup(dev);
/*指定网络设备的管理钩子,关于各个钩子函数的作用以及用法,
请参见/linux/netdev.h中的net_device_ops结构体描述,这是很重要的一部分*/
dev->netdev_ops = &br_netdev_ops;
/*可选netdev操作, 关于各个钩子函数的作用以及用法,
请参见/linux/ethtool.h中的ethtool_ops结构体描述*/
dev->destructor = br_dev_free;
dev->ethtool_ops = &br_ethtool_ops;
SET_NETDEV_DEVTYPE(dev, &br_type);
/*IFF_EBRIDGE内核用来区别网桥设备和其他类型的设备*/
dev->priv_flags = IFF_EBRIDGE | IFF_NO_QUEUE;
dev->features = COMMON_FEATURES | NETIF_F_LLTX | NETIF_F_NETNS_LOCAL |
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
dev->hw_features = COMMON_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX;
dev->vlan_features = COMMON_FEATURES;
br->dev = dev;
spin_lock_init(&br->lock);
INIT_LIST_HEAD(&br->port_list);
spin_lock_init(&br->hash_lock);
/*制定默认优先权*/
br->bridge_id.prio[0] = 0x80;
br->bridge_id.prio[1] = 0x00;
ether_addr_copy(br->group_addr, eth_reserved_addr_base);
br->stp_enabled = BR_NO_STP;
br->group_fwd_mask = BR_GROUPFWD_DEFAULT;
br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
br->designated_root = br->bridge_id;
br->bridge_max_age = br->max_age = 20 * HZ;
br->bridge_hello_time = br->hello_time = 2 * HZ;
br->bridge_forward_delay = br->forward_delay = 15 * HZ;
/*老化时间初值默认为5分钟*/
br->ageing_time = BR_DEFAULT_AGEING_TIME;
/*初始化该网桥的netfilter*/
br_netfilter_rtable_init(br);
/*初始化网桥的各类定时器,hello定时器,垃圾回收定时器等等*/
br_stp_timer_init(br);
/*多播初始化*/
br_multicast_init(br);
}
在上面的函数中,除了br_netdev_ops需要注意以外还有一个需要注意的函数br_netfilter_rtable_init(br);
这个函数是用来初始化Bridging-Firewalling,在后续的章节中,可以看到Netfilter钩子(hook)在桥接程序用于处理入口和出口网络流量的主要位置。
另外,在编译内核时,选中:
Networking support->Networking options->Networking packet filtering(replaces ipchains) -> Bridged IP/ARP packet filtering后,
内核就支持Bridging-Firewalling.Ethernel-Bridging-Tables选项(也就是ebtables).
以上就是,桥设备初始化的相关操作。