Blame view
net/core/lwtunnel.c
8.8 KB
2874c5fd2 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
499a24256 lwtunnel: infrast... |
2 3 4 5 |
/* * lwtunnel Infrastructure for light weight tunnels like mpls * * Authors: Roopa Prabhu, <roopa@cumulusnetworks.com> |
499a24256 lwtunnel: infrast... |
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
*/ #include <linux/capability.h> #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/lwtunnel.h> #include <linux/in.h> #include <linux/init.h> #include <linux/err.h> #include <net/lwtunnel.h> #include <net/rtnetlink.h> |
ffce41962 lwtunnel: support... |
23 |
#include <net/ip6_fib.h> |
3c618c1db net: Rename net/n... |
24 |
#include <net/rtnh.h> |
499a24256 lwtunnel: infrast... |
25 |
|
745041e2a lwtunnel: autoloa... |
26 27 28 29 30 31 32 33 34 35 36 37 |
#ifdef CONFIG_MODULES static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type) { /* Only lwt encaps implemented without using an interface for * the encap need to return a string here. */ switch (encap_type) { case LWTUNNEL_ENCAP_MPLS: return "MPLS"; case LWTUNNEL_ENCAP_ILA: return "ILA"; |
6c8702c60 ipv6: sr: add sup... |
38 39 |
case LWTUNNEL_ENCAP_SEG6: return "SEG6"; |
3a0af8fd6 bpf: BPF for ligh... |
40 41 |
case LWTUNNEL_ENCAP_BPF: return "BPF"; |
d1df6fd8a ipv6: sr: define ... |
42 43 |
case LWTUNNEL_ENCAP_SEG6_LOCAL: return "SEG6LOCAL"; |
a7a29f9c3 net: ipv6: add rp... |
44 45 |
case LWTUNNEL_ENCAP_RPL: return "RPL"; |
745041e2a lwtunnel: autoloa... |
46 47 48 49 50 51 52 53 54 55 56 57 |
case LWTUNNEL_ENCAP_IP6: case LWTUNNEL_ENCAP_IP: case LWTUNNEL_ENCAP_NONE: case __LWTUNNEL_ENCAP_MAX: /* should not have got here */ WARN_ON(1); break; } return NULL; } #endif /* CONFIG_MODULES */ |
499a24256 lwtunnel: infrast... |
58 59 60 61 62 63 64 65 |
struct lwtunnel_state *lwtunnel_state_alloc(int encap_len) { struct lwtunnel_state *lws; lws = kzalloc(sizeof(*lws) + encap_len, GFP_ATOMIC); return lws; } |
08bd10ffb lwtunnel: replace... |
66 |
EXPORT_SYMBOL_GPL(lwtunnel_state_alloc); |
499a24256 lwtunnel: infrast... |
67 |
|
92a99bf3b lwtunnel: Make lw... |
68 |
static const struct lwtunnel_encap_ops __rcu * |
499a24256 lwtunnel: infrast... |
69 70 71 72 73 74 75 76 77 78 79 80 |
lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly; int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops, unsigned int num) { if (num > LWTUNNEL_ENCAP_MAX) return -ERANGE; return !cmpxchg((const struct lwtunnel_encap_ops **) &lwtun_encaps[num], NULL, ops) ? 0 : -1; } |
08bd10ffb lwtunnel: replace... |
81 |
EXPORT_SYMBOL_GPL(lwtunnel_encap_add_ops); |
499a24256 lwtunnel: infrast... |
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops, unsigned int encap_type) { int ret; if (encap_type == LWTUNNEL_ENCAP_NONE || encap_type > LWTUNNEL_ENCAP_MAX) return -ERANGE; ret = (cmpxchg((const struct lwtunnel_encap_ops **) &lwtun_encaps[encap_type], ops, NULL) == ops) ? 0 : -1; synchronize_net(); return ret; } |
08bd10ffb lwtunnel: replace... |
100 |
EXPORT_SYMBOL_GPL(lwtunnel_encap_del_ops); |
499a24256 lwtunnel: infrast... |
101 |
|
faee67694 net: add net avai... |
102 |
int lwtunnel_build_state(struct net *net, u16 encap_type, |
127eb7cd3 lwt: Add cfg argu... |
103 |
struct nlattr *encap, unsigned int family, |
9ae287274 net: add extack a... |
104 105 |
const void *cfg, struct lwtunnel_state **lws, struct netlink_ext_ack *extack) |
499a24256 lwtunnel: infrast... |
106 107 |
{ const struct lwtunnel_encap_ops *ops; |
9ae287274 net: add extack a... |
108 |
bool found = false; |
499a24256 lwtunnel: infrast... |
109 110 111 |
int ret = -EINVAL; if (encap_type == LWTUNNEL_ENCAP_NONE || |
9ae287274 net: add extack a... |
112 113 114 |
encap_type > LWTUNNEL_ENCAP_MAX) { NL_SET_ERR_MSG_ATTR(extack, encap, "Unknown LWT encapsulation type"); |
499a24256 lwtunnel: infrast... |
115 |
return ret; |
9ae287274 net: add extack a... |
116 |
} |
499a24256 lwtunnel: infrast... |
117 118 119 120 |
ret = -EOPNOTSUPP; rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[encap_type]); |
3d25eabbb ip_tunnel: Add ds... |
121 |
if (likely(ops && ops->build_state && try_module_get(ops->owner))) |
9ae287274 net: add extack a... |
122 |
found = true; |
3d25eabbb ip_tunnel: Add ds... |
123 124 125 |
rcu_read_unlock(); if (found) { |
faee67694 net: add net avai... |
126 |
ret = ops->build_state(net, encap, family, cfg, lws, extack); |
85c814016 lwtunnel: Fix oop... |
127 128 |
if (ret) module_put(ops->owner); |
3d25eabbb ip_tunnel: Add ds... |
129 130 131 132 |
} else { /* don't rely on -EOPNOTSUPP to detect match as build_state * handlers could return it */ |
9ae287274 net: add extack a... |
133 134 135 |
NL_SET_ERR_MSG_ATTR(extack, encap, "LWT encapsulation type not supported"); } |
9ed59592e lwtunnel: fix aut... |
136 137 |
return ret; } |
08bd10ffb lwtunnel: replace... |
138 |
EXPORT_SYMBOL_GPL(lwtunnel_build_state); |
9ed59592e lwtunnel: fix aut... |
139 |
|
c255bd681 net: lwtunnel: Ad... |
140 |
int lwtunnel_valid_encap_type(u16 encap_type, struct netlink_ext_ack *extack) |
9ed59592e lwtunnel: fix aut... |
141 142 143 144 145 |
{ const struct lwtunnel_encap_ops *ops; int ret = -EINVAL; if (encap_type == LWTUNNEL_ENCAP_NONE || |
c255bd681 net: lwtunnel: Ad... |
146 147 |
encap_type > LWTUNNEL_ENCAP_MAX) { NL_SET_ERR_MSG(extack, "Unknown lwt encapsulation type"); |
9ed59592e lwtunnel: fix aut... |
148 |
return ret; |
c255bd681 net: lwtunnel: Ad... |
149 |
} |
9ed59592e lwtunnel: fix aut... |
150 151 152 153 |
rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[encap_type]); rcu_read_unlock(); |
745041e2a lwtunnel: autoloa... |
154 155 156 157 158 |
#ifdef CONFIG_MODULES if (!ops) { const char *encap_type_str = lwtunnel_encap_str(encap_type); if (encap_type_str) { |
9ed59592e lwtunnel: fix aut... |
159 |
__rtnl_unlock(); |
745041e2a lwtunnel: autoloa... |
160 |
request_module("rtnl-lwt-%s", encap_type_str); |
9ed59592e lwtunnel: fix aut... |
161 |
rtnl_lock(); |
745041e2a lwtunnel: autoloa... |
162 163 |
rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[encap_type]); |
9ed59592e lwtunnel: fix aut... |
164 |
rcu_read_unlock(); |
745041e2a lwtunnel: autoloa... |
165 166 167 |
} } #endif |
c255bd681 net: lwtunnel: Ad... |
168 169 170 171 172 |
ret = ops ? 0 : -EOPNOTSUPP; if (ret < 0) NL_SET_ERR_MSG(extack, "lwt encapsulation type not supported"); return ret; |
9ed59592e lwtunnel: fix aut... |
173 |
} |
08bd10ffb lwtunnel: replace... |
174 |
EXPORT_SYMBOL_GPL(lwtunnel_valid_encap_type); |
499a24256 lwtunnel: infrast... |
175 |
|
c255bd681 net: lwtunnel: Ad... |
176 177 |
int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining, struct netlink_ext_ack *extack) |
9ed59592e lwtunnel: fix aut... |
178 179 180 181 |
{ struct rtnexthop *rtnh = (struct rtnexthop *)attr; struct nlattr *nla_entype; struct nlattr *attrs; |
9ed59592e lwtunnel: fix aut... |
182 183 184 185 186 187 188 |
u16 encap_type; int attrlen; while (rtnh_ok(rtnh, remaining)) { attrlen = rtnh_attrlen(rtnh); if (attrlen > 0) { attrs = rtnh_attrs(rtnh); |
9ed59592e lwtunnel: fix aut... |
189 190 191 192 |
nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); if (nla_entype) { encap_type = nla_get_u16(nla_entype); |
c255bd681 net: lwtunnel: Ad... |
193 194 |
if (lwtunnel_valid_encap_type(encap_type, extack) != 0) |
9ed59592e lwtunnel: fix aut... |
195 196 197 198 199 200 201 |
return -EOPNOTSUPP; } } rtnh = rtnh_next(rtnh, &remaining); } return 0; |
499a24256 lwtunnel: infrast... |
202 |
} |
08bd10ffb lwtunnel: replace... |
203 |
EXPORT_SYMBOL_GPL(lwtunnel_valid_encap_type_attr); |
499a24256 lwtunnel: infrast... |
204 |
|
1104d9ba4 lwtunnel: Add des... |
205 206 207 208 209 210 211 212 213 214 |
void lwtstate_free(struct lwtunnel_state *lws) { const struct lwtunnel_encap_ops *ops = lwtun_encaps[lws->type]; if (ops->destroy_state) { ops->destroy_state(lws); kfree_rcu(lws, rcu); } else { kfree(lws); } |
85c814016 lwtunnel: Fix oop... |
215 |
module_put(ops->owner); |
1104d9ba4 lwtunnel: Add des... |
216 |
} |
08bd10ffb lwtunnel: replace... |
217 |
EXPORT_SYMBOL_GPL(lwtstate_free); |
1104d9ba4 lwtunnel: Add des... |
218 |
|
ffa8ce54b lwtunnel: Pass en... |
219 220 |
int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate, int encap_attr, int encap_type_attr) |
499a24256 lwtunnel: infrast... |
221 222 223 |
{ const struct lwtunnel_encap_ops *ops; struct nlattr *nest; |
39f370959 lwtunnel: fix err... |
224 |
int ret; |
499a24256 lwtunnel: infrast... |
225 226 227 228 229 230 231 |
if (!lwtstate) return 0; if (lwtstate->type == LWTUNNEL_ENCAP_NONE || lwtstate->type > LWTUNNEL_ENCAP_MAX) return 0; |
ae0be8de9 netlink: make nla... |
232 |
nest = nla_nest_start_noflag(skb, encap_attr); |
a50fe0ffd lwtunnel: check r... |
233 |
if (!nest) |
39f370959 lwtunnel: fix err... |
234 235 236 |
return -EMSGSIZE; ret = -EOPNOTSUPP; |
499a24256 lwtunnel: infrast... |
237 238 239 240 241 242 243 244 245 |
rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[lwtstate->type]); if (likely(ops && ops->fill_encap)) ret = ops->fill_encap(skb, lwtstate); rcu_read_unlock(); if (ret) goto nla_put_failure; nla_nest_end(skb, nest); |
ffa8ce54b lwtunnel: Pass en... |
246 |
ret = nla_put_u16(skb, encap_type_attr, lwtstate->type); |
499a24256 lwtunnel: infrast... |
247 248 249 250 251 252 253 254 255 256 |
if (ret) goto nla_put_failure; return 0; nla_put_failure: nla_nest_cancel(skb, nest); return (ret == -EOPNOTSUPP ? 0 : ret); } |
08bd10ffb lwtunnel: replace... |
257 |
EXPORT_SYMBOL_GPL(lwtunnel_fill_encap); |
499a24256 lwtunnel: infrast... |
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate) { const struct lwtunnel_encap_ops *ops; int ret = 0; if (!lwtstate) return 0; if (lwtstate->type == LWTUNNEL_ENCAP_NONE || lwtstate->type > LWTUNNEL_ENCAP_MAX) return 0; rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[lwtstate->type]); if (likely(ops && ops->get_encap_size)) ret = nla_total_size(ops->get_encap_size(lwtstate)); rcu_read_unlock(); return ret; } |
08bd10ffb lwtunnel: replace... |
279 |
EXPORT_SYMBOL_GPL(lwtunnel_get_encap_size); |
499a24256 lwtunnel: infrast... |
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b) { const struct lwtunnel_encap_ops *ops; int ret = 0; if (!a && !b) return 0; if (!a || !b) return 1; if (a->type != b->type) return 1; if (a->type == LWTUNNEL_ENCAP_NONE || a->type > LWTUNNEL_ENCAP_MAX) return 0; rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[a->type]); if (likely(ops && ops->cmp_encap)) ret = ops->cmp_encap(a, b); rcu_read_unlock(); return ret; } |
08bd10ffb lwtunnel: replace... |
307 |
EXPORT_SYMBOL_GPL(lwtunnel_cmp_encap); |
ffce41962 lwtunnel: support... |
308 |
|
ede2059db dst: Pass net int... |
309 |
int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb) |
ffce41962 lwtunnel: support... |
310 |
{ |
61adedf3e route: move lwtun... |
311 |
struct dst_entry *dst = skb_dst(skb); |
ffce41962 lwtunnel: support... |
312 |
const struct lwtunnel_encap_ops *ops; |
61adedf3e route: move lwtun... |
313 |
struct lwtunnel_state *lwtstate; |
ffce41962 lwtunnel: support... |
314 |
int ret = -EINVAL; |
61adedf3e route: move lwtun... |
315 |
if (!dst) |
ffce41962 lwtunnel: support... |
316 |
goto drop; |
61adedf3e route: move lwtun... |
317 |
lwtstate = dst->lwtstate; |
ffce41962 lwtunnel: support... |
318 319 320 321 322 323 324 325 326 |
if (lwtstate->type == LWTUNNEL_ENCAP_NONE || lwtstate->type > LWTUNNEL_ENCAP_MAX) return 0; ret = -EOPNOTSUPP; rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[lwtstate->type]); if (likely(ops && ops->output)) |
ede2059db dst: Pass net int... |
327 |
ret = ops->output(net, sk, skb); |
ffce41962 lwtunnel: support... |
328 329 330 331 332 333 334 335 |
rcu_read_unlock(); if (ret == -EOPNOTSUPP) goto drop; return ret; drop: |
e11f40b93 lwtunnel: use kfr... |
336 |
kfree_skb(skb); |
ffce41962 lwtunnel: support... |
337 338 339 |
return ret; } |
08bd10ffb lwtunnel: replace... |
340 |
EXPORT_SYMBOL_GPL(lwtunnel_output); |
253686231 lwt: Add support ... |
341 |
|
14972cbd3 net: lwtunnel: Ha... |
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
int lwtunnel_xmit(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); const struct lwtunnel_encap_ops *ops; struct lwtunnel_state *lwtstate; int ret = -EINVAL; if (!dst) goto drop; lwtstate = dst->lwtstate; if (lwtstate->type == LWTUNNEL_ENCAP_NONE || lwtstate->type > LWTUNNEL_ENCAP_MAX) return 0; ret = -EOPNOTSUPP; rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[lwtstate->type]); if (likely(ops && ops->xmit)) ret = ops->xmit(skb); rcu_read_unlock(); if (ret == -EOPNOTSUPP) goto drop; return ret; drop: kfree_skb(skb); return ret; } |
08bd10ffb lwtunnel: replace... |
375 |
EXPORT_SYMBOL_GPL(lwtunnel_xmit); |
14972cbd3 net: lwtunnel: Ha... |
376 |
|
61adedf3e route: move lwtun... |
377 |
int lwtunnel_input(struct sk_buff *skb) |
253686231 lwt: Add support ... |
378 |
{ |
61adedf3e route: move lwtun... |
379 |
struct dst_entry *dst = skb_dst(skb); |
253686231 lwt: Add support ... |
380 |
const struct lwtunnel_encap_ops *ops; |
61adedf3e route: move lwtun... |
381 |
struct lwtunnel_state *lwtstate; |
253686231 lwt: Add support ... |
382 |
int ret = -EINVAL; |
61adedf3e route: move lwtun... |
383 |
if (!dst) |
253686231 lwt: Add support ... |
384 |
goto drop; |
61adedf3e route: move lwtun... |
385 |
lwtstate = dst->lwtstate; |
253686231 lwt: Add support ... |
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
if (lwtstate->type == LWTUNNEL_ENCAP_NONE || lwtstate->type > LWTUNNEL_ENCAP_MAX) return 0; ret = -EOPNOTSUPP; rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[lwtstate->type]); if (likely(ops && ops->input)) ret = ops->input(skb); rcu_read_unlock(); if (ret == -EOPNOTSUPP) goto drop; return ret; drop: kfree_skb(skb); return ret; } |
08bd10ffb lwtunnel: replace... |
408 |
EXPORT_SYMBOL_GPL(lwtunnel_input); |