17e14ea15SSteffen Klassert /* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6. 27e14ea15SSteffen Klassert * 37e14ea15SSteffen Klassert * Copyright (C) 2013 secunet Security Networks AG 47e14ea15SSteffen Klassert * 57e14ea15SSteffen Klassert * Author: 67e14ea15SSteffen Klassert * Steffen Klassert <steffen.klassert@secunet.com> 77e14ea15SSteffen Klassert * 87e14ea15SSteffen Klassert * Based on: 97e14ea15SSteffen Klassert * net/ipv4/xfrm4_protocol.c 107e14ea15SSteffen Klassert * 117e14ea15SSteffen Klassert * This program is free software; you can redistribute it and/or 127e14ea15SSteffen Klassert * modify it under the terms of the GNU General Public License 137e14ea15SSteffen Klassert * as published by the Free Software Foundation; either version 147e14ea15SSteffen Klassert * 2 of the License, or (at your option) any later version. 157e14ea15SSteffen Klassert */ 167e14ea15SSteffen Klassert 177e14ea15SSteffen Klassert #include <linux/init.h> 187e14ea15SSteffen Klassert #include <linux/mutex.h> 197e14ea15SSteffen Klassert #include <linux/skbuff.h> 207e14ea15SSteffen Klassert #include <linux/icmpv6.h> 217e14ea15SSteffen Klassert #include <net/ipv6.h> 227e14ea15SSteffen Klassert #include <net/protocol.h> 237e14ea15SSteffen Klassert #include <net/xfrm.h> 247e14ea15SSteffen Klassert 257e14ea15SSteffen Klassert static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly; 267e14ea15SSteffen Klassert static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly; 277e14ea15SSteffen Klassert static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly; 287e14ea15SSteffen Klassert static DEFINE_MUTEX(xfrm6_protocol_mutex); 297e14ea15SSteffen Klassert 307e14ea15SSteffen Klassert static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol) 317e14ea15SSteffen Klassert { 327e14ea15SSteffen Klassert switch (protocol) { 337e14ea15SSteffen Klassert case IPPROTO_ESP: 347e14ea15SSteffen Klassert return &esp6_handlers; 357e14ea15SSteffen Klassert case IPPROTO_AH: 367e14ea15SSteffen Klassert return &ah6_handlers; 377e14ea15SSteffen Klassert case IPPROTO_COMP: 387e14ea15SSteffen Klassert return &ipcomp6_handlers; 397e14ea15SSteffen Klassert } 407e14ea15SSteffen Klassert 417e14ea15SSteffen Klassert return NULL; 427e14ea15SSteffen Klassert } 437e14ea15SSteffen Klassert 447e14ea15SSteffen Klassert #define for_each_protocol_rcu(head, handler) \ 457e14ea15SSteffen Klassert for (handler = rcu_dereference(head); \ 467e14ea15SSteffen Klassert handler != NULL; \ 477e14ea15SSteffen Klassert handler = rcu_dereference(handler->next)) \ 487e14ea15SSteffen Klassert 497e14ea15SSteffen Klassert int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err) 507e14ea15SSteffen Klassert { 517e14ea15SSteffen Klassert int ret; 527e14ea15SSteffen Klassert struct xfrm6_protocol *handler; 53edb666f0SSteffen Klassert struct xfrm6_protocol __rcu **head = proto_handlers(protocol); 54edb666f0SSteffen Klassert 55edb666f0SSteffen Klassert if (!head) 56edb666f0SSteffen Klassert return 0; 577e14ea15SSteffen Klassert 587e14ea15SSteffen Klassert for_each_protocol_rcu(*proto_handlers(protocol), handler) 597e14ea15SSteffen Klassert if ((ret = handler->cb_handler(skb, err)) <= 0) 607e14ea15SSteffen Klassert return ret; 617e14ea15SSteffen Klassert 627e14ea15SSteffen Klassert return 0; 637e14ea15SSteffen Klassert } 647e14ea15SSteffen Klassert EXPORT_SYMBOL(xfrm6_rcv_cb); 657e14ea15SSteffen Klassert 667e14ea15SSteffen Klassert static int xfrm6_esp_rcv(struct sk_buff *skb) 677e14ea15SSteffen Klassert { 687e14ea15SSteffen Klassert int ret; 697e14ea15SSteffen Klassert struct xfrm6_protocol *handler; 707e14ea15SSteffen Klassert 717e14ea15SSteffen Klassert XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 727e14ea15SSteffen Klassert 737e14ea15SSteffen Klassert for_each_protocol_rcu(esp6_handlers, handler) 747e14ea15SSteffen Klassert if ((ret = handler->handler(skb)) != -EINVAL) 757e14ea15SSteffen Klassert return ret; 767e14ea15SSteffen Klassert 777e14ea15SSteffen Klassert icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 787e14ea15SSteffen Klassert 797e14ea15SSteffen Klassert kfree_skb(skb); 807e14ea15SSteffen Klassert return 0; 817e14ea15SSteffen Klassert } 827e14ea15SSteffen Klassert 83*32bbd879SStefano Brivio static int xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 847e14ea15SSteffen Klassert u8 type, u8 code, int offset, __be32 info) 857e14ea15SSteffen Klassert { 867e14ea15SSteffen Klassert struct xfrm6_protocol *handler; 877e14ea15SSteffen Klassert 887e14ea15SSteffen Klassert for_each_protocol_rcu(esp6_handlers, handler) 897e14ea15SSteffen Klassert if (!handler->err_handler(skb, opt, type, code, offset, info)) 90*32bbd879SStefano Brivio return 0; 91*32bbd879SStefano Brivio 92*32bbd879SStefano Brivio return -ENOENT; 937e14ea15SSteffen Klassert } 947e14ea15SSteffen Klassert 957e14ea15SSteffen Klassert static int xfrm6_ah_rcv(struct sk_buff *skb) 967e14ea15SSteffen Klassert { 977e14ea15SSteffen Klassert int ret; 987e14ea15SSteffen Klassert struct xfrm6_protocol *handler; 997e14ea15SSteffen Klassert 1007e14ea15SSteffen Klassert XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 1017e14ea15SSteffen Klassert 1027e14ea15SSteffen Klassert for_each_protocol_rcu(ah6_handlers, handler) 1037e14ea15SSteffen Klassert if ((ret = handler->handler(skb)) != -EINVAL) 1047e14ea15SSteffen Klassert return ret; 1057e14ea15SSteffen Klassert 1067e14ea15SSteffen Klassert icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 1077e14ea15SSteffen Klassert 1087e14ea15SSteffen Klassert kfree_skb(skb); 1097e14ea15SSteffen Klassert return 0; 1107e14ea15SSteffen Klassert } 1117e14ea15SSteffen Klassert 112*32bbd879SStefano Brivio static int xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 1137e14ea15SSteffen Klassert u8 type, u8 code, int offset, __be32 info) 1147e14ea15SSteffen Klassert { 1157e14ea15SSteffen Klassert struct xfrm6_protocol *handler; 1167e14ea15SSteffen Klassert 1177e14ea15SSteffen Klassert for_each_protocol_rcu(ah6_handlers, handler) 1187e14ea15SSteffen Klassert if (!handler->err_handler(skb, opt, type, code, offset, info)) 119*32bbd879SStefano Brivio return 0; 120*32bbd879SStefano Brivio 121*32bbd879SStefano Brivio return -ENOENT; 1227e14ea15SSteffen Klassert } 1237e14ea15SSteffen Klassert 1247e14ea15SSteffen Klassert static int xfrm6_ipcomp_rcv(struct sk_buff *skb) 1257e14ea15SSteffen Klassert { 1267e14ea15SSteffen Klassert int ret; 1277e14ea15SSteffen Klassert struct xfrm6_protocol *handler; 1287e14ea15SSteffen Klassert 1297e14ea15SSteffen Klassert XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 1307e14ea15SSteffen Klassert 1317e14ea15SSteffen Klassert for_each_protocol_rcu(ipcomp6_handlers, handler) 1327e14ea15SSteffen Klassert if ((ret = handler->handler(skb)) != -EINVAL) 1337e14ea15SSteffen Klassert return ret; 1347e14ea15SSteffen Klassert 1357e14ea15SSteffen Klassert icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 1367e14ea15SSteffen Klassert 1377e14ea15SSteffen Klassert kfree_skb(skb); 1387e14ea15SSteffen Klassert return 0; 1397e14ea15SSteffen Klassert } 1407e14ea15SSteffen Klassert 141*32bbd879SStefano Brivio static int xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 1427e14ea15SSteffen Klassert u8 type, u8 code, int offset, __be32 info) 1437e14ea15SSteffen Klassert { 1447e14ea15SSteffen Klassert struct xfrm6_protocol *handler; 1457e14ea15SSteffen Klassert 1467e14ea15SSteffen Klassert for_each_protocol_rcu(ipcomp6_handlers, handler) 1477e14ea15SSteffen Klassert if (!handler->err_handler(skb, opt, type, code, offset, info)) 148*32bbd879SStefano Brivio return 0; 149*32bbd879SStefano Brivio 150*32bbd879SStefano Brivio return -ENOENT; 1517e14ea15SSteffen Klassert } 1527e14ea15SSteffen Klassert 1537e14ea15SSteffen Klassert static const struct inet6_protocol esp6_protocol = { 1547e14ea15SSteffen Klassert .handler = xfrm6_esp_rcv, 1557e14ea15SSteffen Klassert .err_handler = xfrm6_esp_err, 1567e14ea15SSteffen Klassert .flags = INET6_PROTO_NOPOLICY, 1577e14ea15SSteffen Klassert }; 1587e14ea15SSteffen Klassert 1597e14ea15SSteffen Klassert static const struct inet6_protocol ah6_protocol = { 1607e14ea15SSteffen Klassert .handler = xfrm6_ah_rcv, 1617e14ea15SSteffen Klassert .err_handler = xfrm6_ah_err, 1627e14ea15SSteffen Klassert .flags = INET6_PROTO_NOPOLICY, 1637e14ea15SSteffen Klassert }; 1647e14ea15SSteffen Klassert 1657e14ea15SSteffen Klassert static const struct inet6_protocol ipcomp6_protocol = { 1667e14ea15SSteffen Klassert .handler = xfrm6_ipcomp_rcv, 1677e14ea15SSteffen Klassert .err_handler = xfrm6_ipcomp_err, 1687e14ea15SSteffen Klassert .flags = INET6_PROTO_NOPOLICY, 1697e14ea15SSteffen Klassert }; 1707e14ea15SSteffen Klassert 171960fdfdeSFlorian Westphal static const struct xfrm_input_afinfo xfrm6_input_afinfo = { 1727e14ea15SSteffen Klassert .family = AF_INET6, 1737e14ea15SSteffen Klassert .callback = xfrm6_rcv_cb, 1747e14ea15SSteffen Klassert }; 1757e14ea15SSteffen Klassert 1767e14ea15SSteffen Klassert static inline const struct inet6_protocol *netproto(unsigned char protocol) 1777e14ea15SSteffen Klassert { 1787e14ea15SSteffen Klassert switch (protocol) { 1797e14ea15SSteffen Klassert case IPPROTO_ESP: 1807e14ea15SSteffen Klassert return &esp6_protocol; 1817e14ea15SSteffen Klassert case IPPROTO_AH: 1827e14ea15SSteffen Klassert return &ah6_protocol; 1837e14ea15SSteffen Klassert case IPPROTO_COMP: 1847e14ea15SSteffen Klassert return &ipcomp6_protocol; 1857e14ea15SSteffen Klassert } 1867e14ea15SSteffen Klassert 1877e14ea15SSteffen Klassert return NULL; 1887e14ea15SSteffen Klassert } 1897e14ea15SSteffen Klassert 1907e14ea15SSteffen Klassert int xfrm6_protocol_register(struct xfrm6_protocol *handler, 1917e14ea15SSteffen Klassert unsigned char protocol) 1927e14ea15SSteffen Klassert { 1937e14ea15SSteffen Klassert struct xfrm6_protocol __rcu **pprev; 1947e14ea15SSteffen Klassert struct xfrm6_protocol *t; 1957e14ea15SSteffen Klassert bool add_netproto = false; 1967e14ea15SSteffen Klassert int ret = -EEXIST; 1977e14ea15SSteffen Klassert int priority = handler->priority; 1987e14ea15SSteffen Klassert 199edb666f0SSteffen Klassert if (!proto_handlers(protocol) || !netproto(protocol)) 200edb666f0SSteffen Klassert return -EINVAL; 201edb666f0SSteffen Klassert 2027e14ea15SSteffen Klassert mutex_lock(&xfrm6_protocol_mutex); 2037e14ea15SSteffen Klassert 2047e14ea15SSteffen Klassert if (!rcu_dereference_protected(*proto_handlers(protocol), 2057e14ea15SSteffen Klassert lockdep_is_held(&xfrm6_protocol_mutex))) 2067e14ea15SSteffen Klassert add_netproto = true; 2077e14ea15SSteffen Klassert 2087e14ea15SSteffen Klassert for (pprev = proto_handlers(protocol); 2097e14ea15SSteffen Klassert (t = rcu_dereference_protected(*pprev, 2107e14ea15SSteffen Klassert lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; 2117e14ea15SSteffen Klassert pprev = &t->next) { 2127e14ea15SSteffen Klassert if (t->priority < priority) 2137e14ea15SSteffen Klassert break; 2147e14ea15SSteffen Klassert if (t->priority == priority) 2157e14ea15SSteffen Klassert goto err; 2167e14ea15SSteffen Klassert } 2177e14ea15SSteffen Klassert 2187e14ea15SSteffen Klassert handler->next = *pprev; 2197e14ea15SSteffen Klassert rcu_assign_pointer(*pprev, handler); 2207e14ea15SSteffen Klassert 2217e14ea15SSteffen Klassert ret = 0; 2227e14ea15SSteffen Klassert 2237e14ea15SSteffen Klassert err: 2247e14ea15SSteffen Klassert mutex_unlock(&xfrm6_protocol_mutex); 2257e14ea15SSteffen Klassert 2267e14ea15SSteffen Klassert if (add_netproto) { 2277e14ea15SSteffen Klassert if (inet6_add_protocol(netproto(protocol), protocol)) { 2287e14ea15SSteffen Klassert pr_err("%s: can't add protocol\n", __func__); 2297e14ea15SSteffen Klassert ret = -EAGAIN; 2307e14ea15SSteffen Klassert } 2317e14ea15SSteffen Klassert } 2327e14ea15SSteffen Klassert 2337e14ea15SSteffen Klassert return ret; 2347e14ea15SSteffen Klassert } 2357e14ea15SSteffen Klassert EXPORT_SYMBOL(xfrm6_protocol_register); 2367e14ea15SSteffen Klassert 2377e14ea15SSteffen Klassert int xfrm6_protocol_deregister(struct xfrm6_protocol *handler, 2387e14ea15SSteffen Klassert unsigned char protocol) 2397e14ea15SSteffen Klassert { 2407e14ea15SSteffen Klassert struct xfrm6_protocol __rcu **pprev; 2417e14ea15SSteffen Klassert struct xfrm6_protocol *t; 2427e14ea15SSteffen Klassert int ret = -ENOENT; 2437e14ea15SSteffen Klassert 244edb666f0SSteffen Klassert if (!proto_handlers(protocol) || !netproto(protocol)) 245edb666f0SSteffen Klassert return -EINVAL; 246edb666f0SSteffen Klassert 2477e14ea15SSteffen Klassert mutex_lock(&xfrm6_protocol_mutex); 2487e14ea15SSteffen Klassert 2497e14ea15SSteffen Klassert for (pprev = proto_handlers(protocol); 2507e14ea15SSteffen Klassert (t = rcu_dereference_protected(*pprev, 2517e14ea15SSteffen Klassert lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; 2527e14ea15SSteffen Klassert pprev = &t->next) { 2537e14ea15SSteffen Klassert if (t == handler) { 2547e14ea15SSteffen Klassert *pprev = handler->next; 2557e14ea15SSteffen Klassert ret = 0; 2567e14ea15SSteffen Klassert break; 2577e14ea15SSteffen Klassert } 2587e14ea15SSteffen Klassert } 2597e14ea15SSteffen Klassert 2607e14ea15SSteffen Klassert if (!rcu_dereference_protected(*proto_handlers(protocol), 2617e14ea15SSteffen Klassert lockdep_is_held(&xfrm6_protocol_mutex))) { 2627e14ea15SSteffen Klassert if (inet6_del_protocol(netproto(protocol), protocol) < 0) { 2637e14ea15SSteffen Klassert pr_err("%s: can't remove protocol\n", __func__); 2647e14ea15SSteffen Klassert ret = -EAGAIN; 2657e14ea15SSteffen Klassert } 2667e14ea15SSteffen Klassert } 2677e14ea15SSteffen Klassert 2687e14ea15SSteffen Klassert mutex_unlock(&xfrm6_protocol_mutex); 2697e14ea15SSteffen Klassert 2707e14ea15SSteffen Klassert synchronize_net(); 2717e14ea15SSteffen Klassert 2727e14ea15SSteffen Klassert return ret; 2737e14ea15SSteffen Klassert } 2747e14ea15SSteffen Klassert EXPORT_SYMBOL(xfrm6_protocol_deregister); 2757e14ea15SSteffen Klassert 2767e14ea15SSteffen Klassert int __init xfrm6_protocol_init(void) 2777e14ea15SSteffen Klassert { 2787e14ea15SSteffen Klassert return xfrm_input_register_afinfo(&xfrm6_input_afinfo); 2797e14ea15SSteffen Klassert } 2807e14ea15SSteffen Klassert 2817e14ea15SSteffen Klassert void xfrm6_protocol_fini(void) 2827e14ea15SSteffen Klassert { 2837e14ea15SSteffen Klassert xfrm_input_unregister_afinfo(&xfrm6_input_afinfo); 2847e14ea15SSteffen Klassert } 285