12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27e14ea15SSteffen Klassert /* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6.
37e14ea15SSteffen Klassert *
47e14ea15SSteffen Klassert * Copyright (C) 2013 secunet Security Networks AG
57e14ea15SSteffen Klassert *
67e14ea15SSteffen Klassert * Author:
77e14ea15SSteffen Klassert * Steffen Klassert <steffen.klassert@secunet.com>
87e14ea15SSteffen Klassert *
97e14ea15SSteffen Klassert * Based on:
107e14ea15SSteffen Klassert * net/ipv4/xfrm4_protocol.c
117e14ea15SSteffen Klassert */
127e14ea15SSteffen Klassert
137e14ea15SSteffen Klassert #include <linux/init.h>
147e14ea15SSteffen Klassert #include <linux/mutex.h>
157e14ea15SSteffen Klassert #include <linux/skbuff.h>
167e14ea15SSteffen Klassert #include <linux/icmpv6.h>
17*0146dca7SSabrina Dubroca #include <net/ip6_route.h>
187e14ea15SSteffen Klassert #include <net/ipv6.h>
197e14ea15SSteffen Klassert #include <net/protocol.h>
207e14ea15SSteffen Klassert #include <net/xfrm.h>
217e14ea15SSteffen Klassert
227e14ea15SSteffen Klassert static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly;
237e14ea15SSteffen Klassert static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly;
247e14ea15SSteffen Klassert static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly;
257e14ea15SSteffen Klassert static DEFINE_MUTEX(xfrm6_protocol_mutex);
267e14ea15SSteffen Klassert
proto_handlers(u8 protocol)277e14ea15SSteffen Klassert static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol)
287e14ea15SSteffen Klassert {
297e14ea15SSteffen Klassert switch (protocol) {
307e14ea15SSteffen Klassert case IPPROTO_ESP:
317e14ea15SSteffen Klassert return &esp6_handlers;
327e14ea15SSteffen Klassert case IPPROTO_AH:
337e14ea15SSteffen Klassert return &ah6_handlers;
347e14ea15SSteffen Klassert case IPPROTO_COMP:
357e14ea15SSteffen Klassert return &ipcomp6_handlers;
367e14ea15SSteffen Klassert }
377e14ea15SSteffen Klassert
387e14ea15SSteffen Klassert return NULL;
397e14ea15SSteffen Klassert }
407e14ea15SSteffen Klassert
417e14ea15SSteffen Klassert #define for_each_protocol_rcu(head, handler) \
427e14ea15SSteffen Klassert for (handler = rcu_dereference(head); \
437e14ea15SSteffen Klassert handler != NULL; \
447e14ea15SSteffen Klassert handler = rcu_dereference(handler->next)) \
457e14ea15SSteffen Klassert
xfrm6_rcv_cb(struct sk_buff * skb,u8 protocol,int err)46bb9cd077SFlorian Westphal static int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err)
477e14ea15SSteffen Klassert {
487e14ea15SSteffen Klassert int ret;
497e14ea15SSteffen Klassert struct xfrm6_protocol *handler;
50edb666f0SSteffen Klassert struct xfrm6_protocol __rcu **head = proto_handlers(protocol);
51edb666f0SSteffen Klassert
52edb666f0SSteffen Klassert if (!head)
53edb666f0SSteffen Klassert return 0;
547e14ea15SSteffen Klassert
557e14ea15SSteffen Klassert for_each_protocol_rcu(*proto_handlers(protocol), handler)
567e14ea15SSteffen Klassert if ((ret = handler->cb_handler(skb, err)) <= 0)
577e14ea15SSteffen Klassert return ret;
587e14ea15SSteffen Klassert
597e14ea15SSteffen Klassert return 0;
607e14ea15SSteffen Klassert }
617e14ea15SSteffen Klassert
xfrm6_rcv_encap(struct sk_buff * skb,int nexthdr,__be32 spi,int encap_type)62*0146dca7SSabrina Dubroca int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
63*0146dca7SSabrina Dubroca int encap_type)
64*0146dca7SSabrina Dubroca {
65*0146dca7SSabrina Dubroca int ret;
66*0146dca7SSabrina Dubroca struct xfrm6_protocol *handler;
67*0146dca7SSabrina Dubroca struct xfrm6_protocol __rcu **head = proto_handlers(nexthdr);
68*0146dca7SSabrina Dubroca
69*0146dca7SSabrina Dubroca XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
70*0146dca7SSabrina Dubroca XFRM_SPI_SKB_CB(skb)->family = AF_INET6;
71*0146dca7SSabrina Dubroca XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr);
72*0146dca7SSabrina Dubroca
73*0146dca7SSabrina Dubroca if (!head)
74*0146dca7SSabrina Dubroca goto out;
75*0146dca7SSabrina Dubroca
76*0146dca7SSabrina Dubroca if (!skb_dst(skb)) {
77*0146dca7SSabrina Dubroca const struct ipv6hdr *ip6h = ipv6_hdr(skb);
78*0146dca7SSabrina Dubroca int flags = RT6_LOOKUP_F_HAS_SADDR;
79*0146dca7SSabrina Dubroca struct dst_entry *dst;
80*0146dca7SSabrina Dubroca struct flowi6 fl6 = {
81*0146dca7SSabrina Dubroca .flowi6_iif = skb->dev->ifindex,
82*0146dca7SSabrina Dubroca .daddr = ip6h->daddr,
83*0146dca7SSabrina Dubroca .saddr = ip6h->saddr,
84*0146dca7SSabrina Dubroca .flowlabel = ip6_flowinfo(ip6h),
85*0146dca7SSabrina Dubroca .flowi6_mark = skb->mark,
86*0146dca7SSabrina Dubroca .flowi6_proto = ip6h->nexthdr,
87*0146dca7SSabrina Dubroca };
88*0146dca7SSabrina Dubroca
89*0146dca7SSabrina Dubroca dst = ip6_route_input_lookup(dev_net(skb->dev), skb->dev, &fl6,
90*0146dca7SSabrina Dubroca skb, flags);
91*0146dca7SSabrina Dubroca if (dst->error)
92*0146dca7SSabrina Dubroca goto drop;
93*0146dca7SSabrina Dubroca skb_dst_set(skb, dst);
94*0146dca7SSabrina Dubroca }
95*0146dca7SSabrina Dubroca
96*0146dca7SSabrina Dubroca for_each_protocol_rcu(*head, handler)
97*0146dca7SSabrina Dubroca if ((ret = handler->input_handler(skb, nexthdr, spi, encap_type)) != -EINVAL)
98*0146dca7SSabrina Dubroca return ret;
99*0146dca7SSabrina Dubroca
100*0146dca7SSabrina Dubroca out:
101*0146dca7SSabrina Dubroca icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
102*0146dca7SSabrina Dubroca
103*0146dca7SSabrina Dubroca drop:
104*0146dca7SSabrina Dubroca kfree_skb(skb);
105*0146dca7SSabrina Dubroca return 0;
106*0146dca7SSabrina Dubroca }
107*0146dca7SSabrina Dubroca EXPORT_SYMBOL(xfrm6_rcv_encap);
108*0146dca7SSabrina Dubroca
xfrm6_esp_rcv(struct sk_buff * skb)1097e14ea15SSteffen Klassert static int xfrm6_esp_rcv(struct sk_buff *skb)
1107e14ea15SSteffen Klassert {
1117e14ea15SSteffen Klassert int ret;
1127e14ea15SSteffen Klassert struct xfrm6_protocol *handler;
1137e14ea15SSteffen Klassert
1147e14ea15SSteffen Klassert XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
1157e14ea15SSteffen Klassert
1167e14ea15SSteffen Klassert for_each_protocol_rcu(esp6_handlers, handler)
1177e14ea15SSteffen Klassert if ((ret = handler->handler(skb)) != -EINVAL)
1187e14ea15SSteffen Klassert return ret;
1197e14ea15SSteffen Klassert
1207e14ea15SSteffen Klassert icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
1217e14ea15SSteffen Klassert
1227e14ea15SSteffen Klassert kfree_skb(skb);
1237e14ea15SSteffen Klassert return 0;
1247e14ea15SSteffen Klassert }
1257e14ea15SSteffen Klassert
xfrm6_esp_err(struct sk_buff * skb,struct inet6_skb_parm * opt,u8 type,u8 code,int offset,__be32 info)12632bbd879SStefano Brivio static int xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
1277e14ea15SSteffen Klassert u8 type, u8 code, int offset, __be32 info)
1287e14ea15SSteffen Klassert {
1297e14ea15SSteffen Klassert struct xfrm6_protocol *handler;
1307e14ea15SSteffen Klassert
1317e14ea15SSteffen Klassert for_each_protocol_rcu(esp6_handlers, handler)
1327e14ea15SSteffen Klassert if (!handler->err_handler(skb, opt, type, code, offset, info))
13332bbd879SStefano Brivio return 0;
13432bbd879SStefano Brivio
13532bbd879SStefano Brivio return -ENOENT;
1367e14ea15SSteffen Klassert }
1377e14ea15SSteffen Klassert
xfrm6_ah_rcv(struct sk_buff * skb)1387e14ea15SSteffen Klassert static int xfrm6_ah_rcv(struct sk_buff *skb)
1397e14ea15SSteffen Klassert {
1407e14ea15SSteffen Klassert int ret;
1417e14ea15SSteffen Klassert struct xfrm6_protocol *handler;
1427e14ea15SSteffen Klassert
1437e14ea15SSteffen Klassert XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
1447e14ea15SSteffen Klassert
1457e14ea15SSteffen Klassert for_each_protocol_rcu(ah6_handlers, handler)
1467e14ea15SSteffen Klassert if ((ret = handler->handler(skb)) != -EINVAL)
1477e14ea15SSteffen Klassert return ret;
1487e14ea15SSteffen Klassert
1497e14ea15SSteffen Klassert icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
1507e14ea15SSteffen Klassert
1517e14ea15SSteffen Klassert kfree_skb(skb);
1527e14ea15SSteffen Klassert return 0;
1537e14ea15SSteffen Klassert }
1547e14ea15SSteffen Klassert
xfrm6_ah_err(struct sk_buff * skb,struct inet6_skb_parm * opt,u8 type,u8 code,int offset,__be32 info)15532bbd879SStefano Brivio static int xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
1567e14ea15SSteffen Klassert u8 type, u8 code, int offset, __be32 info)
1577e14ea15SSteffen Klassert {
1587e14ea15SSteffen Klassert struct xfrm6_protocol *handler;
1597e14ea15SSteffen Klassert
1607e14ea15SSteffen Klassert for_each_protocol_rcu(ah6_handlers, handler)
1617e14ea15SSteffen Klassert if (!handler->err_handler(skb, opt, type, code, offset, info))
16232bbd879SStefano Brivio return 0;
16332bbd879SStefano Brivio
16432bbd879SStefano Brivio return -ENOENT;
1657e14ea15SSteffen Klassert }
1667e14ea15SSteffen Klassert
xfrm6_ipcomp_rcv(struct sk_buff * skb)1677e14ea15SSteffen Klassert static int xfrm6_ipcomp_rcv(struct sk_buff *skb)
1687e14ea15SSteffen Klassert {
1697e14ea15SSteffen Klassert int ret;
1707e14ea15SSteffen Klassert struct xfrm6_protocol *handler;
1717e14ea15SSteffen Klassert
1727e14ea15SSteffen Klassert XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
1737e14ea15SSteffen Klassert
1747e14ea15SSteffen Klassert for_each_protocol_rcu(ipcomp6_handlers, handler)
1757e14ea15SSteffen Klassert if ((ret = handler->handler(skb)) != -EINVAL)
1767e14ea15SSteffen Klassert return ret;
1777e14ea15SSteffen Klassert
1787e14ea15SSteffen Klassert icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
1797e14ea15SSteffen Klassert
1807e14ea15SSteffen Klassert kfree_skb(skb);
1817e14ea15SSteffen Klassert return 0;
1827e14ea15SSteffen Klassert }
1837e14ea15SSteffen Klassert
xfrm6_ipcomp_err(struct sk_buff * skb,struct inet6_skb_parm * opt,u8 type,u8 code,int offset,__be32 info)18432bbd879SStefano Brivio static int xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
1857e14ea15SSteffen Klassert u8 type, u8 code, int offset, __be32 info)
1867e14ea15SSteffen Klassert {
1877e14ea15SSteffen Klassert struct xfrm6_protocol *handler;
1887e14ea15SSteffen Klassert
1897e14ea15SSteffen Klassert for_each_protocol_rcu(ipcomp6_handlers, handler)
1907e14ea15SSteffen Klassert if (!handler->err_handler(skb, opt, type, code, offset, info))
19132bbd879SStefano Brivio return 0;
19232bbd879SStefano Brivio
19332bbd879SStefano Brivio return -ENOENT;
1947e14ea15SSteffen Klassert }
1957e14ea15SSteffen Klassert
1967e14ea15SSteffen Klassert static const struct inet6_protocol esp6_protocol = {
1977e14ea15SSteffen Klassert .handler = xfrm6_esp_rcv,
1987e14ea15SSteffen Klassert .err_handler = xfrm6_esp_err,
1997e14ea15SSteffen Klassert .flags = INET6_PROTO_NOPOLICY,
2007e14ea15SSteffen Klassert };
2017e14ea15SSteffen Klassert
2027e14ea15SSteffen Klassert static const struct inet6_protocol ah6_protocol = {
2037e14ea15SSteffen Klassert .handler = xfrm6_ah_rcv,
2047e14ea15SSteffen Klassert .err_handler = xfrm6_ah_err,
2057e14ea15SSteffen Klassert .flags = INET6_PROTO_NOPOLICY,
2067e14ea15SSteffen Klassert };
2077e14ea15SSteffen Klassert
2087e14ea15SSteffen Klassert static const struct inet6_protocol ipcomp6_protocol = {
2097e14ea15SSteffen Klassert .handler = xfrm6_ipcomp_rcv,
2107e14ea15SSteffen Klassert .err_handler = xfrm6_ipcomp_err,
2117e14ea15SSteffen Klassert .flags = INET6_PROTO_NOPOLICY,
2127e14ea15SSteffen Klassert };
2137e14ea15SSteffen Klassert
214960fdfdeSFlorian Westphal static const struct xfrm_input_afinfo xfrm6_input_afinfo = {
2157e14ea15SSteffen Klassert .family = AF_INET6,
2167e14ea15SSteffen Klassert .callback = xfrm6_rcv_cb,
2177e14ea15SSteffen Klassert };
2187e14ea15SSteffen Klassert
netproto(unsigned char protocol)2197e14ea15SSteffen Klassert static inline const struct inet6_protocol *netproto(unsigned char protocol)
2207e14ea15SSteffen Klassert {
2217e14ea15SSteffen Klassert switch (protocol) {
2227e14ea15SSteffen Klassert case IPPROTO_ESP:
2237e14ea15SSteffen Klassert return &esp6_protocol;
2247e14ea15SSteffen Klassert case IPPROTO_AH:
2257e14ea15SSteffen Klassert return &ah6_protocol;
2267e14ea15SSteffen Klassert case IPPROTO_COMP:
2277e14ea15SSteffen Klassert return &ipcomp6_protocol;
2287e14ea15SSteffen Klassert }
2297e14ea15SSteffen Klassert
2307e14ea15SSteffen Klassert return NULL;
2317e14ea15SSteffen Klassert }
2327e14ea15SSteffen Klassert
xfrm6_protocol_register(struct xfrm6_protocol * handler,unsigned char protocol)2337e14ea15SSteffen Klassert int xfrm6_protocol_register(struct xfrm6_protocol *handler,
2347e14ea15SSteffen Klassert unsigned char protocol)
2357e14ea15SSteffen Klassert {
2367e14ea15SSteffen Klassert struct xfrm6_protocol __rcu **pprev;
2377e14ea15SSteffen Klassert struct xfrm6_protocol *t;
2387e14ea15SSteffen Klassert bool add_netproto = false;
2397e14ea15SSteffen Klassert int ret = -EEXIST;
2407e14ea15SSteffen Klassert int priority = handler->priority;
2417e14ea15SSteffen Klassert
242edb666f0SSteffen Klassert if (!proto_handlers(protocol) || !netproto(protocol))
243edb666f0SSteffen Klassert return -EINVAL;
244edb666f0SSteffen Klassert
2457e14ea15SSteffen Klassert mutex_lock(&xfrm6_protocol_mutex);
2467e14ea15SSteffen Klassert
2477e14ea15SSteffen Klassert if (!rcu_dereference_protected(*proto_handlers(protocol),
2487e14ea15SSteffen Klassert lockdep_is_held(&xfrm6_protocol_mutex)))
2497e14ea15SSteffen Klassert add_netproto = true;
2507e14ea15SSteffen Klassert
2517e14ea15SSteffen Klassert for (pprev = proto_handlers(protocol);
2527e14ea15SSteffen Klassert (t = rcu_dereference_protected(*pprev,
2537e14ea15SSteffen Klassert lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
2547e14ea15SSteffen Klassert pprev = &t->next) {
2557e14ea15SSteffen Klassert if (t->priority < priority)
2567e14ea15SSteffen Klassert break;
2577e14ea15SSteffen Klassert if (t->priority == priority)
2587e14ea15SSteffen Klassert goto err;
2597e14ea15SSteffen Klassert }
2607e14ea15SSteffen Klassert
2617e14ea15SSteffen Klassert handler->next = *pprev;
2627e14ea15SSteffen Klassert rcu_assign_pointer(*pprev, handler);
2637e14ea15SSteffen Klassert
2647e14ea15SSteffen Klassert ret = 0;
2657e14ea15SSteffen Klassert
2667e14ea15SSteffen Klassert err:
2677e14ea15SSteffen Klassert mutex_unlock(&xfrm6_protocol_mutex);
2687e14ea15SSteffen Klassert
2697e14ea15SSteffen Klassert if (add_netproto) {
2707e14ea15SSteffen Klassert if (inet6_add_protocol(netproto(protocol), protocol)) {
2717e14ea15SSteffen Klassert pr_err("%s: can't add protocol\n", __func__);
2727e14ea15SSteffen Klassert ret = -EAGAIN;
2737e14ea15SSteffen Klassert }
2747e14ea15SSteffen Klassert }
2757e14ea15SSteffen Klassert
2767e14ea15SSteffen Klassert return ret;
2777e14ea15SSteffen Klassert }
2787e14ea15SSteffen Klassert EXPORT_SYMBOL(xfrm6_protocol_register);
2797e14ea15SSteffen Klassert
xfrm6_protocol_deregister(struct xfrm6_protocol * handler,unsigned char protocol)2807e14ea15SSteffen Klassert int xfrm6_protocol_deregister(struct xfrm6_protocol *handler,
2817e14ea15SSteffen Klassert unsigned char protocol)
2827e14ea15SSteffen Klassert {
2837e14ea15SSteffen Klassert struct xfrm6_protocol __rcu **pprev;
2847e14ea15SSteffen Klassert struct xfrm6_protocol *t;
2857e14ea15SSteffen Klassert int ret = -ENOENT;
2867e14ea15SSteffen Klassert
287edb666f0SSteffen Klassert if (!proto_handlers(protocol) || !netproto(protocol))
288edb666f0SSteffen Klassert return -EINVAL;
289edb666f0SSteffen Klassert
2907e14ea15SSteffen Klassert mutex_lock(&xfrm6_protocol_mutex);
2917e14ea15SSteffen Klassert
2927e14ea15SSteffen Klassert for (pprev = proto_handlers(protocol);
2937e14ea15SSteffen Klassert (t = rcu_dereference_protected(*pprev,
2947e14ea15SSteffen Klassert lockdep_is_held(&xfrm6_protocol_mutex))) != NULL;
2957e14ea15SSteffen Klassert pprev = &t->next) {
2967e14ea15SSteffen Klassert if (t == handler) {
2977e14ea15SSteffen Klassert *pprev = handler->next;
2987e14ea15SSteffen Klassert ret = 0;
2997e14ea15SSteffen Klassert break;
3007e14ea15SSteffen Klassert }
3017e14ea15SSteffen Klassert }
3027e14ea15SSteffen Klassert
3037e14ea15SSteffen Klassert if (!rcu_dereference_protected(*proto_handlers(protocol),
3047e14ea15SSteffen Klassert lockdep_is_held(&xfrm6_protocol_mutex))) {
3057e14ea15SSteffen Klassert if (inet6_del_protocol(netproto(protocol), protocol) < 0) {
3067e14ea15SSteffen Klassert pr_err("%s: can't remove protocol\n", __func__);
3077e14ea15SSteffen Klassert ret = -EAGAIN;
3087e14ea15SSteffen Klassert }
3097e14ea15SSteffen Klassert }
3107e14ea15SSteffen Klassert
3117e14ea15SSteffen Klassert mutex_unlock(&xfrm6_protocol_mutex);
3127e14ea15SSteffen Klassert
3137e14ea15SSteffen Klassert synchronize_net();
3147e14ea15SSteffen Klassert
3157e14ea15SSteffen Klassert return ret;
3167e14ea15SSteffen Klassert }
3177e14ea15SSteffen Klassert EXPORT_SYMBOL(xfrm6_protocol_deregister);
3187e14ea15SSteffen Klassert
xfrm6_protocol_init(void)3197e14ea15SSteffen Klassert int __init xfrm6_protocol_init(void)
3207e14ea15SSteffen Klassert {
3217e14ea15SSteffen Klassert return xfrm_input_register_afinfo(&xfrm6_input_afinfo);
3227e14ea15SSteffen Klassert }
3237e14ea15SSteffen Klassert
xfrm6_protocol_fini(void)3247e14ea15SSteffen Klassert void xfrm6_protocol_fini(void)
3257e14ea15SSteffen Klassert {
3267e14ea15SSteffen Klassert xfrm_input_unregister_afinfo(&xfrm6_input_afinfo);
3277e14ea15SSteffen Klassert }
328