网桥的创建:
->br_add_bridge() 创建网桥的核心工作,创建一个与网桥同名的网络设备。可以通过该设备分配的IP地址来管理该网桥。 同时该设备 是虚
拟的设备,它的接收包和发送包处理函数与一般的真实网卡设备不同。
->dev =
new_bridge_dev(net, name); 分配一个net_device结构
->SET_NETDEV_DEVTYPE(dev, &br_type); 设置该设备为网桥类型
->register_netdevice(dev); 向kernel注册该网桥设备,这样在用户空间就以使用ifconfig来为之分配IP,或通ioctl来对该网桥添加新的接口。
->new_bridge_dev()
->dev = alloc_netdev(sizeof(struct net_bridge), name,br_dev_setup); 分配net_device结构,它的priv数据为net_bridge结构体
br_dev_setup函数初化了net_device结构的很多函数指针。
->br_dev_setup()
->dev->netdev_ops = &
br_netdev_ops; 注册了网桥设备操作函数 提供用户态的相关配置处理
static const struct net_device_ops
br_netdev_ops
= {
.ndo_open = br_dev_open,
.ndo_stop = br_dev_stop,
.ndo_start_xmit = br_dev_xmit,
.ndo_set_mac_address = br_set_mac_address,
.ndo_set_multicast_list = br_dev_set_multicast_list,
.ndo_change_mtu = br_change_mtu,
.ndo_do_ioctl = br_dev_ioctl,
};
仅仅创建网桥,还是不够的。实际应用中的网桥需要添加实际的端口(即物理接口)
->add_del_if(struct net_bridge *br, int ifindex, int isadd) br 网桥,ifindex 添加/删除物理接口的index
->dev = dev_get_by_index(dev_net(br->dev), ifindex); 通过index得到对应的dev结构
->br_add_if(br, dev);
dev->flags & IFF_LOOPBACK) || dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN 仅支持以太网网桥
dev->netdev_ops->ndo_start_xmit == br_dev_xmit 把网桥接口当作物理接口加入到另一个网桥中,是不行的
dev->br_port != NULL 该物理接口已经绑定到另一个网桥
->p =
new_nbp(br, dev); 为该接口创建一个网桥端口数据,并初始化好该端口的相关数据
网桥端口结构
struct net_bridge_port
{
struct net_bridge *br;
struct net_device *dev;
struct list_head list;
/* STP */
u8 priority;
u8 state;
u16 port_no;
unsigned char topology_change_ack;
unsigned char config_pending;
port_id port_id;
port_id designated_port;
bridge_id designated_root;
bridge_id designated_bridge;
u32 path_cost;
u32 designated_cost;
struct timer_list forward_delay_timer;
struct timer_list hold_timer;
struct timer_list message_age_timer;
struct kobject kobj;
struct rcu_head rcu;
unsigned long flags;
#define BR_HAIRPIN_MODE 0x00000001
}
->br_fdb_insert(br, p, dev->dev_addr);
->fdb_insert(br, source, addr); 将该接口的物理地址写入到 MAC-端口映射表中。该MAC是属于网桥内部端口的固定MAC地址,
它在fdb中的记录是固定的,不会失效(agged)
->dev_set_promiscuity(dev, 1); 打开该接口的混杂模式,网桥中的各个端口必须处于混杂模式,网桥才能正确工作
->list_add_rcu(&p->list, &br->port_list); 加到端口列表
net_bridge结构:
struct
net_bridge
{
spinlock_t lock;
struct list_head port_list;
struct net_device *dev;
spinlock_t hash_lock;
struct hlist_head hash[BR_HASH_SIZE];
struct list_head age_list;
unsigned long feature_mask;
#ifdef CONFIG_BRIDGE_NETFILTER
struct rtable fake_rtable;
#endif
unsigned long flags;
#define BR_SET_MAC_ADDR 0x00000001
/* STP */
bridge_id designated_root;
bridge_id bridge_id;
u32 root_path_cost;
unsigned long max_age;
unsigned long hello_time;
unsigned long forward_delay;
unsigned long bridge_max_age;
unsigned long ageing_time;
unsigned long bridge_hello_time;
unsigned long bridge_forward_delay;
u8 group_addr[ETH_ALEN];
u16 root_port;
enum {
BR_NO_STP, /* no spanning tree */
BR_KERNEL_STP, /* old STP in kernel */
BR_USER_STP, /* new RSTP in userspace */
} stp_enabled;
unsigned char topology_change;
unsigned char topology_change_detected;
struct timer_list hello_timer;
struct timer_list tcn_timer;
struct timer_list topology_change_timer;
struct timer_list gc_timer;
struct kobject *ifobj;
}
摘自ULNI:
其中最左边的
net_device是一个代表网桥的虚拟设备结构,它关联了一个
net_bridge结构,这是网桥设备所特有的数据结构。
在net_bridge结构中,
port_list成员下挂一个链表,链表中的每一个节点(
net_bridge_port结构)关联到一个
真实的网口设备的net_devic
e。网口设备也通过其
br_port指针做反向的关联(那么显然,一个网口最多只能同时被绑定到一个网桥)。
net_bridge结构中还维护了一个hash表,是用来处理地址学习的。当网桥准备转发一个报文时,以报文的目的Mac地址为key,如果可以在hash表中索引到一个
net_bridge_fdb_entry结构,通过这个结构能找到一个网口设备的net_device,于是报文就应该从这个网口转发出去;否则,报文将从所有网口转发。