18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 38c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 48c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 58c2ecf20Sopenharmony_ci#include <linux/netpoll.h> 68c2ecf20Sopenharmony_ci#include <linux/export.h> 78c2ecf20Sopenharmony_ci#include "vlan.h" 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_cibool vlan_do_receive(struct sk_buff **skbp) 108c2ecf20Sopenharmony_ci{ 118c2ecf20Sopenharmony_ci struct sk_buff *skb = *skbp; 128c2ecf20Sopenharmony_ci __be16 vlan_proto = skb->vlan_proto; 138c2ecf20Sopenharmony_ci u16 vlan_id = skb_vlan_tag_get_id(skb); 148c2ecf20Sopenharmony_ci struct net_device *vlan_dev; 158c2ecf20Sopenharmony_ci struct vlan_pcpu_stats *rx_stats; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci vlan_dev = vlan_find_dev(skb->dev, vlan_proto, vlan_id); 188c2ecf20Sopenharmony_ci if (!vlan_dev) 198c2ecf20Sopenharmony_ci return false; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci skb = *skbp = skb_share_check(skb, GFP_ATOMIC); 228c2ecf20Sopenharmony_ci if (unlikely(!skb)) 238c2ecf20Sopenharmony_ci return false; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci if (unlikely(!(vlan_dev->flags & IFF_UP))) { 268c2ecf20Sopenharmony_ci kfree_skb(skb); 278c2ecf20Sopenharmony_ci *skbp = NULL; 288c2ecf20Sopenharmony_ci return false; 298c2ecf20Sopenharmony_ci } 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci skb->dev = vlan_dev; 328c2ecf20Sopenharmony_ci if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) { 338c2ecf20Sopenharmony_ci /* Our lower layer thinks this is not local, let's make sure. 348c2ecf20Sopenharmony_ci * This allows the VLAN to have a different MAC than the 358c2ecf20Sopenharmony_ci * underlying device, and still route correctly. */ 368c2ecf20Sopenharmony_ci if (ether_addr_equal_64bits(eth_hdr(skb)->h_dest, vlan_dev->dev_addr)) 378c2ecf20Sopenharmony_ci skb->pkt_type = PACKET_HOST; 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (!(vlan_dev_priv(vlan_dev)->flags & VLAN_FLAG_REORDER_HDR) && 418c2ecf20Sopenharmony_ci !netif_is_macvlan_port(vlan_dev) && 428c2ecf20Sopenharmony_ci !netif_is_bridge_port(vlan_dev)) { 438c2ecf20Sopenharmony_ci unsigned int offset = skb->data - skb_mac_header(skb); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* 468c2ecf20Sopenharmony_ci * vlan_insert_tag expect skb->data pointing to mac header. 478c2ecf20Sopenharmony_ci * So change skb->data before calling it and change back to 488c2ecf20Sopenharmony_ci * original position later 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci skb_push(skb, offset); 518c2ecf20Sopenharmony_ci skb = *skbp = vlan_insert_inner_tag(skb, skb->vlan_proto, 528c2ecf20Sopenharmony_ci skb->vlan_tci, skb->mac_len); 538c2ecf20Sopenharmony_ci if (!skb) 548c2ecf20Sopenharmony_ci return false; 558c2ecf20Sopenharmony_ci skb_pull(skb, offset + VLAN_HLEN); 568c2ecf20Sopenharmony_ci skb_reset_mac_len(skb); 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci skb->priority = vlan_get_ingress_priority(vlan_dev, skb->vlan_tci); 608c2ecf20Sopenharmony_ci __vlan_hwaccel_clear_tag(skb); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci rx_stats = this_cpu_ptr(vlan_dev_priv(vlan_dev)->vlan_pcpu_stats); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci u64_stats_update_begin(&rx_stats->syncp); 658c2ecf20Sopenharmony_ci rx_stats->rx_packets++; 668c2ecf20Sopenharmony_ci rx_stats->rx_bytes += skb->len; 678c2ecf20Sopenharmony_ci if (skb->pkt_type == PACKET_MULTICAST) 688c2ecf20Sopenharmony_ci rx_stats->rx_multicast++; 698c2ecf20Sopenharmony_ci u64_stats_update_end(&rx_stats->syncp); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return true; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* Must be invoked with rcu_read_lock. */ 758c2ecf20Sopenharmony_cistruct net_device *__vlan_find_dev_deep_rcu(struct net_device *dev, 768c2ecf20Sopenharmony_ci __be16 vlan_proto, u16 vlan_id) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct vlan_info *vlan_info = rcu_dereference(dev->vlan_info); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (vlan_info) { 818c2ecf20Sopenharmony_ci return vlan_group_get_device(&vlan_info->grp, 828c2ecf20Sopenharmony_ci vlan_proto, vlan_id); 838c2ecf20Sopenharmony_ci } else { 848c2ecf20Sopenharmony_ci /* 858c2ecf20Sopenharmony_ci * Lower devices of master uppers (bonding, team) do not have 868c2ecf20Sopenharmony_ci * grp assigned to themselves. Grp is assigned to upper device 878c2ecf20Sopenharmony_ci * instead. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci struct net_device *upper_dev; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci upper_dev = netdev_master_upper_dev_get_rcu(dev); 928c2ecf20Sopenharmony_ci if (upper_dev) 938c2ecf20Sopenharmony_ci return __vlan_find_dev_deep_rcu(upper_dev, 948c2ecf20Sopenharmony_ci vlan_proto, vlan_id); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return NULL; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__vlan_find_dev_deep_rcu); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistruct net_device *vlan_dev_real_dev(const struct net_device *dev) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct net_device *ret = vlan_dev_priv(dev)->real_dev; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci while (is_vlan_dev(ret)) 1068c2ecf20Sopenharmony_ci ret = vlan_dev_priv(ret)->real_dev; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return ret; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlan_dev_real_dev); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ciu16 vlan_dev_vlan_id(const struct net_device *dev) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci return vlan_dev_priv(dev)->vlan_id; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlan_dev_vlan_id); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci__be16 vlan_dev_vlan_proto(const struct net_device *dev) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci return vlan_dev_priv(dev)->vlan_proto; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlan_dev_vlan_proto); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* 1258c2ecf20Sopenharmony_ci * vlan info and vid list 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void vlan_group_free(struct vlan_group *grp) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci int i, j; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci for (i = 0; i < VLAN_PROTO_NUM; i++) 1338c2ecf20Sopenharmony_ci for (j = 0; j < VLAN_GROUP_ARRAY_SPLIT_PARTS; j++) 1348c2ecf20Sopenharmony_ci kfree(grp->vlan_devices_arrays[i][j]); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void vlan_info_free(struct vlan_info *vlan_info) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci vlan_group_free(&vlan_info->grp); 1408c2ecf20Sopenharmony_ci kfree(vlan_info); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void vlan_info_rcu_free(struct rcu_head *rcu) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci vlan_info_free(container_of(rcu, struct vlan_info, rcu)); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct vlan_info *vlan_info_alloc(struct net_device *dev) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct vlan_info *vlan_info; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci vlan_info = kzalloc(sizeof(struct vlan_info), GFP_KERNEL); 1538c2ecf20Sopenharmony_ci if (!vlan_info) 1548c2ecf20Sopenharmony_ci return NULL; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci vlan_info->real_dev = dev; 1578c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vlan_info->vid_list); 1588c2ecf20Sopenharmony_ci return vlan_info; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistruct vlan_vid_info { 1628c2ecf20Sopenharmony_ci struct list_head list; 1638c2ecf20Sopenharmony_ci __be16 proto; 1648c2ecf20Sopenharmony_ci u16 vid; 1658c2ecf20Sopenharmony_ci int refcount; 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic bool vlan_hw_filter_capable(const struct net_device *dev, __be16 proto) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci if (proto == htons(ETH_P_8021Q) && 1718c2ecf20Sopenharmony_ci dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) 1728c2ecf20Sopenharmony_ci return true; 1738c2ecf20Sopenharmony_ci if (proto == htons(ETH_P_8021AD) && 1748c2ecf20Sopenharmony_ci dev->features & NETIF_F_HW_VLAN_STAG_FILTER) 1758c2ecf20Sopenharmony_ci return true; 1768c2ecf20Sopenharmony_ci return false; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info, 1808c2ecf20Sopenharmony_ci __be16 proto, u16 vid) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct vlan_vid_info *vid_info; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci list_for_each_entry(vid_info, &vlan_info->vid_list, list) { 1858c2ecf20Sopenharmony_ci if (vid_info->proto == proto && vid_info->vid == vid) 1868c2ecf20Sopenharmony_ci return vid_info; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci return NULL; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic struct vlan_vid_info *vlan_vid_info_alloc(__be16 proto, u16 vid) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct vlan_vid_info *vid_info; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci vid_info = kzalloc(sizeof(struct vlan_vid_info), GFP_KERNEL); 1968c2ecf20Sopenharmony_ci if (!vid_info) 1978c2ecf20Sopenharmony_ci return NULL; 1988c2ecf20Sopenharmony_ci vid_info->proto = proto; 1998c2ecf20Sopenharmony_ci vid_info->vid = vid; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return vid_info; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int vlan_add_rx_filter_info(struct net_device *dev, __be16 proto, u16 vid) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci if (!vlan_hw_filter_capable(dev, proto)) 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (netif_device_present(dev)) 2108c2ecf20Sopenharmony_ci return dev->netdev_ops->ndo_vlan_rx_add_vid(dev, proto, vid); 2118c2ecf20Sopenharmony_ci else 2128c2ecf20Sopenharmony_ci return -ENODEV; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int vlan_kill_rx_filter_info(struct net_device *dev, __be16 proto, u16 vid) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci if (!vlan_hw_filter_capable(dev, proto)) 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (netif_device_present(dev)) 2218c2ecf20Sopenharmony_ci return dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, proto, vid); 2228c2ecf20Sopenharmony_ci else 2238c2ecf20Sopenharmony_ci return -ENODEV; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ciint vlan_for_each(struct net_device *dev, 2278c2ecf20Sopenharmony_ci int (*action)(struct net_device *dev, int vid, void *arg), 2288c2ecf20Sopenharmony_ci void *arg) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct vlan_vid_info *vid_info; 2318c2ecf20Sopenharmony_ci struct vlan_info *vlan_info; 2328c2ecf20Sopenharmony_ci struct net_device *vdev; 2338c2ecf20Sopenharmony_ci int ret; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ASSERT_RTNL(); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci vlan_info = rtnl_dereference(dev->vlan_info); 2388c2ecf20Sopenharmony_ci if (!vlan_info) 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci list_for_each_entry(vid_info, &vlan_info->vid_list, list) { 2428c2ecf20Sopenharmony_ci vdev = vlan_group_get_device(&vlan_info->grp, vid_info->proto, 2438c2ecf20Sopenharmony_ci vid_info->vid); 2448c2ecf20Sopenharmony_ci ret = action(vdev, vid_info->vid, arg); 2458c2ecf20Sopenharmony_ci if (ret) 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlan_for_each); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ciint vlan_filter_push_vids(struct vlan_info *vlan_info, __be16 proto) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct net_device *real_dev = vlan_info->real_dev; 2568c2ecf20Sopenharmony_ci struct vlan_vid_info *vlan_vid_info; 2578c2ecf20Sopenharmony_ci int err; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci list_for_each_entry(vlan_vid_info, &vlan_info->vid_list, list) { 2608c2ecf20Sopenharmony_ci if (vlan_vid_info->proto == proto) { 2618c2ecf20Sopenharmony_ci err = vlan_add_rx_filter_info(real_dev, proto, 2628c2ecf20Sopenharmony_ci vlan_vid_info->vid); 2638c2ecf20Sopenharmony_ci if (err) 2648c2ecf20Sopenharmony_ci goto unwind; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ciunwind: 2718c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(vlan_vid_info, 2728c2ecf20Sopenharmony_ci &vlan_info->vid_list, list) { 2738c2ecf20Sopenharmony_ci if (vlan_vid_info->proto == proto) 2748c2ecf20Sopenharmony_ci vlan_kill_rx_filter_info(real_dev, proto, 2758c2ecf20Sopenharmony_ci vlan_vid_info->vid); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return err; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlan_filter_push_vids); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_civoid vlan_filter_drop_vids(struct vlan_info *vlan_info, __be16 proto) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct vlan_vid_info *vlan_vid_info; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci list_for_each_entry(vlan_vid_info, &vlan_info->vid_list, list) 2878c2ecf20Sopenharmony_ci if (vlan_vid_info->proto == proto) 2888c2ecf20Sopenharmony_ci vlan_kill_rx_filter_info(vlan_info->real_dev, 2898c2ecf20Sopenharmony_ci vlan_vid_info->proto, 2908c2ecf20Sopenharmony_ci vlan_vid_info->vid); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlan_filter_drop_vids); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int __vlan_vid_add(struct vlan_info *vlan_info, __be16 proto, u16 vid, 2958c2ecf20Sopenharmony_ci struct vlan_vid_info **pvid_info) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct net_device *dev = vlan_info->real_dev; 2988c2ecf20Sopenharmony_ci struct vlan_vid_info *vid_info; 2998c2ecf20Sopenharmony_ci int err; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci vid_info = vlan_vid_info_alloc(proto, vid); 3028c2ecf20Sopenharmony_ci if (!vid_info) 3038c2ecf20Sopenharmony_ci return -ENOMEM; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci err = vlan_add_rx_filter_info(dev, proto, vid); 3068c2ecf20Sopenharmony_ci if (err) { 3078c2ecf20Sopenharmony_ci kfree(vid_info); 3088c2ecf20Sopenharmony_ci return err; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci list_add(&vid_info->list, &vlan_info->vid_list); 3128c2ecf20Sopenharmony_ci vlan_info->nr_vids++; 3138c2ecf20Sopenharmony_ci *pvid_info = vid_info; 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ciint vlan_vid_add(struct net_device *dev, __be16 proto, u16 vid) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct vlan_info *vlan_info; 3208c2ecf20Sopenharmony_ci struct vlan_vid_info *vid_info; 3218c2ecf20Sopenharmony_ci bool vlan_info_created = false; 3228c2ecf20Sopenharmony_ci int err; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci ASSERT_RTNL(); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci vlan_info = rtnl_dereference(dev->vlan_info); 3278c2ecf20Sopenharmony_ci if (!vlan_info) { 3288c2ecf20Sopenharmony_ci vlan_info = vlan_info_alloc(dev); 3298c2ecf20Sopenharmony_ci if (!vlan_info) 3308c2ecf20Sopenharmony_ci return -ENOMEM; 3318c2ecf20Sopenharmony_ci vlan_info_created = true; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci vid_info = vlan_vid_info_get(vlan_info, proto, vid); 3348c2ecf20Sopenharmony_ci if (!vid_info) { 3358c2ecf20Sopenharmony_ci err = __vlan_vid_add(vlan_info, proto, vid, &vid_info); 3368c2ecf20Sopenharmony_ci if (err) 3378c2ecf20Sopenharmony_ci goto out_free_vlan_info; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci vid_info->refcount++; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (vlan_info_created) 3428c2ecf20Sopenharmony_ci rcu_assign_pointer(dev->vlan_info, vlan_info); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ciout_free_vlan_info: 3478c2ecf20Sopenharmony_ci if (vlan_info_created) 3488c2ecf20Sopenharmony_ci kfree(vlan_info); 3498c2ecf20Sopenharmony_ci return err; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlan_vid_add); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic void __vlan_vid_del(struct vlan_info *vlan_info, 3548c2ecf20Sopenharmony_ci struct vlan_vid_info *vid_info) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct net_device *dev = vlan_info->real_dev; 3578c2ecf20Sopenharmony_ci __be16 proto = vid_info->proto; 3588c2ecf20Sopenharmony_ci u16 vid = vid_info->vid; 3598c2ecf20Sopenharmony_ci int err; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci err = vlan_kill_rx_filter_info(dev, proto, vid); 3628c2ecf20Sopenharmony_ci if (err && dev->reg_state != NETREG_UNREGISTERING) 3638c2ecf20Sopenharmony_ci netdev_warn(dev, "failed to kill vid %04x/%d\n", proto, vid); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci list_del(&vid_info->list); 3668c2ecf20Sopenharmony_ci kfree(vid_info); 3678c2ecf20Sopenharmony_ci vlan_info->nr_vids--; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_civoid vlan_vid_del(struct net_device *dev, __be16 proto, u16 vid) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct vlan_info *vlan_info; 3738c2ecf20Sopenharmony_ci struct vlan_vid_info *vid_info; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci ASSERT_RTNL(); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci vlan_info = rtnl_dereference(dev->vlan_info); 3788c2ecf20Sopenharmony_ci if (!vlan_info) 3798c2ecf20Sopenharmony_ci return; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci vid_info = vlan_vid_info_get(vlan_info, proto, vid); 3828c2ecf20Sopenharmony_ci if (!vid_info) 3838c2ecf20Sopenharmony_ci return; 3848c2ecf20Sopenharmony_ci vid_info->refcount--; 3858c2ecf20Sopenharmony_ci if (vid_info->refcount == 0) { 3868c2ecf20Sopenharmony_ci __vlan_vid_del(vlan_info, vid_info); 3878c2ecf20Sopenharmony_ci if (vlan_info->nr_vids == 0) { 3888c2ecf20Sopenharmony_ci RCU_INIT_POINTER(dev->vlan_info, NULL); 3898c2ecf20Sopenharmony_ci call_rcu(&vlan_info->rcu, vlan_info_rcu_free); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlan_vid_del); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ciint vlan_vids_add_by_dev(struct net_device *dev, 3968c2ecf20Sopenharmony_ci const struct net_device *by_dev) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct vlan_vid_info *vid_info; 3998c2ecf20Sopenharmony_ci struct vlan_info *vlan_info; 4008c2ecf20Sopenharmony_ci int err; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci ASSERT_RTNL(); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci vlan_info = rtnl_dereference(by_dev->vlan_info); 4058c2ecf20Sopenharmony_ci if (!vlan_info) 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci list_for_each_entry(vid_info, &vlan_info->vid_list, list) { 4098c2ecf20Sopenharmony_ci if (!vlan_hw_filter_capable(by_dev, vid_info->proto)) 4108c2ecf20Sopenharmony_ci continue; 4118c2ecf20Sopenharmony_ci err = vlan_vid_add(dev, vid_info->proto, vid_info->vid); 4128c2ecf20Sopenharmony_ci if (err) 4138c2ecf20Sopenharmony_ci goto unwind; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci return 0; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ciunwind: 4188c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(vid_info, 4198c2ecf20Sopenharmony_ci &vlan_info->vid_list, 4208c2ecf20Sopenharmony_ci list) { 4218c2ecf20Sopenharmony_ci if (!vlan_hw_filter_capable(by_dev, vid_info->proto)) 4228c2ecf20Sopenharmony_ci continue; 4238c2ecf20Sopenharmony_ci vlan_vid_del(dev, vid_info->proto, vid_info->vid); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci return err; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlan_vids_add_by_dev); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_civoid vlan_vids_del_by_dev(struct net_device *dev, 4318c2ecf20Sopenharmony_ci const struct net_device *by_dev) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct vlan_vid_info *vid_info; 4348c2ecf20Sopenharmony_ci struct vlan_info *vlan_info; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci ASSERT_RTNL(); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci vlan_info = rtnl_dereference(by_dev->vlan_info); 4398c2ecf20Sopenharmony_ci if (!vlan_info) 4408c2ecf20Sopenharmony_ci return; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci list_for_each_entry(vid_info, &vlan_info->vid_list, list) { 4438c2ecf20Sopenharmony_ci if (!vlan_hw_filter_capable(by_dev, vid_info->proto)) 4448c2ecf20Sopenharmony_ci continue; 4458c2ecf20Sopenharmony_ci vlan_vid_del(dev, vid_info->proto, vid_info->vid); 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlan_vids_del_by_dev); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cibool vlan_uses_dev(const struct net_device *dev) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct vlan_info *vlan_info; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci ASSERT_RTNL(); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci vlan_info = rtnl_dereference(dev->vlan_info); 4578c2ecf20Sopenharmony_ci if (!vlan_info) 4588c2ecf20Sopenharmony_ci return false; 4598c2ecf20Sopenharmony_ci return vlan_info->grp.nr_vlan_devs ? true : false; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vlan_uses_dev); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic struct sk_buff *vlan_gro_receive(struct list_head *head, 4648c2ecf20Sopenharmony_ci struct sk_buff *skb) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci const struct packet_offload *ptype; 4678c2ecf20Sopenharmony_ci unsigned int hlen, off_vlan; 4688c2ecf20Sopenharmony_ci struct sk_buff *pp = NULL; 4698c2ecf20Sopenharmony_ci struct vlan_hdr *vhdr; 4708c2ecf20Sopenharmony_ci struct sk_buff *p; 4718c2ecf20Sopenharmony_ci __be16 type; 4728c2ecf20Sopenharmony_ci int flush = 1; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci off_vlan = skb_gro_offset(skb); 4758c2ecf20Sopenharmony_ci hlen = off_vlan + sizeof(*vhdr); 4768c2ecf20Sopenharmony_ci vhdr = skb_gro_header_fast(skb, off_vlan); 4778c2ecf20Sopenharmony_ci if (skb_gro_header_hard(skb, hlen)) { 4788c2ecf20Sopenharmony_ci vhdr = skb_gro_header_slow(skb, hlen, off_vlan); 4798c2ecf20Sopenharmony_ci if (unlikely(!vhdr)) 4808c2ecf20Sopenharmony_ci goto out; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci type = vhdr->h_vlan_encapsulated_proto; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci rcu_read_lock(); 4868c2ecf20Sopenharmony_ci ptype = gro_find_receive_by_type(type); 4878c2ecf20Sopenharmony_ci if (!ptype) 4888c2ecf20Sopenharmony_ci goto out_unlock; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci flush = 0; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci list_for_each_entry(p, head, list) { 4938c2ecf20Sopenharmony_ci struct vlan_hdr *vhdr2; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (!NAPI_GRO_CB(p)->same_flow) 4968c2ecf20Sopenharmony_ci continue; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci vhdr2 = (struct vlan_hdr *)(p->data + off_vlan); 4998c2ecf20Sopenharmony_ci if (compare_vlan_header(vhdr, vhdr2)) 5008c2ecf20Sopenharmony_ci NAPI_GRO_CB(p)->same_flow = 0; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci skb_gro_pull(skb, sizeof(*vhdr)); 5048c2ecf20Sopenharmony_ci skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr)); 5058c2ecf20Sopenharmony_ci pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ciout_unlock: 5088c2ecf20Sopenharmony_ci rcu_read_unlock(); 5098c2ecf20Sopenharmony_ciout: 5108c2ecf20Sopenharmony_ci skb_gro_flush_final(skb, pp, flush); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return pp; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic int vlan_gro_complete(struct sk_buff *skb, int nhoff) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data + nhoff); 5188c2ecf20Sopenharmony_ci __be16 type = vhdr->h_vlan_encapsulated_proto; 5198c2ecf20Sopenharmony_ci struct packet_offload *ptype; 5208c2ecf20Sopenharmony_ci int err = -ENOENT; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci rcu_read_lock(); 5238c2ecf20Sopenharmony_ci ptype = gro_find_complete_by_type(type); 5248c2ecf20Sopenharmony_ci if (ptype) 5258c2ecf20Sopenharmony_ci err = ptype->callbacks.gro_complete(skb, nhoff + sizeof(*vhdr)); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci rcu_read_unlock(); 5288c2ecf20Sopenharmony_ci return err; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic struct packet_offload vlan_packet_offloads[] __read_mostly = { 5328c2ecf20Sopenharmony_ci { 5338c2ecf20Sopenharmony_ci .type = cpu_to_be16(ETH_P_8021Q), 5348c2ecf20Sopenharmony_ci .priority = 10, 5358c2ecf20Sopenharmony_ci .callbacks = { 5368c2ecf20Sopenharmony_ci .gro_receive = vlan_gro_receive, 5378c2ecf20Sopenharmony_ci .gro_complete = vlan_gro_complete, 5388c2ecf20Sopenharmony_ci }, 5398c2ecf20Sopenharmony_ci }, 5408c2ecf20Sopenharmony_ci { 5418c2ecf20Sopenharmony_ci .type = cpu_to_be16(ETH_P_8021AD), 5428c2ecf20Sopenharmony_ci .priority = 10, 5438c2ecf20Sopenharmony_ci .callbacks = { 5448c2ecf20Sopenharmony_ci .gro_receive = vlan_gro_receive, 5458c2ecf20Sopenharmony_ci .gro_complete = vlan_gro_complete, 5468c2ecf20Sopenharmony_ci }, 5478c2ecf20Sopenharmony_ci }, 5488c2ecf20Sopenharmony_ci}; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic int __init vlan_offload_init(void) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci unsigned int i; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++) 5558c2ecf20Sopenharmony_ci dev_add_offload(&vlan_packet_offloads[i]); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci return 0; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cifs_initcall(vlan_offload_init); 561