网桥
网桥属于一种虚拟设备,所以在使用之前需要先初始化和注册
网桥部分的初始化在/net/bridge/br.c中,通过初始化函数br_init函数实现
在br_init函数中通过brioctl_set(br_ioctl_deviceless_stub);来创建一个
网桥设备。brioctl_set(br_ioctl_deviceless_stub)->br_add_bridge->new_bridge_dev
最后通过new_bridge_dev创建出一个网桥设备。新创建的ops对应/net/bridge/br_device.c
中的br_netdev_ops。
值得一说的是new_bridge_dev函数返回的也是一个net_device结构,这个结构由new_bridge_dev
调用函数br_dev_setup进行初始化。但是这个net_device是包含在net_bridge中的数据结构,
属于net_bridge的netdev_pri(dev)。net_bridge和net_device的关系可一参考 网络技术内幕 的366页
的图片非常清楚。其链表上的每个net_device对应的才是一块真是的NIC,一块NIC对应一个端口。
网桥设备建立之后需要向网桥设备添加端口了,相当于将网卡对应的net_device绑定到net_bridge上。
没绑定端口的网桥当然是个废设备了。添加端口由函数br_add_if函数实现。br_add_if何时被调用的?
具体调用过程比较长,直接中中间截断开始分析,
从dev_ifsioc开始,具体如下:
dev_ifsioc->br_dev_ioctl
在br_dev_ioctl函数中有如下代码:
... ...
cmd == SIOCBONDCHANGEACTIVE ||
cmd == SIOCGMIIPHY ||
cmd == SIOCGMIIREG ||
cmd == SIOCSMIIREG ||
cmd == SIOCBRADDIF ||
cmd == SIOCBRDELIF ||
cmd == SIOCSHWTSTAMP ||
cmd == SIOCWANDEV) {
err = -EOPNOTSUPP;
if (ops->ndo_do_ioctl) {
if (netif_device_present(dev))
err = ops->ndo_do_ioctl(dev, ifr, cmd);
else
err = -ENODEV;
}
... ...
根据标红处的提示可以发现bridge add interface的时候会执行下面的代码
具体就是ops->ndo_do_ioctl
这会到时net_bridge中的net_device中的ops被执行。而这个ops在新建网桥设备的时候会
初始化为br_netdev_ops,其中的ndo_do_ioctl方法就是br_dev_ioctl
此处就是相当于执行br_dev_ioctl函数。
br_dev_ioctl->add_del_if->br_add_if
具体添加的端口就在br_add_if中完成了。
网桥新建或还需要通过open函数来启动,非常类似dm9000网卡设备。dm9000注册完毕后不是
直接就可以使用的,还需要调用dev_open函数来启动。在dm9000_open函数中还会做一些初始
化的操作,比如注册dm9000网卡中断处理函数等。网桥也一样,需要通过open方法,即br_dev_open
来启动了,并且在这个open方法中对网桥设备做些初始化操作,比如初始化端口等,更新端口状态等。
等所有这些操作执行完毕 网桥设备才可以启用。
网桥设备的帧处理函数br_handle_frame在br_add_if的时候已经通过函数
netdev_rx_handler_register(dev, br_handle_frame, p)注册到
net_bridge的net_device的rx_handler中了。
在L2的帧处理函数__netif_receive_skb中通过语句
rx_handler = rcu_dereference(skb->dev->rx_handler);
调用的就是bridge处理函数br_handle_frame了