linux/net/bridge/br_netlink_tunnel.c
<<
>>
Prefs
   1/*
   2 *      Bridge per vlan tunnel port dst_metadata netlink control interface
   3 *
   4 *      Authors:
   5 *      Roopa Prabhu            <roopa@cumulusnetworks.com>
   6 *
   7 *      This program is free software; you can redistribute it and/or
   8 *      modify it under the terms of the GNU General Public License
   9 *      as published by the Free Software Foundation; either version
  10 *      2 of the License, or (at your option) any later version.
  11 */
  12
  13#include <linux/kernel.h>
  14#include <linux/slab.h>
  15#include <linux/etherdevice.h>
  16#include <net/rtnetlink.h>
  17#include <net/net_namespace.h>
  18#include <net/sock.h>
  19#include <uapi/linux/if_bridge.h>
  20#include <net/dst_metadata.h>
  21
  22#include "br_private.h"
  23#include "br_private_tunnel.h"
  24
  25static size_t __get_vlan_tinfo_size(void)
  26{
  27        return nla_total_size(0) + /* nest IFLA_BRIDGE_VLAN_TUNNEL_INFO */
  28                  nla_total_size(sizeof(u32)) + /* IFLA_BRIDGE_VLAN_TUNNEL_ID */
  29                  nla_total_size(sizeof(u16)) + /* IFLA_BRIDGE_VLAN_TUNNEL_VID */
  30                  nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_VLAN_TUNNEL_FLAGS */
  31}
  32
  33static bool vlan_tunid_inrange(struct net_bridge_vlan *v_curr,
  34                               struct net_bridge_vlan *v_last)
  35{
  36        __be32 tunid_curr = tunnel_id_to_key32(v_curr->tinfo.tunnel_id);
  37        __be32 tunid_last = tunnel_id_to_key32(v_last->tinfo.tunnel_id);
  38
  39        return (be32_to_cpu(tunid_curr) - be32_to_cpu(tunid_last)) == 1;
  40}
  41
  42static int __get_num_vlan_tunnel_infos(struct net_bridge_vlan_group *vg)
  43{
  44        struct net_bridge_vlan *v, *vtbegin = NULL, *vtend = NULL;
  45        int num_tinfos = 0;
  46
  47        /* Count number of vlan infos */
  48        list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
  49                /* only a context, bridge vlan not activated */
  50                if (!br_vlan_should_use(v) || !v->tinfo.tunnel_id)
  51                        continue;
  52
  53                if (!vtbegin) {
  54                        goto initvars;
  55                } else if ((v->vid - vtend->vid) == 1 &&
  56                           vlan_tunid_inrange(v, vtend)) {
  57                        vtend = v;
  58                        continue;
  59                } else {
  60                        if ((vtend->vid - vtbegin->vid) > 0)
  61                                num_tinfos += 2;
  62                        else
  63                                num_tinfos += 1;
  64                }
  65initvars:
  66                vtbegin = v;
  67                vtend = v;
  68        }
  69
  70        if (vtbegin && vtend) {
  71                if ((vtend->vid - vtbegin->vid) > 0)
  72                        num_tinfos += 2;
  73                else
  74                        num_tinfos += 1;
  75        }
  76
  77        return num_tinfos;
  78}
  79
  80int br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg)
  81{
  82        int num_tinfos;
  83
  84        if (!vg)
  85                return 0;
  86
  87        rcu_read_lock();
  88        num_tinfos = __get_num_vlan_tunnel_infos(vg);
  89        rcu_read_unlock();
  90
  91        return num_tinfos * __get_vlan_tinfo_size();
  92}
  93
  94static int br_fill_vlan_tinfo(struct sk_buff *skb, u16 vid,
  95                              __be64 tunnel_id, u16 flags)
  96{
  97        __be32 tid = tunnel_id_to_key32(tunnel_id);
  98        struct nlattr *tmap;
  99
 100        tmap = nla_nest_start(skb, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
 101        if (!tmap)
 102                return -EMSGSIZE;
 103        if (nla_put_u32(skb, IFLA_BRIDGE_VLAN_TUNNEL_ID,
 104                        be32_to_cpu(tid)))
 105                goto nla_put_failure;
 106        if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_VID,
 107                        vid))
 108                goto nla_put_failure;
 109        if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
 110                        flags))
 111                goto nla_put_failure;
 112        nla_nest_end(skb, tmap);
 113
 114        return 0;
 115
 116nla_put_failure:
 117        nla_nest_cancel(skb, tmap);
 118
 119        return -EMSGSIZE;
 120}
 121
 122static int br_fill_vlan_tinfo_range(struct sk_buff *skb,
 123                                    struct net_bridge_vlan *vtbegin,
 124                                    struct net_bridge_vlan *vtend)
 125{
 126        int err;
 127
 128        if (vtend && (vtend->vid - vtbegin->vid) > 0) {
 129                /* add range to skb */
 130                err = br_fill_vlan_tinfo(skb, vtbegin->vid,
 131                                         vtbegin->tinfo.tunnel_id,
 132                                         BRIDGE_VLAN_INFO_RANGE_BEGIN);
 133                if (err)
 134                        return err;
 135
 136                err = br_fill_vlan_tinfo(skb, vtend->vid,
 137                                         vtend->tinfo.tunnel_id,
 138                                         BRIDGE_VLAN_INFO_RANGE_END);
 139                if (err)
 140                        return err;
 141        } else {
 142                err = br_fill_vlan_tinfo(skb, vtbegin->vid,
 143                                         vtbegin->tinfo.tunnel_id,
 144                                         0);
 145                if (err)
 146                        return err;
 147        }
 148
 149        return 0;
 150}
 151
 152int br_fill_vlan_tunnel_info(struct sk_buff *skb,
 153                             struct net_bridge_vlan_group *vg)
 154{
 155        struct net_bridge_vlan *vtbegin = NULL;
 156        struct net_bridge_vlan *vtend = NULL;
 157        struct net_bridge_vlan *v;
 158        int err;
 159
 160        /* Count number of vlan infos */
 161        list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
 162                /* only a context, bridge vlan not activated */
 163                if (!br_vlan_should_use(v))
 164                        continue;
 165
 166                if (!v->tinfo.tunnel_dst)
 167                        continue;
 168
 169                if (!vtbegin) {
 170                        goto initvars;
 171                } else if ((v->vid - vtend->vid) == 1 &&
 172                            vlan_tunid_inrange(v, vtend)) {
 173                        vtend = v;
 174                        continue;
 175                } else {
 176                        err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
 177                        if (err)
 178                                return err;
 179                }
 180initvars:
 181                vtbegin = v;
 182                vtend = v;
 183        }
 184
 185        if (vtbegin) {
 186                err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
 187                if (err)
 188                        return err;
 189        }
 190
 191        return 0;
 192}
 193
 194static const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1] = {
 195        [IFLA_BRIDGE_VLAN_TUNNEL_ID] = { .type = NLA_U32 },
 196        [IFLA_BRIDGE_VLAN_TUNNEL_VID] = { .type = NLA_U16 },
 197        [IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NLA_U16 },
 198};
 199
 200static int br_vlan_tunnel_info(struct net_bridge_port *p, int cmd,
 201                               u16 vid, u32 tun_id)
 202{
 203        int err = 0;
 204
 205        if (!p)
 206                return -EINVAL;
 207
 208        switch (cmd) {
 209        case RTM_SETLINK:
 210                err = nbp_vlan_tunnel_info_add(p, vid, tun_id);
 211                break;
 212        case RTM_DELLINK:
 213                nbp_vlan_tunnel_info_delete(p, vid);
 214                break;
 215        }
 216
 217        return err;
 218}
 219
 220int br_parse_vlan_tunnel_info(struct nlattr *attr,
 221                              struct vtunnel_info *tinfo)
 222{
 223        struct nlattr *tb[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1];
 224        u32 tun_id;
 225        u16 vid, flags = 0;
 226        int err;
 227
 228        memset(tinfo, 0, sizeof(*tinfo));
 229
 230        err = nla_parse_nested(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX, attr,
 231                               vlan_tunnel_policy, NULL);
 232        if (err < 0)
 233                return err;
 234
 235        if (!tb[IFLA_BRIDGE_VLAN_TUNNEL_ID] ||
 236            !tb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
 237                return -EINVAL;
 238
 239        tun_id = nla_get_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
 240        vid = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
 241        if (vid >= VLAN_VID_MASK)
 242                return -ERANGE;
 243
 244        if (tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
 245                flags = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
 246
 247        tinfo->tunid = tun_id;
 248        tinfo->vid = vid;
 249        tinfo->flags = flags;
 250
 251        return 0;
 252}
 253
 254int br_process_vlan_tunnel_info(struct net_bridge *br,
 255                                struct net_bridge_port *p, int cmd,
 256                                struct vtunnel_info *tinfo_curr,
 257                                struct vtunnel_info *tinfo_last)
 258{
 259        int err;
 260
 261        if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
 262                if (tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
 263                        return -EINVAL;
 264                memcpy(tinfo_last, tinfo_curr, sizeof(struct vtunnel_info));
 265        } else if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_END) {
 266                int t, v;
 267
 268                if (!(tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN))
 269                        return -EINVAL;
 270                if ((tinfo_curr->vid - tinfo_last->vid) !=
 271                    (tinfo_curr->tunid - tinfo_last->tunid))
 272                        return -EINVAL;
 273                t = tinfo_last->tunid;
 274                for (v = tinfo_last->vid; v <= tinfo_curr->vid; v++) {
 275                        err = br_vlan_tunnel_info(p, cmd, v, t);
 276                        if (err)
 277                                return err;
 278                        t++;
 279                }
 280                memset(tinfo_last, 0, sizeof(struct vtunnel_info));
 281                memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
 282        } else {
 283                if (tinfo_last->flags)
 284                        return -EINVAL;
 285                err = br_vlan_tunnel_info(p, cmd, tinfo_curr->vid,
 286                                          tinfo_curr->tunid);
 287                if (err)
 288                        return err;
 289                memset(tinfo_last, 0, sizeof(struct vtunnel_info));
 290                memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
 291        }
 292
 293        return 0;
 294}
 295