162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IPv6 Address Label subsystem 462306a36Sopenharmony_ci * for the IPv6 "Default" Source Address Selection 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C)2007 USAGI/WIDE Project 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * Author: 1062306a36Sopenharmony_ci * YOSHIFUJI Hideaki @ USAGI/WIDE Project <yoshfuji@linux-ipv6.org> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/list.h> 1562306a36Sopenharmony_ci#include <linux/rcupdate.h> 1662306a36Sopenharmony_ci#include <linux/in6.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <net/addrconf.h> 1962306a36Sopenharmony_ci#include <linux/if_addrlabel.h> 2062306a36Sopenharmony_ci#include <linux/netlink.h> 2162306a36Sopenharmony_ci#include <linux/rtnetlink.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#if 0 2462306a36Sopenharmony_ci#define ADDRLABEL(x...) printk(x) 2562306a36Sopenharmony_ci#else 2662306a36Sopenharmony_ci#define ADDRLABEL(x...) do { ; } while (0) 2762306a36Sopenharmony_ci#endif 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * Policy Table 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_cistruct ip6addrlbl_entry { 3362306a36Sopenharmony_ci struct in6_addr prefix; 3462306a36Sopenharmony_ci int prefixlen; 3562306a36Sopenharmony_ci int ifindex; 3662306a36Sopenharmony_ci int addrtype; 3762306a36Sopenharmony_ci u32 label; 3862306a36Sopenharmony_ci struct hlist_node list; 3962306a36Sopenharmony_ci struct rcu_head rcu; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Default policy table (RFC6724 + extensions) 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * prefix addr_type label 4662306a36Sopenharmony_ci * ------------------------------------------------------------------------- 4762306a36Sopenharmony_ci * ::1/128 LOOPBACK 0 4862306a36Sopenharmony_ci * ::/0 N/A 1 4962306a36Sopenharmony_ci * 2002::/16 N/A 2 5062306a36Sopenharmony_ci * ::/96 COMPATv4 3 5162306a36Sopenharmony_ci * ::ffff:0:0/96 V4MAPPED 4 5262306a36Sopenharmony_ci * fc00::/7 N/A 5 ULA (RFC 4193) 5362306a36Sopenharmony_ci * 2001::/32 N/A 6 Teredo (RFC 4380) 5462306a36Sopenharmony_ci * 2001:10::/28 N/A 7 ORCHID (RFC 4843) 5562306a36Sopenharmony_ci * fec0::/10 N/A 11 Site-local 5662306a36Sopenharmony_ci * (deprecated by RFC3879) 5762306a36Sopenharmony_ci * 3ffe::/16 N/A 12 6bone 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * Note: 0xffffffff is used if we do not have any policies. 6062306a36Sopenharmony_ci * Note: Labels for ULA and 6to4 are different from labels listed in RFC6724. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define IPV6_ADDR_LABEL_DEFAULT 0xffffffffUL 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic const __net_initconst struct ip6addrlbl_init_table 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci const struct in6_addr *prefix; 6862306a36Sopenharmony_ci int prefixlen; 6962306a36Sopenharmony_ci u32 label; 7062306a36Sopenharmony_ci} ip6addrlbl_init_table[] = { 7162306a36Sopenharmony_ci { /* ::/0 */ 7262306a36Sopenharmony_ci .prefix = &in6addr_any, 7362306a36Sopenharmony_ci .label = 1, 7462306a36Sopenharmony_ci }, { /* fc00::/7 */ 7562306a36Sopenharmony_ci .prefix = &(struct in6_addr){ { { 0xfc } } } , 7662306a36Sopenharmony_ci .prefixlen = 7, 7762306a36Sopenharmony_ci .label = 5, 7862306a36Sopenharmony_ci }, { /* fec0::/10 */ 7962306a36Sopenharmony_ci .prefix = &(struct in6_addr){ { { 0xfe, 0xc0 } } }, 8062306a36Sopenharmony_ci .prefixlen = 10, 8162306a36Sopenharmony_ci .label = 11, 8262306a36Sopenharmony_ci }, { /* 2002::/16 */ 8362306a36Sopenharmony_ci .prefix = &(struct in6_addr){ { { 0x20, 0x02 } } }, 8462306a36Sopenharmony_ci .prefixlen = 16, 8562306a36Sopenharmony_ci .label = 2, 8662306a36Sopenharmony_ci }, { /* 3ffe::/16 */ 8762306a36Sopenharmony_ci .prefix = &(struct in6_addr){ { { 0x3f, 0xfe } } }, 8862306a36Sopenharmony_ci .prefixlen = 16, 8962306a36Sopenharmony_ci .label = 12, 9062306a36Sopenharmony_ci }, { /* 2001::/32 */ 9162306a36Sopenharmony_ci .prefix = &(struct in6_addr){ { { 0x20, 0x01 } } }, 9262306a36Sopenharmony_ci .prefixlen = 32, 9362306a36Sopenharmony_ci .label = 6, 9462306a36Sopenharmony_ci }, { /* 2001:10::/28 */ 9562306a36Sopenharmony_ci .prefix = &(struct in6_addr){ { { 0x20, 0x01, 0x00, 0x10 } } }, 9662306a36Sopenharmony_ci .prefixlen = 28, 9762306a36Sopenharmony_ci .label = 7, 9862306a36Sopenharmony_ci }, { /* ::ffff:0:0 */ 9962306a36Sopenharmony_ci .prefix = &(struct in6_addr){ { { [10] = 0xff, [11] = 0xff } } }, 10062306a36Sopenharmony_ci .prefixlen = 96, 10162306a36Sopenharmony_ci .label = 4, 10262306a36Sopenharmony_ci }, { /* ::/96 */ 10362306a36Sopenharmony_ci .prefix = &in6addr_any, 10462306a36Sopenharmony_ci .prefixlen = 96, 10562306a36Sopenharmony_ci .label = 3, 10662306a36Sopenharmony_ci }, { /* ::1/128 */ 10762306a36Sopenharmony_ci .prefix = &in6addr_loopback, 10862306a36Sopenharmony_ci .prefixlen = 128, 10962306a36Sopenharmony_ci .label = 0, 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Find label */ 11462306a36Sopenharmony_cistatic bool __ip6addrlbl_match(const struct ip6addrlbl_entry *p, 11562306a36Sopenharmony_ci const struct in6_addr *addr, 11662306a36Sopenharmony_ci int addrtype, int ifindex) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci if (p->ifindex && p->ifindex != ifindex) 11962306a36Sopenharmony_ci return false; 12062306a36Sopenharmony_ci if (p->addrtype && p->addrtype != addrtype) 12162306a36Sopenharmony_ci return false; 12262306a36Sopenharmony_ci if (!ipv6_prefix_equal(addr, &p->prefix, p->prefixlen)) 12362306a36Sopenharmony_ci return false; 12462306a36Sopenharmony_ci return true; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic struct ip6addrlbl_entry *__ipv6_addr_label(struct net *net, 12862306a36Sopenharmony_ci const struct in6_addr *addr, 12962306a36Sopenharmony_ci int type, int ifindex) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct ip6addrlbl_entry *p; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) { 13462306a36Sopenharmony_ci if (__ip6addrlbl_match(p, addr, type, ifindex)) 13562306a36Sopenharmony_ci return p; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci return NULL; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciu32 ipv6_addr_label(struct net *net, 14162306a36Sopenharmony_ci const struct in6_addr *addr, int type, int ifindex) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci u32 label; 14462306a36Sopenharmony_ci struct ip6addrlbl_entry *p; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci type &= IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci rcu_read_lock(); 14962306a36Sopenharmony_ci p = __ipv6_addr_label(net, addr, type, ifindex); 15062306a36Sopenharmony_ci label = p ? p->label : IPV6_ADDR_LABEL_DEFAULT; 15162306a36Sopenharmony_ci rcu_read_unlock(); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci ADDRLABEL(KERN_DEBUG "%s(addr=%pI6, type=%d, ifindex=%d) => %08x\n", 15462306a36Sopenharmony_ci __func__, addr, type, ifindex, label); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return label; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* allocate one entry */ 16062306a36Sopenharmony_cistatic struct ip6addrlbl_entry *ip6addrlbl_alloc(const struct in6_addr *prefix, 16162306a36Sopenharmony_ci int prefixlen, int ifindex, 16262306a36Sopenharmony_ci u32 label) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct ip6addrlbl_entry *newp; 16562306a36Sopenharmony_ci int addrtype; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u)\n", 16862306a36Sopenharmony_ci __func__, prefix, prefixlen, ifindex, (unsigned int)label); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci addrtype = ipv6_addr_type(prefix) & (IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci switch (addrtype) { 17362306a36Sopenharmony_ci case IPV6_ADDR_MAPPED: 17462306a36Sopenharmony_ci if (prefixlen > 96) 17562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 17662306a36Sopenharmony_ci if (prefixlen < 96) 17762306a36Sopenharmony_ci addrtype = 0; 17862306a36Sopenharmony_ci break; 17962306a36Sopenharmony_ci case IPV6_ADDR_COMPATv4: 18062306a36Sopenharmony_ci if (prefixlen != 96) 18162306a36Sopenharmony_ci addrtype = 0; 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case IPV6_ADDR_LOOPBACK: 18462306a36Sopenharmony_ci if (prefixlen != 128) 18562306a36Sopenharmony_ci addrtype = 0; 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci newp = kmalloc(sizeof(*newp), GFP_KERNEL); 19062306a36Sopenharmony_ci if (!newp) 19162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ipv6_addr_prefix(&newp->prefix, prefix, prefixlen); 19462306a36Sopenharmony_ci newp->prefixlen = prefixlen; 19562306a36Sopenharmony_ci newp->ifindex = ifindex; 19662306a36Sopenharmony_ci newp->addrtype = addrtype; 19762306a36Sopenharmony_ci newp->label = label; 19862306a36Sopenharmony_ci INIT_HLIST_NODE(&newp->list); 19962306a36Sopenharmony_ci return newp; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* add a label */ 20362306a36Sopenharmony_cistatic int __ip6addrlbl_add(struct net *net, struct ip6addrlbl_entry *newp, 20462306a36Sopenharmony_ci int replace) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct ip6addrlbl_entry *last = NULL, *p = NULL; 20762306a36Sopenharmony_ci struct hlist_node *n; 20862306a36Sopenharmony_ci int ret = 0; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n", __func__, newp, 21162306a36Sopenharmony_ci replace); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { 21462306a36Sopenharmony_ci if (p->prefixlen == newp->prefixlen && 21562306a36Sopenharmony_ci p->ifindex == newp->ifindex && 21662306a36Sopenharmony_ci ipv6_addr_equal(&p->prefix, &newp->prefix)) { 21762306a36Sopenharmony_ci if (!replace) { 21862306a36Sopenharmony_ci ret = -EEXIST; 21962306a36Sopenharmony_ci goto out; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci hlist_replace_rcu(&p->list, &newp->list); 22262306a36Sopenharmony_ci kfree_rcu(p, rcu); 22362306a36Sopenharmony_ci goto out; 22462306a36Sopenharmony_ci } else if ((p->prefixlen == newp->prefixlen && !p->ifindex) || 22562306a36Sopenharmony_ci (p->prefixlen < newp->prefixlen)) { 22662306a36Sopenharmony_ci hlist_add_before_rcu(&newp->list, &p->list); 22762306a36Sopenharmony_ci goto out; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci last = p; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci if (last) 23262306a36Sopenharmony_ci hlist_add_behind_rcu(&newp->list, &last->list); 23362306a36Sopenharmony_ci else 23462306a36Sopenharmony_ci hlist_add_head_rcu(&newp->list, &net->ipv6.ip6addrlbl_table.head); 23562306a36Sopenharmony_ciout: 23662306a36Sopenharmony_ci if (!ret) 23762306a36Sopenharmony_ci net->ipv6.ip6addrlbl_table.seq++; 23862306a36Sopenharmony_ci return ret; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* add a label */ 24262306a36Sopenharmony_cistatic int ip6addrlbl_add(struct net *net, 24362306a36Sopenharmony_ci const struct in6_addr *prefix, int prefixlen, 24462306a36Sopenharmony_ci int ifindex, u32 label, int replace) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct ip6addrlbl_entry *newp; 24762306a36Sopenharmony_ci int ret = 0; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u, replace=%d)\n", 25062306a36Sopenharmony_ci __func__, prefix, prefixlen, ifindex, (unsigned int)label, 25162306a36Sopenharmony_ci replace); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci newp = ip6addrlbl_alloc(prefix, prefixlen, ifindex, label); 25462306a36Sopenharmony_ci if (IS_ERR(newp)) 25562306a36Sopenharmony_ci return PTR_ERR(newp); 25662306a36Sopenharmony_ci spin_lock(&net->ipv6.ip6addrlbl_table.lock); 25762306a36Sopenharmony_ci ret = __ip6addrlbl_add(net, newp, replace); 25862306a36Sopenharmony_ci spin_unlock(&net->ipv6.ip6addrlbl_table.lock); 25962306a36Sopenharmony_ci if (ret) 26062306a36Sopenharmony_ci kfree(newp); 26162306a36Sopenharmony_ci return ret; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/* remove a label */ 26562306a36Sopenharmony_cistatic int __ip6addrlbl_del(struct net *net, 26662306a36Sopenharmony_ci const struct in6_addr *prefix, int prefixlen, 26762306a36Sopenharmony_ci int ifindex) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct ip6addrlbl_entry *p = NULL; 27062306a36Sopenharmony_ci struct hlist_node *n; 27162306a36Sopenharmony_ci int ret = -ESRCH; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n", 27462306a36Sopenharmony_ci __func__, prefix, prefixlen, ifindex); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { 27762306a36Sopenharmony_ci if (p->prefixlen == prefixlen && 27862306a36Sopenharmony_ci p->ifindex == ifindex && 27962306a36Sopenharmony_ci ipv6_addr_equal(&p->prefix, prefix)) { 28062306a36Sopenharmony_ci hlist_del_rcu(&p->list); 28162306a36Sopenharmony_ci kfree_rcu(p, rcu); 28262306a36Sopenharmony_ci ret = 0; 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci return ret; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int ip6addrlbl_del(struct net *net, 29062306a36Sopenharmony_ci const struct in6_addr *prefix, int prefixlen, 29162306a36Sopenharmony_ci int ifindex) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct in6_addr prefix_buf; 29462306a36Sopenharmony_ci int ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n", 29762306a36Sopenharmony_ci __func__, prefix, prefixlen, ifindex); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ipv6_addr_prefix(&prefix_buf, prefix, prefixlen); 30062306a36Sopenharmony_ci spin_lock(&net->ipv6.ip6addrlbl_table.lock); 30162306a36Sopenharmony_ci ret = __ip6addrlbl_del(net, &prefix_buf, prefixlen, ifindex); 30262306a36Sopenharmony_ci spin_unlock(&net->ipv6.ip6addrlbl_table.lock); 30362306a36Sopenharmony_ci return ret; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* add default label */ 30762306a36Sopenharmony_cistatic int __net_init ip6addrlbl_net_init(struct net *net) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct ip6addrlbl_entry *p = NULL; 31062306a36Sopenharmony_ci struct hlist_node *n; 31162306a36Sopenharmony_ci int err; 31262306a36Sopenharmony_ci int i; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ADDRLABEL(KERN_DEBUG "%s\n", __func__); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci spin_lock_init(&net->ipv6.ip6addrlbl_table.lock); 31762306a36Sopenharmony_ci INIT_HLIST_HEAD(&net->ipv6.ip6addrlbl_table.head); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ip6addrlbl_init_table); i++) { 32062306a36Sopenharmony_ci err = ip6addrlbl_add(net, 32162306a36Sopenharmony_ci ip6addrlbl_init_table[i].prefix, 32262306a36Sopenharmony_ci ip6addrlbl_init_table[i].prefixlen, 32362306a36Sopenharmony_ci 0, 32462306a36Sopenharmony_ci ip6addrlbl_init_table[i].label, 0); 32562306a36Sopenharmony_ci if (err) 32662306a36Sopenharmony_ci goto err_ip6addrlbl_add; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cierr_ip6addrlbl_add: 33162306a36Sopenharmony_ci hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { 33262306a36Sopenharmony_ci hlist_del_rcu(&p->list); 33362306a36Sopenharmony_ci kfree_rcu(p, rcu); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci return err; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic void __net_exit ip6addrlbl_net_exit(struct net *net) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct ip6addrlbl_entry *p = NULL; 34162306a36Sopenharmony_ci struct hlist_node *n; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Remove all labels belonging to the exiting net */ 34462306a36Sopenharmony_ci spin_lock(&net->ipv6.ip6addrlbl_table.lock); 34562306a36Sopenharmony_ci hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { 34662306a36Sopenharmony_ci hlist_del_rcu(&p->list); 34762306a36Sopenharmony_ci kfree_rcu(p, rcu); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci spin_unlock(&net->ipv6.ip6addrlbl_table.lock); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic struct pernet_operations ipv6_addr_label_ops = { 35362306a36Sopenharmony_ci .init = ip6addrlbl_net_init, 35462306a36Sopenharmony_ci .exit = ip6addrlbl_net_exit, 35562306a36Sopenharmony_ci}; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ciint __init ipv6_addr_label_init(void) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci return register_pernet_subsys(&ipv6_addr_label_ops); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_civoid ipv6_addr_label_cleanup(void) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci unregister_pernet_subsys(&ipv6_addr_label_ops); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic const struct nla_policy ifal_policy[IFAL_MAX+1] = { 36862306a36Sopenharmony_ci [IFAL_ADDRESS] = { .len = sizeof(struct in6_addr), }, 36962306a36Sopenharmony_ci [IFAL_LABEL] = { .len = sizeof(u32), }, 37062306a36Sopenharmony_ci}; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic bool addrlbl_ifindex_exists(struct net *net, int ifindex) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci struct net_device *dev; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci rcu_read_lock(); 37862306a36Sopenharmony_ci dev = dev_get_by_index_rcu(net, ifindex); 37962306a36Sopenharmony_ci rcu_read_unlock(); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return dev != NULL; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh, 38562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 38862306a36Sopenharmony_ci struct ifaddrlblmsg *ifal; 38962306a36Sopenharmony_ci struct nlattr *tb[IFAL_MAX+1]; 39062306a36Sopenharmony_ci struct in6_addr *pfx; 39162306a36Sopenharmony_ci u32 label; 39262306a36Sopenharmony_ci int err = 0; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(*ifal), tb, IFAL_MAX, 39562306a36Sopenharmony_ci ifal_policy, extack); 39662306a36Sopenharmony_ci if (err < 0) 39762306a36Sopenharmony_ci return err; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci ifal = nlmsg_data(nlh); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (ifal->ifal_family != AF_INET6 || 40262306a36Sopenharmony_ci ifal->ifal_prefixlen > 128) 40362306a36Sopenharmony_ci return -EINVAL; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!tb[IFAL_ADDRESS]) 40662306a36Sopenharmony_ci return -EINVAL; 40762306a36Sopenharmony_ci pfx = nla_data(tb[IFAL_ADDRESS]); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (!tb[IFAL_LABEL]) 41062306a36Sopenharmony_ci return -EINVAL; 41162306a36Sopenharmony_ci label = nla_get_u32(tb[IFAL_LABEL]); 41262306a36Sopenharmony_ci if (label == IPV6_ADDR_LABEL_DEFAULT) 41362306a36Sopenharmony_ci return -EINVAL; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci switch (nlh->nlmsg_type) { 41662306a36Sopenharmony_ci case RTM_NEWADDRLABEL: 41762306a36Sopenharmony_ci if (ifal->ifal_index && 41862306a36Sopenharmony_ci !addrlbl_ifindex_exists(net, ifal->ifal_index)) 41962306a36Sopenharmony_ci return -EINVAL; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci err = ip6addrlbl_add(net, pfx, ifal->ifal_prefixlen, 42262306a36Sopenharmony_ci ifal->ifal_index, label, 42362306a36Sopenharmony_ci nlh->nlmsg_flags & NLM_F_REPLACE); 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci case RTM_DELADDRLABEL: 42662306a36Sopenharmony_ci err = ip6addrlbl_del(net, pfx, ifal->ifal_prefixlen, 42762306a36Sopenharmony_ci ifal->ifal_index); 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci default: 43062306a36Sopenharmony_ci err = -EOPNOTSUPP; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci return err; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void ip6addrlbl_putmsg(struct nlmsghdr *nlh, 43662306a36Sopenharmony_ci int prefixlen, int ifindex, u32 lseq) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct ifaddrlblmsg *ifal = nlmsg_data(nlh); 43962306a36Sopenharmony_ci ifal->ifal_family = AF_INET6; 44062306a36Sopenharmony_ci ifal->__ifal_reserved = 0; 44162306a36Sopenharmony_ci ifal->ifal_prefixlen = prefixlen; 44262306a36Sopenharmony_ci ifal->ifal_flags = 0; 44362306a36Sopenharmony_ci ifal->ifal_index = ifindex; 44462306a36Sopenharmony_ci ifal->ifal_seq = lseq; 44562306a36Sopenharmony_ci}; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic int ip6addrlbl_fill(struct sk_buff *skb, 44862306a36Sopenharmony_ci struct ip6addrlbl_entry *p, 44962306a36Sopenharmony_ci u32 lseq, 45062306a36Sopenharmony_ci u32 portid, u32 seq, int event, 45162306a36Sopenharmony_ci unsigned int flags) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct nlmsghdr *nlh = nlmsg_put(skb, portid, seq, event, 45462306a36Sopenharmony_ci sizeof(struct ifaddrlblmsg), flags); 45562306a36Sopenharmony_ci if (!nlh) 45662306a36Sopenharmony_ci return -EMSGSIZE; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci ip6addrlbl_putmsg(nlh, p->prefixlen, p->ifindex, lseq); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (nla_put_in6_addr(skb, IFAL_ADDRESS, &p->prefix) < 0 || 46162306a36Sopenharmony_ci nla_put_u32(skb, IFAL_LABEL, p->label) < 0) { 46262306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 46362306a36Sopenharmony_ci return -EMSGSIZE; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci nlmsg_end(skb, nlh); 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic int ip6addrlbl_valid_dump_req(const struct nlmsghdr *nlh, 47162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct ifaddrlblmsg *ifal; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) { 47662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for address label dump request"); 47762306a36Sopenharmony_ci return -EINVAL; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci ifal = nlmsg_data(nlh); 48162306a36Sopenharmony_ci if (ifal->__ifal_reserved || ifal->ifal_prefixlen || 48262306a36Sopenharmony_ci ifal->ifal_flags || ifal->ifal_index || ifal->ifal_seq) { 48362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for address label dump request"); 48462306a36Sopenharmony_ci return -EINVAL; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (nlmsg_attrlen(nlh, sizeof(*ifal))) { 48862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid data after header for address label dump request"); 48962306a36Sopenharmony_ci return -EINVAL; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return 0; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 49862306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 49962306a36Sopenharmony_ci struct ip6addrlbl_entry *p; 50062306a36Sopenharmony_ci int idx = 0, s_idx = cb->args[0]; 50162306a36Sopenharmony_ci int err; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (cb->strict_check) { 50462306a36Sopenharmony_ci err = ip6addrlbl_valid_dump_req(nlh, cb->extack); 50562306a36Sopenharmony_ci if (err < 0) 50662306a36Sopenharmony_ci return err; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci rcu_read_lock(); 51062306a36Sopenharmony_ci hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) { 51162306a36Sopenharmony_ci if (idx >= s_idx) { 51262306a36Sopenharmony_ci err = ip6addrlbl_fill(skb, p, 51362306a36Sopenharmony_ci net->ipv6.ip6addrlbl_table.seq, 51462306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 51562306a36Sopenharmony_ci nlh->nlmsg_seq, 51662306a36Sopenharmony_ci RTM_NEWADDRLABEL, 51762306a36Sopenharmony_ci NLM_F_MULTI); 51862306a36Sopenharmony_ci if (err < 0) 51962306a36Sopenharmony_ci break; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci idx++; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci rcu_read_unlock(); 52462306a36Sopenharmony_ci cb->args[0] = idx; 52562306a36Sopenharmony_ci return skb->len; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic inline int ip6addrlbl_msgsize(void) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci return NLMSG_ALIGN(sizeof(struct ifaddrlblmsg)) 53162306a36Sopenharmony_ci + nla_total_size(16) /* IFAL_ADDRESS */ 53262306a36Sopenharmony_ci + nla_total_size(4); /* IFAL_LABEL */ 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int ip6addrlbl_valid_get_req(struct sk_buff *skb, 53662306a36Sopenharmony_ci const struct nlmsghdr *nlh, 53762306a36Sopenharmony_ci struct nlattr **tb, 53862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct ifaddrlblmsg *ifal; 54162306a36Sopenharmony_ci int i, err; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifal))) { 54462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for addrlabel get request"); 54562306a36Sopenharmony_ci return -EINVAL; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (!netlink_strict_get_check(skb)) 54962306a36Sopenharmony_ci return nlmsg_parse_deprecated(nlh, sizeof(*ifal), tb, 55062306a36Sopenharmony_ci IFAL_MAX, ifal_policy, extack); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci ifal = nlmsg_data(nlh); 55362306a36Sopenharmony_ci if (ifal->__ifal_reserved || ifal->ifal_flags || ifal->ifal_seq) { 55462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for addrlabel get request"); 55562306a36Sopenharmony_ci return -EINVAL; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifal), tb, IFAL_MAX, 55962306a36Sopenharmony_ci ifal_policy, extack); 56062306a36Sopenharmony_ci if (err) 56162306a36Sopenharmony_ci return err; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci for (i = 0; i <= IFAL_MAX; i++) { 56462306a36Sopenharmony_ci if (!tb[i]) 56562306a36Sopenharmony_ci continue; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci switch (i) { 56862306a36Sopenharmony_ci case IFAL_ADDRESS: 56962306a36Sopenharmony_ci break; 57062306a36Sopenharmony_ci default: 57162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in addrlabel get request"); 57262306a36Sopenharmony_ci return -EINVAL; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, 58062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 58362306a36Sopenharmony_ci struct ifaddrlblmsg *ifal; 58462306a36Sopenharmony_ci struct nlattr *tb[IFAL_MAX+1]; 58562306a36Sopenharmony_ci struct in6_addr *addr; 58662306a36Sopenharmony_ci u32 lseq; 58762306a36Sopenharmony_ci int err = 0; 58862306a36Sopenharmony_ci struct ip6addrlbl_entry *p; 58962306a36Sopenharmony_ci struct sk_buff *skb; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci err = ip6addrlbl_valid_get_req(in_skb, nlh, tb, extack); 59262306a36Sopenharmony_ci if (err < 0) 59362306a36Sopenharmony_ci return err; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci ifal = nlmsg_data(nlh); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (ifal->ifal_family != AF_INET6 || 59862306a36Sopenharmony_ci ifal->ifal_prefixlen != 128) 59962306a36Sopenharmony_ci return -EINVAL; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (ifal->ifal_index && 60262306a36Sopenharmony_ci !addrlbl_ifindex_exists(net, ifal->ifal_index)) 60362306a36Sopenharmony_ci return -EINVAL; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (!tb[IFAL_ADDRESS]) 60662306a36Sopenharmony_ci return -EINVAL; 60762306a36Sopenharmony_ci addr = nla_data(tb[IFAL_ADDRESS]); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci skb = nlmsg_new(ip6addrlbl_msgsize(), GFP_KERNEL); 61062306a36Sopenharmony_ci if (!skb) 61162306a36Sopenharmony_ci return -ENOBUFS; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci err = -ESRCH; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci rcu_read_lock(); 61662306a36Sopenharmony_ci p = __ipv6_addr_label(net, addr, ipv6_addr_type(addr), ifal->ifal_index); 61762306a36Sopenharmony_ci lseq = net->ipv6.ip6addrlbl_table.seq; 61862306a36Sopenharmony_ci if (p) 61962306a36Sopenharmony_ci err = ip6addrlbl_fill(skb, p, lseq, 62062306a36Sopenharmony_ci NETLINK_CB(in_skb).portid, 62162306a36Sopenharmony_ci nlh->nlmsg_seq, 62262306a36Sopenharmony_ci RTM_NEWADDRLABEL, 0); 62362306a36Sopenharmony_ci rcu_read_unlock(); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (err < 0) { 62662306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 62762306a36Sopenharmony_ci kfree_skb(skb); 62862306a36Sopenharmony_ci } else { 62962306a36Sopenharmony_ci err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci return err; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ciint __init ipv6_addr_label_rtnl_register(void) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci int ret; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWADDRLABEL, 63962306a36Sopenharmony_ci ip6addrlbl_newdel, 64062306a36Sopenharmony_ci NULL, RTNL_FLAG_DOIT_UNLOCKED); 64162306a36Sopenharmony_ci if (ret < 0) 64262306a36Sopenharmony_ci return ret; 64362306a36Sopenharmony_ci ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELADDRLABEL, 64462306a36Sopenharmony_ci ip6addrlbl_newdel, 64562306a36Sopenharmony_ci NULL, RTNL_FLAG_DOIT_UNLOCKED); 64662306a36Sopenharmony_ci if (ret < 0) 64762306a36Sopenharmony_ci return ret; 64862306a36Sopenharmony_ci ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDRLABEL, 64962306a36Sopenharmony_ci ip6addrlbl_get, 65062306a36Sopenharmony_ci ip6addrlbl_dump, RTNL_FLAG_DOIT_UNLOCKED); 65162306a36Sopenharmony_ci return ret; 65262306a36Sopenharmony_ci} 653