162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * INET 802.1Q VLAN 462306a36Sopenharmony_ci * Ethernet-type device handling. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: Ben Greear <greearb@candelatech.com> 762306a36Sopenharmony_ci * Please send support related email to: netdev@vger.kernel.org 862306a36Sopenharmony_ci * VLAN Home Page: http://www.candelatech.com/~greear/vlan.html 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Fixes: 1162306a36Sopenharmony_ci * Fix for packet capture - Nick Eggleston <nick@dccinc.com>; 1262306a36Sopenharmony_ci * Add HW acceleration hooks - David S. Miller <davem@redhat.com>; 1362306a36Sopenharmony_ci * Correct all the locking - David S. Miller <davem@redhat.com>; 1462306a36Sopenharmony_ci * Use hash table for VLAN groups - David S. Miller <davem@redhat.com> 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/capability.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/netdevice.h> 2262306a36Sopenharmony_ci#include <linux/skbuff.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/init.h> 2562306a36Sopenharmony_ci#include <linux/rculist.h> 2662306a36Sopenharmony_ci#include <net/p8022.h> 2762306a36Sopenharmony_ci#include <net/arp.h> 2862306a36Sopenharmony_ci#include <linux/rtnetlink.h> 2962306a36Sopenharmony_ci#include <linux/notifier.h> 3062306a36Sopenharmony_ci#include <net/rtnetlink.h> 3162306a36Sopenharmony_ci#include <net/net_namespace.h> 3262306a36Sopenharmony_ci#include <net/netns/generic.h> 3362306a36Sopenharmony_ci#include <linux/uaccess.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/if_vlan.h> 3662306a36Sopenharmony_ci#include "vlan.h" 3762306a36Sopenharmony_ci#include "vlanproc.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define DRV_VERSION "1.8" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Global VLAN variables */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciunsigned int vlan_net_id __read_mostly; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciconst char vlan_fullname[] = "802.1Q VLAN Support"; 4662306a36Sopenharmony_ciconst char vlan_version[] = DRV_VERSION; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* End of global variables definitions. */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int vlan_group_prealloc_vid(struct vlan_group *vg, 5162306a36Sopenharmony_ci __be16 vlan_proto, u16 vlan_id) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct net_device **array; 5462306a36Sopenharmony_ci unsigned int vidx; 5562306a36Sopenharmony_ci unsigned int size; 5662306a36Sopenharmony_ci int pidx; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ASSERT_RTNL(); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci pidx = vlan_proto_idx(vlan_proto); 6162306a36Sopenharmony_ci if (pidx < 0) 6262306a36Sopenharmony_ci return -EINVAL; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci vidx = vlan_id / VLAN_GROUP_ARRAY_PART_LEN; 6562306a36Sopenharmony_ci array = vg->vlan_devices_arrays[pidx][vidx]; 6662306a36Sopenharmony_ci if (array != NULL) 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci size = sizeof(struct net_device *) * VLAN_GROUP_ARRAY_PART_LEN; 7062306a36Sopenharmony_ci array = kzalloc(size, GFP_KERNEL_ACCOUNT); 7162306a36Sopenharmony_ci if (array == NULL) 7262306a36Sopenharmony_ci return -ENOBUFS; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* paired with smp_rmb() in __vlan_group_get_device() */ 7562306a36Sopenharmony_ci smp_wmb(); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci vg->vlan_devices_arrays[pidx][vidx] = array; 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void vlan_stacked_transfer_operstate(const struct net_device *rootdev, 8262306a36Sopenharmony_ci struct net_device *dev, 8362306a36Sopenharmony_ci struct vlan_dev_priv *vlan) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci if (!(vlan->flags & VLAN_FLAG_BRIDGE_BINDING)) 8662306a36Sopenharmony_ci netif_stacked_transfer_operstate(rootdev, dev); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_civoid unregister_vlan_dev(struct net_device *dev, struct list_head *head) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(dev); 9262306a36Sopenharmony_ci struct net_device *real_dev = vlan->real_dev; 9362306a36Sopenharmony_ci struct vlan_info *vlan_info; 9462306a36Sopenharmony_ci struct vlan_group *grp; 9562306a36Sopenharmony_ci u16 vlan_id = vlan->vlan_id; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci ASSERT_RTNL(); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci vlan_info = rtnl_dereference(real_dev->vlan_info); 10062306a36Sopenharmony_ci BUG_ON(!vlan_info); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci grp = &vlan_info->grp; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci grp->nr_vlan_devs--; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (vlan->flags & VLAN_FLAG_MVRP) 10762306a36Sopenharmony_ci vlan_mvrp_request_leave(dev); 10862306a36Sopenharmony_ci if (vlan->flags & VLAN_FLAG_GVRP) 10962306a36Sopenharmony_ci vlan_gvrp_request_leave(dev); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, NULL); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci netdev_upper_dev_unlink(real_dev, dev); 11462306a36Sopenharmony_ci /* Because unregister_netdevice_queue() makes sure at least one rcu 11562306a36Sopenharmony_ci * grace period is respected before device freeing, 11662306a36Sopenharmony_ci * we dont need to call synchronize_net() here. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci unregister_netdevice_queue(dev, head); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (grp->nr_vlan_devs == 0) { 12162306a36Sopenharmony_ci vlan_mvrp_uninit_applicant(real_dev); 12262306a36Sopenharmony_ci vlan_gvrp_uninit_applicant(real_dev); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ciint vlan_check_real_dev(struct net_device *real_dev, 12962306a36Sopenharmony_ci __be16 protocol, u16 vlan_id, 13062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci const char *name = real_dev->name; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (real_dev->features & NETIF_F_VLAN_CHALLENGED) { 13562306a36Sopenharmony_ci pr_info("VLANs not supported on %s\n", name); 13662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VLANs not supported on device"); 13762306a36Sopenharmony_ci return -EOPNOTSUPP; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (vlan_find_dev(real_dev, protocol, vlan_id) != NULL) { 14162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VLAN device already exists"); 14262306a36Sopenharmony_ci return -EEXIST; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciint register_vlan_dev(struct net_device *dev, struct netlink_ext_ack *extack) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(dev); 15162306a36Sopenharmony_ci struct net_device *real_dev = vlan->real_dev; 15262306a36Sopenharmony_ci u16 vlan_id = vlan->vlan_id; 15362306a36Sopenharmony_ci struct vlan_info *vlan_info; 15462306a36Sopenharmony_ci struct vlan_group *grp; 15562306a36Sopenharmony_ci int err; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci err = vlan_vid_add(real_dev, vlan->vlan_proto, vlan_id); 15862306a36Sopenharmony_ci if (err) 15962306a36Sopenharmony_ci return err; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci vlan_info = rtnl_dereference(real_dev->vlan_info); 16262306a36Sopenharmony_ci /* vlan_info should be there now. vlan_vid_add took care of it */ 16362306a36Sopenharmony_ci BUG_ON(!vlan_info); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci grp = &vlan_info->grp; 16662306a36Sopenharmony_ci if (grp->nr_vlan_devs == 0) { 16762306a36Sopenharmony_ci err = vlan_gvrp_init_applicant(real_dev); 16862306a36Sopenharmony_ci if (err < 0) 16962306a36Sopenharmony_ci goto out_vid_del; 17062306a36Sopenharmony_ci err = vlan_mvrp_init_applicant(real_dev); 17162306a36Sopenharmony_ci if (err < 0) 17262306a36Sopenharmony_ci goto out_uninit_gvrp; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci err = vlan_group_prealloc_vid(grp, vlan->vlan_proto, vlan_id); 17662306a36Sopenharmony_ci if (err < 0) 17762306a36Sopenharmony_ci goto out_uninit_mvrp; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci err = register_netdevice(dev); 18062306a36Sopenharmony_ci if (err < 0) 18162306a36Sopenharmony_ci goto out_uninit_mvrp; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci err = netdev_upper_dev_link(real_dev, dev, extack); 18462306a36Sopenharmony_ci if (err) 18562306a36Sopenharmony_ci goto out_unregister_netdev; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci vlan_stacked_transfer_operstate(real_dev, dev, vlan); 18862306a36Sopenharmony_ci linkwatch_fire_event(dev); /* _MUST_ call rfc2863_policy() */ 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* So, got the sucker initialized, now lets place 19162306a36Sopenharmony_ci * it into our local structure. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, dev); 19462306a36Sopenharmony_ci grp->nr_vlan_devs++; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciout_unregister_netdev: 19962306a36Sopenharmony_ci unregister_netdevice(dev); 20062306a36Sopenharmony_ciout_uninit_mvrp: 20162306a36Sopenharmony_ci if (grp->nr_vlan_devs == 0) 20262306a36Sopenharmony_ci vlan_mvrp_uninit_applicant(real_dev); 20362306a36Sopenharmony_ciout_uninit_gvrp: 20462306a36Sopenharmony_ci if (grp->nr_vlan_devs == 0) 20562306a36Sopenharmony_ci vlan_gvrp_uninit_applicant(real_dev); 20662306a36Sopenharmony_ciout_vid_del: 20762306a36Sopenharmony_ci vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id); 20862306a36Sopenharmony_ci return err; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* Attach a VLAN device to a mac address (ie Ethernet Card). 21262306a36Sopenharmony_ci * Returns 0 if the device was created or a negative error code otherwise. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_cistatic int register_vlan_device(struct net_device *real_dev, u16 vlan_id) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct net_device *new_dev; 21762306a36Sopenharmony_ci struct vlan_dev_priv *vlan; 21862306a36Sopenharmony_ci struct net *net = dev_net(real_dev); 21962306a36Sopenharmony_ci struct vlan_net *vn = net_generic(net, vlan_net_id); 22062306a36Sopenharmony_ci char name[IFNAMSIZ]; 22162306a36Sopenharmony_ci int err; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (vlan_id >= VLAN_VID_MASK) 22462306a36Sopenharmony_ci return -ERANGE; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci err = vlan_check_real_dev(real_dev, htons(ETH_P_8021Q), vlan_id, 22762306a36Sopenharmony_ci NULL); 22862306a36Sopenharmony_ci if (err < 0) 22962306a36Sopenharmony_ci return err; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Gotta set up the fields for the device. */ 23262306a36Sopenharmony_ci switch (vn->name_type) { 23362306a36Sopenharmony_ci case VLAN_NAME_TYPE_RAW_PLUS_VID: 23462306a36Sopenharmony_ci /* name will look like: eth1.0005 */ 23562306a36Sopenharmony_ci snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, vlan_id); 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci case VLAN_NAME_TYPE_PLUS_VID_NO_PAD: 23862306a36Sopenharmony_ci /* Put our vlan.VID in the name. 23962306a36Sopenharmony_ci * Name will look like: vlan5 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci snprintf(name, IFNAMSIZ, "vlan%i", vlan_id); 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci case VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD: 24462306a36Sopenharmony_ci /* Put our vlan.VID in the name. 24562306a36Sopenharmony_ci * Name will look like: eth0.5 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_ci snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, vlan_id); 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci case VLAN_NAME_TYPE_PLUS_VID: 25062306a36Sopenharmony_ci /* Put our vlan.VID in the name. 25162306a36Sopenharmony_ci * Name will look like: vlan0005 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci default: 25462306a36Sopenharmony_ci snprintf(name, IFNAMSIZ, "vlan%.4i", vlan_id); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci new_dev = alloc_netdev(sizeof(struct vlan_dev_priv), name, 25862306a36Sopenharmony_ci NET_NAME_UNKNOWN, vlan_setup); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (new_dev == NULL) 26162306a36Sopenharmony_ci return -ENOBUFS; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci dev_net_set(new_dev, net); 26462306a36Sopenharmony_ci /* need 4 bytes for extra VLAN header info, 26562306a36Sopenharmony_ci * hope the underlying device can handle it. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci new_dev->mtu = real_dev->mtu; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci vlan = vlan_dev_priv(new_dev); 27062306a36Sopenharmony_ci vlan->vlan_proto = htons(ETH_P_8021Q); 27162306a36Sopenharmony_ci vlan->vlan_id = vlan_id; 27262306a36Sopenharmony_ci vlan->real_dev = real_dev; 27362306a36Sopenharmony_ci vlan->dent = NULL; 27462306a36Sopenharmony_ci vlan->flags = VLAN_FLAG_REORDER_HDR; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci new_dev->rtnl_link_ops = &vlan_link_ops; 27762306a36Sopenharmony_ci err = register_vlan_dev(new_dev, NULL); 27862306a36Sopenharmony_ci if (err < 0) 27962306a36Sopenharmony_ci goto out_free_newdev; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ciout_free_newdev: 28462306a36Sopenharmony_ci free_netdev(new_dev); 28562306a36Sopenharmony_ci return err; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic void vlan_sync_address(struct net_device *dev, 28962306a36Sopenharmony_ci struct net_device *vlandev) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* May be called without an actual change */ 29462306a36Sopenharmony_ci if (ether_addr_equal(vlan->real_dev_addr, dev->dev_addr)) 29562306a36Sopenharmony_ci return; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* vlan continues to inherit address of lower device */ 29862306a36Sopenharmony_ci if (vlan_dev_inherit_address(vlandev, dev)) 29962306a36Sopenharmony_ci goto out; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* vlan address was different from the old address and is equal to 30262306a36Sopenharmony_ci * the new address */ 30362306a36Sopenharmony_ci if (!ether_addr_equal(vlandev->dev_addr, vlan->real_dev_addr) && 30462306a36Sopenharmony_ci ether_addr_equal(vlandev->dev_addr, dev->dev_addr)) 30562306a36Sopenharmony_ci dev_uc_del(dev, vlandev->dev_addr); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* vlan address was equal to the old address and is different from 30862306a36Sopenharmony_ci * the new address */ 30962306a36Sopenharmony_ci if (ether_addr_equal(vlandev->dev_addr, vlan->real_dev_addr) && 31062306a36Sopenharmony_ci !ether_addr_equal(vlandev->dev_addr, dev->dev_addr)) 31162306a36Sopenharmony_ci dev_uc_add(dev, vlandev->dev_addr); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ciout: 31462306a36Sopenharmony_ci ether_addr_copy(vlan->real_dev_addr, dev->dev_addr); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic void vlan_transfer_features(struct net_device *dev, 31862306a36Sopenharmony_ci struct net_device *vlandev) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci netif_inherit_tso_max(vlandev, dev); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (vlan_hw_offload_capable(dev->features, vlan->vlan_proto)) 32562306a36Sopenharmony_ci vlandev->hard_header_len = dev->hard_header_len; 32662306a36Sopenharmony_ci else 32762306a36Sopenharmony_ci vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_FCOE) 33062306a36Sopenharmony_ci vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid; 33162306a36Sopenharmony_ci#endif 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci vlandev->priv_flags &= ~IFF_XMIT_DST_RELEASE; 33462306a36Sopenharmony_ci vlandev->priv_flags |= (vlan->real_dev->priv_flags & IFF_XMIT_DST_RELEASE); 33562306a36Sopenharmony_ci vlandev->hw_enc_features = vlan_tnl_features(vlan->real_dev); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci netdev_update_features(vlandev); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int __vlan_device_event(struct net_device *dev, unsigned long event) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci int err = 0; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci switch (event) { 34562306a36Sopenharmony_ci case NETDEV_CHANGENAME: 34662306a36Sopenharmony_ci vlan_proc_rem_dev(dev); 34762306a36Sopenharmony_ci err = vlan_proc_add_dev(dev); 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci case NETDEV_REGISTER: 35062306a36Sopenharmony_ci err = vlan_proc_add_dev(dev); 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci case NETDEV_UNREGISTER: 35362306a36Sopenharmony_ci vlan_proc_rem_dev(dev); 35462306a36Sopenharmony_ci break; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return err; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int vlan_device_event(struct notifier_block *unused, unsigned long event, 36162306a36Sopenharmony_ci void *ptr) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr); 36462306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 36562306a36Sopenharmony_ci struct vlan_group *grp; 36662306a36Sopenharmony_ci struct vlan_info *vlan_info; 36762306a36Sopenharmony_ci int i, flgs; 36862306a36Sopenharmony_ci struct net_device *vlandev; 36962306a36Sopenharmony_ci struct vlan_dev_priv *vlan; 37062306a36Sopenharmony_ci bool last = false; 37162306a36Sopenharmony_ci LIST_HEAD(list); 37262306a36Sopenharmony_ci int err; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (is_vlan_dev(dev)) { 37562306a36Sopenharmony_ci int err = __vlan_device_event(dev, event); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (err) 37862306a36Sopenharmony_ci return notifier_from_errno(err); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if ((event == NETDEV_UP) && 38262306a36Sopenharmony_ci (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) { 38362306a36Sopenharmony_ci pr_info("adding VLAN 0 to HW filter on device %s\n", 38462306a36Sopenharmony_ci dev->name); 38562306a36Sopenharmony_ci vlan_vid_add(dev, htons(ETH_P_8021Q), 0); 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci if (event == NETDEV_DOWN && 38862306a36Sopenharmony_ci (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) 38962306a36Sopenharmony_ci vlan_vid_del(dev, htons(ETH_P_8021Q), 0); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci vlan_info = rtnl_dereference(dev->vlan_info); 39262306a36Sopenharmony_ci if (!vlan_info) 39362306a36Sopenharmony_ci goto out; 39462306a36Sopenharmony_ci grp = &vlan_info->grp; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* It is OK that we do not hold the group lock right now, 39762306a36Sopenharmony_ci * as we run under the RTNL lock. 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci switch (event) { 40162306a36Sopenharmony_ci case NETDEV_CHANGE: 40262306a36Sopenharmony_ci /* Propagate real device state to vlan devices */ 40362306a36Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) 40462306a36Sopenharmony_ci vlan_stacked_transfer_operstate(dev, vlandev, 40562306a36Sopenharmony_ci vlan_dev_priv(vlandev)); 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci case NETDEV_CHANGEADDR: 40962306a36Sopenharmony_ci /* Adjust unicast filters on underlying device */ 41062306a36Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) { 41162306a36Sopenharmony_ci flgs = vlandev->flags; 41262306a36Sopenharmony_ci if (!(flgs & IFF_UP)) 41362306a36Sopenharmony_ci continue; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci vlan_sync_address(dev, vlandev); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci case NETDEV_CHANGEMTU: 42062306a36Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) { 42162306a36Sopenharmony_ci if (vlandev->mtu <= dev->mtu) 42262306a36Sopenharmony_ci continue; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci dev_set_mtu(vlandev, dev->mtu); 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci case NETDEV_FEAT_CHANGE: 42962306a36Sopenharmony_ci /* Propagate device features to underlying device */ 43062306a36Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) 43162306a36Sopenharmony_ci vlan_transfer_features(dev, vlandev); 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci case NETDEV_DOWN: { 43562306a36Sopenharmony_ci struct net_device *tmp; 43662306a36Sopenharmony_ci LIST_HEAD(close_list); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Put all VLANs for this dev in the down state too. */ 43962306a36Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) { 44062306a36Sopenharmony_ci flgs = vlandev->flags; 44162306a36Sopenharmony_ci if (!(flgs & IFF_UP)) 44262306a36Sopenharmony_ci continue; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci vlan = vlan_dev_priv(vlandev); 44562306a36Sopenharmony_ci if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) 44662306a36Sopenharmony_ci list_add(&vlandev->close_list, &close_list); 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci dev_close_many(&close_list, false); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci list_for_each_entry_safe(vlandev, tmp, &close_list, close_list) { 45262306a36Sopenharmony_ci vlan_stacked_transfer_operstate(dev, vlandev, 45362306a36Sopenharmony_ci vlan_dev_priv(vlandev)); 45462306a36Sopenharmony_ci list_del_init(&vlandev->close_list); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci list_del(&close_list); 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci case NETDEV_UP: 46062306a36Sopenharmony_ci /* Put all VLANs for this dev in the up state too. */ 46162306a36Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) { 46262306a36Sopenharmony_ci flgs = dev_get_flags(vlandev); 46362306a36Sopenharmony_ci if (flgs & IFF_UP) 46462306a36Sopenharmony_ci continue; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci vlan = vlan_dev_priv(vlandev); 46762306a36Sopenharmony_ci if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) 46862306a36Sopenharmony_ci dev_change_flags(vlandev, flgs | IFF_UP, 46962306a36Sopenharmony_ci extack); 47062306a36Sopenharmony_ci vlan_stacked_transfer_operstate(dev, vlandev, vlan); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci case NETDEV_UNREGISTER: 47562306a36Sopenharmony_ci /* twiddle thumbs on netns device moves */ 47662306a36Sopenharmony_ci if (dev->reg_state != NETREG_UNREGISTERING) 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) { 48062306a36Sopenharmony_ci /* removal of last vid destroys vlan_info, abort 48162306a36Sopenharmony_ci * afterwards */ 48262306a36Sopenharmony_ci if (vlan_info->nr_vids == 1) 48362306a36Sopenharmony_ci last = true; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci unregister_vlan_dev(vlandev, &list); 48662306a36Sopenharmony_ci if (last) 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci unregister_netdevice_many(&list); 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci case NETDEV_PRE_TYPE_CHANGE: 49362306a36Sopenharmony_ci /* Forbid underlaying device to change its type. */ 49462306a36Sopenharmony_ci if (vlan_uses_dev(dev)) 49562306a36Sopenharmony_ci return NOTIFY_BAD; 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci case NETDEV_NOTIFY_PEERS: 49962306a36Sopenharmony_ci case NETDEV_BONDING_FAILOVER: 50062306a36Sopenharmony_ci case NETDEV_RESEND_IGMP: 50162306a36Sopenharmony_ci /* Propagate to vlan devices */ 50262306a36Sopenharmony_ci vlan_group_for_each_dev(grp, i, vlandev) 50362306a36Sopenharmony_ci call_netdevice_notifiers(event, vlandev); 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci case NETDEV_CVLAN_FILTER_PUSH_INFO: 50762306a36Sopenharmony_ci err = vlan_filter_push_vids(vlan_info, htons(ETH_P_8021Q)); 50862306a36Sopenharmony_ci if (err) 50962306a36Sopenharmony_ci return notifier_from_errno(err); 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci case NETDEV_CVLAN_FILTER_DROP_INFO: 51362306a36Sopenharmony_ci vlan_filter_drop_vids(vlan_info, htons(ETH_P_8021Q)); 51462306a36Sopenharmony_ci break; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci case NETDEV_SVLAN_FILTER_PUSH_INFO: 51762306a36Sopenharmony_ci err = vlan_filter_push_vids(vlan_info, htons(ETH_P_8021AD)); 51862306a36Sopenharmony_ci if (err) 51962306a36Sopenharmony_ci return notifier_from_errno(err); 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci case NETDEV_SVLAN_FILTER_DROP_INFO: 52362306a36Sopenharmony_ci vlan_filter_drop_vids(vlan_info, htons(ETH_P_8021AD)); 52462306a36Sopenharmony_ci break; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ciout: 52862306a36Sopenharmony_ci return NOTIFY_DONE; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic struct notifier_block vlan_notifier_block __read_mostly = { 53262306a36Sopenharmony_ci .notifier_call = vlan_device_event, 53362306a36Sopenharmony_ci}; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci/* 53662306a36Sopenharmony_ci * VLAN IOCTL handler. 53762306a36Sopenharmony_ci * o execute requested action or pass command to the device driver 53862306a36Sopenharmony_ci * arg is really a struct vlan_ioctl_args __user *. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_cistatic int vlan_ioctl_handler(struct net *net, void __user *arg) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci int err; 54362306a36Sopenharmony_ci struct vlan_ioctl_args args; 54462306a36Sopenharmony_ci struct net_device *dev = NULL; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (copy_from_user(&args, arg, sizeof(struct vlan_ioctl_args))) 54762306a36Sopenharmony_ci return -EFAULT; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* Null terminate this sucker, just in case. */ 55062306a36Sopenharmony_ci args.device1[sizeof(args.device1) - 1] = 0; 55162306a36Sopenharmony_ci args.u.device2[sizeof(args.u.device2) - 1] = 0; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci rtnl_lock(); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci switch (args.cmd) { 55662306a36Sopenharmony_ci case SET_VLAN_INGRESS_PRIORITY_CMD: 55762306a36Sopenharmony_ci case SET_VLAN_EGRESS_PRIORITY_CMD: 55862306a36Sopenharmony_ci case SET_VLAN_FLAG_CMD: 55962306a36Sopenharmony_ci case ADD_VLAN_CMD: 56062306a36Sopenharmony_ci case DEL_VLAN_CMD: 56162306a36Sopenharmony_ci case GET_VLAN_REALDEV_NAME_CMD: 56262306a36Sopenharmony_ci case GET_VLAN_VID_CMD: 56362306a36Sopenharmony_ci err = -ENODEV; 56462306a36Sopenharmony_ci dev = __dev_get_by_name(net, args.device1); 56562306a36Sopenharmony_ci if (!dev) 56662306a36Sopenharmony_ci goto out; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci err = -EINVAL; 56962306a36Sopenharmony_ci if (args.cmd != ADD_VLAN_CMD && !is_vlan_dev(dev)) 57062306a36Sopenharmony_ci goto out; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci switch (args.cmd) { 57462306a36Sopenharmony_ci case SET_VLAN_INGRESS_PRIORITY_CMD: 57562306a36Sopenharmony_ci err = -EPERM; 57662306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci vlan_dev_set_ingress_priority(dev, 57962306a36Sopenharmony_ci args.u.skb_priority, 58062306a36Sopenharmony_ci args.vlan_qos); 58162306a36Sopenharmony_ci err = 0; 58262306a36Sopenharmony_ci break; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci case SET_VLAN_EGRESS_PRIORITY_CMD: 58562306a36Sopenharmony_ci err = -EPERM; 58662306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci err = vlan_dev_set_egress_priority(dev, 58962306a36Sopenharmony_ci args.u.skb_priority, 59062306a36Sopenharmony_ci args.vlan_qos); 59162306a36Sopenharmony_ci break; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci case SET_VLAN_FLAG_CMD: 59462306a36Sopenharmony_ci err = -EPERM; 59562306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci err = vlan_dev_change_flags(dev, 59862306a36Sopenharmony_ci args.vlan_qos ? args.u.flag : 0, 59962306a36Sopenharmony_ci args.u.flag); 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci case SET_VLAN_NAME_TYPE_CMD: 60362306a36Sopenharmony_ci err = -EPERM; 60462306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 60562306a36Sopenharmony_ci break; 60662306a36Sopenharmony_ci if (args.u.name_type < VLAN_NAME_TYPE_HIGHEST) { 60762306a36Sopenharmony_ci struct vlan_net *vn; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci vn = net_generic(net, vlan_net_id); 61062306a36Sopenharmony_ci vn->name_type = args.u.name_type; 61162306a36Sopenharmony_ci err = 0; 61262306a36Sopenharmony_ci } else { 61362306a36Sopenharmony_ci err = -EINVAL; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci break; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci case ADD_VLAN_CMD: 61862306a36Sopenharmony_ci err = -EPERM; 61962306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci err = register_vlan_device(dev, args.u.VID); 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci case DEL_VLAN_CMD: 62562306a36Sopenharmony_ci err = -EPERM; 62662306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 62762306a36Sopenharmony_ci break; 62862306a36Sopenharmony_ci unregister_vlan_dev(dev, NULL); 62962306a36Sopenharmony_ci err = 0; 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci case GET_VLAN_REALDEV_NAME_CMD: 63362306a36Sopenharmony_ci err = 0; 63462306a36Sopenharmony_ci vlan_dev_get_realdev_name(dev, args.u.device2, 63562306a36Sopenharmony_ci sizeof(args.u.device2)); 63662306a36Sopenharmony_ci if (copy_to_user(arg, &args, 63762306a36Sopenharmony_ci sizeof(struct vlan_ioctl_args))) 63862306a36Sopenharmony_ci err = -EFAULT; 63962306a36Sopenharmony_ci break; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci case GET_VLAN_VID_CMD: 64262306a36Sopenharmony_ci err = 0; 64362306a36Sopenharmony_ci args.u.VID = vlan_dev_vlan_id(dev); 64462306a36Sopenharmony_ci if (copy_to_user(arg, &args, 64562306a36Sopenharmony_ci sizeof(struct vlan_ioctl_args))) 64662306a36Sopenharmony_ci err = -EFAULT; 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci default: 65062306a36Sopenharmony_ci err = -EOPNOTSUPP; 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ciout: 65462306a36Sopenharmony_ci rtnl_unlock(); 65562306a36Sopenharmony_ci return err; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int __net_init vlan_init_net(struct net *net) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct vlan_net *vn = net_generic(net, vlan_net_id); 66162306a36Sopenharmony_ci int err; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci vn->name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci err = vlan_proc_init(net); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci return err; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic void __net_exit vlan_exit_net(struct net *net) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci vlan_proc_cleanup(net); 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic struct pernet_operations vlan_net_ops = { 67662306a36Sopenharmony_ci .init = vlan_init_net, 67762306a36Sopenharmony_ci .exit = vlan_exit_net, 67862306a36Sopenharmony_ci .id = &vlan_net_id, 67962306a36Sopenharmony_ci .size = sizeof(struct vlan_net), 68062306a36Sopenharmony_ci}; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int __init vlan_proto_init(void) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci int err; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci pr_info("%s v%s\n", vlan_fullname, vlan_version); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci err = register_pernet_subsys(&vlan_net_ops); 68962306a36Sopenharmony_ci if (err < 0) 69062306a36Sopenharmony_ci goto err0; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci err = register_netdevice_notifier(&vlan_notifier_block); 69362306a36Sopenharmony_ci if (err < 0) 69462306a36Sopenharmony_ci goto err2; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci err = vlan_gvrp_init(); 69762306a36Sopenharmony_ci if (err < 0) 69862306a36Sopenharmony_ci goto err3; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci err = vlan_mvrp_init(); 70162306a36Sopenharmony_ci if (err < 0) 70262306a36Sopenharmony_ci goto err4; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci err = vlan_netlink_init(); 70562306a36Sopenharmony_ci if (err < 0) 70662306a36Sopenharmony_ci goto err5; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci vlan_ioctl_set(vlan_ioctl_handler); 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cierr5: 71262306a36Sopenharmony_ci vlan_mvrp_uninit(); 71362306a36Sopenharmony_cierr4: 71462306a36Sopenharmony_ci vlan_gvrp_uninit(); 71562306a36Sopenharmony_cierr3: 71662306a36Sopenharmony_ci unregister_netdevice_notifier(&vlan_notifier_block); 71762306a36Sopenharmony_cierr2: 71862306a36Sopenharmony_ci unregister_pernet_subsys(&vlan_net_ops); 71962306a36Sopenharmony_cierr0: 72062306a36Sopenharmony_ci return err; 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic void __exit vlan_cleanup_module(void) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci vlan_ioctl_set(NULL); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci vlan_netlink_fini(); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci unregister_netdevice_notifier(&vlan_notifier_block); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci unregister_pernet_subsys(&vlan_net_ops); 73262306a36Sopenharmony_ci rcu_barrier(); /* Wait for completion of call_rcu()'s */ 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci vlan_mvrp_uninit(); 73562306a36Sopenharmony_ci vlan_gvrp_uninit(); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cimodule_init(vlan_proto_init); 73962306a36Sopenharmony_cimodule_exit(vlan_cleanup_module); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 74262306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 743