18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2013 secunet Security Networks AG 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: 78c2ecf20Sopenharmony_ci * Steffen Klassert <steffen.klassert@secunet.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on: 108c2ecf20Sopenharmony_ci * net/ipv4/xfrm4_protocol.c 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 168c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 178c2ecf20Sopenharmony_ci#include <net/ip6_route.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_protocol __rcu *esp6_handlers __read_mostly; 238c2ecf20Sopenharmony_cistatic struct xfrm6_protocol __rcu *ah6_handlers __read_mostly; 248c2ecf20Sopenharmony_cistatic struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly; 258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(xfrm6_protocol_mutex); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci switch (protocol) { 308c2ecf20Sopenharmony_ci case IPPROTO_ESP: 318c2ecf20Sopenharmony_ci return &esp6_handlers; 328c2ecf20Sopenharmony_ci case IPPROTO_AH: 338c2ecf20Sopenharmony_ci return &ah6_handlers; 348c2ecf20Sopenharmony_ci case IPPROTO_COMP: 358c2ecf20Sopenharmony_ci return &ipcomp6_handlers; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return NULL; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define for_each_protocol_rcu(head, handler) \ 428c2ecf20Sopenharmony_ci for (handler = rcu_dereference(head); \ 438c2ecf20Sopenharmony_ci handler != NULL; \ 448c2ecf20Sopenharmony_ci handler = rcu_dereference(handler->next)) \ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci int ret; 498c2ecf20Sopenharmony_ci struct xfrm6_protocol *handler; 508c2ecf20Sopenharmony_ci struct xfrm6_protocol __rcu **head = proto_handlers(protocol); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (!head) 538c2ecf20Sopenharmony_ci return 0; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci for_each_protocol_rcu(*proto_handlers(protocol), handler) 568c2ecf20Sopenharmony_ci if ((ret = handler->cb_handler(skb, err)) <= 0) 578c2ecf20Sopenharmony_ci return ret; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ciint xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, 638c2ecf20Sopenharmony_ci int encap_type) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci int ret; 668c2ecf20Sopenharmony_ci struct xfrm6_protocol *handler; 678c2ecf20Sopenharmony_ci struct xfrm6_protocol __rcu **head = proto_handlers(nexthdr); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 708c2ecf20Sopenharmony_ci XFRM_SPI_SKB_CB(skb)->family = AF_INET6; 718c2ecf20Sopenharmony_ci XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (!head) 748c2ecf20Sopenharmony_ci goto out; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (!skb_dst(skb)) { 778c2ecf20Sopenharmony_ci const struct ipv6hdr *ip6h = ipv6_hdr(skb); 788c2ecf20Sopenharmony_ci int flags = RT6_LOOKUP_F_HAS_SADDR; 798c2ecf20Sopenharmony_ci struct dst_entry *dst; 808c2ecf20Sopenharmony_ci struct flowi6 fl6 = { 818c2ecf20Sopenharmony_ci .flowi6_iif = skb->dev->ifindex, 828c2ecf20Sopenharmony_ci .daddr = ip6h->daddr, 838c2ecf20Sopenharmony_ci .saddr = ip6h->saddr, 848c2ecf20Sopenharmony_ci .flowlabel = ip6_flowinfo(ip6h), 858c2ecf20Sopenharmony_ci .flowi6_mark = skb->mark, 868c2ecf20Sopenharmony_ci .flowi6_proto = ip6h->nexthdr, 878c2ecf20Sopenharmony_ci }; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6, 908c2ecf20Sopenharmony_ci skb, flags); 918c2ecf20Sopenharmony_ci if (dst->error) 928c2ecf20Sopenharmony_ci goto drop; 938c2ecf20Sopenharmony_ci skb_dst_set(skb, dst); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci for_each_protocol_rcu(*head, handler) 978c2ecf20Sopenharmony_ci if ((ret = handler->input_handler(skb, nexthdr, spi, encap_type)) != -EINVAL) 988c2ecf20Sopenharmony_ci return ret; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ciout: 1018c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cidrop: 1048c2ecf20Sopenharmony_ci kfree_skb(skb); 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm6_rcv_encap); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int xfrm6_esp_rcv(struct sk_buff *skb) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci int ret; 1128c2ecf20Sopenharmony_ci struct xfrm6_protocol *handler; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci for_each_protocol_rcu(esp6_handlers, handler) 1178c2ecf20Sopenharmony_ci if ((ret = handler->handler(skb)) != -EINVAL) 1188c2ecf20Sopenharmony_ci return ret; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci kfree_skb(skb); 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 1278c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct xfrm6_protocol *handler; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci for_each_protocol_rcu(esp6_handlers, handler) 1328c2ecf20Sopenharmony_ci if (!handler->err_handler(skb, opt, type, code, offset, info)) 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return -ENOENT; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int xfrm6_ah_rcv(struct sk_buff *skb) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int ret; 1418c2ecf20Sopenharmony_ci struct xfrm6_protocol *handler; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci for_each_protocol_rcu(ah6_handlers, handler) 1468c2ecf20Sopenharmony_ci if ((ret = handler->handler(skb)) != -EINVAL) 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci kfree_skb(skb); 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 1568c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct xfrm6_protocol *handler; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci for_each_protocol_rcu(ah6_handlers, handler) 1618c2ecf20Sopenharmony_ci if (!handler->err_handler(skb, opt, type, code, offset, info)) 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return -ENOENT; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int xfrm6_ipcomp_rcv(struct sk_buff *skb) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int ret; 1708c2ecf20Sopenharmony_ci struct xfrm6_protocol *handler; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci for_each_protocol_rcu(ipcomp6_handlers, handler) 1758c2ecf20Sopenharmony_ci if ((ret = handler->handler(skb)) != -EINVAL) 1768c2ecf20Sopenharmony_ci return ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci kfree_skb(skb); 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 1858c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct xfrm6_protocol *handler; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci for_each_protocol_rcu(ipcomp6_handlers, handler) 1908c2ecf20Sopenharmony_ci if (!handler->err_handler(skb, opt, type, code, offset, info)) 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return -ENOENT; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic const struct inet6_protocol esp6_protocol = { 1978c2ecf20Sopenharmony_ci .handler = xfrm6_esp_rcv, 1988c2ecf20Sopenharmony_ci .err_handler = xfrm6_esp_err, 1998c2ecf20Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY, 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic const struct inet6_protocol ah6_protocol = { 2038c2ecf20Sopenharmony_ci .handler = xfrm6_ah_rcv, 2048c2ecf20Sopenharmony_ci .err_handler = xfrm6_ah_err, 2058c2ecf20Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY, 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic const struct inet6_protocol ipcomp6_protocol = { 2098c2ecf20Sopenharmony_ci .handler = xfrm6_ipcomp_rcv, 2108c2ecf20Sopenharmony_ci .err_handler = xfrm6_ipcomp_err, 2118c2ecf20Sopenharmony_ci .flags = INET6_PROTO_NOPOLICY, 2128c2ecf20Sopenharmony_ci}; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic const struct xfrm_input_afinfo xfrm6_input_afinfo = { 2158c2ecf20Sopenharmony_ci .family = AF_INET6, 2168c2ecf20Sopenharmony_ci .callback = xfrm6_rcv_cb, 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic inline const struct inet6_protocol *netproto(unsigned char protocol) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci switch (protocol) { 2228c2ecf20Sopenharmony_ci case IPPROTO_ESP: 2238c2ecf20Sopenharmony_ci return &esp6_protocol; 2248c2ecf20Sopenharmony_ci case IPPROTO_AH: 2258c2ecf20Sopenharmony_ci return &ah6_protocol; 2268c2ecf20Sopenharmony_ci case IPPROTO_COMP: 2278c2ecf20Sopenharmony_ci return &ipcomp6_protocol; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return NULL; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ciint xfrm6_protocol_register(struct xfrm6_protocol *handler, 2348c2ecf20Sopenharmony_ci unsigned char protocol) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct xfrm6_protocol __rcu **pprev; 2378c2ecf20Sopenharmony_ci struct xfrm6_protocol *t; 2388c2ecf20Sopenharmony_ci bool add_netproto = false; 2398c2ecf20Sopenharmony_ci int ret = -EEXIST; 2408c2ecf20Sopenharmony_ci int priority = handler->priority; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (!proto_handlers(protocol) || !netproto(protocol)) 2438c2ecf20Sopenharmony_ci return -EINVAL; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci mutex_lock(&xfrm6_protocol_mutex); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (!rcu_dereference_protected(*proto_handlers(protocol), 2488c2ecf20Sopenharmony_ci lockdep_is_held(&xfrm6_protocol_mutex))) 2498c2ecf20Sopenharmony_ci add_netproto = true; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci for (pprev = proto_handlers(protocol); 2528c2ecf20Sopenharmony_ci (t = rcu_dereference_protected(*pprev, 2538c2ecf20Sopenharmony_ci lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; 2548c2ecf20Sopenharmony_ci pprev = &t->next) { 2558c2ecf20Sopenharmony_ci if (t->priority < priority) 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci if (t->priority == priority) 2588c2ecf20Sopenharmony_ci goto err; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci handler->next = *pprev; 2628c2ecf20Sopenharmony_ci rcu_assign_pointer(*pprev, handler); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci ret = 0; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cierr: 2678c2ecf20Sopenharmony_ci mutex_unlock(&xfrm6_protocol_mutex); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (add_netproto) { 2708c2ecf20Sopenharmony_ci if (inet6_add_protocol(netproto(protocol), protocol)) { 2718c2ecf20Sopenharmony_ci pr_err("%s: can't add protocol\n", __func__); 2728c2ecf20Sopenharmony_ci ret = -EAGAIN; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm6_protocol_register); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ciint xfrm6_protocol_deregister(struct xfrm6_protocol *handler, 2818c2ecf20Sopenharmony_ci unsigned char protocol) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct xfrm6_protocol __rcu **pprev; 2848c2ecf20Sopenharmony_ci struct xfrm6_protocol *t; 2858c2ecf20Sopenharmony_ci int ret = -ENOENT; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (!proto_handlers(protocol) || !netproto(protocol)) 2888c2ecf20Sopenharmony_ci return -EINVAL; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci mutex_lock(&xfrm6_protocol_mutex); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci for (pprev = proto_handlers(protocol); 2938c2ecf20Sopenharmony_ci (t = rcu_dereference_protected(*pprev, 2948c2ecf20Sopenharmony_ci lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; 2958c2ecf20Sopenharmony_ci pprev = &t->next) { 2968c2ecf20Sopenharmony_ci if (t == handler) { 2978c2ecf20Sopenharmony_ci *pprev = handler->next; 2988c2ecf20Sopenharmony_ci ret = 0; 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (!rcu_dereference_protected(*proto_handlers(protocol), 3048c2ecf20Sopenharmony_ci lockdep_is_held(&xfrm6_protocol_mutex))) { 3058c2ecf20Sopenharmony_ci if (inet6_del_protocol(netproto(protocol), protocol) < 0) { 3068c2ecf20Sopenharmony_ci pr_err("%s: can't remove protocol\n", __func__); 3078c2ecf20Sopenharmony_ci ret = -EAGAIN; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci mutex_unlock(&xfrm6_protocol_mutex); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci synchronize_net(); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return ret; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xfrm6_protocol_deregister); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ciint __init xfrm6_protocol_init(void) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci return xfrm_input_register_afinfo(&xfrm6_input_afinfo); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_civoid xfrm6_protocol_fini(void) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci xfrm_input_unregister_afinfo(&xfrm6_input_afinfo); 3278c2ecf20Sopenharmony_ci} 328