Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Bridge per vlan tunnel port dst_metadata netlink control interface
0004  *
0005  *  Authors:
0006  *  Roopa Prabhu        <roopa@cumulusnetworks.com>
0007  */
0008 
0009 #include <linux/kernel.h>
0010 #include <linux/slab.h>
0011 #include <linux/etherdevice.h>
0012 #include <net/rtnetlink.h>
0013 #include <net/net_namespace.h>
0014 #include <net/sock.h>
0015 #include <uapi/linux/if_bridge.h>
0016 #include <net/dst_metadata.h>
0017 
0018 #include "br_private.h"
0019 #include "br_private_tunnel.h"
0020 
0021 static size_t __get_vlan_tinfo_size(void)
0022 {
0023     return nla_total_size(0) + /* nest IFLA_BRIDGE_VLAN_TUNNEL_INFO */
0024           nla_total_size(sizeof(u32)) + /* IFLA_BRIDGE_VLAN_TUNNEL_ID */
0025           nla_total_size(sizeof(u16)) + /* IFLA_BRIDGE_VLAN_TUNNEL_VID */
0026           nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_VLAN_TUNNEL_FLAGS */
0027 }
0028 
0029 bool vlan_tunid_inrange(const struct net_bridge_vlan *v_curr,
0030             const struct net_bridge_vlan *v_last)
0031 {
0032     __be32 tunid_curr = tunnel_id_to_key32(v_curr->tinfo.tunnel_id);
0033     __be32 tunid_last = tunnel_id_to_key32(v_last->tinfo.tunnel_id);
0034 
0035     return (be32_to_cpu(tunid_curr) - be32_to_cpu(tunid_last)) == 1;
0036 }
0037 
0038 static int __get_num_vlan_tunnel_infos(struct net_bridge_vlan_group *vg)
0039 {
0040     struct net_bridge_vlan *v, *vtbegin = NULL, *vtend = NULL;
0041     int num_tinfos = 0;
0042 
0043     /* Count number of vlan infos */
0044     list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
0045         /* only a context, bridge vlan not activated */
0046         if (!br_vlan_should_use(v) || !v->tinfo.tunnel_id)
0047             continue;
0048 
0049         if (!vtbegin) {
0050             goto initvars;
0051         } else if ((v->vid - vtend->vid) == 1 &&
0052                vlan_tunid_inrange(v, vtend)) {
0053             vtend = v;
0054             continue;
0055         } else {
0056             if ((vtend->vid - vtbegin->vid) > 0)
0057                 num_tinfos += 2;
0058             else
0059                 num_tinfos += 1;
0060         }
0061 initvars:
0062         vtbegin = v;
0063         vtend = v;
0064     }
0065 
0066     if (vtbegin && vtend) {
0067         if ((vtend->vid - vtbegin->vid) > 0)
0068             num_tinfos += 2;
0069         else
0070             num_tinfos += 1;
0071     }
0072 
0073     return num_tinfos;
0074 }
0075 
0076 int br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg)
0077 {
0078     int num_tinfos;
0079 
0080     if (!vg)
0081         return 0;
0082 
0083     rcu_read_lock();
0084     num_tinfos = __get_num_vlan_tunnel_infos(vg);
0085     rcu_read_unlock();
0086 
0087     return num_tinfos * __get_vlan_tinfo_size();
0088 }
0089 
0090 static int br_fill_vlan_tinfo(struct sk_buff *skb, u16 vid,
0091                   __be64 tunnel_id, u16 flags)
0092 {
0093     __be32 tid = tunnel_id_to_key32(tunnel_id);
0094     struct nlattr *tmap;
0095 
0096     tmap = nla_nest_start_noflag(skb, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
0097     if (!tmap)
0098         return -EMSGSIZE;
0099     if (nla_put_u32(skb, IFLA_BRIDGE_VLAN_TUNNEL_ID,
0100             be32_to_cpu(tid)))
0101         goto nla_put_failure;
0102     if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_VID,
0103             vid))
0104         goto nla_put_failure;
0105     if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
0106             flags))
0107         goto nla_put_failure;
0108     nla_nest_end(skb, tmap);
0109 
0110     return 0;
0111 
0112 nla_put_failure:
0113     nla_nest_cancel(skb, tmap);
0114 
0115     return -EMSGSIZE;
0116 }
0117 
0118 static int br_fill_vlan_tinfo_range(struct sk_buff *skb,
0119                     struct net_bridge_vlan *vtbegin,
0120                     struct net_bridge_vlan *vtend)
0121 {
0122     int err;
0123 
0124     if (vtend && (vtend->vid - vtbegin->vid) > 0) {
0125         /* add range to skb */
0126         err = br_fill_vlan_tinfo(skb, vtbegin->vid,
0127                      vtbegin->tinfo.tunnel_id,
0128                      BRIDGE_VLAN_INFO_RANGE_BEGIN);
0129         if (err)
0130             return err;
0131 
0132         err = br_fill_vlan_tinfo(skb, vtend->vid,
0133                      vtend->tinfo.tunnel_id,
0134                      BRIDGE_VLAN_INFO_RANGE_END);
0135         if (err)
0136             return err;
0137     } else {
0138         err = br_fill_vlan_tinfo(skb, vtbegin->vid,
0139                      vtbegin->tinfo.tunnel_id,
0140                      0);
0141         if (err)
0142             return err;
0143     }
0144 
0145     return 0;
0146 }
0147 
0148 int br_fill_vlan_tunnel_info(struct sk_buff *skb,
0149                  struct net_bridge_vlan_group *vg)
0150 {
0151     struct net_bridge_vlan *vtbegin = NULL;
0152     struct net_bridge_vlan *vtend = NULL;
0153     struct net_bridge_vlan *v;
0154     int err;
0155 
0156     /* Count number of vlan infos */
0157     list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
0158         /* only a context, bridge vlan not activated */
0159         if (!br_vlan_should_use(v))
0160             continue;
0161 
0162         if (!v->tinfo.tunnel_dst)
0163             continue;
0164 
0165         if (!vtbegin) {
0166             goto initvars;
0167         } else if ((v->vid - vtend->vid) == 1 &&
0168                 vlan_tunid_inrange(v, vtend)) {
0169             vtend = v;
0170             continue;
0171         } else {
0172             err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
0173             if (err)
0174                 return err;
0175         }
0176 initvars:
0177         vtbegin = v;
0178         vtend = v;
0179     }
0180 
0181     if (vtbegin) {
0182         err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
0183         if (err)
0184             return err;
0185     }
0186 
0187     return 0;
0188 }
0189 
0190 static const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1] = {
0191     [IFLA_BRIDGE_VLAN_TUNNEL_ID] = { .type = NLA_U32 },
0192     [IFLA_BRIDGE_VLAN_TUNNEL_VID] = { .type = NLA_U16 },
0193     [IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NLA_U16 },
0194 };
0195 
0196 int br_vlan_tunnel_info(const struct net_bridge_port *p, int cmd,
0197             u16 vid, u32 tun_id, bool *changed)
0198 {
0199     int err = 0;
0200 
0201     if (!p)
0202         return -EINVAL;
0203 
0204     switch (cmd) {
0205     case RTM_SETLINK:
0206         err = nbp_vlan_tunnel_info_add(p, vid, tun_id);
0207         if (!err)
0208             *changed = true;
0209         break;
0210     case RTM_DELLINK:
0211         if (!nbp_vlan_tunnel_info_delete(p, vid))
0212             *changed = true;
0213         break;
0214     }
0215 
0216     return err;
0217 }
0218 
0219 int br_parse_vlan_tunnel_info(struct nlattr *attr,
0220                   struct vtunnel_info *tinfo)
0221 {
0222     struct nlattr *tb[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1];
0223     u32 tun_id;
0224     u16 vid, flags = 0;
0225     int err;
0226 
0227     memset(tinfo, 0, sizeof(*tinfo));
0228 
0229     err = nla_parse_nested_deprecated(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
0230                       attr, vlan_tunnel_policy, NULL);
0231     if (err < 0)
0232         return err;
0233 
0234     if (!tb[IFLA_BRIDGE_VLAN_TUNNEL_ID] ||
0235         !tb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
0236         return -EINVAL;
0237 
0238     tun_id = nla_get_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
0239     vid = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
0240     if (vid >= VLAN_VID_MASK)
0241         return -ERANGE;
0242 
0243     if (tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
0244         flags = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
0245 
0246     tinfo->tunid = tun_id;
0247     tinfo->vid = vid;
0248     tinfo->flags = flags;
0249 
0250     return 0;
0251 }
0252 
0253 /* send a notification if v_curr can't enter the range and start a new one */
0254 static void __vlan_tunnel_handle_range(const struct net_bridge_port *p,
0255                        struct net_bridge_vlan **v_start,
0256                        struct net_bridge_vlan **v_end,
0257                        int v_curr, bool curr_change)
0258 {
0259     struct net_bridge_vlan_group *vg;
0260     struct net_bridge_vlan *v;
0261 
0262     vg = nbp_vlan_group(p);
0263     if (!vg)
0264         return;
0265 
0266     v = br_vlan_find(vg, v_curr);
0267 
0268     if (!*v_start)
0269         goto out_init;
0270 
0271     if (v && curr_change && br_vlan_can_enter_range(v, *v_end)) {
0272         *v_end = v;
0273         return;
0274     }
0275 
0276     br_vlan_notify(p->br, p, (*v_start)->vid, (*v_end)->vid, RTM_NEWVLAN);
0277 out_init:
0278     /* we start a range only if there are any changes to notify about */
0279     *v_start = curr_change ? v : NULL;
0280     *v_end = *v_start;
0281 }
0282 
0283 int br_process_vlan_tunnel_info(const struct net_bridge *br,
0284                 const struct net_bridge_port *p, int cmd,
0285                 struct vtunnel_info *tinfo_curr,
0286                 struct vtunnel_info *tinfo_last,
0287                 bool *changed)
0288 {
0289     int err;
0290 
0291     if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
0292         if (tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
0293             return -EINVAL;
0294         memcpy(tinfo_last, tinfo_curr, sizeof(struct vtunnel_info));
0295     } else if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_END) {
0296         struct net_bridge_vlan *v_start = NULL, *v_end = NULL;
0297         int t, v;
0298 
0299         if (!(tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN))
0300             return -EINVAL;
0301         if ((tinfo_curr->vid - tinfo_last->vid) !=
0302             (tinfo_curr->tunid - tinfo_last->tunid))
0303             return -EINVAL;
0304         t = tinfo_last->tunid;
0305         for (v = tinfo_last->vid; v <= tinfo_curr->vid; v++) {
0306             bool curr_change = false;
0307 
0308             err = br_vlan_tunnel_info(p, cmd, v, t, &curr_change);
0309             if (err)
0310                 break;
0311             t++;
0312 
0313             if (curr_change)
0314                 *changed = curr_change;
0315              __vlan_tunnel_handle_range(p, &v_start, &v_end, v,
0316                             curr_change);
0317         }
0318         if (v_start && v_end)
0319             br_vlan_notify(br, p, v_start->vid, v_end->vid,
0320                        RTM_NEWVLAN);
0321         if (err)
0322             return err;
0323 
0324         memset(tinfo_last, 0, sizeof(struct vtunnel_info));
0325         memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
0326     } else {
0327         if (tinfo_last->flags)
0328             return -EINVAL;
0329         err = br_vlan_tunnel_info(p, cmd, tinfo_curr->vid,
0330                       tinfo_curr->tunid, changed);
0331         if (err)
0332             return err;
0333         br_vlan_notify(br, p, tinfo_curr->vid, 0, RTM_NEWVLAN);
0334         memset(tinfo_last, 0, sizeof(struct vtunnel_info));
0335         memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
0336     }
0337 
0338     return 0;
0339 }