162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VLAN netlink control interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2007 Patrick McHardy <kaber@trash.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/netdevice.h> 1062306a36Sopenharmony_ci#include <linux/if_vlan.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <net/net_namespace.h> 1362306a36Sopenharmony_ci#include <net/netlink.h> 1462306a36Sopenharmony_ci#include <net/rtnetlink.h> 1562306a36Sopenharmony_ci#include "vlan.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic const struct nla_policy vlan_policy[IFLA_VLAN_MAX + 1] = { 1962306a36Sopenharmony_ci [IFLA_VLAN_ID] = { .type = NLA_U16 }, 2062306a36Sopenharmony_ci [IFLA_VLAN_FLAGS] = { .len = sizeof(struct ifla_vlan_flags) }, 2162306a36Sopenharmony_ci [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED }, 2262306a36Sopenharmony_ci [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, 2362306a36Sopenharmony_ci [IFLA_VLAN_PROTOCOL] = { .type = NLA_U16 }, 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic const struct nla_policy vlan_map_policy[IFLA_VLAN_QOS_MAX + 1] = { 2762306a36Sopenharmony_ci [IFLA_VLAN_QOS_MAPPING] = { .len = sizeof(struct ifla_vlan_qos_mapping) }, 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic inline int vlan_validate_qos_map(struct nlattr *attr) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci if (!attr) 3462306a36Sopenharmony_ci return 0; 3562306a36Sopenharmony_ci return nla_validate_nested_deprecated(attr, IFLA_VLAN_QOS_MAX, 3662306a36Sopenharmony_ci vlan_map_policy, NULL); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int vlan_validate(struct nlattr *tb[], struct nlattr *data[], 4062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct ifla_vlan_flags *flags; 4362306a36Sopenharmony_ci u16 id; 4462306a36Sopenharmony_ci int err; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (tb[IFLA_ADDRESS]) { 4762306a36Sopenharmony_ci if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) { 4862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid link address"); 4962306a36Sopenharmony_ci return -EINVAL; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) { 5262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid link address"); 5362306a36Sopenharmony_ci return -EADDRNOTAVAIL; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (!data) { 5862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VLAN properties not specified"); 5962306a36Sopenharmony_ci return -EINVAL; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (data[IFLA_VLAN_PROTOCOL]) { 6362306a36Sopenharmony_ci switch (nla_get_be16(data[IFLA_VLAN_PROTOCOL])) { 6462306a36Sopenharmony_ci case htons(ETH_P_8021Q): 6562306a36Sopenharmony_ci case htons(ETH_P_8021AD): 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci default: 6862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid VLAN protocol"); 6962306a36Sopenharmony_ci return -EPROTONOSUPPORT; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (data[IFLA_VLAN_ID]) { 7462306a36Sopenharmony_ci id = nla_get_u16(data[IFLA_VLAN_ID]); 7562306a36Sopenharmony_ci if (id >= VLAN_VID_MASK) { 7662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid VLAN id"); 7762306a36Sopenharmony_ci return -ERANGE; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci if (data[IFLA_VLAN_FLAGS]) { 8162306a36Sopenharmony_ci flags = nla_data(data[IFLA_VLAN_FLAGS]); 8262306a36Sopenharmony_ci if ((flags->flags & flags->mask) & 8362306a36Sopenharmony_ci ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP | 8462306a36Sopenharmony_ci VLAN_FLAG_LOOSE_BINDING | VLAN_FLAG_MVRP | 8562306a36Sopenharmony_ci VLAN_FLAG_BRIDGE_BINDING)) { 8662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid VLAN flags"); 8762306a36Sopenharmony_ci return -EINVAL; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci err = vlan_validate_qos_map(data[IFLA_VLAN_INGRESS_QOS]); 9262306a36Sopenharmony_ci if (err < 0) { 9362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid ingress QOS map"); 9462306a36Sopenharmony_ci return err; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci err = vlan_validate_qos_map(data[IFLA_VLAN_EGRESS_QOS]); 9762306a36Sopenharmony_ci if (err < 0) { 9862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid egress QOS map"); 9962306a36Sopenharmony_ci return err; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int vlan_changelink(struct net_device *dev, struct nlattr *tb[], 10562306a36Sopenharmony_ci struct nlattr *data[], 10662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct ifla_vlan_flags *flags; 10962306a36Sopenharmony_ci struct ifla_vlan_qos_mapping *m; 11062306a36Sopenharmony_ci struct nlattr *attr; 11162306a36Sopenharmony_ci int rem, err; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (data[IFLA_VLAN_FLAGS]) { 11462306a36Sopenharmony_ci flags = nla_data(data[IFLA_VLAN_FLAGS]); 11562306a36Sopenharmony_ci err = vlan_dev_change_flags(dev, flags->flags, flags->mask); 11662306a36Sopenharmony_ci if (err) 11762306a36Sopenharmony_ci return err; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci if (data[IFLA_VLAN_INGRESS_QOS]) { 12062306a36Sopenharmony_ci nla_for_each_nested(attr, data[IFLA_VLAN_INGRESS_QOS], rem) { 12162306a36Sopenharmony_ci if (nla_type(attr) != IFLA_VLAN_QOS_MAPPING) 12262306a36Sopenharmony_ci continue; 12362306a36Sopenharmony_ci m = nla_data(attr); 12462306a36Sopenharmony_ci vlan_dev_set_ingress_priority(dev, m->to, m->from); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci if (data[IFLA_VLAN_EGRESS_QOS]) { 12862306a36Sopenharmony_ci nla_for_each_nested(attr, data[IFLA_VLAN_EGRESS_QOS], rem) { 12962306a36Sopenharmony_ci if (nla_type(attr) != IFLA_VLAN_QOS_MAPPING) 13062306a36Sopenharmony_ci continue; 13162306a36Sopenharmony_ci m = nla_data(attr); 13262306a36Sopenharmony_ci err = vlan_dev_set_egress_priority(dev, m->from, m->to); 13362306a36Sopenharmony_ci if (err) 13462306a36Sopenharmony_ci return err; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int vlan_newlink(struct net *src_net, struct net_device *dev, 14162306a36Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 14262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(dev); 14562306a36Sopenharmony_ci struct net_device *real_dev; 14662306a36Sopenharmony_ci unsigned int max_mtu; 14762306a36Sopenharmony_ci __be16 proto; 14862306a36Sopenharmony_ci int err; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (!data[IFLA_VLAN_ID]) { 15162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "VLAN id not specified"); 15262306a36Sopenharmony_ci return -EINVAL; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (!tb[IFLA_LINK]) { 15662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "link not specified"); 15762306a36Sopenharmony_ci return -EINVAL; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); 16162306a36Sopenharmony_ci if (!real_dev) { 16262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "link does not exist"); 16362306a36Sopenharmony_ci return -ENODEV; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (data[IFLA_VLAN_PROTOCOL]) 16762306a36Sopenharmony_ci proto = nla_get_be16(data[IFLA_VLAN_PROTOCOL]); 16862306a36Sopenharmony_ci else 16962306a36Sopenharmony_ci proto = htons(ETH_P_8021Q); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci vlan->vlan_proto = proto; 17262306a36Sopenharmony_ci vlan->vlan_id = nla_get_u16(data[IFLA_VLAN_ID]); 17362306a36Sopenharmony_ci vlan->real_dev = real_dev; 17462306a36Sopenharmony_ci dev->priv_flags |= (real_dev->priv_flags & IFF_XMIT_DST_RELEASE); 17562306a36Sopenharmony_ci vlan->flags = VLAN_FLAG_REORDER_HDR; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci err = vlan_check_real_dev(real_dev, vlan->vlan_proto, vlan->vlan_id, 17862306a36Sopenharmony_ci extack); 17962306a36Sopenharmony_ci if (err < 0) 18062306a36Sopenharmony_ci return err; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci max_mtu = netif_reduces_vlan_mtu(real_dev) ? real_dev->mtu - VLAN_HLEN : 18362306a36Sopenharmony_ci real_dev->mtu; 18462306a36Sopenharmony_ci if (!tb[IFLA_MTU]) 18562306a36Sopenharmony_ci dev->mtu = max_mtu; 18662306a36Sopenharmony_ci else if (dev->mtu > max_mtu) 18762306a36Sopenharmony_ci return -EINVAL; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Note: If this initial vlan_changelink() fails, we need 19062306a36Sopenharmony_ci * to call vlan_dev_free_egress_priority() to free memory. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci err = vlan_changelink(dev, tb, data, extack); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!err) 19562306a36Sopenharmony_ci err = register_vlan_dev(dev, extack); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (err) 19862306a36Sopenharmony_ci vlan_dev_free_egress_priority(dev); 19962306a36Sopenharmony_ci return err; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic inline size_t vlan_qos_map_size(unsigned int n) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci if (n == 0) 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci /* IFLA_VLAN_{EGRESS,INGRESS}_QOS + n * IFLA_VLAN_QOS_MAPPING */ 20762306a36Sopenharmony_ci return nla_total_size(sizeof(struct nlattr)) + 20862306a36Sopenharmony_ci nla_total_size(sizeof(struct ifla_vlan_qos_mapping)) * n; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic size_t vlan_get_size(const struct net_device *dev) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(dev); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return nla_total_size(2) + /* IFLA_VLAN_PROTOCOL */ 21662306a36Sopenharmony_ci nla_total_size(2) + /* IFLA_VLAN_ID */ 21762306a36Sopenharmony_ci nla_total_size(sizeof(struct ifla_vlan_flags)) + /* IFLA_VLAN_FLAGS */ 21862306a36Sopenharmony_ci vlan_qos_map_size(vlan->nr_ingress_mappings) + 21962306a36Sopenharmony_ci vlan_qos_map_size(vlan->nr_egress_mappings); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int vlan_fill_info(struct sk_buff *skb, const struct net_device *dev) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct vlan_dev_priv *vlan = vlan_dev_priv(dev); 22562306a36Sopenharmony_ci struct vlan_priority_tci_mapping *pm; 22662306a36Sopenharmony_ci struct ifla_vlan_flags f; 22762306a36Sopenharmony_ci struct ifla_vlan_qos_mapping m; 22862306a36Sopenharmony_ci struct nlattr *nest; 22962306a36Sopenharmony_ci unsigned int i; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (nla_put_be16(skb, IFLA_VLAN_PROTOCOL, vlan->vlan_proto) || 23262306a36Sopenharmony_ci nla_put_u16(skb, IFLA_VLAN_ID, vlan->vlan_id)) 23362306a36Sopenharmony_ci goto nla_put_failure; 23462306a36Sopenharmony_ci if (vlan->flags) { 23562306a36Sopenharmony_ci f.flags = vlan->flags; 23662306a36Sopenharmony_ci f.mask = ~0; 23762306a36Sopenharmony_ci if (nla_put(skb, IFLA_VLAN_FLAGS, sizeof(f), &f)) 23862306a36Sopenharmony_ci goto nla_put_failure; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci if (vlan->nr_ingress_mappings) { 24162306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, IFLA_VLAN_INGRESS_QOS); 24262306a36Sopenharmony_ci if (nest == NULL) 24362306a36Sopenharmony_ci goto nla_put_failure; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vlan->ingress_priority_map); i++) { 24662306a36Sopenharmony_ci if (!vlan->ingress_priority_map[i]) 24762306a36Sopenharmony_ci continue; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci m.from = i; 25062306a36Sopenharmony_ci m.to = vlan->ingress_priority_map[i]; 25162306a36Sopenharmony_ci if (nla_put(skb, IFLA_VLAN_QOS_MAPPING, 25262306a36Sopenharmony_ci sizeof(m), &m)) 25362306a36Sopenharmony_ci goto nla_put_failure; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci nla_nest_end(skb, nest); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (vlan->nr_egress_mappings) { 25962306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, IFLA_VLAN_EGRESS_QOS); 26062306a36Sopenharmony_ci if (nest == NULL) 26162306a36Sopenharmony_ci goto nla_put_failure; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) { 26462306a36Sopenharmony_ci for (pm = vlan->egress_priority_map[i]; pm; 26562306a36Sopenharmony_ci pm = pm->next) { 26662306a36Sopenharmony_ci if (!pm->vlan_qos) 26762306a36Sopenharmony_ci continue; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci m.from = pm->priority; 27062306a36Sopenharmony_ci m.to = (pm->vlan_qos >> 13) & 0x7; 27162306a36Sopenharmony_ci if (nla_put(skb, IFLA_VLAN_QOS_MAPPING, 27262306a36Sopenharmony_ci sizeof(m), &m)) 27362306a36Sopenharmony_ci goto nla_put_failure; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci nla_nest_end(skb, nest); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cinla_put_failure: 28162306a36Sopenharmony_ci return -EMSGSIZE; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic struct net *vlan_get_link_net(const struct net_device *dev) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return dev_net(real_dev); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistruct rtnl_link_ops vlan_link_ops __read_mostly = { 29262306a36Sopenharmony_ci .kind = "vlan", 29362306a36Sopenharmony_ci .maxtype = IFLA_VLAN_MAX, 29462306a36Sopenharmony_ci .policy = vlan_policy, 29562306a36Sopenharmony_ci .priv_size = sizeof(struct vlan_dev_priv), 29662306a36Sopenharmony_ci .setup = vlan_setup, 29762306a36Sopenharmony_ci .validate = vlan_validate, 29862306a36Sopenharmony_ci .newlink = vlan_newlink, 29962306a36Sopenharmony_ci .changelink = vlan_changelink, 30062306a36Sopenharmony_ci .dellink = unregister_vlan_dev, 30162306a36Sopenharmony_ci .get_size = vlan_get_size, 30262306a36Sopenharmony_ci .fill_info = vlan_fill_info, 30362306a36Sopenharmony_ci .get_link_net = vlan_get_link_net, 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ciint __init vlan_netlink_init(void) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci return rtnl_link_register(&vlan_link_ops); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_civoid __exit vlan_netlink_fini(void) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci rtnl_link_unregister(&vlan_link_ops); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("vlan"); 317