18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SR-IPv6 implementation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: 68c2ecf20Sopenharmony_ci * David Lebrun <david.lebrun@uclouvain.be> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/socket.h> 128c2ecf20Sopenharmony_ci#include <linux/net.h> 138c2ecf20Sopenharmony_ci#include <linux/in6.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/rhashtable.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <net/ipv6.h> 188c2ecf20Sopenharmony_ci#include <net/protocol.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <net/seg6.h> 218c2ecf20Sopenharmony_ci#include <net/genetlink.h> 228c2ecf20Sopenharmony_ci#include <linux/seg6.h> 238c2ecf20Sopenharmony_ci#include <linux/seg6_genl.h> 248c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 258c2ecf20Sopenharmony_ci#include <net/seg6_hmac.h> 268c2ecf20Sopenharmony_ci#endif 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cibool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci unsigned int tlv_offset; 318c2ecf20Sopenharmony_ci int max_last_entry; 328c2ecf20Sopenharmony_ci int trailing; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (srh->type != IPV6_SRCRT_TYPE_4) 358c2ecf20Sopenharmony_ci return false; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (((srh->hdrlen + 1) << 3) != len) 388c2ecf20Sopenharmony_ci return false; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (!reduced && srh->segments_left > srh->first_segment) { 418c2ecf20Sopenharmony_ci return false; 428c2ecf20Sopenharmony_ci } else { 438c2ecf20Sopenharmony_ci max_last_entry = (srh->hdrlen / 2) - 1; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (srh->first_segment > max_last_entry) 468c2ecf20Sopenharmony_ci return false; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (srh->segments_left > srh->first_segment + 1) 498c2ecf20Sopenharmony_ci return false; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci trailing = len - tlv_offset; 558c2ecf20Sopenharmony_ci if (trailing < 0) 568c2ecf20Sopenharmony_ci return false; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci while (trailing) { 598c2ecf20Sopenharmony_ci struct sr6_tlv *tlv; 608c2ecf20Sopenharmony_ci unsigned int tlv_len; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (trailing < sizeof(*tlv)) 638c2ecf20Sopenharmony_ci return false; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci tlv = (struct sr6_tlv *)((unsigned char *)srh + tlv_offset); 668c2ecf20Sopenharmony_ci tlv_len = sizeof(*tlv) + tlv->len; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci trailing -= tlv_len; 698c2ecf20Sopenharmony_ci if (trailing < 0) 708c2ecf20Sopenharmony_ci return false; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci tlv_offset += tlv_len; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return true; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic struct genl_family seg6_genl_family; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = { 818c2ecf20Sopenharmony_ci [SEG6_ATTR_DST] = { .type = NLA_BINARY, 828c2ecf20Sopenharmony_ci .len = sizeof(struct in6_addr) }, 838c2ecf20Sopenharmony_ci [SEG6_ATTR_DSTLEN] = { .type = NLA_S32, }, 848c2ecf20Sopenharmony_ci [SEG6_ATTR_HMACKEYID] = { .type = NLA_U32, }, 858c2ecf20Sopenharmony_ci [SEG6_ATTR_SECRET] = { .type = NLA_BINARY, }, 868c2ecf20Sopenharmony_ci [SEG6_ATTR_SECRETLEN] = { .type = NLA_U8, }, 878c2ecf20Sopenharmony_ci [SEG6_ATTR_ALGID] = { .type = NLA_U8, }, 888c2ecf20Sopenharmony_ci [SEG6_ATTR_HMACINFO] = { .type = NLA_NESTED, }, 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct net *net = genl_info_net(info); 968c2ecf20Sopenharmony_ci struct seg6_pernet_data *sdata; 978c2ecf20Sopenharmony_ci struct seg6_hmac_info *hinfo; 988c2ecf20Sopenharmony_ci u32 hmackeyid; 998c2ecf20Sopenharmony_ci char *secret; 1008c2ecf20Sopenharmony_ci int err = 0; 1018c2ecf20Sopenharmony_ci u8 algid; 1028c2ecf20Sopenharmony_ci u8 slen; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci sdata = seg6_pernet(net); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!info->attrs[SEG6_ATTR_HMACKEYID] || 1078c2ecf20Sopenharmony_ci !info->attrs[SEG6_ATTR_SECRETLEN] || 1088c2ecf20Sopenharmony_ci !info->attrs[SEG6_ATTR_ALGID]) 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci hmackeyid = nla_get_u32(info->attrs[SEG6_ATTR_HMACKEYID]); 1128c2ecf20Sopenharmony_ci slen = nla_get_u8(info->attrs[SEG6_ATTR_SECRETLEN]); 1138c2ecf20Sopenharmony_ci algid = nla_get_u8(info->attrs[SEG6_ATTR_ALGID]); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (hmackeyid == 0) 1168c2ecf20Sopenharmony_ci return -EINVAL; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (slen > SEG6_HMAC_SECRET_LEN) 1198c2ecf20Sopenharmony_ci return -EINVAL; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci mutex_lock(&sdata->lock); 1228c2ecf20Sopenharmony_ci hinfo = seg6_hmac_info_lookup(net, hmackeyid); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (!slen) { 1258c2ecf20Sopenharmony_ci if (!hinfo) 1268c2ecf20Sopenharmony_ci err = -ENOENT; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci err = seg6_hmac_info_del(net, hmackeyid); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci goto out_unlock; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (!info->attrs[SEG6_ATTR_SECRET]) { 1348c2ecf20Sopenharmony_ci err = -EINVAL; 1358c2ecf20Sopenharmony_ci goto out_unlock; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (slen > nla_len(info->attrs[SEG6_ATTR_SECRET])) { 1398c2ecf20Sopenharmony_ci err = -EINVAL; 1408c2ecf20Sopenharmony_ci goto out_unlock; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (hinfo) { 1448c2ecf20Sopenharmony_ci err = seg6_hmac_info_del(net, hmackeyid); 1458c2ecf20Sopenharmony_ci if (err) 1468c2ecf20Sopenharmony_ci goto out_unlock; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci secret = (char *)nla_data(info->attrs[SEG6_ATTR_SECRET]); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci hinfo = kzalloc(sizeof(*hinfo), GFP_KERNEL); 1528c2ecf20Sopenharmony_ci if (!hinfo) { 1538c2ecf20Sopenharmony_ci err = -ENOMEM; 1548c2ecf20Sopenharmony_ci goto out_unlock; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci memcpy(hinfo->secret, secret, slen); 1588c2ecf20Sopenharmony_ci hinfo->slen = slen; 1598c2ecf20Sopenharmony_ci hinfo->alg_id = algid; 1608c2ecf20Sopenharmony_ci hinfo->hmackeyid = hmackeyid; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci err = seg6_hmac_info_add(net, hmackeyid, hinfo); 1638c2ecf20Sopenharmony_ci if (err) 1648c2ecf20Sopenharmony_ci kfree(hinfo); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ciout_unlock: 1678c2ecf20Sopenharmony_ci mutex_unlock(&sdata->lock); 1688c2ecf20Sopenharmony_ci return err; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci#else 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci return -ENOTSUPP; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci#endif 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct net *net = genl_info_net(info); 1838c2ecf20Sopenharmony_ci struct in6_addr *val, *t_old, *t_new; 1848c2ecf20Sopenharmony_ci struct seg6_pernet_data *sdata; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci sdata = seg6_pernet(net); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (!info->attrs[SEG6_ATTR_DST]) 1898c2ecf20Sopenharmony_ci return -EINVAL; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci val = nla_data(info->attrs[SEG6_ATTR_DST]); 1928c2ecf20Sopenharmony_ci t_new = kmemdup(val, sizeof(*val), GFP_KERNEL); 1938c2ecf20Sopenharmony_ci if (!t_new) 1948c2ecf20Sopenharmony_ci return -ENOMEM; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci mutex_lock(&sdata->lock); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci t_old = sdata->tun_src; 1998c2ecf20Sopenharmony_ci rcu_assign_pointer(sdata->tun_src, t_new); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci mutex_unlock(&sdata->lock); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci synchronize_net(); 2048c2ecf20Sopenharmony_ci kfree(t_old); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int seg6_genl_get_tunsrc(struct sk_buff *skb, struct genl_info *info) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct net *net = genl_info_net(info); 2128c2ecf20Sopenharmony_ci struct in6_addr *tun_src; 2138c2ecf20Sopenharmony_ci struct sk_buff *msg; 2148c2ecf20Sopenharmony_ci void *hdr; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci msg = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 2178c2ecf20Sopenharmony_ci if (!msg) 2188c2ecf20Sopenharmony_ci return -ENOMEM; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, 2218c2ecf20Sopenharmony_ci &seg6_genl_family, 0, SEG6_CMD_GET_TUNSRC); 2228c2ecf20Sopenharmony_ci if (!hdr) 2238c2ecf20Sopenharmony_ci goto free_msg; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci rcu_read_lock(); 2268c2ecf20Sopenharmony_ci tun_src = rcu_dereference(seg6_pernet(net)->tun_src); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (nla_put(msg, SEG6_ATTR_DST, sizeof(struct in6_addr), tun_src)) 2298c2ecf20Sopenharmony_ci goto nla_put_failure; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci rcu_read_unlock(); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci genlmsg_end(msg, hdr); 2348c2ecf20Sopenharmony_ci return genlmsg_reply(msg, info); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cinla_put_failure: 2378c2ecf20Sopenharmony_ci rcu_read_unlock(); 2388c2ecf20Sopenharmony_cifree_msg: 2398c2ecf20Sopenharmony_ci nlmsg_free(msg); 2408c2ecf20Sopenharmony_ci return -ENOMEM; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int __seg6_hmac_fill_info(struct seg6_hmac_info *hinfo, 2468c2ecf20Sopenharmony_ci struct sk_buff *msg) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci if (nla_put_u32(msg, SEG6_ATTR_HMACKEYID, hinfo->hmackeyid) || 2498c2ecf20Sopenharmony_ci nla_put_u8(msg, SEG6_ATTR_SECRETLEN, hinfo->slen) || 2508c2ecf20Sopenharmony_ci nla_put(msg, SEG6_ATTR_SECRET, hinfo->slen, hinfo->secret) || 2518c2ecf20Sopenharmony_ci nla_put_u8(msg, SEG6_ATTR_ALGID, hinfo->alg_id)) 2528c2ecf20Sopenharmony_ci return -1; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int __seg6_genl_dumphmac_element(struct seg6_hmac_info *hinfo, 2588c2ecf20Sopenharmony_ci u32 portid, u32 seq, u32 flags, 2598c2ecf20Sopenharmony_ci struct sk_buff *skb, u8 cmd) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci void *hdr; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci hdr = genlmsg_put(skb, portid, seq, &seg6_genl_family, flags, cmd); 2648c2ecf20Sopenharmony_ci if (!hdr) 2658c2ecf20Sopenharmony_ci return -ENOMEM; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (__seg6_hmac_fill_info(hinfo, skb) < 0) 2688c2ecf20Sopenharmony_ci goto nla_put_failure; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci genlmsg_end(skb, hdr); 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cinla_put_failure: 2748c2ecf20Sopenharmony_ci genlmsg_cancel(skb, hdr); 2758c2ecf20Sopenharmony_ci return -EMSGSIZE; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int seg6_genl_dumphmac_start(struct netlink_callback *cb) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct net *net = sock_net(cb->skb->sk); 2818c2ecf20Sopenharmony_ci struct seg6_pernet_data *sdata; 2828c2ecf20Sopenharmony_ci struct rhashtable_iter *iter; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci sdata = seg6_pernet(net); 2858c2ecf20Sopenharmony_ci iter = (struct rhashtable_iter *)cb->args[0]; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (!iter) { 2888c2ecf20Sopenharmony_ci iter = kmalloc(sizeof(*iter), GFP_KERNEL); 2898c2ecf20Sopenharmony_ci if (!iter) 2908c2ecf20Sopenharmony_ci return -ENOMEM; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci cb->args[0] = (long)iter; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci rhashtable_walk_enter(&sdata->hmac_infos, iter); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic int seg6_genl_dumphmac_done(struct netlink_callback *cb) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci rhashtable_walk_exit(iter); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci kfree(iter); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; 3148c2ecf20Sopenharmony_ci struct seg6_hmac_info *hinfo; 3158c2ecf20Sopenharmony_ci int ret; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci rhashtable_walk_start(iter); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci for (;;) { 3208c2ecf20Sopenharmony_ci hinfo = rhashtable_walk_next(iter); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (IS_ERR(hinfo)) { 3238c2ecf20Sopenharmony_ci if (PTR_ERR(hinfo) == -EAGAIN) 3248c2ecf20Sopenharmony_ci continue; 3258c2ecf20Sopenharmony_ci ret = PTR_ERR(hinfo); 3268c2ecf20Sopenharmony_ci goto done; 3278c2ecf20Sopenharmony_ci } else if (!hinfo) { 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci ret = __seg6_genl_dumphmac_element(hinfo, 3328c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 3338c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, 3348c2ecf20Sopenharmony_ci NLM_F_MULTI, 3358c2ecf20Sopenharmony_ci skb, SEG6_CMD_DUMPHMAC); 3368c2ecf20Sopenharmony_ci if (ret) 3378c2ecf20Sopenharmony_ci goto done; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci ret = skb->len; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cidone: 3438c2ecf20Sopenharmony_ci rhashtable_walk_stop(iter); 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci#else 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int seg6_genl_dumphmac_start(struct netlink_callback *cb) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int seg6_genl_dumphmac_done(struct netlink_callback *cb) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci return -ENOTSUPP; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci#endif 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int __net_init seg6_net_init(struct net *net) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct seg6_pernet_data *sdata; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci sdata = kzalloc(sizeof(*sdata), GFP_KERNEL); 3718c2ecf20Sopenharmony_ci if (!sdata) 3728c2ecf20Sopenharmony_ci return -ENOMEM; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci mutex_init(&sdata->lock); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci sdata->tun_src = kzalloc(sizeof(*sdata->tun_src), GFP_KERNEL); 3778c2ecf20Sopenharmony_ci if (!sdata->tun_src) { 3788c2ecf20Sopenharmony_ci kfree(sdata); 3798c2ecf20Sopenharmony_ci return -ENOMEM; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci net->ipv6.seg6_data = sdata; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 3858c2ecf20Sopenharmony_ci seg6_hmac_net_init(net); 3868c2ecf20Sopenharmony_ci#endif 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic void __net_exit seg6_net_exit(struct net *net) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct seg6_pernet_data *sdata = seg6_pernet(net); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 3968c2ecf20Sopenharmony_ci seg6_hmac_net_exit(net); 3978c2ecf20Sopenharmony_ci#endif 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci kfree(sdata->tun_src); 4008c2ecf20Sopenharmony_ci kfree(sdata); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic struct pernet_operations ip6_segments_ops = { 4048c2ecf20Sopenharmony_ci .init = seg6_net_init, 4058c2ecf20Sopenharmony_ci .exit = seg6_net_exit, 4068c2ecf20Sopenharmony_ci}; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic const struct genl_ops seg6_genl_ops[] = { 4098c2ecf20Sopenharmony_ci { 4108c2ecf20Sopenharmony_ci .cmd = SEG6_CMD_SETHMAC, 4118c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 4128c2ecf20Sopenharmony_ci .doit = seg6_genl_sethmac, 4138c2ecf20Sopenharmony_ci .flags = GENL_ADMIN_PERM, 4148c2ecf20Sopenharmony_ci }, 4158c2ecf20Sopenharmony_ci { 4168c2ecf20Sopenharmony_ci .cmd = SEG6_CMD_DUMPHMAC, 4178c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 4188c2ecf20Sopenharmony_ci .start = seg6_genl_dumphmac_start, 4198c2ecf20Sopenharmony_ci .dumpit = seg6_genl_dumphmac, 4208c2ecf20Sopenharmony_ci .done = seg6_genl_dumphmac_done, 4218c2ecf20Sopenharmony_ci .flags = GENL_ADMIN_PERM, 4228c2ecf20Sopenharmony_ci }, 4238c2ecf20Sopenharmony_ci { 4248c2ecf20Sopenharmony_ci .cmd = SEG6_CMD_SET_TUNSRC, 4258c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 4268c2ecf20Sopenharmony_ci .doit = seg6_genl_set_tunsrc, 4278c2ecf20Sopenharmony_ci .flags = GENL_ADMIN_PERM, 4288c2ecf20Sopenharmony_ci }, 4298c2ecf20Sopenharmony_ci { 4308c2ecf20Sopenharmony_ci .cmd = SEG6_CMD_GET_TUNSRC, 4318c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 4328c2ecf20Sopenharmony_ci .doit = seg6_genl_get_tunsrc, 4338c2ecf20Sopenharmony_ci .flags = GENL_ADMIN_PERM, 4348c2ecf20Sopenharmony_ci }, 4358c2ecf20Sopenharmony_ci}; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic struct genl_family seg6_genl_family __ro_after_init = { 4388c2ecf20Sopenharmony_ci .hdrsize = 0, 4398c2ecf20Sopenharmony_ci .name = SEG6_GENL_NAME, 4408c2ecf20Sopenharmony_ci .version = SEG6_GENL_VERSION, 4418c2ecf20Sopenharmony_ci .maxattr = SEG6_ATTR_MAX, 4428c2ecf20Sopenharmony_ci .policy = seg6_genl_policy, 4438c2ecf20Sopenharmony_ci .netnsok = true, 4448c2ecf20Sopenharmony_ci .parallel_ops = true, 4458c2ecf20Sopenharmony_ci .ops = seg6_genl_ops, 4468c2ecf20Sopenharmony_ci .n_ops = ARRAY_SIZE(seg6_genl_ops), 4478c2ecf20Sopenharmony_ci .module = THIS_MODULE, 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ciint __init seg6_init(void) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci int err; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci err = register_pernet_subsys(&ip6_segments_ops); 4558c2ecf20Sopenharmony_ci if (err) 4568c2ecf20Sopenharmony_ci goto out; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci err = genl_register_family(&seg6_genl_family); 4598c2ecf20Sopenharmony_ci if (err) 4608c2ecf20Sopenharmony_ci goto out_unregister_pernet; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_LWTUNNEL 4638c2ecf20Sopenharmony_ci err = seg6_iptunnel_init(); 4648c2ecf20Sopenharmony_ci if (err) 4658c2ecf20Sopenharmony_ci goto out_unregister_genl; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci err = seg6_local_init(); 4688c2ecf20Sopenharmony_ci if (err) { 4698c2ecf20Sopenharmony_ci seg6_iptunnel_exit(); 4708c2ecf20Sopenharmony_ci goto out_unregister_genl; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci#endif 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 4758c2ecf20Sopenharmony_ci err = seg6_hmac_init(); 4768c2ecf20Sopenharmony_ci if (err) 4778c2ecf20Sopenharmony_ci goto out_unregister_iptun; 4788c2ecf20Sopenharmony_ci#endif 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci pr_info("Segment Routing with IPv6\n"); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ciout: 4838c2ecf20Sopenharmony_ci return err; 4848c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 4858c2ecf20Sopenharmony_ciout_unregister_iptun: 4868c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_LWTUNNEL 4878c2ecf20Sopenharmony_ci seg6_local_exit(); 4888c2ecf20Sopenharmony_ci seg6_iptunnel_exit(); 4898c2ecf20Sopenharmony_ci#endif 4908c2ecf20Sopenharmony_ci#endif 4918c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_LWTUNNEL 4928c2ecf20Sopenharmony_ciout_unregister_genl: 4938c2ecf20Sopenharmony_ci genl_unregister_family(&seg6_genl_family); 4948c2ecf20Sopenharmony_ci#endif 4958c2ecf20Sopenharmony_ciout_unregister_pernet: 4968c2ecf20Sopenharmony_ci unregister_pernet_subsys(&ip6_segments_ops); 4978c2ecf20Sopenharmony_ci goto out; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_civoid seg6_exit(void) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 5038c2ecf20Sopenharmony_ci seg6_hmac_exit(); 5048c2ecf20Sopenharmony_ci#endif 5058c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_LWTUNNEL 5068c2ecf20Sopenharmony_ci seg6_iptunnel_exit(); 5078c2ecf20Sopenharmony_ci#endif 5088c2ecf20Sopenharmony_ci unregister_pernet_subsys(&ip6_segments_ops); 5098c2ecf20Sopenharmony_ci genl_unregister_family(&seg6_genl_family); 5108c2ecf20Sopenharmony_ci} 511