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

Configure Feed

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

at v5.3-rc5 279 lines 6.4 kB view raw
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 21static struct xfrm6_protocol __rcu *esp6_handlers __read_mostly; 22static struct xfrm6_protocol __rcu *ah6_handlers __read_mostly; 23static struct xfrm6_protocol __rcu *ipcomp6_handlers __read_mostly; 24static DEFINE_MUTEX(xfrm6_protocol_mutex); 25 26static inline struct xfrm6_protocol __rcu **proto_handlers(u8 protocol) 27{ 28 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 45static 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 if (!head) 52 return 0; 53 54 for_each_protocol_rcu(*proto_handlers(protocol), handler) 55 if ((ret = handler->cb_handler(skb, err)) <= 0) 56 return ret; 57 58 return 0; 59} 60 61static int xfrm6_esp_rcv(struct sk_buff *skb) 62{ 63 int ret; 64 struct xfrm6_protocol *handler; 65 66 XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 67 68 for_each_protocol_rcu(esp6_handlers, handler) 69 if ((ret = handler->handler(skb)) != -EINVAL) 70 return ret; 71 72 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 73 74 kfree_skb(skb); 75 return 0; 76} 77 78static 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 for_each_protocol_rcu(esp6_handlers, handler) 84 if (!handler->err_handler(skb, opt, type, code, offset, info)) 85 return 0; 86 87 return -ENOENT; 88} 89 90static int xfrm6_ah_rcv(struct sk_buff *skb) 91{ 92 int ret; 93 struct xfrm6_protocol *handler; 94 95 XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 96 97 for_each_protocol_rcu(ah6_handlers, handler) 98 if ((ret = handler->handler(skb)) != -EINVAL) 99 return ret; 100 101 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 102 103 kfree_skb(skb); 104 return 0; 105} 106 107static 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 for_each_protocol_rcu(ah6_handlers, handler) 113 if (!handler->err_handler(skb, opt, type, code, offset, info)) 114 return 0; 115 116 return -ENOENT; 117} 118 119static int xfrm6_ipcomp_rcv(struct sk_buff *skb) 120{ 121 int ret; 122 struct xfrm6_protocol *handler; 123 124 XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; 125 126 for_each_protocol_rcu(ipcomp6_handlers, handler) 127 if ((ret = handler->handler(skb)) != -EINVAL) 128 return ret; 129 130 icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0); 131 132 kfree_skb(skb); 133 return 0; 134} 135 136static 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 for_each_protocol_rcu(ipcomp6_handlers, handler) 142 if (!handler->err_handler(skb, opt, type, code, offset, info)) 143 return 0; 144 145 return -ENOENT; 146} 147 148static const struct inet6_protocol esp6_protocol = { 149 .handler = xfrm6_esp_rcv, 150 .err_handler = xfrm6_esp_err, 151 .flags = INET6_PROTO_NOPOLICY, 152}; 153 154static const struct inet6_protocol ah6_protocol = { 155 .handler = xfrm6_ah_rcv, 156 .err_handler = xfrm6_ah_err, 157 .flags = INET6_PROTO_NOPOLICY, 158}; 159 160static const struct inet6_protocol ipcomp6_protocol = { 161 .handler = xfrm6_ipcomp_rcv, 162 .err_handler = xfrm6_ipcomp_err, 163 .flags = INET6_PROTO_NOPOLICY, 164}; 165 166static const struct xfrm_input_afinfo xfrm6_input_afinfo = { 167 .family = AF_INET6, 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}