18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/jhash.h> 38c2ecf20Sopenharmony_ci#include <linux/netfilter.h> 48c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 58c2ecf20Sopenharmony_ci#include <linux/rhashtable.h> 68c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 78c2ecf20Sopenharmony_ci#include <net/genetlink.h> 88c2ecf20Sopenharmony_ci#include <net/ila.h> 98c2ecf20Sopenharmony_ci#include <net/netns/generic.h> 108c2ecf20Sopenharmony_ci#include <uapi/linux/genetlink.h> 118c2ecf20Sopenharmony_ci#include "ila.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistruct ila_xlat_params { 148c2ecf20Sopenharmony_ci struct ila_params ip; 158c2ecf20Sopenharmony_ci int ifindex; 168c2ecf20Sopenharmony_ci}; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct ila_map { 198c2ecf20Sopenharmony_ci struct ila_xlat_params xp; 208c2ecf20Sopenharmony_ci struct rhash_head node; 218c2ecf20Sopenharmony_ci struct ila_map __rcu *next; 228c2ecf20Sopenharmony_ci struct rcu_head rcu; 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define MAX_LOCKS 1024 268c2ecf20Sopenharmony_ci#define LOCKS_PER_CPU 10 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int alloc_ila_locks(struct ila_net *ilan) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return alloc_bucket_spinlocks(&ilan->xlat.locks, &ilan->xlat.locks_mask, 318c2ecf20Sopenharmony_ci MAX_LOCKS, LOCKS_PER_CPU, 328c2ecf20Sopenharmony_ci GFP_KERNEL); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic u32 hashrnd __read_mostly; 368c2ecf20Sopenharmony_cistatic __always_inline void __ila_hash_secret_init(void) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci net_get_random_once(&hashrnd, sizeof(hashrnd)); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic inline u32 ila_locator_hash(struct ila_locator loc) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci u32 *v = (u32 *)loc.v32; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci __ila_hash_secret_init(); 468c2ecf20Sopenharmony_ci return jhash_2words(v[0], v[1], hashrnd); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic inline spinlock_t *ila_get_lock(struct ila_net *ilan, 508c2ecf20Sopenharmony_ci struct ila_locator loc) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci return &ilan->xlat.locks[ila_locator_hash(loc) & ilan->xlat.locks_mask]; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic inline int ila_cmp_wildcards(struct ila_map *ila, 568c2ecf20Sopenharmony_ci struct ila_addr *iaddr, int ifindex) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return (ila->xp.ifindex && ila->xp.ifindex != ifindex); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic inline int ila_cmp_params(struct ila_map *ila, 628c2ecf20Sopenharmony_ci struct ila_xlat_params *xp) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci return (ila->xp.ifindex != xp->ifindex); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int ila_cmpfn(struct rhashtable_compare_arg *arg, 688c2ecf20Sopenharmony_ci const void *obj) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci const struct ila_map *ila = obj; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return (ila->xp.ip.locator_match.v64 != *(__be64 *)arg->key); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic inline int ila_order(struct ila_map *ila) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci int score = 0; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (ila->xp.ifindex) 808c2ecf20Sopenharmony_ci score += 1 << 1; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return score; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic const struct rhashtable_params rht_params = { 868c2ecf20Sopenharmony_ci .nelem_hint = 1024, 878c2ecf20Sopenharmony_ci .head_offset = offsetof(struct ila_map, node), 888c2ecf20Sopenharmony_ci .key_offset = offsetof(struct ila_map, xp.ip.locator_match), 898c2ecf20Sopenharmony_ci .key_len = sizeof(u64), /* identifier */ 908c2ecf20Sopenharmony_ci .max_size = 1048576, 918c2ecf20Sopenharmony_ci .min_size = 256, 928c2ecf20Sopenharmony_ci .automatic_shrinking = true, 938c2ecf20Sopenharmony_ci .obj_cmpfn = ila_cmpfn, 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int parse_nl_config(struct genl_info *info, 978c2ecf20Sopenharmony_ci struct ila_xlat_params *xp) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci memset(xp, 0, sizeof(*xp)); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (info->attrs[ILA_ATTR_LOCATOR]) 1028c2ecf20Sopenharmony_ci xp->ip.locator.v64 = (__force __be64)nla_get_u64( 1038c2ecf20Sopenharmony_ci info->attrs[ILA_ATTR_LOCATOR]); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (info->attrs[ILA_ATTR_LOCATOR_MATCH]) 1068c2ecf20Sopenharmony_ci xp->ip.locator_match.v64 = (__force __be64)nla_get_u64( 1078c2ecf20Sopenharmony_ci info->attrs[ILA_ATTR_LOCATOR_MATCH]); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (info->attrs[ILA_ATTR_CSUM_MODE]) 1108c2ecf20Sopenharmony_ci xp->ip.csum_mode = nla_get_u8(info->attrs[ILA_ATTR_CSUM_MODE]); 1118c2ecf20Sopenharmony_ci else 1128c2ecf20Sopenharmony_ci xp->ip.csum_mode = ILA_CSUM_NO_ACTION; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (info->attrs[ILA_ATTR_IDENT_TYPE]) 1158c2ecf20Sopenharmony_ci xp->ip.ident_type = nla_get_u8( 1168c2ecf20Sopenharmony_ci info->attrs[ILA_ATTR_IDENT_TYPE]); 1178c2ecf20Sopenharmony_ci else 1188c2ecf20Sopenharmony_ci xp->ip.ident_type = ILA_ATYPE_USE_FORMAT; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (info->attrs[ILA_ATTR_IFINDEX]) 1218c2ecf20Sopenharmony_ci xp->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* Must be called with rcu readlock */ 1278c2ecf20Sopenharmony_cistatic inline struct ila_map *ila_lookup_wildcards(struct ila_addr *iaddr, 1288c2ecf20Sopenharmony_ci int ifindex, 1298c2ecf20Sopenharmony_ci struct ila_net *ilan) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct ila_map *ila; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ila = rhashtable_lookup_fast(&ilan->xlat.rhash_table, &iaddr->loc, 1348c2ecf20Sopenharmony_ci rht_params); 1358c2ecf20Sopenharmony_ci while (ila) { 1368c2ecf20Sopenharmony_ci if (!ila_cmp_wildcards(ila, iaddr, ifindex)) 1378c2ecf20Sopenharmony_ci return ila; 1388c2ecf20Sopenharmony_ci ila = rcu_access_pointer(ila->next); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return NULL; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* Must be called with rcu readlock */ 1458c2ecf20Sopenharmony_cistatic inline struct ila_map *ila_lookup_by_params(struct ila_xlat_params *xp, 1468c2ecf20Sopenharmony_ci struct ila_net *ilan) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct ila_map *ila; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ila = rhashtable_lookup_fast(&ilan->xlat.rhash_table, 1518c2ecf20Sopenharmony_ci &xp->ip.locator_match, 1528c2ecf20Sopenharmony_ci rht_params); 1538c2ecf20Sopenharmony_ci while (ila) { 1548c2ecf20Sopenharmony_ci if (!ila_cmp_params(ila, xp)) 1558c2ecf20Sopenharmony_ci return ila; 1568c2ecf20Sopenharmony_ci ila = rcu_access_pointer(ila->next); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return NULL; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic inline void ila_release(struct ila_map *ila) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci kfree_rcu(ila, rcu); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void ila_free_node(struct ila_map *ila) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct ila_map *next; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Assume rcu_readlock held */ 1728c2ecf20Sopenharmony_ci while (ila) { 1738c2ecf20Sopenharmony_ci next = rcu_access_pointer(ila->next); 1748c2ecf20Sopenharmony_ci ila_release(ila); 1758c2ecf20Sopenharmony_ci ila = next; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void ila_free_cb(void *ptr, void *arg) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci ila_free_node((struct ila_map *)ptr); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int ila_xlat_addr(struct sk_buff *skb, bool sir2ila); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic unsigned int 1878c2ecf20Sopenharmony_ciila_nf_input(void *priv, 1888c2ecf20Sopenharmony_ci struct sk_buff *skb, 1898c2ecf20Sopenharmony_ci const struct nf_hook_state *state) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci ila_xlat_addr(skb, false); 1928c2ecf20Sopenharmony_ci return NF_ACCEPT; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic const struct nf_hook_ops ila_nf_hook_ops[] = { 1968c2ecf20Sopenharmony_ci { 1978c2ecf20Sopenharmony_ci .hook = ila_nf_input, 1988c2ecf20Sopenharmony_ci .pf = NFPROTO_IPV6, 1998c2ecf20Sopenharmony_ci .hooknum = NF_INET_PRE_ROUTING, 2008c2ecf20Sopenharmony_ci .priority = -1, 2018c2ecf20Sopenharmony_ci }, 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int ila_add_mapping(struct net *net, struct ila_xlat_params *xp) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct ila_net *ilan = net_generic(net, ila_net_id); 2078c2ecf20Sopenharmony_ci struct ila_map *ila, *head; 2088c2ecf20Sopenharmony_ci spinlock_t *lock = ila_get_lock(ilan, xp->ip.locator_match); 2098c2ecf20Sopenharmony_ci int err = 0, order; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (!ilan->xlat.hooks_registered) { 2128c2ecf20Sopenharmony_ci /* We defer registering net hooks in the namespace until the 2138c2ecf20Sopenharmony_ci * first mapping is added. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_ci err = nf_register_net_hooks(net, ila_nf_hook_ops, 2168c2ecf20Sopenharmony_ci ARRAY_SIZE(ila_nf_hook_ops)); 2178c2ecf20Sopenharmony_ci if (err) 2188c2ecf20Sopenharmony_ci return err; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ilan->xlat.hooks_registered = true; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci ila = kzalloc(sizeof(*ila), GFP_KERNEL); 2248c2ecf20Sopenharmony_ci if (!ila) 2258c2ecf20Sopenharmony_ci return -ENOMEM; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci ila_init_saved_csum(&xp->ip); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ila->xp = *xp; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci order = ila_order(ila); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci spin_lock(lock); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci head = rhashtable_lookup_fast(&ilan->xlat.rhash_table, 2368c2ecf20Sopenharmony_ci &xp->ip.locator_match, 2378c2ecf20Sopenharmony_ci rht_params); 2388c2ecf20Sopenharmony_ci if (!head) { 2398c2ecf20Sopenharmony_ci /* New entry for the rhash_table */ 2408c2ecf20Sopenharmony_ci err = rhashtable_lookup_insert_fast(&ilan->xlat.rhash_table, 2418c2ecf20Sopenharmony_ci &ila->node, rht_params); 2428c2ecf20Sopenharmony_ci } else { 2438c2ecf20Sopenharmony_ci struct ila_map *tila = head, *prev = NULL; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci do { 2468c2ecf20Sopenharmony_ci if (!ila_cmp_params(tila, xp)) { 2478c2ecf20Sopenharmony_ci err = -EEXIST; 2488c2ecf20Sopenharmony_ci goto out; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (order > ila_order(tila)) 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci prev = tila; 2558c2ecf20Sopenharmony_ci tila = rcu_dereference_protected(tila->next, 2568c2ecf20Sopenharmony_ci lockdep_is_held(lock)); 2578c2ecf20Sopenharmony_ci } while (tila); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (prev) { 2608c2ecf20Sopenharmony_ci /* Insert in sub list of head */ 2618c2ecf20Sopenharmony_ci RCU_INIT_POINTER(ila->next, tila); 2628c2ecf20Sopenharmony_ci rcu_assign_pointer(prev->next, ila); 2638c2ecf20Sopenharmony_ci } else { 2648c2ecf20Sopenharmony_ci /* Make this ila new head */ 2658c2ecf20Sopenharmony_ci RCU_INIT_POINTER(ila->next, head); 2668c2ecf20Sopenharmony_ci err = rhashtable_replace_fast(&ilan->xlat.rhash_table, 2678c2ecf20Sopenharmony_ci &head->node, 2688c2ecf20Sopenharmony_ci &ila->node, rht_params); 2698c2ecf20Sopenharmony_ci if (err) 2708c2ecf20Sopenharmony_ci goto out; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ciout: 2758c2ecf20Sopenharmony_ci spin_unlock(lock); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (err) 2788c2ecf20Sopenharmony_ci kfree(ila); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return err; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int ila_del_mapping(struct net *net, struct ila_xlat_params *xp) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct ila_net *ilan = net_generic(net, ila_net_id); 2868c2ecf20Sopenharmony_ci struct ila_map *ila, *head, *prev; 2878c2ecf20Sopenharmony_ci spinlock_t *lock = ila_get_lock(ilan, xp->ip.locator_match); 2888c2ecf20Sopenharmony_ci int err = -ENOENT; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci spin_lock(lock); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci head = rhashtable_lookup_fast(&ilan->xlat.rhash_table, 2938c2ecf20Sopenharmony_ci &xp->ip.locator_match, rht_params); 2948c2ecf20Sopenharmony_ci ila = head; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci prev = NULL; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci while (ila) { 2998c2ecf20Sopenharmony_ci if (ila_cmp_params(ila, xp)) { 3008c2ecf20Sopenharmony_ci prev = ila; 3018c2ecf20Sopenharmony_ci ila = rcu_dereference_protected(ila->next, 3028c2ecf20Sopenharmony_ci lockdep_is_held(lock)); 3038c2ecf20Sopenharmony_ci continue; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci err = 0; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (prev) { 3098c2ecf20Sopenharmony_ci /* Not head, just delete from list */ 3108c2ecf20Sopenharmony_ci rcu_assign_pointer(prev->next, ila->next); 3118c2ecf20Sopenharmony_ci } else { 3128c2ecf20Sopenharmony_ci /* It is the head. If there is something in the 3138c2ecf20Sopenharmony_ci * sublist we need to make a new head. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci head = rcu_dereference_protected(ila->next, 3168c2ecf20Sopenharmony_ci lockdep_is_held(lock)); 3178c2ecf20Sopenharmony_ci if (head) { 3188c2ecf20Sopenharmony_ci /* Put first entry in the sublist into the 3198c2ecf20Sopenharmony_ci * table 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci err = rhashtable_replace_fast( 3228c2ecf20Sopenharmony_ci &ilan->xlat.rhash_table, &ila->node, 3238c2ecf20Sopenharmony_ci &head->node, rht_params); 3248c2ecf20Sopenharmony_ci if (err) 3258c2ecf20Sopenharmony_ci goto out; 3268c2ecf20Sopenharmony_ci } else { 3278c2ecf20Sopenharmony_ci /* Entry no longer used */ 3288c2ecf20Sopenharmony_ci err = rhashtable_remove_fast( 3298c2ecf20Sopenharmony_ci &ilan->xlat.rhash_table, 3308c2ecf20Sopenharmony_ci &ila->node, rht_params); 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ila_release(ila); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ciout: 3408c2ecf20Sopenharmony_ci spin_unlock(lock); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return err; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ciint ila_xlat_nl_cmd_add_mapping(struct sk_buff *skb, struct genl_info *info) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct net *net = genl_info_net(info); 3488c2ecf20Sopenharmony_ci struct ila_xlat_params p; 3498c2ecf20Sopenharmony_ci int err; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci err = parse_nl_config(info, &p); 3528c2ecf20Sopenharmony_ci if (err) 3538c2ecf20Sopenharmony_ci return err; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return ila_add_mapping(net, &p); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ciint ila_xlat_nl_cmd_del_mapping(struct sk_buff *skb, struct genl_info *info) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct net *net = genl_info_net(info); 3618c2ecf20Sopenharmony_ci struct ila_xlat_params xp; 3628c2ecf20Sopenharmony_ci int err; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci err = parse_nl_config(info, &xp); 3658c2ecf20Sopenharmony_ci if (err) 3668c2ecf20Sopenharmony_ci return err; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ila_del_mapping(net, &xp); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci return 0; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic inline spinlock_t *lock_from_ila_map(struct ila_net *ilan, 3748c2ecf20Sopenharmony_ci struct ila_map *ila) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci return ila_get_lock(ilan, ila->xp.ip.locator_match); 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ciint ila_xlat_nl_cmd_flush(struct sk_buff *skb, struct genl_info *info) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct net *net = genl_info_net(info); 3828c2ecf20Sopenharmony_ci struct ila_net *ilan = net_generic(net, ila_net_id); 3838c2ecf20Sopenharmony_ci struct rhashtable_iter iter; 3848c2ecf20Sopenharmony_ci struct ila_map *ila; 3858c2ecf20Sopenharmony_ci spinlock_t *lock; 3868c2ecf20Sopenharmony_ci int ret = 0; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci rhashtable_walk_enter(&ilan->xlat.rhash_table, &iter); 3898c2ecf20Sopenharmony_ci rhashtable_walk_start(&iter); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci for (;;) { 3928c2ecf20Sopenharmony_ci ila = rhashtable_walk_next(&iter); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (IS_ERR(ila)) { 3958c2ecf20Sopenharmony_ci if (PTR_ERR(ila) == -EAGAIN) 3968c2ecf20Sopenharmony_ci continue; 3978c2ecf20Sopenharmony_ci ret = PTR_ERR(ila); 3988c2ecf20Sopenharmony_ci goto done; 3998c2ecf20Sopenharmony_ci } else if (!ila) { 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci lock = lock_from_ila_map(ilan, ila); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci spin_lock(lock); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci ret = rhashtable_remove_fast(&ilan->xlat.rhash_table, 4088c2ecf20Sopenharmony_ci &ila->node, rht_params); 4098c2ecf20Sopenharmony_ci if (!ret) 4108c2ecf20Sopenharmony_ci ila_free_node(ila); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci spin_unlock(lock); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (ret) 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cidone: 4198c2ecf20Sopenharmony_ci rhashtable_walk_stop(&iter); 4208c2ecf20Sopenharmony_ci rhashtable_walk_exit(&iter); 4218c2ecf20Sopenharmony_ci return ret; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic int ila_fill_info(struct ila_map *ila, struct sk_buff *msg) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci if (nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR, 4278c2ecf20Sopenharmony_ci (__force u64)ila->xp.ip.locator.v64, 4288c2ecf20Sopenharmony_ci ILA_ATTR_PAD) || 4298c2ecf20Sopenharmony_ci nla_put_u64_64bit(msg, ILA_ATTR_LOCATOR_MATCH, 4308c2ecf20Sopenharmony_ci (__force u64)ila->xp.ip.locator_match.v64, 4318c2ecf20Sopenharmony_ci ILA_ATTR_PAD) || 4328c2ecf20Sopenharmony_ci nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->xp.ifindex) || 4338c2ecf20Sopenharmony_ci nla_put_u8(msg, ILA_ATTR_CSUM_MODE, ila->xp.ip.csum_mode) || 4348c2ecf20Sopenharmony_ci nla_put_u8(msg, ILA_ATTR_IDENT_TYPE, ila->xp.ip.ident_type)) 4358c2ecf20Sopenharmony_ci return -1; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic int ila_dump_info(struct ila_map *ila, 4418c2ecf20Sopenharmony_ci u32 portid, u32 seq, u32 flags, 4428c2ecf20Sopenharmony_ci struct sk_buff *skb, u8 cmd) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci void *hdr; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci hdr = genlmsg_put(skb, portid, seq, &ila_nl_family, flags, cmd); 4478c2ecf20Sopenharmony_ci if (!hdr) 4488c2ecf20Sopenharmony_ci return -ENOMEM; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (ila_fill_info(ila, skb) < 0) 4518c2ecf20Sopenharmony_ci goto nla_put_failure; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci genlmsg_end(skb, hdr); 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cinla_put_failure: 4578c2ecf20Sopenharmony_ci genlmsg_cancel(skb, hdr); 4588c2ecf20Sopenharmony_ci return -EMSGSIZE; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ciint ila_xlat_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct net *net = genl_info_net(info); 4648c2ecf20Sopenharmony_ci struct ila_net *ilan = net_generic(net, ila_net_id); 4658c2ecf20Sopenharmony_ci struct sk_buff *msg; 4668c2ecf20Sopenharmony_ci struct ila_xlat_params xp; 4678c2ecf20Sopenharmony_ci struct ila_map *ila; 4688c2ecf20Sopenharmony_ci int ret; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci ret = parse_nl_config(info, &xp); 4718c2ecf20Sopenharmony_ci if (ret) 4728c2ecf20Sopenharmony_ci return ret; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 4758c2ecf20Sopenharmony_ci if (!msg) 4768c2ecf20Sopenharmony_ci return -ENOMEM; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci rcu_read_lock(); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci ret = -ESRCH; 4818c2ecf20Sopenharmony_ci ila = ila_lookup_by_params(&xp, ilan); 4828c2ecf20Sopenharmony_ci if (ila) { 4838c2ecf20Sopenharmony_ci ret = ila_dump_info(ila, 4848c2ecf20Sopenharmony_ci info->snd_portid, 4858c2ecf20Sopenharmony_ci info->snd_seq, 0, msg, 4868c2ecf20Sopenharmony_ci info->genlhdr->cmd); 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci rcu_read_unlock(); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (ret < 0) 4928c2ecf20Sopenharmony_ci goto out_free; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return genlmsg_reply(msg, info); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ciout_free: 4978c2ecf20Sopenharmony_ci nlmsg_free(msg); 4988c2ecf20Sopenharmony_ci return ret; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistruct ila_dump_iter { 5028c2ecf20Sopenharmony_ci struct rhashtable_iter rhiter; 5038c2ecf20Sopenharmony_ci int skip; 5048c2ecf20Sopenharmony_ci}; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ciint ila_xlat_nl_dump_start(struct netlink_callback *cb) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct net *net = sock_net(cb->skb->sk); 5098c2ecf20Sopenharmony_ci struct ila_net *ilan = net_generic(net, ila_net_id); 5108c2ecf20Sopenharmony_ci struct ila_dump_iter *iter; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci iter = kmalloc(sizeof(*iter), GFP_KERNEL); 5138c2ecf20Sopenharmony_ci if (!iter) 5148c2ecf20Sopenharmony_ci return -ENOMEM; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci rhashtable_walk_enter(&ilan->xlat.rhash_table, &iter->rhiter); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci iter->skip = 0; 5198c2ecf20Sopenharmony_ci cb->args[0] = (long)iter; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ciint ila_xlat_nl_dump_done(struct netlink_callback *cb) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args[0]; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci rhashtable_walk_exit(&iter->rhiter); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci kfree(iter); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ciint ila_xlat_nl_dump(struct sk_buff *skb, struct netlink_callback *cb) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args[0]; 5388c2ecf20Sopenharmony_ci struct rhashtable_iter *rhiter = &iter->rhiter; 5398c2ecf20Sopenharmony_ci int skip = iter->skip; 5408c2ecf20Sopenharmony_ci struct ila_map *ila; 5418c2ecf20Sopenharmony_ci int ret; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci rhashtable_walk_start(rhiter); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* Get first entry */ 5468c2ecf20Sopenharmony_ci ila = rhashtable_walk_peek(rhiter); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (ila && !IS_ERR(ila) && skip) { 5498c2ecf20Sopenharmony_ci /* Skip over visited entries */ 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci while (ila && skip) { 5528c2ecf20Sopenharmony_ci /* Skip over any ila entries in this list that we 5538c2ecf20Sopenharmony_ci * have already dumped. 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_ci ila = rcu_access_pointer(ila->next); 5568c2ecf20Sopenharmony_ci skip--; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci skip = 0; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci for (;;) { 5638c2ecf20Sopenharmony_ci if (IS_ERR(ila)) { 5648c2ecf20Sopenharmony_ci ret = PTR_ERR(ila); 5658c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 5668c2ecf20Sopenharmony_ci /* Table has changed and iter has reset. Return 5678c2ecf20Sopenharmony_ci * -EAGAIN to the application even if we have 5688c2ecf20Sopenharmony_ci * written data to the skb. The application 5698c2ecf20Sopenharmony_ci * needs to deal with this. 5708c2ecf20Sopenharmony_ci */ 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci goto out_ret; 5738c2ecf20Sopenharmony_ci } else { 5748c2ecf20Sopenharmony_ci break; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci } else if (!ila) { 5778c2ecf20Sopenharmony_ci ret = 0; 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci while (ila) { 5828c2ecf20Sopenharmony_ci ret = ila_dump_info(ila, NETLINK_CB(cb->skb).portid, 5838c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 5848c2ecf20Sopenharmony_ci skb, ILA_CMD_GET); 5858c2ecf20Sopenharmony_ci if (ret) 5868c2ecf20Sopenharmony_ci goto out; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci skip++; 5898c2ecf20Sopenharmony_ci ila = rcu_access_pointer(ila->next); 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci skip = 0; 5938c2ecf20Sopenharmony_ci ila = rhashtable_walk_next(rhiter); 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ciout: 5978c2ecf20Sopenharmony_ci iter->skip = skip; 5988c2ecf20Sopenharmony_ci ret = (skb->len ? : ret); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ciout_ret: 6018c2ecf20Sopenharmony_ci rhashtable_walk_stop(rhiter); 6028c2ecf20Sopenharmony_ci return ret; 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ciint ila_xlat_init_net(struct net *net) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct ila_net *ilan = net_generic(net, ila_net_id); 6088c2ecf20Sopenharmony_ci int err; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci err = alloc_ila_locks(ilan); 6118c2ecf20Sopenharmony_ci if (err) 6128c2ecf20Sopenharmony_ci return err; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci rhashtable_init(&ilan->xlat.rhash_table, &rht_params); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci return 0; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_civoid ila_xlat_exit_net(struct net *net) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct ila_net *ilan = net_generic(net, ila_net_id); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci rhashtable_free_and_destroy(&ilan->xlat.rhash_table, ila_free_cb, NULL); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci free_bucket_spinlocks(ilan->xlat.locks); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (ilan->xlat.hooks_registered) 6288c2ecf20Sopenharmony_ci nf_unregister_net_hooks(net, ila_nf_hook_ops, 6298c2ecf20Sopenharmony_ci ARRAY_SIZE(ila_nf_hook_ops)); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic int ila_xlat_addr(struct sk_buff *skb, bool sir2ila) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct ila_map *ila; 6358c2ecf20Sopenharmony_ci struct ipv6hdr *ip6h = ipv6_hdr(skb); 6368c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 6378c2ecf20Sopenharmony_ci struct ila_net *ilan = net_generic(net, ila_net_id); 6388c2ecf20Sopenharmony_ci struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* Assumes skb contains a valid IPv6 header that is pulled */ 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* No check here that ILA type in the mapping matches what is in the 6438c2ecf20Sopenharmony_ci * address. We assume that whatever sender gaves us can be translated. 6448c2ecf20Sopenharmony_ci * The checksum mode however is relevant. 6458c2ecf20Sopenharmony_ci */ 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci rcu_read_lock(); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci ila = ila_lookup_wildcards(iaddr, skb->dev->ifindex, ilan); 6508c2ecf20Sopenharmony_ci if (ila) 6518c2ecf20Sopenharmony_ci ila_update_ipv6_locator(skb, &ila->xp.ip, sir2ila); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci rcu_read_unlock(); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci return 0; 6568c2ecf20Sopenharmony_ci} 657