18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C)2003,2004 USAGI/WIDE Project 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors Mitsuru KANDA <mk@linux-ipv6.org> 68c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <net/ipv6.h> 198c2ecf20Sopenharmony_ci#include <net/protocol.h> 208c2ecf20Sopenharmony_ci#include <net/xfrm.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic struct xfrm6_tunnel __rcu *tunnel6_handlers __read_mostly; 238c2ecf20Sopenharmony_cistatic struct xfrm6_tunnel __rcu *tunnel46_handlers __read_mostly; 248c2ecf20Sopenharmony_cistatic struct xfrm6_tunnel __rcu *tunnelmpls6_handlers __read_mostly; 258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(tunnel6_mutex); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic inline int xfrm6_tunnel_mpls_supported(void) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci return IS_ENABLED(CONFIG_MPLS); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciint xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct xfrm6_tunnel __rcu **pprev; 358c2ecf20Sopenharmony_ci struct xfrm6_tunnel *t; 368c2ecf20Sopenharmony_ci int ret = -EEXIST; 378c2ecf20Sopenharmony_ci int priority = handler->priority; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci mutex_lock(&tunnel6_mutex); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci switch (family) { 428c2ecf20Sopenharmony_ci case AF_INET6: 438c2ecf20Sopenharmony_ci pprev = &tunnel6_handlers; 448c2ecf20Sopenharmony_ci break; 458c2ecf20Sopenharmony_ci case AF_INET: 468c2ecf20Sopenharmony_ci pprev = &tunnel46_handlers; 478c2ecf20Sopenharmony_ci break; 488c2ecf20Sopenharmony_ci case AF_MPLS: 498c2ecf20Sopenharmony_ci pprev = &tunnelmpls6_handlers; 508c2ecf20Sopenharmony_ci break; 518c2ecf20Sopenharmony_ci default: 528c2ecf20Sopenharmony_ci goto err; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci for (; (t = rcu_dereference_protected(*pprev, 568c2ecf20Sopenharmony_ci lockdep_is_held(&tunnel6_mutex))) != NULL; 578c2ecf20Sopenharmony_ci pprev = &t->next) { 588c2ecf20Sopenharmony_ci if (t->priority > priority) 598c2ecf20Sopenharmony_ci break; 608c2ecf20Sopenharmony_ci if (t->priority == priority) 618c2ecf20Sopenharmony_ci goto err; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci handler->next = *pprev; 658c2ecf20Sopenharmony_ci rcu_assign_pointer(*pprev, handler); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci ret = 0; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cierr: 708c2ecf20Sopenharmony_ci mutex_unlock(&tunnel6_mutex); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return ret; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm6_tunnel_register); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciint xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct xfrm6_tunnel __rcu **pprev; 798c2ecf20Sopenharmony_ci struct xfrm6_tunnel *t; 808c2ecf20Sopenharmony_ci int ret = -ENOENT; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci mutex_lock(&tunnel6_mutex); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci switch (family) { 858c2ecf20Sopenharmony_ci case AF_INET6: 868c2ecf20Sopenharmony_ci pprev = &tunnel6_handlers; 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci case AF_INET: 898c2ecf20Sopenharmony_ci pprev = &tunnel46_handlers; 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci case AF_MPLS: 928c2ecf20Sopenharmony_ci pprev = &tunnelmpls6_handlers; 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci default: 958c2ecf20Sopenharmony_ci goto err; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci for (; (t = rcu_dereference_protected(*pprev, 998c2ecf20Sopenharmony_ci lockdep_is_held(&tunnel6_mutex))) != NULL; 1008c2ecf20Sopenharmony_ci pprev = &t->next) { 1018c2ecf20Sopenharmony_ci if (t == handler) { 1028c2ecf20Sopenharmony_ci *pprev = handler->next; 1038c2ecf20Sopenharmony_ci ret = 0; 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cierr: 1098c2ecf20Sopenharmony_ci mutex_unlock(&tunnel6_mutex); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci synchronize_net(); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm6_tunnel_deregister); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define for_each_tunnel_rcu(head, handler) \ 1188c2ecf20Sopenharmony_ci for (handler = rcu_dereference(head); \ 1198c2ecf20Sopenharmony_ci handler != NULL; \ 1208c2ecf20Sopenharmony_ci handler = rcu_dereference(handler->next)) \ 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int tunnelmpls6_rcv(struct sk_buff *skb) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct xfrm6_tunnel *handler; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 1278c2ecf20Sopenharmony_ci goto drop; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci for_each_tunnel_rcu(tunnelmpls6_handlers, handler) 1308c2ecf20Sopenharmony_ci if (!handler->handler(skb)) 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cidrop: 1368c2ecf20Sopenharmony_ci kfree_skb(skb); 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int tunnel6_rcv(struct sk_buff *skb) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct xfrm6_tunnel *handler; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 1458c2ecf20Sopenharmony_ci goto drop; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci for_each_tunnel_rcu(tunnel6_handlers, handler) 1488c2ecf20Sopenharmony_ci if (!handler->handler(skb)) 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cidrop: 1548c2ecf20Sopenharmony_ci kfree_skb(skb); 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INET6_XFRM_TUNNEL) 1598c2ecf20Sopenharmony_cistatic int tunnel6_rcv_cb(struct sk_buff *skb, u8 proto, int err) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct xfrm6_tunnel __rcu *head; 1628c2ecf20Sopenharmony_ci struct xfrm6_tunnel *handler; 1638c2ecf20Sopenharmony_ci int ret; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci head = (proto == IPPROTO_IPV6) ? tunnel6_handlers : tunnel46_handlers; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci for_each_tunnel_rcu(head, handler) { 1688c2ecf20Sopenharmony_ci if (handler->cb_handler) { 1698c2ecf20Sopenharmony_ci ret = handler->cb_handler(skb, err); 1708c2ecf20Sopenharmony_ci if (ret <= 0) 1718c2ecf20Sopenharmony_ci return ret; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic const struct xfrm_input_afinfo tunnel6_input_afinfo = { 1798c2ecf20Sopenharmony_ci .family = AF_INET6, 1808c2ecf20Sopenharmony_ci .is_ipip = true, 1818c2ecf20Sopenharmony_ci .callback = tunnel6_rcv_cb, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci#endif 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int tunnel46_rcv(struct sk_buff *skb) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct xfrm6_tunnel *handler; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct iphdr))) 1908c2ecf20Sopenharmony_ci goto drop; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci for_each_tunnel_rcu(tunnel46_handlers, handler) 1938c2ecf20Sopenharmony_ci if (!handler->handler(skb)) 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cidrop: 1998c2ecf20Sopenharmony_ci kfree_skb(skb); 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int tunnel6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 2048c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct xfrm6_tunnel *handler; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci for_each_tunnel_rcu(tunnel6_handlers, handler) 2098c2ecf20Sopenharmony_ci if (!handler->err_handler(skb, opt, type, code, offset, info)) 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return -ENOENT; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int tunnel46_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 2168c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct xfrm6_tunnel *handler; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci for_each_tunnel_rcu(tunnel46_handlers, handler) 2218c2ecf20Sopenharmony_ci if (!handler->err_handler(skb, opt, type, code, offset, info)) 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return -ENOENT; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int tunnelmpls6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 2288c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct xfrm6_tunnel *handler; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci for_each_tunnel_rcu(tunnelmpls6_handlers, handler) 2338c2ecf20Sopenharmony_ci if (!handler->err_handler(skb, opt, type, code, offset, info)) 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return -ENOENT; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic const struct inet6_protocol tunnel6_protocol = { 2408c2ecf20Sopenharmony_ci .handler = tunnel6_rcv, 2418c2ecf20Sopenharmony_ci .err_handler = tunnel6_err, 2428c2ecf20Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic const struct inet6_protocol tunnel46_protocol = { 2468c2ecf20Sopenharmony_ci .handler = tunnel46_rcv, 2478c2ecf20Sopenharmony_ci .err_handler = tunnel46_err, 2488c2ecf20Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic const struct inet6_protocol tunnelmpls6_protocol = { 2528c2ecf20Sopenharmony_ci .handler = tunnelmpls6_rcv, 2538c2ecf20Sopenharmony_ci .err_handler = tunnelmpls6_err, 2548c2ecf20Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int __init tunnel6_init(void) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci if (inet6_add_protocol(&tunnel6_protocol, IPPROTO_IPV6)) { 2608c2ecf20Sopenharmony_ci pr_err("%s: can't add protocol\n", __func__); 2618c2ecf20Sopenharmony_ci return -EAGAIN; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci if (inet6_add_protocol(&tunnel46_protocol, IPPROTO_IPIP)) { 2648c2ecf20Sopenharmony_ci pr_err("%s: can't add protocol\n", __func__); 2658c2ecf20Sopenharmony_ci inet6_del_protocol(&tunnel6_protocol, IPPROTO_IPV6); 2668c2ecf20Sopenharmony_ci return -EAGAIN; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci if (xfrm6_tunnel_mpls_supported() && 2698c2ecf20Sopenharmony_ci inet6_add_protocol(&tunnelmpls6_protocol, IPPROTO_MPLS)) { 2708c2ecf20Sopenharmony_ci pr_err("%s: can't add protocol\n", __func__); 2718c2ecf20Sopenharmony_ci inet6_del_protocol(&tunnel6_protocol, IPPROTO_IPV6); 2728c2ecf20Sopenharmony_ci inet6_del_protocol(&tunnel46_protocol, IPPROTO_IPIP); 2738c2ecf20Sopenharmony_ci return -EAGAIN; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INET6_XFRM_TUNNEL) 2768c2ecf20Sopenharmony_ci if (xfrm_input_register_afinfo(&tunnel6_input_afinfo)) { 2778c2ecf20Sopenharmony_ci pr_err("%s: can't add input afinfo\n", __func__); 2788c2ecf20Sopenharmony_ci inet6_del_protocol(&tunnel6_protocol, IPPROTO_IPV6); 2798c2ecf20Sopenharmony_ci inet6_del_protocol(&tunnel46_protocol, IPPROTO_IPIP); 2808c2ecf20Sopenharmony_ci if (xfrm6_tunnel_mpls_supported()) 2818c2ecf20Sopenharmony_ci inet6_del_protocol(&tunnelmpls6_protocol, IPPROTO_MPLS); 2828c2ecf20Sopenharmony_ci return -EAGAIN; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci#endif 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic void __exit tunnel6_fini(void) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INET6_XFRM_TUNNEL) 2918c2ecf20Sopenharmony_ci if (xfrm_input_unregister_afinfo(&tunnel6_input_afinfo)) 2928c2ecf20Sopenharmony_ci pr_err("%s: can't remove input afinfo\n", __func__); 2938c2ecf20Sopenharmony_ci#endif 2948c2ecf20Sopenharmony_ci if (inet6_del_protocol(&tunnel46_protocol, IPPROTO_IPIP)) 2958c2ecf20Sopenharmony_ci pr_err("%s: can't remove protocol\n", __func__); 2968c2ecf20Sopenharmony_ci if (inet6_del_protocol(&tunnel6_protocol, IPPROTO_IPV6)) 2978c2ecf20Sopenharmony_ci pr_err("%s: can't remove protocol\n", __func__); 2988c2ecf20Sopenharmony_ci if (xfrm6_tunnel_mpls_supported() && 2998c2ecf20Sopenharmony_ci inet6_del_protocol(&tunnelmpls6_protocol, IPPROTO_MPLS)) 3008c2ecf20Sopenharmony_ci pr_err("%s: can't remove protocol\n", __func__); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cimodule_init(tunnel6_init); 3048c2ecf20Sopenharmony_cimodule_exit(tunnel6_fini); 3058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 306