linux/net/8021q/vlan_core.c
<<
>>
Prefs
   1#include <linux/skbuff.h>
   2#include <linux/netdevice.h>
   3#include <linux/if_vlan.h>
   4#include <linux/netpoll.h>
   5#include <linux/export.h>
   6#include "vlan.h"
   7
   8bool vlan_do_receive(struct sk_buff **skbp, bool last_handler)
   9{
  10        struct sk_buff *skb = *skbp;
  11        u16 vlan_id = skb->vlan_tci & VLAN_VID_MASK;
  12        struct net_device *vlan_dev;
  13        struct vlan_pcpu_stats *rx_stats;
  14
  15        vlan_dev = vlan_find_dev(skb->dev, vlan_id);
  16        if (!vlan_dev) {
  17                /* Only the last call to vlan_do_receive() should change
  18                 * pkt_type to PACKET_OTHERHOST
  19                 */
  20                if (vlan_id && last_handler)
  21                        skb->pkt_type = PACKET_OTHERHOST;
  22                return false;
  23        }
  24
  25        skb = *skbp = skb_share_check(skb, GFP_ATOMIC);
  26        if (unlikely(!skb))
  27                return false;
  28
  29        skb->dev = vlan_dev;
  30        if (skb->pkt_type == PACKET_OTHERHOST) {
  31                /* Our lower layer thinks this is not local, let's make sure.
  32                 * This allows the VLAN to have a different MAC than the
  33                 * underlying device, and still route correctly. */
  34                if (ether_addr_equal(eth_hdr(skb)->h_dest, vlan_dev->dev_addr))
  35                        skb->pkt_type = PACKET_HOST;
  36        }
  37
  38        if (!(vlan_dev_priv(vlan_dev)->flags & VLAN_FLAG_REORDER_HDR)) {
  39                unsigned int offset = skb->data - skb_mac_header(skb);
  40
  41                /*
  42                 * vlan_insert_tag expect skb->data pointing to mac header.
  43                 * So change skb->data before calling it and change back to
  44                 * original position later
  45                 */
  46                skb_push(skb, offset);
  47                skb = *skbp = vlan_insert_tag(skb, skb->vlan_tci);
  48                if (!skb)
  49                        return false;
  50                skb_pull(skb, offset + VLAN_HLEN);
  51                skb_reset_mac_len(skb);
  52        }
  53
  54        skb->priority = vlan_get_ingress_priority(vlan_dev, skb->vlan_tci);
  55        skb->vlan_tci = 0;
  56
  57        rx_stats = this_cpu_ptr(vlan_dev_priv(vlan_dev)->vlan_pcpu_stats);
  58
  59        u64_stats_update_begin(&rx_stats->syncp);
  60        rx_stats->rx_packets++;
  61        rx_stats->rx_bytes += skb->len;
  62        if (skb->pkt_type == PACKET_MULTICAST)
  63                rx_stats->rx_multicast++;
  64        u64_stats_update_end(&rx_stats->syncp);
  65
  66        return true;
  67}
  68
  69/* Must be invoked with rcu_read_lock or with RTNL. */
  70struct net_device *__vlan_find_dev_deep(struct net_device *real_dev,
  71                                        u16 vlan_id)
  72{
  73        struct vlan_info *vlan_info = rcu_dereference_rtnl(real_dev->vlan_info);
  74
  75        if (vlan_info) {
  76                return vlan_group_get_device(&vlan_info->grp, vlan_id);
  77        } else {
  78                /*
  79                 * Bonding slaves do not have grp assigned to themselves.
  80                 * Grp is assigned to bonding master instead.
  81                 */
  82                if (netif_is_bond_slave(real_dev))
  83                        return __vlan_find_dev_deep(real_dev->master, vlan_id);
  84        }
  85
  86        return NULL;
  87}
  88EXPORT_SYMBOL(__vlan_find_dev_deep);
  89
  90struct net_device *vlan_dev_real_dev(const struct net_device *dev)
  91{
  92        return vlan_dev_priv(dev)->real_dev;
  93}
  94EXPORT_SYMBOL(vlan_dev_real_dev);
  95
  96u16 vlan_dev_vlan_id(const struct net_device *dev)
  97{
  98        return vlan_dev_priv(dev)->vlan_id;
  99}
 100EXPORT_SYMBOL(vlan_dev_vlan_id);
 101
 102static struct sk_buff *vlan_reorder_header(struct sk_buff *skb)
 103{
 104        if (skb_cow(skb, skb_headroom(skb)) < 0)
 105                return NULL;
 106        memmove(skb->data - ETH_HLEN, skb->data - VLAN_ETH_HLEN, 2 * ETH_ALEN);
 107        skb->mac_header += VLAN_HLEN;
 108        skb_reset_mac_len(skb);
 109        return skb;
 110}
 111
 112struct sk_buff *vlan_untag(struct sk_buff *skb)
 113{
 114        struct vlan_hdr *vhdr;
 115        u16 vlan_tci;
 116
 117        if (unlikely(vlan_tx_tag_present(skb))) {
 118                /* vlan_tci is already set-up so leave this for another time */
 119                return skb;
 120        }
 121
 122        skb = skb_share_check(skb, GFP_ATOMIC);
 123        if (unlikely(!skb))
 124                goto err_free;
 125
 126        if (unlikely(!pskb_may_pull(skb, VLAN_HLEN)))
 127                goto err_free;
 128
 129        vhdr = (struct vlan_hdr *) skb->data;
 130        vlan_tci = ntohs(vhdr->h_vlan_TCI);
 131        __vlan_hwaccel_put_tag(skb, vlan_tci);
 132
 133        skb_pull_rcsum(skb, VLAN_HLEN);
 134        vlan_set_encap_proto(skb, vhdr);
 135
 136        skb = vlan_reorder_header(skb);
 137        if (unlikely(!skb))
 138                goto err_free;
 139
 140        skb_reset_network_header(skb);
 141        skb_reset_transport_header(skb);
 142        return skb;
 143
 144err_free:
 145        kfree_skb(skb);
 146        return NULL;
 147}
 148
 149
 150/*
 151 * vlan info and vid list
 152 */
 153
 154static void vlan_group_free(struct vlan_group *grp)
 155{
 156        int i;
 157
 158        for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++)
 159                kfree(grp->vlan_devices_arrays[i]);
 160}
 161
 162static void vlan_info_free(struct vlan_info *vlan_info)
 163{
 164        vlan_group_free(&vlan_info->grp);
 165        kfree(vlan_info);
 166}
 167
 168static void vlan_info_rcu_free(struct rcu_head *rcu)
 169{
 170        vlan_info_free(container_of(rcu, struct vlan_info, rcu));
 171}
 172
 173static struct vlan_info *vlan_info_alloc(struct net_device *dev)
 174{
 175        struct vlan_info *vlan_info;
 176
 177        vlan_info = kzalloc(sizeof(struct vlan_info), GFP_KERNEL);
 178        if (!vlan_info)
 179                return NULL;
 180
 181        vlan_info->real_dev = dev;
 182        INIT_LIST_HEAD(&vlan_info->vid_list);
 183        return vlan_info;
 184}
 185
 186struct vlan_vid_info {
 187        struct list_head list;
 188        unsigned short vid;
 189        int refcount;
 190};
 191
 192static struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info,
 193                                               unsigned short vid)
 194{
 195        struct vlan_vid_info *vid_info;
 196
 197        list_for_each_entry(vid_info, &vlan_info->vid_list, list) {
 198                if (vid_info->vid == vid)
 199                        return vid_info;
 200        }
 201        return NULL;
 202}
 203
 204static struct vlan_vid_info *vlan_vid_info_alloc(unsigned short vid)
 205{
 206        struct vlan_vid_info *vid_info;
 207
 208        vid_info = kzalloc(sizeof(struct vlan_vid_info), GFP_KERNEL);
 209        if (!vid_info)
 210                return NULL;
 211        vid_info->vid = vid;
 212
 213        return vid_info;
 214}
 215
 216static int __vlan_vid_add(struct vlan_info *vlan_info, unsigned short vid,
 217                          struct vlan_vid_info **pvid_info)
 218{
 219        struct net_device *dev = vlan_info->real_dev;
 220        const struct net_device_ops *ops = dev->netdev_ops;
 221        struct vlan_vid_info *vid_info;
 222        int err;
 223
 224        vid_info = vlan_vid_info_alloc(vid);
 225        if (!vid_info)
 226                return -ENOMEM;
 227
 228        if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
 229            ops->ndo_vlan_rx_add_vid) {
 230                err =  ops->ndo_vlan_rx_add_vid(dev, vid);
 231                if (err) {
 232                        kfree(vid_info);
 233                        return err;
 234                }
 235        }
 236        list_add(&vid_info->list, &vlan_info->vid_list);
 237        vlan_info->nr_vids++;
 238        *pvid_info = vid_info;
 239        return 0;
 240}
 241
 242int vlan_vid_add(struct net_device *dev, unsigned short vid)
 243{
 244        struct vlan_info *vlan_info;
 245        struct vlan_vid_info *vid_info;
 246        bool vlan_info_created = false;
 247        int err;
 248
 249        ASSERT_RTNL();
 250
 251        vlan_info = rtnl_dereference(dev->vlan_info);
 252        if (!vlan_info) {
 253                vlan_info = vlan_info_alloc(dev);
 254                if (!vlan_info)
 255                        return -ENOMEM;
 256                vlan_info_created = true;
 257        }
 258        vid_info = vlan_vid_info_get(vlan_info, vid);
 259        if (!vid_info) {
 260                err = __vlan_vid_add(vlan_info, vid, &vid_info);
 261                if (err)
 262                        goto out_free_vlan_info;
 263        }
 264        vid_info->refcount++;
 265
 266        if (vlan_info_created)
 267                rcu_assign_pointer(dev->vlan_info, vlan_info);
 268
 269        return 0;
 270
 271out_free_vlan_info:
 272        if (vlan_info_created)
 273                kfree(vlan_info);
 274        return err;
 275}
 276EXPORT_SYMBOL(vlan_vid_add);
 277
 278static void __vlan_vid_del(struct vlan_info *vlan_info,
 279                           struct vlan_vid_info *vid_info)
 280{
 281        struct net_device *dev = vlan_info->real_dev;
 282        const struct net_device_ops *ops = dev->netdev_ops;
 283        unsigned short vid = vid_info->vid;
 284        int err;
 285
 286        if ((dev->features & NETIF_F_HW_VLAN_FILTER) &&
 287             ops->ndo_vlan_rx_kill_vid) {
 288                err = ops->ndo_vlan_rx_kill_vid(dev, vid);
 289                if (err) {
 290                        pr_warn("failed to kill vid %d for device %s\n",
 291                                vid, dev->name);
 292                }
 293        }
 294        list_del(&vid_info->list);
 295        kfree(vid_info);
 296        vlan_info->nr_vids--;
 297}
 298
 299void vlan_vid_del(struct net_device *dev, unsigned short vid)
 300{
 301        struct vlan_info *vlan_info;
 302        struct vlan_vid_info *vid_info;
 303
 304        ASSERT_RTNL();
 305
 306        vlan_info = rtnl_dereference(dev->vlan_info);
 307        if (!vlan_info)
 308                return;
 309
 310        vid_info = vlan_vid_info_get(vlan_info, vid);
 311        if (!vid_info)
 312                return;
 313        vid_info->refcount--;
 314        if (vid_info->refcount == 0) {
 315                __vlan_vid_del(vlan_info, vid_info);
 316                if (vlan_info->nr_vids == 0) {
 317                        RCU_INIT_POINTER(dev->vlan_info, NULL);
 318                        call_rcu(&vlan_info->rcu, vlan_info_rcu_free);
 319                }
 320        }
 321}
 322EXPORT_SYMBOL(vlan_vid_del);
 323
 324int vlan_vids_add_by_dev(struct net_device *dev,
 325                         const struct net_device *by_dev)
 326{
 327        struct vlan_vid_info *vid_info;
 328        struct vlan_info *vlan_info;
 329        int err;
 330
 331        ASSERT_RTNL();
 332
 333        vlan_info = rtnl_dereference(by_dev->vlan_info);
 334        if (!vlan_info)
 335                return 0;
 336
 337        list_for_each_entry(vid_info, &vlan_info->vid_list, list) {
 338                err = vlan_vid_add(dev, vid_info->vid);
 339                if (err)
 340                        goto unwind;
 341        }
 342        return 0;
 343
 344unwind:
 345        list_for_each_entry_continue_reverse(vid_info,
 346                                             &vlan_info->vid_list,
 347                                             list) {
 348                vlan_vid_del(dev, vid_info->vid);
 349        }
 350
 351        return err;
 352}
 353EXPORT_SYMBOL(vlan_vids_add_by_dev);
 354
 355void vlan_vids_del_by_dev(struct net_device *dev,
 356                          const struct net_device *by_dev)
 357{
 358        struct vlan_vid_info *vid_info;
 359        struct vlan_info *vlan_info;
 360
 361        ASSERT_RTNL();
 362
 363        vlan_info = rtnl_dereference(by_dev->vlan_info);
 364        if (!vlan_info)
 365                return;
 366
 367        list_for_each_entry(vid_info, &vlan_info->vid_list, list)
 368                vlan_vid_del(dev, vid_info->vid);
 369}
 370EXPORT_SYMBOL(vlan_vids_del_by_dev);
 371
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.