网桥VLAN GROUP组

网桥新加入的VLAN group功能,替换了之前用bitmap来保存接口vlan信息的设计,bitmap主要用来做vlan filtering功能。加入VLAN group之后,不仅可以做vlan filtering,还可以实现global per-vlan features, but not for filtering。另外增加了vlan隧道功能,根据tunnel id为没有vlan的数据包添加tunnel绑定的vlan id。

之前网桥与子接口相关的vlan信息都由net_port_vlans结构保存。一个接口配置的所有vlan id都按照tag/untag属性保存在不同的bitmap中。

struct net_port_vlans {
    u16             pvid;
    unsigned long   vlan_bitmap[BR_VLAN_BITMAP_LEN];
    unsigned long   untagged_bitmap[BR_VLAN_BITMAP_LEN];
    u16             num_vlans;
};


VLAN GROUP加入之后,去掉了以上的net_port_vlan结构体,网桥及其子接口的结构体都新增了一个net_bridge_vlan_group成员,用来保存所有配置的vlan表项(net_bridge_vlan)信息,vlan单个表项结构体名字上虽带有bridge,但是不只网桥,子接口也是使用net_bridge_vlan表示自己的单个vlan表项。

struct net_bridge_vlan_group {
    struct rhashtable       vlan_hash;
    struct rhashtable       tunnel_hash;
    struct list_head        vlan_list;
    u16             num_vlans;
    u16             pvid;
};


新的VLAN GROUP的定义,去掉了net_port_vlan中的bitmap结构,改为链表保存vlan表项信息。其中flags变量中的bit2位用来区分vlan是tag还是untag:

#define BRIDGE_VLAN_INFO_UNTAGGED   (1<<2)  /* VLAN egresses untagged */

既然网桥与子接口共用net_bridge_vlan结构表示vlan表项,就涉及到如何区分二者。其定义中有两个联合体用来标识此vlan为网桥或者子接口vlan时,具有的不同成员。当为网桥的vlan表项时,第一个联合体中br指针指向网桥(net_bridge),port指针为空;否则为子接口vlan表项时,port指针指向子接口,br指针为空。

如果flags设置了BRIDGE_VLAN_INFO_MASTER标志,第一个联合体中br指针指向网桥(net_bridge);如果flags没有MASTER标志,br无效,port指针指向网桥子接口的数据结构。当此vlan做为一个子接口的vlan表项,brvlan指向此接口所属网桥的VLAN GROUP中具有相同vlan id的vlan表项。

struct net_bridge_vlan {
    struct rhash_head       vnode;
    u16             vid;
    u16             flags;
    union {
        struct net_bridge   *br;
        struct net_bridge_port  *port;
    };
    union {
        refcount_t      refcnt;
        struct net_bridge_vlan  *brvlan;
    };

}


网桥VLAN的添加


VLAN的添加分为2种,一个是为网桥添加VLAN(br_vlan_add),另一种是为网桥子接口添加VLAN(nbp_vlan_add)。为网桥自身添加vlan时,flags中设置BRIDGE_VLAN_INFO_MASTER标志,如果此vlan也可用于vlan过滤功能需要设置BRIDGE_VLAN_INFO_BRENTRY标志。以上两个标志表示此vlan为网桥vlan组中的vlan并且要用于vlan过滤。使用bridge命令为网桥添加master的vlan时,内核默认此vlan做过滤使用(自动加上BRIDGE_VLAN_INFO_BRENTRY标志)。

ip link add br_test type bridge
ip link set eth0 master br_test

bridge vlan add dev br_test vid 10 self
bridge vlan add dev br_test vid 20 master


函数br_vlan_should_use完成以上的判断。之后将可用作过滤的vlan所对应的子接口的mac地址与vlan id添加到FDB转发表中。 另外VLAN GROUP中的vlan个数(num_vlans),只会在判断添加的vlan为可用时增加。所谓可用的vlan,即可用于二层转发数据包。


static inline bool br_vlan_should_use(const struct net_bridge_vlan *v)
{
    if (br_vlan_is_master(v)) {
		return (br_vlan_is_brentry(v) ? true : false);
    }
    return true;
}
static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
{
    if (br_vlan_should_use(v)) {
        br_fdb_insert(br, p, dev->dev_addr, v->vid);
        vg->num_vlans++;
    }
}

在数据包接收过程中,检查数据包携带的vlan id,如果在group中能找到此id,并且group中的vlan可用于过滤功能,允许此数据包通过。


static bool __allowed_ingress(const struct net_bridge *br, struct net_bridge_vlan_group *vg, struct sk_buff *skb, u16 *vid)
{
    v = br_vlan_find(vg, *vid);
    if (!v || !br_vlan_should_use(v))
        goto drop;
}


子接口VLAN添加


为子接口添加vlan时,首先要得到一个相同id的主VLAN,在函数br_vlan_get_master中如果没有找到主VLAN,随即创建一个,由于子接口VLAN要引用此主VLAN,在返回主vlan前,增加其引用计数。注意此时的主VLAN并不用作过滤功能!!

如果设置了BRIDGE_VLAN_INFO_MASTER表示此vlan要同时在网桥上做vlan过滤,需要额外增加一个网桥主vlan,设置BRIDGE_VLAN_INFO_BRENTRY标志做过滤。


static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
{
    if (p) {
        /* need to work on the master vlan too */
        if (flags & BRIDGE_VLAN_INFO_MASTER) {
            err = br_vlan_add(br, v->vid, flags | BRIDGE_VLAN_INFO_BRENTRY, &changed);
        }
        masterv = br_vlan_get_master(br, v->vid);
        v->brvlan = masterv;
    }
}


纯粹主VLAN添加


纯粹的不用作过滤的主vlan,目前看还不能通过ip命令直接创建。其是在创建子接口vlan时,通过指定BRIDGE_VLAN_INFO_MASTER标志附带创建出来的。不做vlan过滤,也没看到起有其它功能。仅可见所有子接口的stats字段(struct br_vlan_stats)都是指向master的stats字段,可汇总所有子接口的vlan统计信息。


static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
{
        masterv = br_vlan_get_master(br, v->vid);
        v->brvlan = masterv;
        v->stats = masterv->stats;
}


内核版本

Linux-4.15


猜你喜欢

转载自blog.csdn.net/sinat_20184565/article/details/81063086