|
@@ -20,6 +20,7 @@
|
|
|
|
|
|
#include "br_private.h"
|
|
|
#include "br_private_stp.h"
|
|
|
+#include "br_private_tunnel.h"
|
|
|
|
|
|
static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
|
|
|
u32 filter_mask)
|
|
@@ -95,9 +96,10 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev,
|
|
|
u32 filter_mask)
|
|
|
{
|
|
|
struct net_bridge_vlan_group *vg = NULL;
|
|
|
- struct net_bridge_port *p;
|
|
|
+ struct net_bridge_port *p = NULL;
|
|
|
struct net_bridge *br;
|
|
|
int num_vlan_infos;
|
|
|
+ size_t vinfo_sz = 0;
|
|
|
|
|
|
rcu_read_lock();
|
|
|
if (br_port_exists(dev)) {
|
|
@@ -110,8 +112,13 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev,
|
|
|
num_vlan_infos = br_get_num_vlan_infos(vg, filter_mask);
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
+ if (p && (p->flags & BR_VLAN_TUNNEL))
|
|
|
+ vinfo_sz += br_get_vlan_tunnel_info_size(vg);
|
|
|
+
|
|
|
/* Each VLAN is returned in bridge_vlan_info along with flags */
|
|
|
- return num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info));
|
|
|
+ vinfo_sz += num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info));
|
|
|
+
|
|
|
+ return vinfo_sz;
|
|
|
}
|
|
|
|
|
|
static inline size_t br_port_info_size(void)
|
|
@@ -128,6 +135,7 @@ static inline size_t br_port_info_size(void)
|
|
|
+ nla_total_size(1) /* IFLA_BRPORT_UNICAST_FLOOD */
|
|
|
+ nla_total_size(1) /* IFLA_BRPORT_PROXYARP */
|
|
|
+ nla_total_size(1) /* IFLA_BRPORT_PROXYARP_WIFI */
|
|
|
+ + nla_total_size(1) /* IFLA_BRPORT_VLAN_TUNNEL */
|
|
|
+ nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */
|
|
|
+ nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */
|
|
|
+ nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */
|
|
@@ -194,7 +202,9 @@ static int br_port_fill_attrs(struct sk_buff *skb,
|
|
|
nla_put_u16(skb, IFLA_BRPORT_NO, p->port_no) ||
|
|
|
nla_put_u8(skb, IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
|
|
|
p->topology_change_ack) ||
|
|
|
- nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending))
|
|
|
+ nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending) ||
|
|
|
+ nla_put_u8(skb, IFLA_BRPORT_VLAN_TUNNEL, !!(p->flags &
|
|
|
+ BR_VLAN_TUNNEL)))
|
|
|
return -EMSGSIZE;
|
|
|
|
|
|
timerval = br_timer_value(&p->message_age_timer);
|
|
@@ -417,6 +427,9 @@ static int br_fill_ifinfo(struct sk_buff *skb,
|
|
|
err = br_fill_ifvlaninfo_compressed(skb, vg);
|
|
|
else
|
|
|
err = br_fill_ifvlaninfo(skb, vg);
|
|
|
+
|
|
|
+ if (port && (port->flags & BR_VLAN_TUNNEL))
|
|
|
+ err = br_fill_vlan_tunnel_info(skb, vg);
|
|
|
rcu_read_unlock();
|
|
|
if (err)
|
|
|
goto nla_put_failure;
|
|
@@ -517,60 +530,91 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int br_process_vlan_info(struct net_bridge *br,
|
|
|
+ struct net_bridge_port *p, int cmd,
|
|
|
+ struct bridge_vlan_info *vinfo_curr,
|
|
|
+ struct bridge_vlan_info **vinfo_last)
|
|
|
+{
|
|
|
+ if (!vinfo_curr->vid || vinfo_curr->vid >= VLAN_VID_MASK)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (vinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
|
|
|
+ /* check if we are already processing a range */
|
|
|
+ if (*vinfo_last)
|
|
|
+ return -EINVAL;
|
|
|
+ *vinfo_last = vinfo_curr;
|
|
|
+ /* don't allow range of pvids */
|
|
|
+ if ((*vinfo_last)->flags & BRIDGE_VLAN_INFO_PVID)
|
|
|
+ return -EINVAL;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (*vinfo_last) {
|
|
|
+ struct bridge_vlan_info tmp_vinfo;
|
|
|
+ int v, err;
|
|
|
+
|
|
|
+ if (!(vinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_END))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (vinfo_curr->vid <= (*vinfo_last)->vid)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ memcpy(&tmp_vinfo, *vinfo_last,
|
|
|
+ sizeof(struct bridge_vlan_info));
|
|
|
+ for (v = (*vinfo_last)->vid; v <= vinfo_curr->vid; v++) {
|
|
|
+ tmp_vinfo.vid = v;
|
|
|
+ err = br_vlan_info(br, p, cmd, &tmp_vinfo);
|
|
|
+ if (err)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ *vinfo_last = NULL;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return br_vlan_info(br, p, cmd, vinfo_curr);
|
|
|
+}
|
|
|
+
|
|
|
static int br_afspec(struct net_bridge *br,
|
|
|
struct net_bridge_port *p,
|
|
|
struct nlattr *af_spec,
|
|
|
int cmd)
|
|
|
{
|
|
|
- struct bridge_vlan_info *vinfo_start = NULL;
|
|
|
- struct bridge_vlan_info *vinfo = NULL;
|
|
|
+ struct bridge_vlan_info *vinfo_curr = NULL;
|
|
|
+ struct bridge_vlan_info *vinfo_last = NULL;
|
|
|
struct nlattr *attr;
|
|
|
- int err = 0;
|
|
|
- int rem;
|
|
|
+ struct vtunnel_info tinfo_last = {};
|
|
|
+ struct vtunnel_info tinfo_curr = {};
|
|
|
+ int err = 0, rem;
|
|
|
|
|
|
nla_for_each_nested(attr, af_spec, rem) {
|
|
|
- if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)
|
|
|
- continue;
|
|
|
- if (nla_len(attr) != sizeof(struct bridge_vlan_info))
|
|
|
- return -EINVAL;
|
|
|
- vinfo = nla_data(attr);
|
|
|
- if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK)
|
|
|
- return -EINVAL;
|
|
|
- if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
|
|
|
- if (vinfo_start)
|
|
|
+ err = 0;
|
|
|
+ switch (nla_type(attr)) {
|
|
|
+ case IFLA_BRIDGE_VLAN_TUNNEL_INFO:
|
|
|
+ if (!(p->flags & BR_VLAN_TUNNEL))
|
|
|
return -EINVAL;
|
|
|
- vinfo_start = vinfo;
|
|
|
- /* don't allow range of pvids */
|
|
|
- if (vinfo_start->flags & BRIDGE_VLAN_INFO_PVID)
|
|
|
+ err = br_parse_vlan_tunnel_info(attr, &tinfo_curr);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ err = br_process_vlan_tunnel_info(br, p, cmd,
|
|
|
+ &tinfo_curr,
|
|
|
+ &tinfo_last);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ break;
|
|
|
+ case IFLA_BRIDGE_VLAN_INFO:
|
|
|
+ if (nla_len(attr) != sizeof(struct bridge_vlan_info))
|
|
|
return -EINVAL;
|
|
|
- continue;
|
|
|
+ vinfo_curr = nla_data(attr);
|
|
|
+ err = br_process_vlan_info(br, p, cmd, vinfo_curr,
|
|
|
+ &vinfo_last);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
- if (vinfo_start) {
|
|
|
- struct bridge_vlan_info tmp_vinfo;
|
|
|
- int v;
|
|
|
-
|
|
|
- if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- if (vinfo->vid <= vinfo_start->vid)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- memcpy(&tmp_vinfo, vinfo_start,
|
|
|
- sizeof(struct bridge_vlan_info));
|
|
|
-
|
|
|
- for (v = vinfo_start->vid; v <= vinfo->vid; v++) {
|
|
|
- tmp_vinfo.vid = v;
|
|
|
- err = br_vlan_info(br, p, cmd, &tmp_vinfo);
|
|
|
- if (err)
|
|
|
- break;
|
|
|
- }
|
|
|
- vinfo_start = NULL;
|
|
|
- } else {
|
|
|
- err = br_vlan_info(br, p, cmd, vinfo);
|
|
|
- }
|
|
|
if (err)
|
|
|
- break;
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
return err;
|
|
@@ -630,8 +674,9 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
|
|
|
/* Process bridge protocol info on port */
|
|
|
static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
|
|
|
{
|
|
|
- int err;
|
|
|
unsigned long old_flags = p->flags;
|
|
|
+ bool br_vlan_tunnel_old = false;
|
|
|
+ int err;
|
|
|
|
|
|
br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
|
|
|
br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
|
|
@@ -644,6 +689,11 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
|
|
|
br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP);
|
|
|
br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI);
|
|
|
|
|
|
+ br_vlan_tunnel_old = (p->flags & BR_VLAN_TUNNEL) ? true : false;
|
|
|
+ br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL);
|
|
|
+ if (br_vlan_tunnel_old && !(p->flags & BR_VLAN_TUNNEL))
|
|
|
+ nbp_vlan_tunnel_info_flush(p);
|
|
|
+
|
|
|
if (tb[IFLA_BRPORT_COST]) {
|
|
|
err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST]));
|
|
|
if (err)
|