Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later 2 : : /* xfrm6_protocol.c - Generic xfrm protocol multiplexer for ipv6. 3 : : * 4 : : * Copyright (C) 2013 secunet Security Networks AG 5 : : * 6 : : * Author: 7 : : * Steffen Klassert <steffen.klassert@secunet.com> 8 : : * 9 : : * Based on: 10 : : * net/ipv4/xfrm4_protocol.c 11 : : */ 12 : : 13 : : #include <linux/init.h> 14 : : #include <linux/mutex.h> 15 : : #include <linux/skbuff.h> 16 : : #include <linux/icmpv6.h> 17 : : #include <net/ipv6.h> 18 : : #include <net/protocol.h> 19 : : #include <net/xfrm.h> 20 : : 21 : : static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly; 22 : : static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly; 23 : : static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly; 24 : : static DEFINE_MUTEX(xfrm6_protocol_mutex); 25 : : 26 : : static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol) 27 : : { 28 : 0 : switch (protocol) { 29 : : case IPPROTO_ESP: 30 : : return &esp6_handlers; 31 : : case IPPROTO_AH: 32 : : return &ah6_handlers; 33 : : case IPPROTO_COMP: 34 : : return &ipcomp6_handlers; 35 : : } 36 : : 37 : : return NULL; 38 : : } 39 : : 40 : : #define for_each_protocol_rcu(head, handler) \ 41 : : for (handler = rcu_dereference(head); \ 42 : : handler != NULL; \ 43 : : handler = rcu_dereference(handler->next)) \ 44 : : 45 : 0 : static int xfrm6_rcv_cb(struct sk_buff *skb, u8 protocol, int err) 46 : : { 47 : : int ret; 48 : : struct xfrm6_protocol *handler; 49 : : struct xfrm6_protocol __rcu **head = proto_handlers(protocol); 50 : : 51 : 0 : if (!head) 52 : : return 0; 53 : : 54 : 0 : for_each_protocol_rcu(*proto_handlers(protocol), handler) 55 : 0 : if ((ret = handler->cb_handler(skb, err)) <= 0) 56 : 0 : return ret; 57 : : 58 : : return 0; 59 : : } 60 : : 61 : 0 : static int xfrm6_esp_rcv(struct sk_buff *skb) 62 : : { 63 : : int ret; 64 : : struct xfrm6_protocol *handler; 65 : : 66 : 0 : XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 67 : : 68 : 0 : for_each_protocol_rcu(esp6_handlers, handler) 69 : 0 : if ((ret = handler->handler(skb)) != -EINVAL) 70 : 0 : return ret; 71 : : 72 : 0 : icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 73 : : 74 : 0 : kfree_skb(skb); 75 : 0 : return 0; 76 : : } 77 : : 78 : 0 : static int xfrm6_esp_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 79 : : u8 type, u8 code, int offset, __be32 info) 80 : : { 81 : : struct xfrm6_protocol *handler; 82 : : 83 : 0 : for_each_protocol_rcu(esp6_handlers, handler) 84 : 0 : if (!handler->err_handler(skb, opt, type, code, offset, info)) 85 : : return 0; 86 : : 87 : : return -ENOENT; 88 : : } 89 : : 90 : 0 : static int xfrm6_ah_rcv(struct sk_buff *skb) 91 : : { 92 : : int ret; 93 : : struct xfrm6_protocol *handler; 94 : : 95 : 0 : XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 96 : : 97 : 0 : for_each_protocol_rcu(ah6_handlers, handler) 98 : 0 : if ((ret = handler->handler(skb)) != -EINVAL) 99 : 0 : return ret; 100 : : 101 : 0 : icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 102 : : 103 : 0 : kfree_skb(skb); 104 : 0 : return 0; 105 : : } 106 : : 107 : 0 : static int xfrm6_ah_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 108 : : u8 type, u8 code, int offset, __be32 info) 109 : : { 110 : : struct xfrm6_protocol *handler; 111 : : 112 : 0 : for_each_protocol_rcu(ah6_handlers, handler) 113 : 0 : if (!handler->err_handler(skb, opt, type, code, offset, info)) 114 : : return 0; 115 : : 116 : : return -ENOENT; 117 : : } 118 : : 119 : 0 : static int xfrm6_ipcomp_rcv(struct sk_buff *skb) 120 : : { 121 : : int ret; 122 : : struct xfrm6_protocol *handler; 123 : : 124 : 0 : XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 125 : : 126 : 0 : for_each_protocol_rcu(ipcomp6_handlers, handler) 127 : 0 : if ((ret = handler->handler(skb)) != -EINVAL) 128 : 0 : return ret; 129 : : 130 : 0 : icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 131 : : 132 : 0 : kfree_skb(skb); 133 : 0 : return 0; 134 : : } 135 : : 136 : 0 : static int xfrm6_ipcomp_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 137 : : u8 type, u8 code, int offset, __be32 info) 138 : : { 139 : : struct xfrm6_protocol *handler; 140 : : 141 : 0 : for_each_protocol_rcu(ipcomp6_handlers, handler) 142 : 0 : if (!handler->err_handler(skb, opt, type, code, offset, info)) 143 : : return 0; 144 : : 145 : : return -ENOENT; 146 : : } 147 : : 148 : : static const struct inet6_protocol esp6_protocol = { 149 : : .handler = xfrm6_esp_rcv, 150 : : .err_handler = xfrm6_esp_err, 151 : : .flags = INET6_PROTO_NOPOLICY, 152 : : }; 153 : : 154 : : static const struct inet6_protocol ah6_protocol = { 155 : : .handler = xfrm6_ah_rcv, 156 : : .err_handler = xfrm6_ah_err, 157 : : .flags = INET6_PROTO_NOPOLICY, 158 : : }; 159 : : 160 : : static const struct inet6_protocol ipcomp6_protocol = { 161 : : .handler = xfrm6_ipcomp_rcv, 162 : : .err_handler = xfrm6_ipcomp_err, 163 : : .flags = INET6_PROTO_NOPOLICY, 164 : : }; 165 : : 166 : : static const struct xfrm_input_afinfo xfrm6_input_afinfo = { 167 : : .family = AF_INET6, 168 : : .callback = xfrm6_rcv_cb, 169 : : }; 170 : : 171 : : static inline const struct inet6_protocol *netproto(unsigned char protocol) 172 : : { 173 : 0 : switch (protocol) { 174 : : case IPPROTO_ESP: 175 : : return &esp6_protocol; 176 : : case IPPROTO_AH: 177 : : return &ah6_protocol; 178 : : case IPPROTO_COMP: 179 : : return &ipcomp6_protocol; 180 : : } 181 : : 182 : : return NULL; 183 : : } 184 : : 185 : 0 : int xfrm6_protocol_register(struct xfrm6_protocol *handler, 186 : : unsigned char protocol) 187 : : { 188 : : struct xfrm6_protocol __rcu **pprev; 189 : : struct xfrm6_protocol *t; 190 : : bool add_netproto = false; 191 : : int ret = -EEXIST; 192 : 0 : int priority = handler->priority; 193 : : 194 : 0 : if (!proto_handlers(protocol) || !netproto(protocol)) 195 : : return -EINVAL; 196 : : 197 : 0 : mutex_lock(&xfrm6_protocol_mutex); 198 : : 199 : 0 : if (!rcu_dereference_protected(*proto_handlers(protocol), 200 : : lockdep_is_held(&xfrm6_protocol_mutex))) 201 : : add_netproto = true; 202 : : 203 : 0 : for (pprev = proto_handlers(protocol); 204 : 0 : (t = rcu_dereference_protected(*pprev, 205 : : lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; 206 : 0 : pprev = &t->next) { 207 : 0 : if (t->priority < priority) 208 : : break; 209 : 0 : if (t->priority == priority) 210 : : goto err; 211 : : } 212 : : 213 : 0 : handler->next = *pprev; 214 : 0 : rcu_assign_pointer(*pprev, handler); 215 : : 216 : : ret = 0; 217 : : 218 : : err: 219 : 0 : mutex_unlock(&xfrm6_protocol_mutex); 220 : : 221 : 0 : if (add_netproto) { 222 : 0 : if (inet6_add_protocol(netproto(protocol), protocol)) { 223 : 0 : pr_err("%s: can't add protocol\n", __func__); 224 : : ret = -EAGAIN; 225 : : } 226 : : } 227 : : 228 : 0 : return ret; 229 : : } 230 : : EXPORT_SYMBOL(xfrm6_protocol_register); 231 : : 232 : 0 : int xfrm6_protocol_deregister(struct xfrm6_protocol *handler, 233 : : unsigned char protocol) 234 : : { 235 : : struct xfrm6_protocol __rcu **pprev; 236 : : struct xfrm6_protocol *t; 237 : : int ret = -ENOENT; 238 : : 239 : 0 : if (!proto_handlers(protocol) || !netproto(protocol)) 240 : : return -EINVAL; 241 : : 242 : 0 : mutex_lock(&xfrm6_protocol_mutex); 243 : : 244 : 0 : for (pprev = proto_handlers(protocol); 245 : 0 : (t = rcu_dereference_protected(*pprev, 246 : : lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; 247 : 0 : pprev = &t->next) { 248 : 0 : if (t == handler) { 249 : 0 : *pprev = handler->next; 250 : : ret = 0; 251 : 0 : break; 252 : : } 253 : : } 254 : : 255 : 0 : if (!rcu_dereference_protected(*proto_handlers(protocol), 256 : : lockdep_is_held(&xfrm6_protocol_mutex))) { 257 : 0 : if (inet6_del_protocol(netproto(protocol), protocol) < 0) { 258 : 0 : pr_err("%s: can't remove protocol\n", __func__); 259 : : ret = -EAGAIN; 260 : : } 261 : : } 262 : : 263 : 0 : mutex_unlock(&xfrm6_protocol_mutex); 264 : : 265 : 0 : synchronize_net(); 266 : : 267 : 0 : return ret; 268 : : } 269 : : EXPORT_SYMBOL(xfrm6_protocol_deregister); 270 : : 271 : 3 : int __init xfrm6_protocol_init(void) 272 : : { 273 : 3 : return xfrm_input_register_afinfo(&xfrm6_input_afinfo); 274 : : } 275 : : 276 : 0 : void xfrm6_protocol_fini(void) 277 : : { 278 : 0 : xfrm_input_unregister_afinfo(&xfrm6_input_afinfo); 279 : 0 : }