162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/skbuff.h> 362306a36Sopenharmony_ci#include <linux/netdevice.h> 462306a36Sopenharmony_ci#include <linux/if_vlan.h> 562306a36Sopenharmony_ci#include <linux/netpoll.h> 662306a36Sopenharmony_ci#include <linux/export.h> 762306a36Sopenharmony_ci#include <net/gro.h> 862306a36Sopenharmony_ci#include "vlan.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cibool vlan_do_receive(struct sk_buff **skbp) 1162306a36Sopenharmony_ci{ 1262306a36Sopenharmony_ci struct sk_buff *skb = *skbp; 1362306a36Sopenharmony_ci __be16 vlan_proto = skb->vlan_proto; 1462306a36Sopenharmony_ci u16 vlan_id = skb_vlan_tag_get_id(skb); 1562306a36Sopenharmony_ci struct net_device *vlan_dev; 1662306a36Sopenharmony_ci struct vlan_pcpu_stats *rx_stats; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci vlan_dev = vlan_find_dev(skb->dev, vlan_proto, vlan_id); 1962306a36Sopenharmony_ci if (!vlan_dev) 2062306a36Sopenharmony_ci return false; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci skb = *skbp = skb_share_check(skb, GFP_ATOMIC); 2362306a36Sopenharmony_ci if (unlikely(!skb)) 2462306a36Sopenharmony_ci return false; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci if (unlikely(!(vlan_dev->flags & IFF_UP))) { 2762306a36Sopenharmony_ci kfree_skb(skb); 2862306a36Sopenharmony_ci *skbp = NULL; 2962306a36Sopenharmony_ci return false; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci skb->dev = vlan_dev; 3362306a36Sopenharmony_ci if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) { 3462306a36Sopenharmony_ci /* Our lower layer thinks this is not local, let's make sure. 3562306a36Sopenharmony_ci * This allows the VLAN to have a different MAC than the 3662306a36Sopenharmony_ci * underlying device, and still route correctly. */ 3762306a36Sopenharmony_ci if (ether_addr_equal_64bits(eth_hdr(skb)->h_dest, vlan_dev->dev_addr)) 3862306a36Sopenharmony_ci skb->pkt_type = PACKET_HOST; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (!(vlan_dev_priv(vlan_dev)->flags & VLAN_FLAG_REORDER_HDR) && 4262306a36Sopenharmony_ci !netif_is_macvlan_port(vlan_dev) && 4362306a36Sopenharmony_ci !netif_is_bridge_port(vlan_dev)) { 4462306a36Sopenharmony_ci unsigned int offset = skb->data - skb_mac_header(skb); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* 4762306a36Sopenharmony_ci * vlan_insert_tag expect skb->data pointing to mac header. 4862306a36Sopenharmony_ci * So change skb->data before calling it and change back to 4962306a36Sopenharmony_ci * original position later 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci skb_push(skb, offset); 5262306a36Sopenharmony_ci skb = *skbp = vlan_insert_inner_tag(skb, skb->vlan_proto, 5362306a36Sopenharmony_ci skb->vlan_tci, skb->mac_len); 5462306a36Sopenharmony_ci if (!skb) 5562306a36Sopenharmony_ci return false; 5662306a36Sopenharmony_ci skb_pull(skb, offset + VLAN_HLEN); 5762306a36Sopenharmony_ci skb_reset_mac_len(skb); 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci skb->priority = vlan_get_ingress_priority(vlan_dev, skb->vlan_tci); 6162306a36Sopenharmony_ci __vlan_hwaccel_clear_tag(skb); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci rx_stats = this_cpu_ptr(vlan_dev_priv(vlan_dev)->vlan_pcpu_stats); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci u64_stats_update_begin(&rx_stats->syncp); 6662306a36Sopenharmony_ci u64_stats_inc(&rx_stats->rx_packets); 6762306a36Sopenharmony_ci u64_stats_add(&rx_stats->rx_bytes, skb->len); 6862306a36Sopenharmony_ci if (skb->pkt_type == PACKET_MULTICAST) 6962306a36Sopenharmony_ci u64_stats_inc(&rx_stats->rx_multicast); 7062306a36Sopenharmony_ci u64_stats_update_end(&rx_stats->syncp); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return true; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* Must be invoked with rcu_read_lock. */ 7662306a36Sopenharmony_cistruct net_device *__vlan_find_dev_deep_rcu(struct net_device *dev, 7762306a36Sopenharmony_ci __be16 vlan_proto, u16 vlan_id) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct vlan_info *vlan_info = rcu_dereference(dev->vlan_info); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (vlan_info) { 8262306a36Sopenharmony_ci return vlan_group_get_device(&vlan_info->grp, 8362306a36Sopenharmony_ci vlan_proto, vlan_id); 8462306a36Sopenharmony_ci } else { 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * Lower devices of master uppers (bonding, team) do not have 8762306a36Sopenharmony_ci * grp assigned to themselves. Grp is assigned to upper device 8862306a36Sopenharmony_ci * instead. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci struct net_device *upper_dev; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci upper_dev = netdev_master_upper_dev_get_rcu(dev); 9362306a36Sopenharmony_ci if (upper_dev) 9462306a36Sopenharmony_ci return __vlan_find_dev_deep_rcu(upper_dev, 9562306a36Sopenharmony_ci vlan_proto, vlan_id); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return NULL; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ciEXPORT_SYMBOL(__vlan_find_dev_deep_rcu); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct net_device *vlan_dev_real_dev(const struct net_device *dev) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct net_device *ret = vlan_dev_priv(dev)->real_dev; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci while (is_vlan_dev(ret)) 10762306a36Sopenharmony_ci ret = vlan_dev_priv(ret)->real_dev; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return ret; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ciEXPORT_SYMBOL(vlan_dev_real_dev); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ciu16 vlan_dev_vlan_id(const struct net_device *dev) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci return vlan_dev_priv(dev)->vlan_id; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ciEXPORT_SYMBOL(vlan_dev_vlan_id); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci__be16 vlan_dev_vlan_proto(const struct net_device *dev) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci return vlan_dev_priv(dev)->vlan_proto; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ciEXPORT_SYMBOL(vlan_dev_vlan_proto); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* 12662306a36Sopenharmony_ci * vlan info and vid list 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void vlan_group_free(struct vlan_group *grp) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci int i, j; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci for (i = 0; i < VLAN_PROTO_NUM; i++) 13462306a36Sopenharmony_ci for (j = 0; j < VLAN_GROUP_ARRAY_SPLIT_PARTS; j++) 13562306a36Sopenharmony_ci kfree(grp->vlan_devices_arrays[i][j]); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void vlan_info_free(struct vlan_info *vlan_info) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci vlan_group_free(&vlan_info->grp); 14162306a36Sopenharmony_ci kfree(vlan_info); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void vlan_info_rcu_free(struct rcu_head *rcu) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci vlan_info_free(container_of(rcu, struct vlan_info, rcu)); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic struct vlan_info *vlan_info_alloc(struct net_device *dev) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct vlan_info *vlan_info; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci vlan_info = kzalloc(sizeof(struct vlan_info), GFP_KERNEL); 15462306a36Sopenharmony_ci if (!vlan_info) 15562306a36Sopenharmony_ci return NULL; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci vlan_info->real_dev = dev; 15862306a36Sopenharmony_ci INIT_LIST_HEAD(&vlan_info->vid_list); 15962306a36Sopenharmony_ci return vlan_info; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistruct vlan_vid_info { 16362306a36Sopenharmony_ci struct list_head list; 16462306a36Sopenharmony_ci __be16 proto; 16562306a36Sopenharmony_ci u16 vid; 16662306a36Sopenharmony_ci int refcount; 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic bool vlan_hw_filter_capable(const struct net_device *dev, __be16 proto) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci if (proto == htons(ETH_P_8021Q) && 17262306a36Sopenharmony_ci dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) 17362306a36Sopenharmony_ci return true; 17462306a36Sopenharmony_ci if (proto == htons(ETH_P_8021AD) && 17562306a36Sopenharmony_ci dev->features & NETIF_F_HW_VLAN_STAG_FILTER) 17662306a36Sopenharmony_ci return true; 17762306a36Sopenharmony_ci return false; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info, 18162306a36Sopenharmony_ci __be16 proto, u16 vid) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct vlan_vid_info *vid_info; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci list_for_each_entry(vid_info, &vlan_info->vid_list, list) { 18662306a36Sopenharmony_ci if (vid_info->proto == proto && vid_info->vid == vid) 18762306a36Sopenharmony_ci return vid_info; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci return NULL; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic struct vlan_vid_info *vlan_vid_info_alloc(__be16 proto, u16 vid) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct vlan_vid_info *vid_info; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci vid_info = kzalloc(sizeof(struct vlan_vid_info), GFP_KERNEL); 19762306a36Sopenharmony_ci if (!vid_info) 19862306a36Sopenharmony_ci return NULL; 19962306a36Sopenharmony_ci vid_info->proto = proto; 20062306a36Sopenharmony_ci vid_info->vid = vid; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return vid_info; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int vlan_add_rx_filter_info(struct net_device *dev, __be16 proto, u16 vid) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci if (!vlan_hw_filter_capable(dev, proto)) 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (netif_device_present(dev)) 21162306a36Sopenharmony_ci return dev->netdev_ops->ndo_vlan_rx_add_vid(dev, proto, vid); 21262306a36Sopenharmony_ci else 21362306a36Sopenharmony_ci return -ENODEV; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int vlan_kill_rx_filter_info(struct net_device *dev, __be16 proto, u16 vid) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci if (!vlan_hw_filter_capable(dev, proto)) 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (netif_device_present(dev)) 22262306a36Sopenharmony_ci return dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, proto, vid); 22362306a36Sopenharmony_ci else 22462306a36Sopenharmony_ci return -ENODEV; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ciint vlan_for_each(struct net_device *dev, 22862306a36Sopenharmony_ci int (*action)(struct net_device *dev, int vid, void *arg), 22962306a36Sopenharmony_ci void *arg) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct vlan_vid_info *vid_info; 23262306a36Sopenharmony_ci struct vlan_info *vlan_info; 23362306a36Sopenharmony_ci struct net_device *vdev; 23462306a36Sopenharmony_ci int ret; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ASSERT_RTNL(); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci vlan_info = rtnl_dereference(dev->vlan_info); 23962306a36Sopenharmony_ci if (!vlan_info) 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci list_for_each_entry(vid_info, &vlan_info->vid_list, list) { 24362306a36Sopenharmony_ci vdev = vlan_group_get_device(&vlan_info->grp, vid_info->proto, 24462306a36Sopenharmony_ci vid_info->vid); 24562306a36Sopenharmony_ci ret = action(vdev, vid_info->vid, arg); 24662306a36Sopenharmony_ci if (ret) 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ciEXPORT_SYMBOL(vlan_for_each); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ciint vlan_filter_push_vids(struct vlan_info *vlan_info, __be16 proto) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct net_device *real_dev = vlan_info->real_dev; 25762306a36Sopenharmony_ci struct vlan_vid_info *vlan_vid_info; 25862306a36Sopenharmony_ci int err; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci list_for_each_entry(vlan_vid_info, &vlan_info->vid_list, list) { 26162306a36Sopenharmony_ci if (vlan_vid_info->proto == proto) { 26262306a36Sopenharmony_ci err = vlan_add_rx_filter_info(real_dev, proto, 26362306a36Sopenharmony_ci vlan_vid_info->vid); 26462306a36Sopenharmony_ci if (err) 26562306a36Sopenharmony_ci goto unwind; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ciunwind: 27262306a36Sopenharmony_ci list_for_each_entry_continue_reverse(vlan_vid_info, 27362306a36Sopenharmony_ci &vlan_info->vid_list, list) { 27462306a36Sopenharmony_ci if (vlan_vid_info->proto == proto) 27562306a36Sopenharmony_ci vlan_kill_rx_filter_info(real_dev, proto, 27662306a36Sopenharmony_ci vlan_vid_info->vid); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return err; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ciEXPORT_SYMBOL(vlan_filter_push_vids); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_civoid vlan_filter_drop_vids(struct vlan_info *vlan_info, __be16 proto) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct vlan_vid_info *vlan_vid_info; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci list_for_each_entry(vlan_vid_info, &vlan_info->vid_list, list) 28862306a36Sopenharmony_ci if (vlan_vid_info->proto == proto) 28962306a36Sopenharmony_ci vlan_kill_rx_filter_info(vlan_info->real_dev, 29062306a36Sopenharmony_ci vlan_vid_info->proto, 29162306a36Sopenharmony_ci vlan_vid_info->vid); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL(vlan_filter_drop_vids); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int __vlan_vid_add(struct vlan_info *vlan_info, __be16 proto, u16 vid, 29662306a36Sopenharmony_ci struct vlan_vid_info **pvid_info) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct net_device *dev = vlan_info->real_dev; 29962306a36Sopenharmony_ci struct vlan_vid_info *vid_info; 30062306a36Sopenharmony_ci int err; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci vid_info = vlan_vid_info_alloc(proto, vid); 30362306a36Sopenharmony_ci if (!vid_info) 30462306a36Sopenharmony_ci return -ENOMEM; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci err = vlan_add_rx_filter_info(dev, proto, vid); 30762306a36Sopenharmony_ci if (err) { 30862306a36Sopenharmony_ci kfree(vid_info); 30962306a36Sopenharmony_ci return err; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci list_add(&vid_info->list, &vlan_info->vid_list); 31362306a36Sopenharmony_ci vlan_info->nr_vids++; 31462306a36Sopenharmony_ci *pvid_info = vid_info; 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ciint vlan_vid_add(struct net_device *dev, __be16 proto, u16 vid) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct vlan_info *vlan_info; 32162306a36Sopenharmony_ci struct vlan_vid_info *vid_info; 32262306a36Sopenharmony_ci bool vlan_info_created = false; 32362306a36Sopenharmony_ci int err; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci ASSERT_RTNL(); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci vlan_info = rtnl_dereference(dev->vlan_info); 32862306a36Sopenharmony_ci if (!vlan_info) { 32962306a36Sopenharmony_ci vlan_info = vlan_info_alloc(dev); 33062306a36Sopenharmony_ci if (!vlan_info) 33162306a36Sopenharmony_ci return -ENOMEM; 33262306a36Sopenharmony_ci vlan_info_created = true; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci vid_info = vlan_vid_info_get(vlan_info, proto, vid); 33562306a36Sopenharmony_ci if (!vid_info) { 33662306a36Sopenharmony_ci err = __vlan_vid_add(vlan_info, proto, vid, &vid_info); 33762306a36Sopenharmony_ci if (err) 33862306a36Sopenharmony_ci goto out_free_vlan_info; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci vid_info->refcount++; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (vlan_info_created) 34362306a36Sopenharmony_ci rcu_assign_pointer(dev->vlan_info, vlan_info); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ciout_free_vlan_info: 34862306a36Sopenharmony_ci if (vlan_info_created) 34962306a36Sopenharmony_ci kfree(vlan_info); 35062306a36Sopenharmony_ci return err; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ciEXPORT_SYMBOL(vlan_vid_add); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic void __vlan_vid_del(struct vlan_info *vlan_info, 35562306a36Sopenharmony_ci struct vlan_vid_info *vid_info) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct net_device *dev = vlan_info->real_dev; 35862306a36Sopenharmony_ci __be16 proto = vid_info->proto; 35962306a36Sopenharmony_ci u16 vid = vid_info->vid; 36062306a36Sopenharmony_ci int err; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci err = vlan_kill_rx_filter_info(dev, proto, vid); 36362306a36Sopenharmony_ci if (err && dev->reg_state != NETREG_UNREGISTERING) 36462306a36Sopenharmony_ci netdev_warn(dev, "failed to kill vid %04x/%d\n", proto, vid); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci list_del(&vid_info->list); 36762306a36Sopenharmony_ci kfree(vid_info); 36862306a36Sopenharmony_ci vlan_info->nr_vids--; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_civoid vlan_vid_del(struct net_device *dev, __be16 proto, u16 vid) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct vlan_info *vlan_info; 37462306a36Sopenharmony_ci struct vlan_vid_info *vid_info; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci ASSERT_RTNL(); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci vlan_info = rtnl_dereference(dev->vlan_info); 37962306a36Sopenharmony_ci if (!vlan_info) 38062306a36Sopenharmony_ci return; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci vid_info = vlan_vid_info_get(vlan_info, proto, vid); 38362306a36Sopenharmony_ci if (!vid_info) 38462306a36Sopenharmony_ci return; 38562306a36Sopenharmony_ci vid_info->refcount--; 38662306a36Sopenharmony_ci if (vid_info->refcount == 0) { 38762306a36Sopenharmony_ci __vlan_vid_del(vlan_info, vid_info); 38862306a36Sopenharmony_ci if (vlan_info->nr_vids == 0) { 38962306a36Sopenharmony_ci RCU_INIT_POINTER(dev->vlan_info, NULL); 39062306a36Sopenharmony_ci call_rcu(&vlan_info->rcu, vlan_info_rcu_free); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ciEXPORT_SYMBOL(vlan_vid_del); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ciint vlan_vids_add_by_dev(struct net_device *dev, 39762306a36Sopenharmony_ci const struct net_device *by_dev) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct vlan_vid_info *vid_info; 40062306a36Sopenharmony_ci struct vlan_info *vlan_info; 40162306a36Sopenharmony_ci int err; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci ASSERT_RTNL(); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci vlan_info = rtnl_dereference(by_dev->vlan_info); 40662306a36Sopenharmony_ci if (!vlan_info) 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci list_for_each_entry(vid_info, &vlan_info->vid_list, list) { 41062306a36Sopenharmony_ci if (!vlan_hw_filter_capable(by_dev, vid_info->proto)) 41162306a36Sopenharmony_ci continue; 41262306a36Sopenharmony_ci err = vlan_vid_add(dev, vid_info->proto, vid_info->vid); 41362306a36Sopenharmony_ci if (err) 41462306a36Sopenharmony_ci goto unwind; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ciunwind: 41962306a36Sopenharmony_ci list_for_each_entry_continue_reverse(vid_info, 42062306a36Sopenharmony_ci &vlan_info->vid_list, 42162306a36Sopenharmony_ci list) { 42262306a36Sopenharmony_ci if (!vlan_hw_filter_capable(by_dev, vid_info->proto)) 42362306a36Sopenharmony_ci continue; 42462306a36Sopenharmony_ci vlan_vid_del(dev, vid_info->proto, vid_info->vid); 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return err; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ciEXPORT_SYMBOL(vlan_vids_add_by_dev); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_civoid vlan_vids_del_by_dev(struct net_device *dev, 43262306a36Sopenharmony_ci const struct net_device *by_dev) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct vlan_vid_info *vid_info; 43562306a36Sopenharmony_ci struct vlan_info *vlan_info; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci ASSERT_RTNL(); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci vlan_info = rtnl_dereference(by_dev->vlan_info); 44062306a36Sopenharmony_ci if (!vlan_info) 44162306a36Sopenharmony_ci return; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci list_for_each_entry(vid_info, &vlan_info->vid_list, list) { 44462306a36Sopenharmony_ci if (!vlan_hw_filter_capable(by_dev, vid_info->proto)) 44562306a36Sopenharmony_ci continue; 44662306a36Sopenharmony_ci vlan_vid_del(dev, vid_info->proto, vid_info->vid); 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ciEXPORT_SYMBOL(vlan_vids_del_by_dev); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cibool vlan_uses_dev(const struct net_device *dev) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct vlan_info *vlan_info; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci ASSERT_RTNL(); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci vlan_info = rtnl_dereference(dev->vlan_info); 45862306a36Sopenharmony_ci if (!vlan_info) 45962306a36Sopenharmony_ci return false; 46062306a36Sopenharmony_ci return vlan_info->grp.nr_vlan_devs ? true : false; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ciEXPORT_SYMBOL(vlan_uses_dev); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic struct sk_buff *vlan_gro_receive(struct list_head *head, 46562306a36Sopenharmony_ci struct sk_buff *skb) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci const struct packet_offload *ptype; 46862306a36Sopenharmony_ci unsigned int hlen, off_vlan; 46962306a36Sopenharmony_ci struct sk_buff *pp = NULL; 47062306a36Sopenharmony_ci struct vlan_hdr *vhdr; 47162306a36Sopenharmony_ci struct sk_buff *p; 47262306a36Sopenharmony_ci __be16 type; 47362306a36Sopenharmony_ci int flush = 1; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci off_vlan = skb_gro_offset(skb); 47662306a36Sopenharmony_ci hlen = off_vlan + sizeof(*vhdr); 47762306a36Sopenharmony_ci vhdr = skb_gro_header(skb, hlen, off_vlan); 47862306a36Sopenharmony_ci if (unlikely(!vhdr)) 47962306a36Sopenharmony_ci goto out; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci type = vhdr->h_vlan_encapsulated_proto; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci ptype = gro_find_receive_by_type(type); 48462306a36Sopenharmony_ci if (!ptype) 48562306a36Sopenharmony_ci goto out; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci flush = 0; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci list_for_each_entry(p, head, list) { 49062306a36Sopenharmony_ci struct vlan_hdr *vhdr2; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (!NAPI_GRO_CB(p)->same_flow) 49362306a36Sopenharmony_ci continue; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci vhdr2 = (struct vlan_hdr *)(p->data + off_vlan); 49662306a36Sopenharmony_ci if (compare_vlan_header(vhdr, vhdr2)) 49762306a36Sopenharmony_ci NAPI_GRO_CB(p)->same_flow = 0; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci skb_gro_pull(skb, sizeof(*vhdr)); 50162306a36Sopenharmony_ci skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr)); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci pp = indirect_call_gro_receive_inet(ptype->callbacks.gro_receive, 50462306a36Sopenharmony_ci ipv6_gro_receive, inet_gro_receive, 50562306a36Sopenharmony_ci head, skb); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ciout: 50862306a36Sopenharmony_ci skb_gro_flush_final(skb, pp, flush); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return pp; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int vlan_gro_complete(struct sk_buff *skb, int nhoff) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data + nhoff); 51662306a36Sopenharmony_ci __be16 type = vhdr->h_vlan_encapsulated_proto; 51762306a36Sopenharmony_ci struct packet_offload *ptype; 51862306a36Sopenharmony_ci int err = -ENOENT; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci ptype = gro_find_complete_by_type(type); 52162306a36Sopenharmony_ci if (ptype) 52262306a36Sopenharmony_ci err = INDIRECT_CALL_INET(ptype->callbacks.gro_complete, 52362306a36Sopenharmony_ci ipv6_gro_complete, inet_gro_complete, 52462306a36Sopenharmony_ci skb, nhoff + sizeof(*vhdr)); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return err; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic struct packet_offload vlan_packet_offloads[] __read_mostly = { 53062306a36Sopenharmony_ci { 53162306a36Sopenharmony_ci .type = cpu_to_be16(ETH_P_8021Q), 53262306a36Sopenharmony_ci .priority = 10, 53362306a36Sopenharmony_ci .callbacks = { 53462306a36Sopenharmony_ci .gro_receive = vlan_gro_receive, 53562306a36Sopenharmony_ci .gro_complete = vlan_gro_complete, 53662306a36Sopenharmony_ci }, 53762306a36Sopenharmony_ci }, 53862306a36Sopenharmony_ci { 53962306a36Sopenharmony_ci .type = cpu_to_be16(ETH_P_8021AD), 54062306a36Sopenharmony_ci .priority = 10, 54162306a36Sopenharmony_ci .callbacks = { 54262306a36Sopenharmony_ci .gro_receive = vlan_gro_receive, 54362306a36Sopenharmony_ci .gro_complete = vlan_gro_complete, 54462306a36Sopenharmony_ci }, 54562306a36Sopenharmony_ci }, 54662306a36Sopenharmony_ci}; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic int __init vlan_offload_init(void) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci unsigned int i; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++) 55362306a36Sopenharmony_ci dev_add_offload(&vlan_packet_offloads[i]); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cifs_initcall(vlan_offload_init); 559