Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.16-rc4 278 lines 6.6 kB view raw
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 const struct xfrm_input_afinfo xfrm6_input_afinfo = { 166 .family = AF_INET6, 167 .callback = xfrm6_rcv_cb, 168}; 169 170static inline const struct inet6_protocol *netproto(unsigned char protocol) 171{ 172 switch (protocol) { 173 case IPPROTO_ESP: 174 return &esp6_protocol; 175 case IPPROTO_AH: 176 return &ah6_protocol; 177 case IPPROTO_COMP: 178 return &ipcomp6_protocol; 179 } 180 181 return NULL; 182} 183 184int xfrm6_protocol_register(struct xfrm6_protocol *handler, 185 unsigned char protocol) 186{ 187 struct xfrm6_protocol __rcu **pprev; 188 struct xfrm6_protocol *t; 189 bool add_netproto = false; 190 int ret = -EEXIST; 191 int priority = handler->priority; 192 193 if (!proto_handlers(protocol) || !netproto(protocol)) 194 return -EINVAL; 195 196 mutex_lock(&xfrm6_protocol_mutex); 197 198 if (!rcu_dereference_protected(*proto_handlers(protocol), 199 lockdep_is_held(&xfrm6_protocol_mutex))) 200 add_netproto = true; 201 202 for (pprev = proto_handlers(protocol); 203 (t = rcu_dereference_protected(*pprev, 204 lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; 205 pprev = &t->next) { 206 if (t->priority < priority) 207 break; 208 if (t->priority == priority) 209 goto err; 210 } 211 212 handler->next = *pprev; 213 rcu_assign_pointer(*pprev, handler); 214 215 ret = 0; 216 217err: 218 mutex_unlock(&xfrm6_protocol_mutex); 219 220 if (add_netproto) { 221 if (inet6_add_protocol(netproto(protocol), protocol)) { 222 pr_err("%s: can't add protocol\n", __func__); 223 ret = -EAGAIN; 224 } 225 } 226 227 return ret; 228} 229EXPORT_SYMBOL(xfrm6_protocol_register); 230 231int xfrm6_protocol_deregister(struct xfrm6_protocol *handler, 232 unsigned char protocol) 233{ 234 struct xfrm6_protocol __rcu **pprev; 235 struct xfrm6_protocol *t; 236 int ret = -ENOENT; 237 238 if (!proto_handlers(protocol) || !netproto(protocol)) 239 return -EINVAL; 240 241 mutex_lock(&xfrm6_protocol_mutex); 242 243 for (pprev = proto_handlers(protocol); 244 (t = rcu_dereference_protected(*pprev, 245 lockdep_is_held(&xfrm6_protocol_mutex))) != NULL; 246 pprev = &t->next) { 247 if (t == handler) { 248 *pprev = handler->next; 249 ret = 0; 250 break; 251 } 252 } 253 254 if (!rcu_dereference_protected(*proto_handlers(protocol), 255 lockdep_is_held(&xfrm6_protocol_mutex))) { 256 if (inet6_del_protocol(netproto(protocol), protocol) < 0) { 257 pr_err("%s: can't remove protocol\n", __func__); 258 ret = -EAGAIN; 259 } 260 } 261 262 mutex_unlock(&xfrm6_protocol_mutex); 263 264 synchronize_net(); 265 266 return ret; 267} 268EXPORT_SYMBOL(xfrm6_protocol_deregister); 269 270int __init xfrm6_protocol_init(void) 271{ 272 return xfrm_input_register_afinfo(&xfrm6_input_afinfo); 273} 274 275void xfrm6_protocol_fini(void) 276{ 277 xfrm_input_unregister_afinfo(&xfrm6_input_afinfo); 278}