(1) bridge ioctl
int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct net_bridge *br = netdev_priv(dev);
switch(cmd) {
case SIOCDEVPRIVATE:
return old_dev_ioctl(dev, rq, cmd);
case SIOCBRADDIF:
case SIOCBRDELIF:
return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);
}
pr_debug("Bridge does not support ioctl 0x%x\n", cmd);
return -EOPNOTSUPP;
}
static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
通过这个可以看出来,在调用add_del_if的时候,使用了cmd==SIOCBRADDIF. 在函数声明里面用了 int isadd.
这样当函数和调用者在不同的文件里面的时候,用这种方法,可以不需要让add_def_if()所在的文件里面包含
SIOCBRADDIF的声明。
(2)设置网桥
转自21CN
网桥工具的安装:
默认Ubuntu是没有网桥设置工具(brctl)的。你需要安装bridge-utils,这里在我的pc2上:
SYSHUNTER-UBUNTU# apt-get install bridge-utils
配置网桥:
先创建一个网桥接口:
SYSHUNTER-UBUNTU# brctl addbr br0
将两块已有的网卡添加到网桥:
SYSHUNTER-UBUNTU# brctl addif br0 eth0
SYSHUNTER-UBUNTU# brctl addif br0 eth1
将两块网卡IP设置为0,它们已经不再需要了:
SYSHUNTER-UBUNTU# ifconfig eth0 0.0.0.0
SYSHUNTER-UBUNTU# ifconfig eth1 0.0.0.0
给新网桥设置一个IP:
SYSHUNTER-UBUNTU# ifconfig br0 192.168.1.10
(3)用户空间工具
ifconfig工具在包net-tools里面,查看其源代码,发现ifconfig使用的是ioctl来与内核空间交互的。
fd=socket(.....);
ret=ioctl(fd,cmd,XXX);
由于用的socket文件句柄所以调用sock_ioctl( );
(4)bridge默认不编译到内核里面,可以设置其编译进内核。
bridge相关初始化代码在net/bridge/br.c
int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
static int __init br_init(void)
{
br_fdb_init();
#ifdef CONFIG_BRIDGE_NETFILTER
if (br_netfilter_init())
return 1;
#endif
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_get_hook = br_fdb_get;
br_fdb_put_hook = br_fdb_put;
#endif
register_netdevice_notifier(&br_device_notifier);
return 0;
}
module_init(br_init)
可以看到bridge模块的初始化函数是br_init.
他做了几件事情:(netfilter的部分先不管)
1)初始化forwarding database.
2) 设置bridge相关的ioctl
3)设置收包函数
4)注册bridge模块的notifier.
(5)
当用户调用 ### brctl addbr br0
建一个新的bridge.
用户空间程序会调用ioctl,并最终调用到br_ioctl_deviceless_stub()
这个函数从用户空间得到bridge的名字,并调用br_add_bridge( )添加该bridge.
br_add_bridge()主要做几件事情:
1)建一个net_bridge结构和一个net_device结构,这2个结构相对应,代表了这个bridge.
net_bridge br;
net_device dev;
dev->priv = br;
br->dev = dev;
2)register_netdevice(dev);
3)ret = br_sysfs_addbr(dev);
4)初始化一些与stp相关的东西。
(6)当用户调用 ### brctl addif br0 eth0
把eth0添加到bridge里面,成为bridge的一个端口。
会调用br_dev_ioctl( )。
并最终调用ret = br_add_if(br, dev);来添加这个端口。
会做下面几件事:1)生成一个net_bridge_port结构,表示这个端口。
2)由于该端口本身就是一个实际的设备,如一块网卡,所以他本身有一个net_device结构。
net_bridge_port port;
net_device dev;
net_bridge br;
port->dev = dev;
port->br = br;
dev->br_port = port;
add_list(br->portlist,port);
3)设置stp相关的部分。
(7)收包。
当网卡被加入到bridge里面以后,其net_device结构中多了一项 dev->br_port = port; (该dev对应bridge port)
包进网卡还是按照正常流程走。
到了netif_receive_skb()里面会调用
if (handle_bridge(&skb, &pt_prev, &ret))
goto out;
进行bridge相关的包处理。
static __inline__ int handle_bridge(struct sk_buff **pskb,
struct packet_type **pt_prev, int *ret)
{
struct net_bridge_port *port;
if ((*pskb)->pkt_type == PACKET_LOOPBACK ||
(port = rcu_dereference((*pskb)->dev->br_port)) == NULL)
return 0;
if (*pt_prev) {
*ret = deliver_skb(*pskb, *pt_prev);
*pt_prev = NULL;
}
return br_handle_frame_hook(port, pskb);
}
可以看到如果该包是loopback的直接返回。
如果收该包的dev不是一个bridge port则返回。
所以当一个dev被加成一个bridge port的时候,从这个dev收来的包就会调用 br_handle_frame_hook()
进行处理。
(8)bridge相关的包处理进行以下步骤:
对包进行判断,drop错包
|
V
进行源地址学习
|
V
目的地址判断---------BPDU包---->进行
|
V
查找fdb做转发判断
+
flooding<-----+----->forward to other bridge port
|
V
hand packet to upper layer
当需要把这个skb送到upper layer的时候,会把skb中的dev域替换成bridge的dev.