18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * INET 802.1Q VLAN 48c2ecf20Sopenharmony_ci * Ethernet-type device handling. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: Ben Greear <greearb@candelatech.com> 78c2ecf20Sopenharmony_ci * Please send support related email to: netdev@vger.kernel.org 88c2ecf20Sopenharmony_ci * VLAN Home Page: http://www.candelatech.com/~greear/vlan.html 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Fixes: 118c2ecf20Sopenharmony_ci * Fix for packet capture - Nick Eggleston <nick@dccinc.com>; 128c2ecf20Sopenharmony_ci * Add HW acceleration hooks - David S. Miller <davem@redhat.com>; 138c2ecf20Sopenharmony_ci * Correct all the locking - David S. Miller <davem@redhat.com>; 148c2ecf20Sopenharmony_ci * Use hash table for VLAN groups - David S. Miller <davem@redhat.com> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/capability.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 228c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci#include <linux/rculist.h> 268c2ecf20Sopenharmony_ci#include <net/p8022.h> 278c2ecf20Sopenharmony_ci#include <net/arp.h> 288c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 298c2ecf20Sopenharmony_ci#include <linux/notifier.h> 308c2ecf20Sopenharmony_ci#include <net/rtnetlink.h> 318c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 328c2ecf20Sopenharmony_ci#include <net/netns/generic.h> 338c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 368c2ecf20Sopenharmony_ci#include "vlan.h" 378c2ecf20Sopenharmony_ci#include "vlanproc.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define DRV_VERSION "1.8" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* Global VLAN variables */ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ciunsigned int vlan_net_id __read_mostly; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ciconst char vlan_fullname[] = "802.1Q VLAN Support"; 468c2ecf20Sopenharmony_ciconst char vlan_version[] = DRV_VERSION; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* End of global variables definitions. */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int vlan_group_prealloc_vid(struct vlan_group *vg, 518c2ecf20Sopenharmony_ci __be16 vlan_proto, u16 vlan_id) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct net_device **array; 548c2ecf20Sopenharmony_ci unsigned int vidx; 558c2ecf20Sopenharmony_ci unsigned int size; 568c2ecf20Sopenharmony_ci int pidx; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci ASSERT_RTNL(); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci pidx = vlan_proto_idx(vlan_proto); 618c2ecf20Sopenharmony_ci if (pidx < 0) 628c2ecf20Sopenharmony_ci return -EINVAL; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci vidx = vlan_id / VLAN_GROUP_ARRAY_PART_LEN; 658c2ecf20Sopenharmony_ci array = vg->vlan_devices_arrays[pidx][vidx]; 668c2ecf20Sopenharmony_ci if (array != NULL) 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci size = sizeof(struct net_device *) * VLAN_GROUP_ARRAY_PART_LEN; 708c2ecf20Sopenharmony_ci array = kzalloc(size, GFP_KERNEL); 718c2ecf20Sopenharmony_ci if (array == NULL) 728c2ecf20Sopenharmony_ci return -ENOBUFS; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci vg->vlan_devices_arrays[pidx][vidx] = array; 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void vlan_stacked_transfer_operstate(const struct net_device *rootdev, 798c2ecf20Sopenharmony_ci struct net_device *dev, 808c2ecf20Sopenharmony_ci struct vlan_dev_priv *vlan) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci if (!(vlan->flags & VLAN_FLAG_BRIDGE_BINDING)) 838c2ecf20Sopenharmony_ci netif_stacked_transfer_operstate(rootdev, dev); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_civoid unregister_vlan_dev(struct net_device *dev, struct list_head *head) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(dev); 898c2ecf20Sopenharmony_ci struct net_device *real_dev = vlan->real_dev; 908c2ecf20Sopenharmony_ci struct vlan_info *vlan_info; 918c2ecf20Sopenharmony_ci struct vlan_group *grp; 928c2ecf20Sopenharmony_ci u16 vlan_id = vlan->vlan_id; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci ASSERT_RTNL(); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci vlan_info = rtnl_dereference(real_dev->vlan_info); 978c2ecf20Sopenharmony_ci BUG_ON(!vlan_info); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci grp = &vlan_info->grp; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci grp->nr_vlan_devs--; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (vlan->flags & VLAN_FLAG_MVRP) 1048c2ecf20Sopenharmony_ci vlan_mvrp_request_leave(dev); 1058c2ecf20Sopenharmony_ci if (vlan->flags & VLAN_FLAG_GVRP) 1068c2ecf20Sopenharmony_ci vlan_gvrp_request_leave(dev); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, NULL); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci netdev_upper_dev_unlink(real_dev, dev); 1118c2ecf20Sopenharmony_ci /* Because unregister_netdevice_queue() makes sure at least one rcu 1128c2ecf20Sopenharmony_ci * grace period is respected before device freeing, 1138c2ecf20Sopenharmony_ci * we dont need to call synchronize_net() here. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci unregister_netdevice_queue(dev, head); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (grp->nr_vlan_devs == 0) { 1188c2ecf20Sopenharmony_ci vlan_mvrp_uninit_applicant(real_dev); 1198c2ecf20Sopenharmony_ci vlan_gvrp_uninit_applicant(real_dev); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciint vlan_check_real_dev(struct net_device *real_dev, 1268c2ecf20Sopenharmony_ci __be16 protocol, u16 vlan_id, 1278c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci const char *name = real_dev->name; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (real_dev->features & NETIF_F_VLAN_CHALLENGED) { 1328c2ecf20Sopenharmony_ci pr_info("VLANs not supported on %s\n", name); 1338c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VLANs not supported on device"); 1348c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (vlan_find_dev(real_dev, protocol, vlan_id) != NULL) { 1388c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VLAN device already exists"); 1398c2ecf20Sopenharmony_ci return -EEXIST; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ciint register_vlan_dev(struct net_device *dev, struct netlink_ext_ack *extack) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(dev); 1488c2ecf20Sopenharmony_ci struct net_device *real_dev = vlan->real_dev; 1498c2ecf20Sopenharmony_ci u16 vlan_id = vlan->vlan_id; 1508c2ecf20Sopenharmony_ci struct vlan_info *vlan_info; 1518c2ecf20Sopenharmony_ci struct vlan_group *grp; 1528c2ecf20Sopenharmony_ci int err; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci err = vlan_vid_add(real_dev, vlan->vlan_proto, vlan_id); 1558c2ecf20Sopenharmony_ci if (err) 1568c2ecf20Sopenharmony_ci return err; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci vlan_info = rtnl_dereference(real_dev->vlan_info); 1598c2ecf20Sopenharmony_ci /* vlan_info should be there now. vlan_vid_add took care of it */ 1608c2ecf20Sopenharmony_ci BUG_ON(!vlan_info); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci grp = &vlan_info->grp; 1638c2ecf20Sopenharmony_ci if (grp->nr_vlan_devs == 0) { 1648c2ecf20Sopenharmony_ci err = vlan_gvrp_init_applicant(real_dev); 1658c2ecf20Sopenharmony_ci if (err < 0) 1668c2ecf20Sopenharmony_ci goto out_vid_del; 1678c2ecf20Sopenharmony_ci err = vlan_mvrp_init_applicant(real_dev); 1688c2ecf20Sopenharmony_ci if (err < 0) 1698c2ecf20Sopenharmony_ci goto out_uninit_gvrp; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci err = vlan_group_prealloc_vid(grp, vlan->vlan_proto, vlan_id); 1738c2ecf20Sopenharmony_ci if (err < 0) 1748c2ecf20Sopenharmony_ci goto out_uninit_mvrp; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci err = register_netdevice(dev); 1778c2ecf20Sopenharmony_ci if (err < 0) 1788c2ecf20Sopenharmony_ci goto out_uninit_mvrp; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci err = netdev_upper_dev_link(real_dev, dev, extack); 1818c2ecf20Sopenharmony_ci if (err) 1828c2ecf20Sopenharmony_ci goto out_unregister_netdev; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci vlan_stacked_transfer_operstate(real_dev, dev, vlan); 1858c2ecf20Sopenharmony_ci linkwatch_fire_event(dev); /* _MUST_ call rfc2863_policy() */ 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* So, got the sucker initialized, now lets place 1888c2ecf20Sopenharmony_ci * it into our local structure. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, dev); 1918c2ecf20Sopenharmony_ci grp->nr_vlan_devs++; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ciout_unregister_netdev: 1968c2ecf20Sopenharmony_ci unregister_netdevice(dev); 1978c2ecf20Sopenharmony_ciout_uninit_mvrp: 1988c2ecf20Sopenharmony_ci if (grp->nr_vlan_devs == 0) 1998c2ecf20Sopenharmony_ci vlan_mvrp_uninit_applicant(real_dev); 2008c2ecf20Sopenharmony_ciout_uninit_gvrp: 2018c2ecf20Sopenharmony_ci if (grp->nr_vlan_devs == 0) 2028c2ecf20Sopenharmony_ci vlan_gvrp_uninit_applicant(real_dev); 2038c2ecf20Sopenharmony_ciout_vid_del: 2048c2ecf20Sopenharmony_ci vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id); 2058c2ecf20Sopenharmony_ci return err; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/* Attach a VLAN device to a mac address (ie Ethernet Card). 2098c2ecf20Sopenharmony_ci * Returns 0 if the device was created or a negative error code otherwise. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_cistatic int register_vlan_device(struct net_device *real_dev, u16 vlan_id) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct net_device *new_dev; 2148c2ecf20Sopenharmony_ci struct vlan_dev_priv *vlan; 2158c2ecf20Sopenharmony_ci struct net *net = dev_net(real_dev); 2168c2ecf20Sopenharmony_ci struct vlan_net *vn = net_generic(net, vlan_net_id); 2178c2ecf20Sopenharmony_ci char name[IFNAMSIZ]; 2188c2ecf20Sopenharmony_ci int err; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (vlan_id >= VLAN_VID_MASK) 2218c2ecf20Sopenharmony_ci return -ERANGE; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci err = vlan_check_real_dev(real_dev, htons(ETH_P_8021Q), vlan_id, 2248c2ecf20Sopenharmony_ci NULL); 2258c2ecf20Sopenharmony_ci if (err < 0) 2268c2ecf20Sopenharmony_ci return err; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Gotta set up the fields for the device. */ 2298c2ecf20Sopenharmony_ci switch (vn->name_type) { 2308c2ecf20Sopenharmony_ci case VLAN_NAME_TYPE_RAW_PLUS_VID: 2318c2ecf20Sopenharmony_ci /* name will look like: eth1.0005 */ 2328c2ecf20Sopenharmony_ci snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, vlan_id); 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci case VLAN_NAME_TYPE_PLUS_VID_NO_PAD: 2358c2ecf20Sopenharmony_ci /* Put our vlan.VID in the name. 2368c2ecf20Sopenharmony_ci * Name will look like: vlan5 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci snprintf(name, IFNAMSIZ, "vlan%i", vlan_id); 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci case VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD: 2418c2ecf20Sopenharmony_ci /* Put our vlan.VID in the name. 2428c2ecf20Sopenharmony_ci * Name will look like: eth0.5 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, vlan_id); 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case VLAN_NAME_TYPE_PLUS_VID: 2478c2ecf20Sopenharmony_ci /* Put our vlan.VID in the name. 2488c2ecf20Sopenharmony_ci * Name will look like: vlan0005 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci default: 2518c2ecf20Sopenharmony_ci snprintf(name, IFNAMSIZ, "vlan%.4i", vlan_id); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci new_dev = alloc_netdev(sizeof(struct vlan_dev_priv), name, 2558c2ecf20Sopenharmony_ci NET_NAME_UNKNOWN, vlan_setup); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (new_dev == NULL) 2588c2ecf20Sopenharmony_ci return -ENOBUFS; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci dev_net_set(new_dev, net); 2618c2ecf20Sopenharmony_ci /* need 4 bytes for extra VLAN header info, 2628c2ecf20Sopenharmony_ci * hope the underlying device can handle it. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci new_dev->mtu = real_dev->mtu; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci vlan = vlan_dev_priv(new_dev); 2678c2ecf20Sopenharmony_ci vlan->vlan_proto = htons(ETH_P_8021Q); 2688c2ecf20Sopenharmony_ci vlan->vlan_id = vlan_id; 2698c2ecf20Sopenharmony_ci vlan->real_dev = real_dev; 2708c2ecf20Sopenharmony_ci vlan->dent = NULL; 2718c2ecf20Sopenharmony_ci vlan->flags = VLAN_FLAG_REORDER_HDR; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci new_dev->rtnl_link_ops = &vlan_link_ops; 2748c2ecf20Sopenharmony_ci err = register_vlan_dev(new_dev, NULL); 2758c2ecf20Sopenharmony_ci if (err < 0) 2768c2ecf20Sopenharmony_ci goto out_free_newdev; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ciout_free_newdev: 2818c2ecf20Sopenharmony_ci free_netdev(new_dev); 2828c2ecf20Sopenharmony_ci return err; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic void vlan_sync_address(struct net_device *dev, 2868c2ecf20Sopenharmony_ci struct net_device *vlandev) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* May be called without an actual change */ 2918c2ecf20Sopenharmony_ci if (ether_addr_equal(vlan->real_dev_addr, dev->dev_addr)) 2928c2ecf20Sopenharmony_ci return; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* vlan continues to inherit address of lower device */ 2958c2ecf20Sopenharmony_ci if (vlan_dev_inherit_address(vlandev, dev)) 2968c2ecf20Sopenharmony_ci goto out; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* vlan address was different from the old address and is equal to 2998c2ecf20Sopenharmony_ci * the new address */ 3008c2ecf20Sopenharmony_ci if (!ether_addr_equal(vlandev->dev_addr, vlan->real_dev_addr) && 3018c2ecf20Sopenharmony_ci ether_addr_equal(vlandev->dev_addr, dev->dev_addr)) 3028c2ecf20Sopenharmony_ci dev_uc_del(dev, vlandev->dev_addr); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* vlan address was equal to the old address and is different from 3058c2ecf20Sopenharmony_ci * the new address */ 3068c2ecf20Sopenharmony_ci if (ether_addr_equal(vlandev->dev_addr, vlan->real_dev_addr) && 3078c2ecf20Sopenharmony_ci !ether_addr_equal(vlandev->dev_addr, dev->dev_addr)) 3088c2ecf20Sopenharmony_ci dev_uc_add(dev, vlandev->dev_addr); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ciout: 3118c2ecf20Sopenharmony_ci ether_addr_copy(vlan->real_dev_addr, dev->dev_addr); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void vlan_transfer_features(struct net_device *dev, 3158c2ecf20Sopenharmony_ci struct net_device *vlandev) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci vlandev->gso_max_size = dev->gso_max_size; 3208c2ecf20Sopenharmony_ci vlandev->gso_max_segs = dev->gso_max_segs; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (vlan_hw_offload_capable(dev->features, vlan->vlan_proto)) 3238c2ecf20Sopenharmony_ci vlandev->hard_header_len = dev->hard_header_len; 3248c2ecf20Sopenharmony_ci else 3258c2ecf20Sopenharmony_ci vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_FCOE) 3288c2ecf20Sopenharmony_ci vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid; 3298c2ecf20Sopenharmony_ci#endif 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci vlandev->priv_flags &= ~IFF_XMIT_DST_RELEASE; 3328c2ecf20Sopenharmony_ci vlandev->priv_flags |= (vlan->real_dev->priv_flags & IFF_XMIT_DST_RELEASE); 3338c2ecf20Sopenharmony_ci vlandev->hw_enc_features = vlan_tnl_features(vlan->real_dev); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci netdev_update_features(vlandev); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int __vlan_device_event(struct net_device *dev, unsigned long event) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci int err = 0; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci switch (event) { 3438c2ecf20Sopenharmony_ci case NETDEV_CHANGENAME: 3448c2ecf20Sopenharmony_ci vlan_proc_rem_dev(dev); 3458c2ecf20Sopenharmony_ci err = vlan_proc_add_dev(dev); 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci case NETDEV_REGISTER: 3488c2ecf20Sopenharmony_ci err = vlan_proc_add_dev(dev); 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 3518c2ecf20Sopenharmony_ci vlan_proc_rem_dev(dev); 3528c2ecf20Sopenharmony_ci break; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return err; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int vlan_device_event(struct notifier_block *unused, unsigned long event, 3598c2ecf20Sopenharmony_ci void *ptr) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr); 3628c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 3638c2ecf20Sopenharmony_ci struct vlan_group *grp; 3648c2ecf20Sopenharmony_ci struct vlan_info *vlan_info; 3658c2ecf20Sopenharmony_ci int i, flgs; 3668c2ecf20Sopenharmony_ci struct net_device *vlandev; 3678c2ecf20Sopenharmony_ci struct vlan_dev_priv *vlan; 3688c2ecf20Sopenharmony_ci bool last = false; 3698c2ecf20Sopenharmony_ci LIST_HEAD(list); 3708c2ecf20Sopenharmony_ci int err; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (is_vlan_dev(dev)) { 3738c2ecf20Sopenharmony_ci int err = __vlan_device_event(dev, event); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (err) 3768c2ecf20Sopenharmony_ci return notifier_from_errno(err); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if ((event == NETDEV_UP) && 3808c2ecf20Sopenharmony_ci (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) { 3818c2ecf20Sopenharmony_ci pr_info("adding VLAN 0 to HW filter on device %s\n", 3828c2ecf20Sopenharmony_ci dev->name); 3838c2ecf20Sopenharmony_ci vlan_vid_add(dev, htons(ETH_P_8021Q), 0); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci if (event == NETDEV_DOWN && 3868c2ecf20Sopenharmony_ci (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) 3878c2ecf20Sopenharmony_ci vlan_vid_del(dev, htons(ETH_P_8021Q), 0); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci vlan_info = rtnl_dereference(dev->vlan_info); 3908c2ecf20Sopenharmony_ci if (!vlan_info) 3918c2ecf20Sopenharmony_ci goto out; 3928c2ecf20Sopenharmony_ci grp = &vlan_info->grp; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* It is OK that we do not hold the group lock right now, 3958c2ecf20Sopenharmony_ci * as we run under the RTNL lock. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci switch (event) { 3998c2ecf20Sopenharmony_ci case NETDEV_CHANGE: 4008c2ecf20Sopenharmony_ci /* Propagate real device state to vlan devices */ 4018c2ecf20Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) 4028c2ecf20Sopenharmony_ci vlan_stacked_transfer_operstate(dev, vlandev, 4038c2ecf20Sopenharmony_ci vlan_dev_priv(vlandev)); 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci case NETDEV_CHANGEADDR: 4078c2ecf20Sopenharmony_ci /* Adjust unicast filters on underlying device */ 4088c2ecf20Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) { 4098c2ecf20Sopenharmony_ci flgs = vlandev->flags; 4108c2ecf20Sopenharmony_ci if (!(flgs & IFF_UP)) 4118c2ecf20Sopenharmony_ci continue; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci vlan_sync_address(dev, vlandev); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci case NETDEV_CHANGEMTU: 4188c2ecf20Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) { 4198c2ecf20Sopenharmony_ci if (vlandev->mtu <= dev->mtu) 4208c2ecf20Sopenharmony_ci continue; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci dev_set_mtu(vlandev, dev->mtu); 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci case NETDEV_FEAT_CHANGE: 4278c2ecf20Sopenharmony_ci /* Propagate device features to underlying device */ 4288c2ecf20Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) 4298c2ecf20Sopenharmony_ci vlan_transfer_features(dev, vlandev); 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci case NETDEV_DOWN: { 4338c2ecf20Sopenharmony_ci struct net_device *tmp; 4348c2ecf20Sopenharmony_ci LIST_HEAD(close_list); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* Put all VLANs for this dev in the down state too. */ 4378c2ecf20Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) { 4388c2ecf20Sopenharmony_ci flgs = vlandev->flags; 4398c2ecf20Sopenharmony_ci if (!(flgs & IFF_UP)) 4408c2ecf20Sopenharmony_ci continue; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci vlan = vlan_dev_priv(vlandev); 4438c2ecf20Sopenharmony_ci if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) 4448c2ecf20Sopenharmony_ci list_add(&vlandev->close_list, &close_list); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci dev_close_many(&close_list, false); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci list_for_each_entry_safe(vlandev, tmp, &close_list, close_list) { 4508c2ecf20Sopenharmony_ci vlan_stacked_transfer_operstate(dev, vlandev, 4518c2ecf20Sopenharmony_ci vlan_dev_priv(vlandev)); 4528c2ecf20Sopenharmony_ci list_del_init(&vlandev->close_list); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci list_del(&close_list); 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci case NETDEV_UP: 4588c2ecf20Sopenharmony_ci /* Put all VLANs for this dev in the up state too. */ 4598c2ecf20Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) { 4608c2ecf20Sopenharmony_ci flgs = dev_get_flags(vlandev); 4618c2ecf20Sopenharmony_ci if (flgs & IFF_UP) 4628c2ecf20Sopenharmony_ci continue; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci vlan = vlan_dev_priv(vlandev); 4658c2ecf20Sopenharmony_ci if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) 4668c2ecf20Sopenharmony_ci dev_change_flags(vlandev, flgs | IFF_UP, 4678c2ecf20Sopenharmony_ci extack); 4688c2ecf20Sopenharmony_ci vlan_stacked_transfer_operstate(dev, vlandev, vlan); 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci break; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 4738c2ecf20Sopenharmony_ci /* twiddle thumbs on netns device moves */ 4748c2ecf20Sopenharmony_ci if (dev->reg_state != NETREG_UNREGISTERING) 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) { 4788c2ecf20Sopenharmony_ci /* removal of last vid destroys vlan_info, abort 4798c2ecf20Sopenharmony_ci * afterwards */ 4808c2ecf20Sopenharmony_ci if (vlan_info->nr_vids == 1) 4818c2ecf20Sopenharmony_ci last = true; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci unregister_vlan_dev(vlandev, &list); 4848c2ecf20Sopenharmony_ci if (last) 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci unregister_netdevice_many(&list); 4888c2ecf20Sopenharmony_ci break; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci case NETDEV_PRE_TYPE_CHANGE: 4918c2ecf20Sopenharmony_ci /* Forbid underlaying device to change its type. */ 4928c2ecf20Sopenharmony_ci if (vlan_uses_dev(dev)) 4938c2ecf20Sopenharmony_ci return NOTIFY_BAD; 4948c2ecf20Sopenharmony_ci break; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci case NETDEV_NOTIFY_PEERS: 4978c2ecf20Sopenharmony_ci case NETDEV_BONDING_FAILOVER: 4988c2ecf20Sopenharmony_ci case NETDEV_RESEND_IGMP: 4998c2ecf20Sopenharmony_ci /* Propagate to vlan devices */ 5008c2ecf20Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) 5018c2ecf20Sopenharmony_ci call_netdevice_notifiers(event, vlandev); 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci case NETDEV_CVLAN_FILTER_PUSH_INFO: 5058c2ecf20Sopenharmony_ci err = vlan_filter_push_vids(vlan_info, htons(ETH_P_8021Q)); 5068c2ecf20Sopenharmony_ci if (err) 5078c2ecf20Sopenharmony_ci return notifier_from_errno(err); 5088c2ecf20Sopenharmony_ci break; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci case NETDEV_CVLAN_FILTER_DROP_INFO: 5118c2ecf20Sopenharmony_ci vlan_filter_drop_vids(vlan_info, htons(ETH_P_8021Q)); 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci case NETDEV_SVLAN_FILTER_PUSH_INFO: 5158c2ecf20Sopenharmony_ci err = vlan_filter_push_vids(vlan_info, htons(ETH_P_8021AD)); 5168c2ecf20Sopenharmony_ci if (err) 5178c2ecf20Sopenharmony_ci return notifier_from_errno(err); 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci case NETDEV_SVLAN_FILTER_DROP_INFO: 5218c2ecf20Sopenharmony_ci vlan_filter_drop_vids(vlan_info, htons(ETH_P_8021AD)); 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ciout: 5268c2ecf20Sopenharmony_ci return NOTIFY_DONE; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic struct notifier_block vlan_notifier_block __read_mostly = { 5308c2ecf20Sopenharmony_ci .notifier_call = vlan_device_event, 5318c2ecf20Sopenharmony_ci}; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci/* 5348c2ecf20Sopenharmony_ci * VLAN IOCTL handler. 5358c2ecf20Sopenharmony_ci * o execute requested action or pass command to the device driver 5368c2ecf20Sopenharmony_ci * arg is really a struct vlan_ioctl_args __user *. 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_cistatic int vlan_ioctl_handler(struct net *net, void __user *arg) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci int err; 5418c2ecf20Sopenharmony_ci struct vlan_ioctl_args args; 5428c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (copy_from_user(&args, arg, sizeof(struct vlan_ioctl_args))) 5458c2ecf20Sopenharmony_ci return -EFAULT; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* Null terminate this sucker, just in case. */ 5488c2ecf20Sopenharmony_ci args.device1[sizeof(args.device1) - 1] = 0; 5498c2ecf20Sopenharmony_ci args.u.device2[sizeof(args.u.device2) - 1] = 0; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci rtnl_lock(); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci switch (args.cmd) { 5548c2ecf20Sopenharmony_ci case SET_VLAN_INGRESS_PRIORITY_CMD: 5558c2ecf20Sopenharmony_ci case SET_VLAN_EGRESS_PRIORITY_CMD: 5568c2ecf20Sopenharmony_ci case SET_VLAN_FLAG_CMD: 5578c2ecf20Sopenharmony_ci case ADD_VLAN_CMD: 5588c2ecf20Sopenharmony_ci case DEL_VLAN_CMD: 5598c2ecf20Sopenharmony_ci case GET_VLAN_REALDEV_NAME_CMD: 5608c2ecf20Sopenharmony_ci case GET_VLAN_VID_CMD: 5618c2ecf20Sopenharmony_ci err = -ENODEV; 5628c2ecf20Sopenharmony_ci dev = __dev_get_by_name(net, args.device1); 5638c2ecf20Sopenharmony_ci if (!dev) 5648c2ecf20Sopenharmony_ci goto out; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci err = -EINVAL; 5678c2ecf20Sopenharmony_ci if (args.cmd != ADD_VLAN_CMD && !is_vlan_dev(dev)) 5688c2ecf20Sopenharmony_ci goto out; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci switch (args.cmd) { 5728c2ecf20Sopenharmony_ci case SET_VLAN_INGRESS_PRIORITY_CMD: 5738c2ecf20Sopenharmony_ci err = -EPERM; 5748c2ecf20Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 5758c2ecf20Sopenharmony_ci break; 5768c2ecf20Sopenharmony_ci vlan_dev_set_ingress_priority(dev, 5778c2ecf20Sopenharmony_ci args.u.skb_priority, 5788c2ecf20Sopenharmony_ci args.vlan_qos); 5798c2ecf20Sopenharmony_ci err = 0; 5808c2ecf20Sopenharmony_ci break; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci case SET_VLAN_EGRESS_PRIORITY_CMD: 5838c2ecf20Sopenharmony_ci err = -EPERM; 5848c2ecf20Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 5858c2ecf20Sopenharmony_ci break; 5868c2ecf20Sopenharmony_ci err = vlan_dev_set_egress_priority(dev, 5878c2ecf20Sopenharmony_ci args.u.skb_priority, 5888c2ecf20Sopenharmony_ci args.vlan_qos); 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci case SET_VLAN_FLAG_CMD: 5928c2ecf20Sopenharmony_ci err = -EPERM; 5938c2ecf20Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 5948c2ecf20Sopenharmony_ci break; 5958c2ecf20Sopenharmony_ci err = vlan_dev_change_flags(dev, 5968c2ecf20Sopenharmony_ci args.vlan_qos ? args.u.flag : 0, 5978c2ecf20Sopenharmony_ci args.u.flag); 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci case SET_VLAN_NAME_TYPE_CMD: 6018c2ecf20Sopenharmony_ci err = -EPERM; 6028c2ecf20Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci if (args.u.name_type < VLAN_NAME_TYPE_HIGHEST) { 6058c2ecf20Sopenharmony_ci struct vlan_net *vn; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci vn = net_generic(net, vlan_net_id); 6088c2ecf20Sopenharmony_ci vn->name_type = args.u.name_type; 6098c2ecf20Sopenharmony_ci err = 0; 6108c2ecf20Sopenharmony_ci } else { 6118c2ecf20Sopenharmony_ci err = -EINVAL; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci case ADD_VLAN_CMD: 6168c2ecf20Sopenharmony_ci err = -EPERM; 6178c2ecf20Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 6188c2ecf20Sopenharmony_ci break; 6198c2ecf20Sopenharmony_ci err = register_vlan_device(dev, args.u.VID); 6208c2ecf20Sopenharmony_ci break; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci case DEL_VLAN_CMD: 6238c2ecf20Sopenharmony_ci err = -EPERM; 6248c2ecf20Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 6258c2ecf20Sopenharmony_ci break; 6268c2ecf20Sopenharmony_ci unregister_vlan_dev(dev, NULL); 6278c2ecf20Sopenharmony_ci err = 0; 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci case GET_VLAN_REALDEV_NAME_CMD: 6318c2ecf20Sopenharmony_ci err = 0; 6328c2ecf20Sopenharmony_ci vlan_dev_get_realdev_name(dev, args.u.device2); 6338c2ecf20Sopenharmony_ci if (copy_to_user(arg, &args, 6348c2ecf20Sopenharmony_ci sizeof(struct vlan_ioctl_args))) 6358c2ecf20Sopenharmony_ci err = -EFAULT; 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci case GET_VLAN_VID_CMD: 6398c2ecf20Sopenharmony_ci err = 0; 6408c2ecf20Sopenharmony_ci args.u.VID = vlan_dev_vlan_id(dev); 6418c2ecf20Sopenharmony_ci if (copy_to_user(arg, &args, 6428c2ecf20Sopenharmony_ci sizeof(struct vlan_ioctl_args))) 6438c2ecf20Sopenharmony_ci err = -EFAULT; 6448c2ecf20Sopenharmony_ci break; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci default: 6478c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 6488c2ecf20Sopenharmony_ci break; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ciout: 6518c2ecf20Sopenharmony_ci rtnl_unlock(); 6528c2ecf20Sopenharmony_ci return err; 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic int __net_init vlan_init_net(struct net *net) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct vlan_net *vn = net_generic(net, vlan_net_id); 6588c2ecf20Sopenharmony_ci int err; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci vn->name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci err = vlan_proc_init(net); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci return err; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic void __net_exit vlan_exit_net(struct net *net) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci vlan_proc_cleanup(net); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic struct pernet_operations vlan_net_ops = { 6738c2ecf20Sopenharmony_ci .init = vlan_init_net, 6748c2ecf20Sopenharmony_ci .exit = vlan_exit_net, 6758c2ecf20Sopenharmony_ci .id = &vlan_net_id, 6768c2ecf20Sopenharmony_ci .size = sizeof(struct vlan_net), 6778c2ecf20Sopenharmony_ci}; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic int __init vlan_proto_init(void) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci int err; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci pr_info("%s v%s\n", vlan_fullname, vlan_version); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci err = register_pernet_subsys(&vlan_net_ops); 6868c2ecf20Sopenharmony_ci if (err < 0) 6878c2ecf20Sopenharmony_ci goto err0; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci err = register_netdevice_notifier(&vlan_notifier_block); 6908c2ecf20Sopenharmony_ci if (err < 0) 6918c2ecf20Sopenharmony_ci goto err2; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci err = vlan_gvrp_init(); 6948c2ecf20Sopenharmony_ci if (err < 0) 6958c2ecf20Sopenharmony_ci goto err3; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci err = vlan_mvrp_init(); 6988c2ecf20Sopenharmony_ci if (err < 0) 6998c2ecf20Sopenharmony_ci goto err4; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci err = vlan_netlink_init(); 7028c2ecf20Sopenharmony_ci if (err < 0) 7038c2ecf20Sopenharmony_ci goto err5; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci vlan_ioctl_set(vlan_ioctl_handler); 7068c2ecf20Sopenharmony_ci return 0; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cierr5: 7098c2ecf20Sopenharmony_ci vlan_mvrp_uninit(); 7108c2ecf20Sopenharmony_cierr4: 7118c2ecf20Sopenharmony_ci vlan_gvrp_uninit(); 7128c2ecf20Sopenharmony_cierr3: 7138c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&vlan_notifier_block); 7148c2ecf20Sopenharmony_cierr2: 7158c2ecf20Sopenharmony_ci unregister_pernet_subsys(&vlan_net_ops); 7168c2ecf20Sopenharmony_cierr0: 7178c2ecf20Sopenharmony_ci return err; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic void __exit vlan_cleanup_module(void) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci vlan_ioctl_set(NULL); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci vlan_netlink_fini(); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&vlan_notifier_block); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci unregister_pernet_subsys(&vlan_net_ops); 7298c2ecf20Sopenharmony_ci rcu_barrier(); /* Wait for completion of call_rcu()'s */ 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci vlan_mvrp_uninit(); 7328c2ecf20Sopenharmony_ci vlan_gvrp_uninit(); 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cimodule_init(vlan_proto_init); 7368c2ecf20Sopenharmony_cimodule_exit(vlan_cleanup_module); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 7398c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 740