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