Blame view
net/ipv4/tunnel4.c
6.55 KB
09c434b8a
|
1 |
// SPDX-License-Identifier: GPL-2.0-only |
d2acc3479
|
2 3 4 5 6 7 8 9 |
/* tunnel4.c: Generic IP tunnel transformer. * * Copyright (C) 2003 David S. Miller (davem@redhat.com) */ #include <linux/init.h> #include <linux/module.h> #include <linux/mutex.h> |
8afe97e5d
|
10 |
#include <linux/mpls.h> |
d2acc3479
|
11 12 |
#include <linux/netdevice.h> #include <linux/skbuff.h> |
5a0e3ad6a
|
13 |
#include <linux/slab.h> |
50fba2aa7
|
14 15 |
#include <net/icmp.h> #include <net/ip.h> |
d2acc3479
|
16 17 |
#include <net/protocol.h> #include <net/xfrm.h> |
b33eab084
|
18 19 |
static struct xfrm_tunnel __rcu *tunnel4_handlers __read_mostly; static struct xfrm_tunnel __rcu *tunnel64_handlers __read_mostly; |
8afe97e5d
|
20 |
static struct xfrm_tunnel __rcu *tunnelmpls4_handlers __read_mostly; |
d2acc3479
|
21 |
static DEFINE_MUTEX(tunnel4_mutex); |
b33eab084
|
22 |
static inline struct xfrm_tunnel __rcu **fam_handlers(unsigned short family) |
358352b8b
|
23 |
{ |
8afe97e5d
|
24 25 26 |
return (family == AF_INET) ? &tunnel4_handlers : (family == AF_INET6) ? &tunnel64_handlers : &tunnelmpls4_handlers; |
358352b8b
|
27 |
} |
c0d56408e
|
28 |
int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family) |
d2acc3479
|
29 |
{ |
b33eab084
|
30 31 |
struct xfrm_tunnel __rcu **pprev; struct xfrm_tunnel *t; |
d2acc3479
|
32 33 34 35 |
int ret = -EEXIST; int priority = handler->priority; mutex_lock(&tunnel4_mutex); |
b33eab084
|
36 37 38 39 40 |
for (pprev = fam_handlers(family); (t = rcu_dereference_protected(*pprev, lockdep_is_held(&tunnel4_mutex))) != NULL; pprev = &t->next) { if (t->priority > priority) |
d2acc3479
|
41 |
break; |
b33eab084
|
42 |
if (t->priority == priority) |
d2acc3479
|
43 44 45 46 |
goto err; } handler->next = *pprev; |
49d61e239
|
47 |
rcu_assign_pointer(*pprev, handler); |
d2acc3479
|
48 49 50 51 52 53 54 55 |
ret = 0; err: mutex_unlock(&tunnel4_mutex); return ret; } |
d2acc3479
|
56 |
EXPORT_SYMBOL(xfrm4_tunnel_register); |
c0d56408e
|
57 |
int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family) |
d2acc3479
|
58 |
{ |
b33eab084
|
59 60 |
struct xfrm_tunnel __rcu **pprev; struct xfrm_tunnel *t; |
d2acc3479
|
61 62 63 |
int ret = -ENOENT; mutex_lock(&tunnel4_mutex); |
b33eab084
|
64 65 66 67 68 |
for (pprev = fam_handlers(family); (t = rcu_dereference_protected(*pprev, lockdep_is_held(&tunnel4_mutex))) != NULL; pprev = &t->next) { if (t == handler) { |
d2acc3479
|
69 70 71 72 73 74 75 76 77 78 79 80 |
*pprev = handler->next; ret = 0; break; } } mutex_unlock(&tunnel4_mutex); synchronize_net(); return ret; } |
d2acc3479
|
81 |
EXPORT_SYMBOL(xfrm4_tunnel_deregister); |
875168a93
|
82 83 84 85 |
#define for_each_tunnel_rcu(head, handler) \ for (handler = rcu_dereference(head); \ handler != NULL; \ handler = rcu_dereference(handler->next)) \ |
82695b30f
|
86 |
|
d2acc3479
|
87 88 89 |
static int tunnel4_rcv(struct sk_buff *skb) { struct xfrm_tunnel *handler; |
50fba2aa7
|
90 91 |
if (!pskb_may_pull(skb, sizeof(struct iphdr))) goto drop; |
875168a93
|
92 |
for_each_tunnel_rcu(tunnel4_handlers, handler) |
d2acc3479
|
93 94 |
if (!handler->handler(skb)) return 0; |
50fba2aa7
|
95 96 97 |
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); drop: |
d2acc3479
|
98 99 100 |
kfree_skb(skb); return 0; } |
6df2db5d3
|
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
#if IS_ENABLED(CONFIG_INET_XFRM_TUNNEL) static int tunnel4_rcv_cb(struct sk_buff *skb, u8 proto, int err) { struct xfrm_tunnel __rcu *head; struct xfrm_tunnel *handler; int ret; head = (proto == IPPROTO_IPIP) ? tunnel4_handlers : tunnel64_handlers; for_each_tunnel_rcu(head, handler) { if (handler->cb_handler) { ret = handler->cb_handler(skb, err); if (ret <= 0) return ret; } } return 0; } static const struct xfrm_input_afinfo tunnel4_input_afinfo = { .family = AF_INET, .is_ipip = true, .callback = tunnel4_rcv_cb, }; #endif |
dfd56b8b3
|
127 |
#if IS_ENABLED(CONFIG_IPV6) |
c0d56408e
|
128 129 130 |
static int tunnel64_rcv(struct sk_buff *skb) { struct xfrm_tunnel *handler; |
baa2bfb8a
|
131 |
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) |
c0d56408e
|
132 |
goto drop; |
875168a93
|
133 |
for_each_tunnel_rcu(tunnel64_handlers, handler) |
c0d56408e
|
134 135 136 137 138 139 140 141 142 143 |
if (!handler->handler(skb)) return 0; icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); drop: kfree_skb(skb); return 0; } #endif |
8afe97e5d
|
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
#if IS_ENABLED(CONFIG_MPLS) static int tunnelmpls4_rcv(struct sk_buff *skb) { struct xfrm_tunnel *handler; if (!pskb_may_pull(skb, sizeof(struct mpls_label))) goto drop; for_each_tunnel_rcu(tunnelmpls4_handlers, handler) if (!handler->handler(skb)) return 0; icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); drop: kfree_skb(skb); return 0; } #endif |
32bbd8793
|
163 |
static int tunnel4_err(struct sk_buff *skb, u32 info) |
d2acc3479
|
164 165 |
{ struct xfrm_tunnel *handler; |
875168a93
|
166 |
for_each_tunnel_rcu(tunnel4_handlers, handler) |
d2acc3479
|
167 |
if (!handler->err_handler(skb, info)) |
32bbd8793
|
168 169 170 |
return 0; return -ENOENT; |
d2acc3479
|
171 |
} |
dfd56b8b3
|
172 |
#if IS_ENABLED(CONFIG_IPV6) |
32bbd8793
|
173 |
static int tunnel64_err(struct sk_buff *skb, u32 info) |
99f933263
|
174 175 |
{ struct xfrm_tunnel *handler; |
875168a93
|
176 |
for_each_tunnel_rcu(tunnel64_handlers, handler) |
99f933263
|
177 |
if (!handler->err_handler(skb, info)) |
32bbd8793
|
178 179 180 |
return 0; return -ENOENT; |
99f933263
|
181 182 |
} #endif |
8afe97e5d
|
183 |
#if IS_ENABLED(CONFIG_MPLS) |
32bbd8793
|
184 |
static int tunnelmpls4_err(struct sk_buff *skb, u32 info) |
8afe97e5d
|
185 186 187 188 189 |
{ struct xfrm_tunnel *handler; for_each_tunnel_rcu(tunnelmpls4_handlers, handler) if (!handler->err_handler(skb, info)) |
32bbd8793
|
190 191 192 |
return 0; return -ENOENT; |
8afe97e5d
|
193 194 |
} #endif |
32613090a
|
195 |
static const struct net_protocol tunnel4_protocol = { |
d2acc3479
|
196 197 198 |
.handler = tunnel4_rcv, .err_handler = tunnel4_err, .no_policy = 1, |
4597a0ce0
|
199 |
.netns_ok = 1, |
d2acc3479
|
200 |
}; |
dfd56b8b3
|
201 |
#if IS_ENABLED(CONFIG_IPV6) |
32613090a
|
202 |
static const struct net_protocol tunnel64_protocol = { |
c0d56408e
|
203 |
.handler = tunnel64_rcv, |
99f933263
|
204 |
.err_handler = tunnel64_err, |
c0d56408e
|
205 |
.no_policy = 1, |
b0970c428
|
206 |
.netns_ok = 1, |
c0d56408e
|
207 208 |
}; #endif |
8afe97e5d
|
209 210 211 212 213 214 215 216 |
#if IS_ENABLED(CONFIG_MPLS) static const struct net_protocol tunnelmpls4_protocol = { .handler = tunnelmpls4_rcv, .err_handler = tunnelmpls4_err, .no_policy = 1, .netns_ok = 1, }; #endif |
d2acc3479
|
217 218 |
static int __init tunnel4_init(void) { |
8afe97e5d
|
219 |
if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP)) |
aa9667e7f
|
220 |
goto err; |
dfd56b8b3
|
221 |
#if IS_ENABLED(CONFIG_IPV6) |
aa9667e7f
|
222 223 224 225 |
if (inet_add_protocol(&tunnel64_protocol, IPPROTO_IPV6)) { inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP); goto err; } |
8afe97e5d
|
226 227 |
#endif #if IS_ENABLED(CONFIG_MPLS) |
aa9667e7f
|
228 229 230 231 232 233 234 |
if (inet_add_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS)) { inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP); #if IS_ENABLED(CONFIG_IPV6) inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6); #endif goto err; } |
c0d56408e
|
235 |
#endif |
6df2db5d3
|
236 237 238 239 240 241 242 243 244 245 246 247 |
#if IS_ENABLED(CONFIG_INET_XFRM_TUNNEL) if (xfrm_input_register_afinfo(&tunnel4_input_afinfo)) { inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP); #if IS_ENABLED(CONFIG_IPV6) inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6); #endif #if IS_ENABLED(CONFIG_MPLS) inet_del_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS); #endif goto err; } #endif |
d2acc3479
|
248 |
return 0; |
8afe97e5d
|
249 |
|
aa9667e7f
|
250 |
err: |
8afe97e5d
|
251 252 253 |
pr_err("%s: can't add protocol ", __func__); return -EAGAIN; |
d2acc3479
|
254 255 256 257 |
} static void __exit tunnel4_fini(void) { |
6df2db5d3
|
258 259 260 261 262 |
#if IS_ENABLED(CONFIG_INET_XFRM_TUNNEL) if (xfrm_input_unregister_afinfo(&tunnel4_input_afinfo)) pr_err("tunnel4 close: can't remove input afinfo "); #endif |
8afe97e5d
|
263 264 265 266 267 |
#if IS_ENABLED(CONFIG_MPLS) if (inet_del_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS)) pr_err("tunnelmpls4 close: can't remove protocol "); #endif |
dfd56b8b3
|
268 |
#if IS_ENABLED(CONFIG_IPV6) |
c0d56408e
|
269 |
if (inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6)) |
058bd4d2a
|
270 271 |
pr_err("tunnel64 close: can't remove protocol "); |
c0d56408e
|
272 |
#endif |
d2acc3479
|
273 |
if (inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP)) |
058bd4d2a
|
274 275 |
pr_err("tunnel4 close: can't remove protocol "); |
d2acc3479
|
276 277 278 279 280 |
} module_init(tunnel4_init); module_exit(tunnel4_fini); MODULE_LICENSE("GPL"); |