.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * INET An implementation of the TCP/IP protocol suite for the LINUX |
---|
3 | 4 | * operating system. INET is implemented using the BSD Socket |
---|
.. | .. |
---|
6 | 7 | * IPv4 Forwarding Information Base: semantics. |
---|
7 | 8 | * |
---|
8 | 9 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
---|
9 | | - * |
---|
10 | | - * This program is free software; you can redistribute it and/or |
---|
11 | | - * modify it under the terms of the GNU General Public License |
---|
12 | | - * as published by the Free Software Foundation; either version |
---|
13 | | - * 2 of the License, or (at your option) any later version. |
---|
14 | 10 | */ |
---|
15 | 11 | |
---|
16 | 12 | #include <linux/uaccess.h> |
---|
.. | .. |
---|
33 | 29 | #include <linux/init.h> |
---|
34 | 30 | #include <linux/slab.h> |
---|
35 | 31 | #include <linux/netlink.h> |
---|
| 32 | +#include <linux/hash.h> |
---|
| 33 | +#include <linux/nospec.h> |
---|
36 | 34 | |
---|
37 | 35 | #include <net/arp.h> |
---|
38 | 36 | #include <net/ip.h> |
---|
.. | .. |
---|
41 | 39 | #include <net/tcp.h> |
---|
42 | 40 | #include <net/sock.h> |
---|
43 | 41 | #include <net/ip_fib.h> |
---|
44 | | -#include <net/netlink.h> |
---|
| 42 | +#include <net/ip6_fib.h> |
---|
45 | 43 | #include <net/nexthop.h> |
---|
| 44 | +#include <net/netlink.h> |
---|
| 45 | +#include <net/rtnh.h> |
---|
46 | 46 | #include <net/lwtunnel.h> |
---|
47 | 47 | #include <net/fib_notifier.h> |
---|
| 48 | +#include <net/addrconf.h> |
---|
48 | 49 | |
---|
49 | 50 | #include "fib_lookup.h" |
---|
50 | 51 | |
---|
.. | .. |
---|
58 | 59 | #define DEVINDEX_HASHSIZE (1U << DEVINDEX_HASHBITS) |
---|
59 | 60 | static struct hlist_head fib_info_devhash[DEVINDEX_HASHSIZE]; |
---|
60 | 61 | |
---|
| 62 | +/* for_nexthops and change_nexthops only used when nexthop object |
---|
| 63 | + * is not set in a fib_info. The logic within can reference fib_nh. |
---|
| 64 | + */ |
---|
61 | 65 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
62 | 66 | |
---|
63 | 67 | #define for_nexthops(fi) { \ |
---|
64 | 68 | int nhsel; const struct fib_nh *nh; \ |
---|
65 | 69 | for (nhsel = 0, nh = (fi)->fib_nh; \ |
---|
66 | | - nhsel < (fi)->fib_nhs; \ |
---|
| 70 | + nhsel < fib_info_num_path((fi)); \ |
---|
67 | 71 | nh++, nhsel++) |
---|
68 | 72 | |
---|
69 | 73 | #define change_nexthops(fi) { \ |
---|
70 | 74 | int nhsel; struct fib_nh *nexthop_nh; \ |
---|
71 | 75 | for (nhsel = 0, nexthop_nh = (struct fib_nh *)((fi)->fib_nh); \ |
---|
72 | | - nhsel < (fi)->fib_nhs; \ |
---|
| 76 | + nhsel < fib_info_num_path((fi)); \ |
---|
73 | 77 | nexthop_nh++, nhsel++) |
---|
74 | 78 | |
---|
75 | 79 | #else /* CONFIG_IP_ROUTE_MULTIPATH */ |
---|
.. | .. |
---|
157 | 161 | dst_release_immediate(&rt->dst); |
---|
158 | 162 | } |
---|
159 | 163 | |
---|
160 | | -static void free_nh_exceptions(struct fib_nh *nh) |
---|
| 164 | +static void free_nh_exceptions(struct fib_nh_common *nhc) |
---|
161 | 165 | { |
---|
162 | 166 | struct fnhe_hash_bucket *hash; |
---|
163 | 167 | int i; |
---|
164 | 168 | |
---|
165 | | - hash = rcu_dereference_protected(nh->nh_exceptions, 1); |
---|
| 169 | + hash = rcu_dereference_protected(nhc->nhc_exceptions, 1); |
---|
166 | 170 | if (!hash) |
---|
167 | 171 | return; |
---|
168 | 172 | for (i = 0; i < FNHE_HASH_SIZE; i++) { |
---|
.. | .. |
---|
204 | 208 | free_percpu(rtp); |
---|
205 | 209 | } |
---|
206 | 210 | |
---|
| 211 | +void fib_nh_common_release(struct fib_nh_common *nhc) |
---|
| 212 | +{ |
---|
| 213 | + if (nhc->nhc_dev) |
---|
| 214 | + dev_put(nhc->nhc_dev); |
---|
| 215 | + |
---|
| 216 | + lwtstate_put(nhc->nhc_lwtstate); |
---|
| 217 | + rt_fibinfo_free_cpus(nhc->nhc_pcpu_rth_output); |
---|
| 218 | + rt_fibinfo_free(&nhc->nhc_rth_input); |
---|
| 219 | + free_nh_exceptions(nhc); |
---|
| 220 | +} |
---|
| 221 | +EXPORT_SYMBOL_GPL(fib_nh_common_release); |
---|
| 222 | + |
---|
| 223 | +void fib_nh_release(struct net *net, struct fib_nh *fib_nh) |
---|
| 224 | +{ |
---|
| 225 | +#ifdef CONFIG_IP_ROUTE_CLASSID |
---|
| 226 | + if (fib_nh->nh_tclassid) |
---|
| 227 | + atomic_dec(&net->ipv4.fib_num_tclassid_users); |
---|
| 228 | +#endif |
---|
| 229 | + fib_nh_common_release(&fib_nh->nh_common); |
---|
| 230 | +} |
---|
| 231 | + |
---|
207 | 232 | /* Release a nexthop info record */ |
---|
208 | 233 | static void free_fib_info_rcu(struct rcu_head *head) |
---|
209 | 234 | { |
---|
210 | 235 | struct fib_info *fi = container_of(head, struct fib_info, rcu); |
---|
211 | | - struct dst_metrics *m; |
---|
212 | 236 | |
---|
213 | | - change_nexthops(fi) { |
---|
214 | | - if (nexthop_nh->nh_dev) |
---|
215 | | - dev_put(nexthop_nh->nh_dev); |
---|
216 | | - lwtstate_put(nexthop_nh->nh_lwtstate); |
---|
217 | | - free_nh_exceptions(nexthop_nh); |
---|
218 | | - rt_fibinfo_free_cpus(nexthop_nh->nh_pcpu_rth_output); |
---|
219 | | - rt_fibinfo_free(&nexthop_nh->nh_rth_input); |
---|
220 | | - } endfor_nexthops(fi); |
---|
| 237 | + if (fi->nh) { |
---|
| 238 | + nexthop_put(fi->nh); |
---|
| 239 | + } else { |
---|
| 240 | + change_nexthops(fi) { |
---|
| 241 | + fib_nh_release(fi->fib_net, nexthop_nh); |
---|
| 242 | + } endfor_nexthops(fi); |
---|
| 243 | + } |
---|
221 | 244 | |
---|
222 | | - m = fi->fib_metrics; |
---|
223 | | - if (m != &dst_default_metrics && refcount_dec_and_test(&m->refcnt)) |
---|
224 | | - kfree(m); |
---|
| 245 | + ip_fib_metrics_put(fi->fib_metrics); |
---|
| 246 | + |
---|
225 | 247 | kfree(fi); |
---|
226 | 248 | } |
---|
227 | 249 | |
---|
.. | .. |
---|
231 | 253 | pr_warn("Freeing alive fib_info %p\n", fi); |
---|
232 | 254 | return; |
---|
233 | 255 | } |
---|
234 | | - fib_info_cnt--; |
---|
235 | | -#ifdef CONFIG_IP_ROUTE_CLASSID |
---|
236 | | - change_nexthops(fi) { |
---|
237 | | - if (nexthop_nh->nh_tclassid) |
---|
238 | | - fi->fib_net->ipv4.fib_num_tclassid_users--; |
---|
239 | | - } endfor_nexthops(fi); |
---|
240 | | -#endif |
---|
| 256 | + |
---|
241 | 257 | call_rcu(&fi->rcu, free_fib_info_rcu); |
---|
242 | 258 | } |
---|
243 | 259 | EXPORT_SYMBOL_GPL(free_fib_info); |
---|
.. | .. |
---|
247 | 263 | spin_lock_bh(&fib_info_lock); |
---|
248 | 264 | if (fi && --fi->fib_treeref == 0) { |
---|
249 | 265 | hlist_del(&fi->fib_hash); |
---|
| 266 | + |
---|
| 267 | + /* Paired with READ_ONCE() in fib_create_info(). */ |
---|
| 268 | + WRITE_ONCE(fib_info_cnt, fib_info_cnt - 1); |
---|
| 269 | + |
---|
250 | 270 | if (fi->fib_prefsrc) |
---|
251 | 271 | hlist_del(&fi->fib_lhash); |
---|
252 | | - change_nexthops(fi) { |
---|
253 | | - if (!nexthop_nh->nh_dev) |
---|
254 | | - continue; |
---|
255 | | - hlist_del(&nexthop_nh->nh_hash); |
---|
256 | | - } endfor_nexthops(fi) |
---|
257 | | - fi->fib_dead = 1; |
---|
| 272 | + if (fi->nh) { |
---|
| 273 | + list_del(&fi->nh_list); |
---|
| 274 | + } else { |
---|
| 275 | + change_nexthops(fi) { |
---|
| 276 | + if (!nexthop_nh->fib_nh_dev) |
---|
| 277 | + continue; |
---|
| 278 | + hlist_del(&nexthop_nh->nh_hash); |
---|
| 279 | + } endfor_nexthops(fi) |
---|
| 280 | + } |
---|
| 281 | + /* Paired with READ_ONCE() from fib_table_lookup() */ |
---|
| 282 | + WRITE_ONCE(fi->fib_dead, 1); |
---|
258 | 283 | fib_info_put(fi); |
---|
259 | 284 | } |
---|
260 | 285 | spin_unlock_bh(&fib_info_lock); |
---|
261 | 286 | } |
---|
262 | 287 | |
---|
263 | | -static inline int nh_comp(const struct fib_info *fi, const struct fib_info *ofi) |
---|
| 288 | +static inline int nh_comp(struct fib_info *fi, struct fib_info *ofi) |
---|
264 | 289 | { |
---|
265 | | - const struct fib_nh *onh = ofi->fib_nh; |
---|
| 290 | + const struct fib_nh *onh; |
---|
| 291 | + |
---|
| 292 | + if (fi->nh || ofi->nh) |
---|
| 293 | + return nexthop_cmp(fi->nh, ofi->nh) ? 0 : -1; |
---|
| 294 | + |
---|
| 295 | + if (ofi->fib_nhs == 0) |
---|
| 296 | + return 0; |
---|
266 | 297 | |
---|
267 | 298 | for_nexthops(fi) { |
---|
268 | | - if (nh->nh_oif != onh->nh_oif || |
---|
269 | | - nh->nh_gw != onh->nh_gw || |
---|
270 | | - nh->nh_scope != onh->nh_scope || |
---|
| 299 | + onh = fib_info_nh(ofi, nhsel); |
---|
| 300 | + |
---|
| 301 | + if (nh->fib_nh_oif != onh->fib_nh_oif || |
---|
| 302 | + nh->fib_nh_gw_family != onh->fib_nh_gw_family || |
---|
| 303 | + nh->fib_nh_scope != onh->fib_nh_scope || |
---|
271 | 304 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
272 | | - nh->nh_weight != onh->nh_weight || |
---|
| 305 | + nh->fib_nh_weight != onh->fib_nh_weight || |
---|
273 | 306 | #endif |
---|
274 | 307 | #ifdef CONFIG_IP_ROUTE_CLASSID |
---|
275 | 308 | nh->nh_tclassid != onh->nh_tclassid || |
---|
276 | 309 | #endif |
---|
277 | | - lwtunnel_cmp_encap(nh->nh_lwtstate, onh->nh_lwtstate) || |
---|
278 | | - ((nh->nh_flags ^ onh->nh_flags) & ~RTNH_COMPARE_MASK)) |
---|
| 310 | + lwtunnel_cmp_encap(nh->fib_nh_lws, onh->fib_nh_lws) || |
---|
| 311 | + ((nh->fib_nh_flags ^ onh->fib_nh_flags) & ~RTNH_COMPARE_MASK)) |
---|
279 | 312 | return -1; |
---|
280 | | - onh++; |
---|
| 313 | + |
---|
| 314 | + if (nh->fib_nh_gw_family == AF_INET && |
---|
| 315 | + nh->fib_nh_gw4 != onh->fib_nh_gw4) |
---|
| 316 | + return -1; |
---|
| 317 | + |
---|
| 318 | + if (nh->fib_nh_gw_family == AF_INET6 && |
---|
| 319 | + ipv6_addr_cmp(&nh->fib_nh_gw6, &onh->fib_nh_gw6)) |
---|
| 320 | + return -1; |
---|
281 | 321 | } endfor_nexthops(fi); |
---|
282 | 322 | return 0; |
---|
283 | 323 | } |
---|
284 | 324 | |
---|
285 | 325 | static inline unsigned int fib_devindex_hashfn(unsigned int val) |
---|
286 | 326 | { |
---|
287 | | - unsigned int mask = DEVINDEX_HASHSIZE - 1; |
---|
288 | | - |
---|
289 | | - return (val ^ |
---|
290 | | - (val >> DEVINDEX_HASHBITS) ^ |
---|
291 | | - (val >> (DEVINDEX_HASHBITS * 2))) & mask; |
---|
| 327 | + return hash_32(val, DEVINDEX_HASHBITS); |
---|
292 | 328 | } |
---|
293 | 329 | |
---|
294 | | -static inline unsigned int fib_info_hashfn(const struct fib_info *fi) |
---|
| 330 | +static struct hlist_head * |
---|
| 331 | +fib_info_devhash_bucket(const struct net_device *dev) |
---|
| 332 | +{ |
---|
| 333 | + u32 val = net_hash_mix(dev_net(dev)) ^ dev->ifindex; |
---|
| 334 | + |
---|
| 335 | + return &fib_info_devhash[fib_devindex_hashfn(val)]; |
---|
| 336 | +} |
---|
| 337 | + |
---|
| 338 | +static unsigned int fib_info_hashfn_1(int init_val, u8 protocol, u8 scope, |
---|
| 339 | + u32 prefsrc, u32 priority) |
---|
| 340 | +{ |
---|
| 341 | + unsigned int val = init_val; |
---|
| 342 | + |
---|
| 343 | + val ^= (protocol << 8) | scope; |
---|
| 344 | + val ^= prefsrc; |
---|
| 345 | + val ^= priority; |
---|
| 346 | + |
---|
| 347 | + return val; |
---|
| 348 | +} |
---|
| 349 | + |
---|
| 350 | +static unsigned int fib_info_hashfn_result(unsigned int val) |
---|
295 | 351 | { |
---|
296 | 352 | unsigned int mask = (fib_info_hash_size - 1); |
---|
297 | | - unsigned int val = fi->fib_nhs; |
---|
298 | | - |
---|
299 | | - val ^= (fi->fib_protocol << 8) | fi->fib_scope; |
---|
300 | | - val ^= (__force u32)fi->fib_prefsrc; |
---|
301 | | - val ^= fi->fib_priority; |
---|
302 | | - for_nexthops(fi) { |
---|
303 | | - val ^= fib_devindex_hashfn(nh->nh_oif); |
---|
304 | | - } endfor_nexthops(fi) |
---|
305 | 353 | |
---|
306 | 354 | return (val ^ (val >> 7) ^ (val >> 12)) & mask; |
---|
307 | 355 | } |
---|
308 | 356 | |
---|
309 | | -static struct fib_info *fib_find_info(const struct fib_info *nfi) |
---|
| 357 | +static inline unsigned int fib_info_hashfn(struct fib_info *fi) |
---|
| 358 | +{ |
---|
| 359 | + unsigned int val; |
---|
| 360 | + |
---|
| 361 | + val = fib_info_hashfn_1(fi->fib_nhs, fi->fib_protocol, |
---|
| 362 | + fi->fib_scope, (__force u32)fi->fib_prefsrc, |
---|
| 363 | + fi->fib_priority); |
---|
| 364 | + |
---|
| 365 | + if (fi->nh) { |
---|
| 366 | + val ^= fib_devindex_hashfn(fi->nh->id); |
---|
| 367 | + } else { |
---|
| 368 | + for_nexthops(fi) { |
---|
| 369 | + val ^= fib_devindex_hashfn(nh->fib_nh_oif); |
---|
| 370 | + } endfor_nexthops(fi) |
---|
| 371 | + } |
---|
| 372 | + |
---|
| 373 | + return fib_info_hashfn_result(val); |
---|
| 374 | +} |
---|
| 375 | + |
---|
| 376 | +/* no metrics, only nexthop id */ |
---|
| 377 | +static struct fib_info *fib_find_info_nh(struct net *net, |
---|
| 378 | + const struct fib_config *cfg) |
---|
| 379 | +{ |
---|
| 380 | + struct hlist_head *head; |
---|
| 381 | + struct fib_info *fi; |
---|
| 382 | + unsigned int hash; |
---|
| 383 | + |
---|
| 384 | + hash = fib_info_hashfn_1(fib_devindex_hashfn(cfg->fc_nh_id), |
---|
| 385 | + cfg->fc_protocol, cfg->fc_scope, |
---|
| 386 | + (__force u32)cfg->fc_prefsrc, |
---|
| 387 | + cfg->fc_priority); |
---|
| 388 | + hash = fib_info_hashfn_result(hash); |
---|
| 389 | + head = &fib_info_hash[hash]; |
---|
| 390 | + |
---|
| 391 | + hlist_for_each_entry(fi, head, fib_hash) { |
---|
| 392 | + if (!net_eq(fi->fib_net, net)) |
---|
| 393 | + continue; |
---|
| 394 | + if (!fi->nh || fi->nh->id != cfg->fc_nh_id) |
---|
| 395 | + continue; |
---|
| 396 | + if (cfg->fc_protocol == fi->fib_protocol && |
---|
| 397 | + cfg->fc_scope == fi->fib_scope && |
---|
| 398 | + cfg->fc_prefsrc == fi->fib_prefsrc && |
---|
| 399 | + cfg->fc_priority == fi->fib_priority && |
---|
| 400 | + cfg->fc_type == fi->fib_type && |
---|
| 401 | + cfg->fc_table == fi->fib_tb_id && |
---|
| 402 | + !((cfg->fc_flags ^ fi->fib_flags) & ~RTNH_COMPARE_MASK)) |
---|
| 403 | + return fi; |
---|
| 404 | + } |
---|
| 405 | + |
---|
| 406 | + return NULL; |
---|
| 407 | +} |
---|
| 408 | + |
---|
| 409 | +static struct fib_info *fib_find_info(struct fib_info *nfi) |
---|
310 | 410 | { |
---|
311 | 411 | struct hlist_head *head; |
---|
312 | 412 | struct fib_info *fi; |
---|
.. | .. |
---|
325 | 425 | nfi->fib_prefsrc == fi->fib_prefsrc && |
---|
326 | 426 | nfi->fib_priority == fi->fib_priority && |
---|
327 | 427 | nfi->fib_type == fi->fib_type && |
---|
| 428 | + nfi->fib_tb_id == fi->fib_tb_id && |
---|
328 | 429 | memcmp(nfi->fib_metrics, fi->fib_metrics, |
---|
329 | 430 | sizeof(u32) * RTAX_MAX) == 0 && |
---|
330 | 431 | !((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_COMPARE_MASK) && |
---|
331 | | - (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0)) |
---|
| 432 | + nh_comp(fi, nfi) == 0) |
---|
332 | 433 | return fi; |
---|
333 | 434 | } |
---|
334 | 435 | |
---|
.. | .. |
---|
342 | 443 | { |
---|
343 | 444 | struct hlist_head *head; |
---|
344 | 445 | struct fib_nh *nh; |
---|
345 | | - unsigned int hash; |
---|
346 | 446 | |
---|
347 | 447 | spin_lock(&fib_info_lock); |
---|
348 | 448 | |
---|
349 | | - hash = fib_devindex_hashfn(dev->ifindex); |
---|
350 | | - head = &fib_info_devhash[hash]; |
---|
| 449 | + head = fib_info_devhash_bucket(dev); |
---|
| 450 | + |
---|
351 | 451 | hlist_for_each_entry(nh, head, nh_hash) { |
---|
352 | | - if (nh->nh_dev == dev && |
---|
353 | | - nh->nh_gw == gw && |
---|
354 | | - !(nh->nh_flags & RTNH_F_DEAD)) { |
---|
| 452 | + if (nh->fib_nh_dev == dev && |
---|
| 453 | + nh->fib_nh_gw4 == gw && |
---|
| 454 | + !(nh->fib_nh_flags & RTNH_F_DEAD)) { |
---|
355 | 455 | spin_unlock(&fib_info_lock); |
---|
356 | 456 | return 0; |
---|
357 | 457 | } |
---|
.. | .. |
---|
370 | 470 | + nla_total_size(4) /* RTA_PRIORITY */ |
---|
371 | 471 | + nla_total_size(4) /* RTA_PREFSRC */ |
---|
372 | 472 | + nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */ |
---|
| 473 | + unsigned int nhs = fib_info_num_path(fi); |
---|
373 | 474 | |
---|
374 | 475 | /* space for nested metrics */ |
---|
375 | 476 | payload += nla_total_size((RTAX_MAX * nla_total_size(4))); |
---|
376 | 477 | |
---|
377 | | - if (fi->fib_nhs) { |
---|
| 478 | + if (fi->nh) |
---|
| 479 | + payload += nla_total_size(4); /* RTA_NH_ID */ |
---|
| 480 | + |
---|
| 481 | + if (nhs) { |
---|
378 | 482 | size_t nh_encapsize = 0; |
---|
379 | | - /* Also handles the special case fib_nhs == 1 */ |
---|
| 483 | + /* Also handles the special case nhs == 1 */ |
---|
380 | 484 | |
---|
381 | 485 | /* each nexthop is packed in an attribute */ |
---|
382 | 486 | size_t nhsize = nla_total_size(sizeof(struct rtnexthop)); |
---|
| 487 | + unsigned int i; |
---|
383 | 488 | |
---|
384 | 489 | /* may contain flow and gateway attribute */ |
---|
385 | 490 | nhsize += 2 * nla_total_size(4); |
---|
386 | 491 | |
---|
387 | 492 | /* grab encap info */ |
---|
388 | | - for_nexthops(fi) { |
---|
389 | | - if (nh->nh_lwtstate) { |
---|
| 493 | + for (i = 0; i < fib_info_num_path(fi); i++) { |
---|
| 494 | + struct fib_nh_common *nhc = fib_info_nhc(fi, i); |
---|
| 495 | + |
---|
| 496 | + if (nhc->nhc_lwtstate) { |
---|
390 | 497 | /* RTA_ENCAP_TYPE */ |
---|
391 | 498 | nh_encapsize += lwtunnel_get_encap_size( |
---|
392 | | - nh->nh_lwtstate); |
---|
| 499 | + nhc->nhc_lwtstate); |
---|
393 | 500 | /* RTA_ENCAP */ |
---|
394 | 501 | nh_encapsize += nla_total_size(2); |
---|
395 | 502 | } |
---|
396 | | - } endfor_nexthops(fi); |
---|
| 503 | + } |
---|
397 | 504 | |
---|
398 | 505 | /* all nexthops are packed in a nested attribute */ |
---|
399 | | - payload += nla_total_size((fi->fib_nhs * nhsize) + |
---|
400 | | - nh_encapsize); |
---|
| 506 | + payload += nla_total_size((nhs * nhsize) + nh_encapsize); |
---|
401 | 507 | |
---|
402 | 508 | } |
---|
403 | 509 | |
---|
.. | .. |
---|
408 | 514 | int dst_len, u32 tb_id, const struct nl_info *info, |
---|
409 | 515 | unsigned int nlm_flags) |
---|
410 | 516 | { |
---|
| 517 | + struct fib_rt_info fri; |
---|
411 | 518 | struct sk_buff *skb; |
---|
412 | 519 | u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; |
---|
413 | 520 | int err = -ENOBUFS; |
---|
.. | .. |
---|
416 | 523 | if (!skb) |
---|
417 | 524 | goto errout; |
---|
418 | 525 | |
---|
419 | | - err = fib_dump_info(skb, info->portid, seq, event, tb_id, |
---|
420 | | - fa->fa_type, key, dst_len, |
---|
421 | | - fa->fa_tos, fa->fa_info, nlm_flags); |
---|
| 526 | + fri.fi = fa->fa_info; |
---|
| 527 | + fri.tb_id = tb_id; |
---|
| 528 | + fri.dst = key; |
---|
| 529 | + fri.dst_len = dst_len; |
---|
| 530 | + fri.tos = fa->fa_tos; |
---|
| 531 | + fri.type = fa->fa_type; |
---|
| 532 | + fri.offload = fa->offload; |
---|
| 533 | + fri.trap = fa->trap; |
---|
| 534 | + err = fib_dump_info(skb, info->portid, seq, event, &fri, nlm_flags); |
---|
422 | 535 | if (err < 0) { |
---|
423 | 536 | /* -EMSGSIZE implies BUG in fib_nlmsg_size() */ |
---|
424 | 537 | WARN_ON(err == -EMSGSIZE); |
---|
.. | .. |
---|
437 | 550 | struct fib_info **last_resort, int *last_idx, |
---|
438 | 551 | int dflt) |
---|
439 | 552 | { |
---|
| 553 | + const struct fib_nh_common *nhc = fib_info_nhc(fi, 0); |
---|
440 | 554 | struct neighbour *n; |
---|
441 | 555 | int state = NUD_NONE; |
---|
442 | 556 | |
---|
443 | | - n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].nh_gw, fi->fib_dev); |
---|
| 557 | + if (likely(nhc->nhc_gw_family == AF_INET)) |
---|
| 558 | + n = neigh_lookup(&arp_tbl, &nhc->nhc_gw.ipv4, nhc->nhc_dev); |
---|
| 559 | + else if (nhc->nhc_gw_family == AF_INET6) |
---|
| 560 | + n = neigh_lookup(ipv6_stub->nd_tbl, &nhc->nhc_gw.ipv6, |
---|
| 561 | + nhc->nhc_dev); |
---|
| 562 | + else |
---|
| 563 | + n = NULL; |
---|
| 564 | + |
---|
444 | 565 | if (n) { |
---|
445 | 566 | state = n->nud_state; |
---|
446 | 567 | neigh_release(n); |
---|
.. | .. |
---|
457 | 578 | *last_idx = order; |
---|
458 | 579 | } |
---|
459 | 580 | return 1; |
---|
| 581 | +} |
---|
| 582 | + |
---|
| 583 | +int fib_nh_common_init(struct net *net, struct fib_nh_common *nhc, |
---|
| 584 | + struct nlattr *encap, u16 encap_type, |
---|
| 585 | + void *cfg, gfp_t gfp_flags, |
---|
| 586 | + struct netlink_ext_ack *extack) |
---|
| 587 | +{ |
---|
| 588 | + int err; |
---|
| 589 | + |
---|
| 590 | + nhc->nhc_pcpu_rth_output = alloc_percpu_gfp(struct rtable __rcu *, |
---|
| 591 | + gfp_flags); |
---|
| 592 | + if (!nhc->nhc_pcpu_rth_output) |
---|
| 593 | + return -ENOMEM; |
---|
| 594 | + |
---|
| 595 | + if (encap) { |
---|
| 596 | + struct lwtunnel_state *lwtstate; |
---|
| 597 | + |
---|
| 598 | + if (encap_type == LWTUNNEL_ENCAP_NONE) { |
---|
| 599 | + NL_SET_ERR_MSG(extack, "LWT encap type not specified"); |
---|
| 600 | + err = -EINVAL; |
---|
| 601 | + goto lwt_failure; |
---|
| 602 | + } |
---|
| 603 | + err = lwtunnel_build_state(net, encap_type, encap, |
---|
| 604 | + nhc->nhc_family, cfg, &lwtstate, |
---|
| 605 | + extack); |
---|
| 606 | + if (err) |
---|
| 607 | + goto lwt_failure; |
---|
| 608 | + |
---|
| 609 | + nhc->nhc_lwtstate = lwtstate_get(lwtstate); |
---|
| 610 | + } |
---|
| 611 | + |
---|
| 612 | + return 0; |
---|
| 613 | + |
---|
| 614 | +lwt_failure: |
---|
| 615 | + rt_fibinfo_free_cpus(nhc->nhc_pcpu_rth_output); |
---|
| 616 | + nhc->nhc_pcpu_rth_output = NULL; |
---|
| 617 | + return err; |
---|
| 618 | +} |
---|
| 619 | +EXPORT_SYMBOL_GPL(fib_nh_common_init); |
---|
| 620 | + |
---|
| 621 | +int fib_nh_init(struct net *net, struct fib_nh *nh, |
---|
| 622 | + struct fib_config *cfg, int nh_weight, |
---|
| 623 | + struct netlink_ext_ack *extack) |
---|
| 624 | +{ |
---|
| 625 | + int err; |
---|
| 626 | + |
---|
| 627 | + nh->fib_nh_family = AF_INET; |
---|
| 628 | + |
---|
| 629 | + err = fib_nh_common_init(net, &nh->nh_common, cfg->fc_encap, |
---|
| 630 | + cfg->fc_encap_type, cfg, GFP_KERNEL, extack); |
---|
| 631 | + if (err) |
---|
| 632 | + return err; |
---|
| 633 | + |
---|
| 634 | + nh->fib_nh_oif = cfg->fc_oif; |
---|
| 635 | + nh->fib_nh_gw_family = cfg->fc_gw_family; |
---|
| 636 | + if (cfg->fc_gw_family == AF_INET) |
---|
| 637 | + nh->fib_nh_gw4 = cfg->fc_gw4; |
---|
| 638 | + else if (cfg->fc_gw_family == AF_INET6) |
---|
| 639 | + nh->fib_nh_gw6 = cfg->fc_gw6; |
---|
| 640 | + |
---|
| 641 | + nh->fib_nh_flags = cfg->fc_flags; |
---|
| 642 | + |
---|
| 643 | +#ifdef CONFIG_IP_ROUTE_CLASSID |
---|
| 644 | + nh->nh_tclassid = cfg->fc_flow; |
---|
| 645 | + if (nh->nh_tclassid) |
---|
| 646 | + atomic_inc(&net->ipv4.fib_num_tclassid_users); |
---|
| 647 | +#endif |
---|
| 648 | +#ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
| 649 | + nh->fib_nh_weight = nh_weight; |
---|
| 650 | +#endif |
---|
| 651 | + return 0; |
---|
460 | 652 | } |
---|
461 | 653 | |
---|
462 | 654 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
.. | .. |
---|
481 | 673 | return nhs; |
---|
482 | 674 | } |
---|
483 | 675 | |
---|
| 676 | +static int fib_gw_from_attr(__be32 *gw, struct nlattr *nla, |
---|
| 677 | + struct netlink_ext_ack *extack) |
---|
| 678 | +{ |
---|
| 679 | + if (nla_len(nla) < sizeof(*gw)) { |
---|
| 680 | + NL_SET_ERR_MSG(extack, "Invalid IPv4 address in RTA_GATEWAY"); |
---|
| 681 | + return -EINVAL; |
---|
| 682 | + } |
---|
| 683 | + |
---|
| 684 | + *gw = nla_get_in_addr(nla); |
---|
| 685 | + |
---|
| 686 | + return 0; |
---|
| 687 | +} |
---|
| 688 | + |
---|
| 689 | +/* only called when fib_nh is integrated into fib_info */ |
---|
484 | 690 | static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, |
---|
485 | 691 | int remaining, struct fib_config *cfg, |
---|
486 | 692 | struct netlink_ext_ack *extack) |
---|
487 | 693 | { |
---|
| 694 | + struct net *net = fi->fib_net; |
---|
| 695 | + struct fib_config fib_cfg; |
---|
| 696 | + struct fib_nh *nh; |
---|
488 | 697 | int ret; |
---|
489 | 698 | |
---|
490 | 699 | change_nexthops(fi) { |
---|
491 | 700 | int attrlen; |
---|
| 701 | + |
---|
| 702 | + memset(&fib_cfg, 0, sizeof(fib_cfg)); |
---|
492 | 703 | |
---|
493 | 704 | if (!rtnh_ok(rtnh, remaining)) { |
---|
494 | 705 | NL_SET_ERR_MSG(extack, |
---|
.. | .. |
---|
502 | 713 | return -EINVAL; |
---|
503 | 714 | } |
---|
504 | 715 | |
---|
505 | | - nexthop_nh->nh_flags = |
---|
506 | | - (cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags; |
---|
507 | | - nexthop_nh->nh_oif = rtnh->rtnh_ifindex; |
---|
508 | | - nexthop_nh->nh_weight = rtnh->rtnh_hops + 1; |
---|
| 716 | + fib_cfg.fc_flags = (cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags; |
---|
| 717 | + fib_cfg.fc_oif = rtnh->rtnh_ifindex; |
---|
509 | 718 | |
---|
510 | 719 | attrlen = rtnh_attrlen(rtnh); |
---|
511 | 720 | if (attrlen > 0) { |
---|
512 | | - struct nlattr *nla, *attrs = rtnh_attrs(rtnh); |
---|
| 721 | + struct nlattr *nla, *nlav, *attrs = rtnh_attrs(rtnh); |
---|
513 | 722 | |
---|
514 | 723 | nla = nla_find(attrs, attrlen, RTA_GATEWAY); |
---|
515 | | - nexthop_nh->nh_gw = nla ? nla_get_in_addr(nla) : 0; |
---|
516 | | -#ifdef CONFIG_IP_ROUTE_CLASSID |
---|
517 | | - nla = nla_find(attrs, attrlen, RTA_FLOW); |
---|
518 | | - nexthop_nh->nh_tclassid = nla ? nla_get_u32(nla) : 0; |
---|
519 | | - if (nexthop_nh->nh_tclassid) |
---|
520 | | - fi->fib_net->ipv4.fib_num_tclassid_users++; |
---|
521 | | -#endif |
---|
522 | | - nla = nla_find(attrs, attrlen, RTA_ENCAP); |
---|
| 724 | + nlav = nla_find(attrs, attrlen, RTA_VIA); |
---|
| 725 | + if (nla && nlav) { |
---|
| 726 | + NL_SET_ERR_MSG(extack, |
---|
| 727 | + "Nexthop configuration can not contain both GATEWAY and VIA"); |
---|
| 728 | + return -EINVAL; |
---|
| 729 | + } |
---|
523 | 730 | if (nla) { |
---|
524 | | - struct lwtunnel_state *lwtstate; |
---|
525 | | - struct nlattr *nla_entype; |
---|
526 | | - |
---|
527 | | - nla_entype = nla_find(attrs, attrlen, |
---|
528 | | - RTA_ENCAP_TYPE); |
---|
529 | | - if (!nla_entype) { |
---|
530 | | - NL_SET_BAD_ATTR(extack, nla); |
---|
531 | | - NL_SET_ERR_MSG(extack, |
---|
532 | | - "Encap type is missing"); |
---|
533 | | - goto err_inval; |
---|
534 | | - } |
---|
535 | | - |
---|
536 | | - ret = lwtunnel_build_state(nla_get_u16( |
---|
537 | | - nla_entype), |
---|
538 | | - nla, AF_INET, cfg, |
---|
539 | | - &lwtstate, extack); |
---|
| 731 | + ret = fib_gw_from_attr(&fib_cfg.fc_gw4, nla, |
---|
| 732 | + extack); |
---|
540 | 733 | if (ret) |
---|
541 | 734 | goto errout; |
---|
542 | | - nexthop_nh->nh_lwtstate = |
---|
543 | | - lwtstate_get(lwtstate); |
---|
| 735 | + |
---|
| 736 | + if (fib_cfg.fc_gw4) |
---|
| 737 | + fib_cfg.fc_gw_family = AF_INET; |
---|
| 738 | + } else if (nlav) { |
---|
| 739 | + ret = fib_gw_from_via(&fib_cfg, nlav, extack); |
---|
| 740 | + if (ret) |
---|
| 741 | + goto errout; |
---|
544 | 742 | } |
---|
| 743 | + |
---|
| 744 | + nla = nla_find(attrs, attrlen, RTA_FLOW); |
---|
| 745 | + if (nla) { |
---|
| 746 | + if (nla_len(nla) < sizeof(u32)) { |
---|
| 747 | + NL_SET_ERR_MSG(extack, "Invalid RTA_FLOW"); |
---|
| 748 | + return -EINVAL; |
---|
| 749 | + } |
---|
| 750 | + fib_cfg.fc_flow = nla_get_u32(nla); |
---|
| 751 | + } |
---|
| 752 | + |
---|
| 753 | + fib_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); |
---|
| 754 | + /* RTA_ENCAP_TYPE length checked in |
---|
| 755 | + * lwtunnel_valid_encap_type_attr |
---|
| 756 | + */ |
---|
| 757 | + nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); |
---|
| 758 | + if (nla) |
---|
| 759 | + fib_cfg.fc_encap_type = nla_get_u16(nla); |
---|
545 | 760 | } |
---|
| 761 | + |
---|
| 762 | + ret = fib_nh_init(net, nexthop_nh, &fib_cfg, |
---|
| 763 | + rtnh->rtnh_hops + 1, extack); |
---|
| 764 | + if (ret) |
---|
| 765 | + goto errout; |
---|
546 | 766 | |
---|
547 | 767 | rtnh = rtnh_next(rtnh, &remaining); |
---|
548 | 768 | } endfor_nexthops(fi); |
---|
549 | 769 | |
---|
550 | | - return 0; |
---|
551 | | - |
---|
552 | | -err_inval: |
---|
553 | 770 | ret = -EINVAL; |
---|
554 | | - |
---|
| 771 | + nh = fib_info_nh(fi, 0); |
---|
| 772 | + if (cfg->fc_oif && nh->fib_nh_oif != cfg->fc_oif) { |
---|
| 773 | + NL_SET_ERR_MSG(extack, |
---|
| 774 | + "Nexthop device index does not match RTA_OIF"); |
---|
| 775 | + goto errout; |
---|
| 776 | + } |
---|
| 777 | + if (cfg->fc_gw_family) { |
---|
| 778 | + if (cfg->fc_gw_family != nh->fib_nh_gw_family || |
---|
| 779 | + (cfg->fc_gw_family == AF_INET && |
---|
| 780 | + nh->fib_nh_gw4 != cfg->fc_gw4) || |
---|
| 781 | + (cfg->fc_gw_family == AF_INET6 && |
---|
| 782 | + ipv6_addr_cmp(&nh->fib_nh_gw6, &cfg->fc_gw6))) { |
---|
| 783 | + NL_SET_ERR_MSG(extack, |
---|
| 784 | + "Nexthop gateway does not match RTA_GATEWAY or RTA_VIA"); |
---|
| 785 | + goto errout; |
---|
| 786 | + } |
---|
| 787 | + } |
---|
| 788 | +#ifdef CONFIG_IP_ROUTE_CLASSID |
---|
| 789 | + if (cfg->fc_flow && nh->nh_tclassid != cfg->fc_flow) { |
---|
| 790 | + NL_SET_ERR_MSG(extack, |
---|
| 791 | + "Nexthop class id does not match RTA_FLOW"); |
---|
| 792 | + goto errout; |
---|
| 793 | + } |
---|
| 794 | +#endif |
---|
| 795 | + ret = 0; |
---|
555 | 796 | errout: |
---|
556 | 797 | return ret; |
---|
557 | 798 | } |
---|
558 | 799 | |
---|
| 800 | +/* only called when fib_nh is integrated into fib_info */ |
---|
559 | 801 | static void fib_rebalance(struct fib_info *fi) |
---|
560 | 802 | { |
---|
561 | 803 | int total; |
---|
562 | 804 | int w; |
---|
563 | | - struct in_device *in_dev; |
---|
564 | 805 | |
---|
565 | | - if (fi->fib_nhs < 2) |
---|
| 806 | + if (fib_info_num_path(fi) < 2) |
---|
566 | 807 | return; |
---|
567 | 808 | |
---|
568 | 809 | total = 0; |
---|
569 | 810 | for_nexthops(fi) { |
---|
570 | | - if (nh->nh_flags & RTNH_F_DEAD) |
---|
| 811 | + if (nh->fib_nh_flags & RTNH_F_DEAD) |
---|
571 | 812 | continue; |
---|
572 | 813 | |
---|
573 | | - in_dev = __in_dev_get_rtnl(nh->nh_dev); |
---|
574 | | - |
---|
575 | | - if (in_dev && |
---|
576 | | - IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && |
---|
577 | | - nh->nh_flags & RTNH_F_LINKDOWN) |
---|
| 814 | + if (ip_ignore_linkdown(nh->fib_nh_dev) && |
---|
| 815 | + nh->fib_nh_flags & RTNH_F_LINKDOWN) |
---|
578 | 816 | continue; |
---|
579 | 817 | |
---|
580 | | - total += nh->nh_weight; |
---|
| 818 | + total += nh->fib_nh_weight; |
---|
581 | 819 | } endfor_nexthops(fi); |
---|
582 | 820 | |
---|
583 | 821 | w = 0; |
---|
584 | 822 | change_nexthops(fi) { |
---|
585 | 823 | int upper_bound; |
---|
586 | 824 | |
---|
587 | | - in_dev = __in_dev_get_rtnl(nexthop_nh->nh_dev); |
---|
588 | | - |
---|
589 | | - if (nexthop_nh->nh_flags & RTNH_F_DEAD) { |
---|
| 825 | + if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) { |
---|
590 | 826 | upper_bound = -1; |
---|
591 | | - } else if (in_dev && |
---|
592 | | - IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && |
---|
593 | | - nexthop_nh->nh_flags & RTNH_F_LINKDOWN) { |
---|
| 827 | + } else if (ip_ignore_linkdown(nexthop_nh->fib_nh_dev) && |
---|
| 828 | + nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) { |
---|
594 | 829 | upper_bound = -1; |
---|
595 | 830 | } else { |
---|
596 | | - w += nexthop_nh->nh_weight; |
---|
| 831 | + w += nexthop_nh->fib_nh_weight; |
---|
597 | 832 | upper_bound = DIV_ROUND_CLOSEST_ULL((u64)w << 31, |
---|
598 | 833 | total) - 1; |
---|
599 | 834 | } |
---|
600 | 835 | |
---|
601 | | - atomic_set(&nexthop_nh->nh_upper_bound, upper_bound); |
---|
| 836 | + atomic_set(&nexthop_nh->fib_nh_upper_bound, upper_bound); |
---|
602 | 837 | } endfor_nexthops(fi); |
---|
603 | 838 | } |
---|
604 | 839 | #else /* CONFIG_IP_ROUTE_MULTIPATH */ |
---|
| 840 | + |
---|
| 841 | +static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, |
---|
| 842 | + int remaining, struct fib_config *cfg, |
---|
| 843 | + struct netlink_ext_ack *extack) |
---|
| 844 | +{ |
---|
| 845 | + NL_SET_ERR_MSG(extack, "Multipath support not enabled in kernel"); |
---|
| 846 | + |
---|
| 847 | + return -EINVAL; |
---|
| 848 | +} |
---|
605 | 849 | |
---|
606 | 850 | #define fib_rebalance(fi) do { } while (0) |
---|
607 | 851 | |
---|
608 | 852 | #endif /* CONFIG_IP_ROUTE_MULTIPATH */ |
---|
609 | 853 | |
---|
610 | | -static int fib_encap_match(u16 encap_type, |
---|
| 854 | +static int fib_encap_match(struct net *net, u16 encap_type, |
---|
611 | 855 | struct nlattr *encap, |
---|
612 | 856 | const struct fib_nh *nh, |
---|
613 | 857 | const struct fib_config *cfg, |
---|
.. | .. |
---|
619 | 863 | if (encap_type == LWTUNNEL_ENCAP_NONE) |
---|
620 | 864 | return 0; |
---|
621 | 865 | |
---|
622 | | - ret = lwtunnel_build_state(encap_type, encap, AF_INET, |
---|
| 866 | + ret = lwtunnel_build_state(net, encap_type, encap, AF_INET, |
---|
623 | 867 | cfg, &lwtstate, extack); |
---|
624 | 868 | if (!ret) { |
---|
625 | | - result = lwtunnel_cmp_encap(lwtstate, nh->nh_lwtstate); |
---|
| 869 | + result = lwtunnel_cmp_encap(lwtstate, nh->fib_nh_lws); |
---|
626 | 870 | lwtstate_free(lwtstate); |
---|
627 | 871 | } |
---|
628 | 872 | |
---|
629 | 873 | return result; |
---|
630 | 874 | } |
---|
631 | 875 | |
---|
632 | | -int fib_nh_match(struct fib_config *cfg, struct fib_info *fi, |
---|
| 876 | +int fib_nh_match(struct net *net, struct fib_config *cfg, struct fib_info *fi, |
---|
633 | 877 | struct netlink_ext_ack *extack) |
---|
634 | 878 | { |
---|
635 | 879 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
.. | .. |
---|
640 | 884 | if (cfg->fc_priority && cfg->fc_priority != fi->fib_priority) |
---|
641 | 885 | return 1; |
---|
642 | 886 | |
---|
643 | | - if (cfg->fc_oif || cfg->fc_gw) { |
---|
| 887 | + if (cfg->fc_nh_id) { |
---|
| 888 | + if (fi->nh && cfg->fc_nh_id == fi->nh->id) |
---|
| 889 | + return 0; |
---|
| 890 | + return 1; |
---|
| 891 | + } |
---|
| 892 | + |
---|
| 893 | + if (fi->nh) { |
---|
| 894 | + if (cfg->fc_oif || cfg->fc_gw_family || cfg->fc_mp) |
---|
| 895 | + return 1; |
---|
| 896 | + return 0; |
---|
| 897 | + } |
---|
| 898 | + |
---|
| 899 | + if (cfg->fc_oif || cfg->fc_gw_family) { |
---|
| 900 | + struct fib_nh *nh; |
---|
| 901 | + |
---|
| 902 | + nh = fib_info_nh(fi, 0); |
---|
644 | 903 | if (cfg->fc_encap) { |
---|
645 | | - if (fib_encap_match(cfg->fc_encap_type, cfg->fc_encap, |
---|
646 | | - fi->fib_nh, cfg, extack)) |
---|
| 904 | + if (fib_encap_match(net, cfg->fc_encap_type, |
---|
| 905 | + cfg->fc_encap, nh, cfg, extack)) |
---|
647 | 906 | return 1; |
---|
648 | 907 | } |
---|
649 | 908 | #ifdef CONFIG_IP_ROUTE_CLASSID |
---|
650 | 909 | if (cfg->fc_flow && |
---|
651 | | - cfg->fc_flow != fi->fib_nh->nh_tclassid) |
---|
| 910 | + cfg->fc_flow != nh->nh_tclassid) |
---|
652 | 911 | return 1; |
---|
653 | 912 | #endif |
---|
654 | | - if ((!cfg->fc_oif || cfg->fc_oif == fi->fib_nh->nh_oif) && |
---|
655 | | - (!cfg->fc_gw || cfg->fc_gw == fi->fib_nh->nh_gw)) |
---|
656 | | - return 0; |
---|
657 | | - return 1; |
---|
| 913 | + if ((cfg->fc_oif && cfg->fc_oif != nh->fib_nh_oif) || |
---|
| 914 | + (cfg->fc_gw_family && |
---|
| 915 | + cfg->fc_gw_family != nh->fib_nh_gw_family)) |
---|
| 916 | + return 1; |
---|
| 917 | + |
---|
| 918 | + if (cfg->fc_gw_family == AF_INET && |
---|
| 919 | + cfg->fc_gw4 != nh->fib_nh_gw4) |
---|
| 920 | + return 1; |
---|
| 921 | + |
---|
| 922 | + if (cfg->fc_gw_family == AF_INET6 && |
---|
| 923 | + ipv6_addr_cmp(&cfg->fc_gw6, &nh->fib_nh_gw6)) |
---|
| 924 | + return 1; |
---|
| 925 | + |
---|
| 926 | + return 0; |
---|
658 | 927 | } |
---|
659 | 928 | |
---|
660 | 929 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
.. | .. |
---|
670 | 939 | if (!rtnh_ok(rtnh, remaining)) |
---|
671 | 940 | return -EINVAL; |
---|
672 | 941 | |
---|
673 | | - if (rtnh->rtnh_ifindex && rtnh->rtnh_ifindex != nh->nh_oif) |
---|
| 942 | + if (rtnh->rtnh_ifindex && rtnh->rtnh_ifindex != nh->fib_nh_oif) |
---|
674 | 943 | return 1; |
---|
675 | 944 | |
---|
676 | 945 | attrlen = rtnh_attrlen(rtnh); |
---|
677 | 946 | if (attrlen > 0) { |
---|
678 | | - struct nlattr *nla, *attrs = rtnh_attrs(rtnh); |
---|
| 947 | + struct nlattr *nla, *nlav, *attrs = rtnh_attrs(rtnh); |
---|
| 948 | + int err; |
---|
679 | 949 | |
---|
680 | 950 | nla = nla_find(attrs, attrlen, RTA_GATEWAY); |
---|
681 | | - if (nla && nla_get_in_addr(nla) != nh->nh_gw) |
---|
682 | | - return 1; |
---|
| 951 | + nlav = nla_find(attrs, attrlen, RTA_VIA); |
---|
| 952 | + if (nla && nlav) { |
---|
| 953 | + NL_SET_ERR_MSG(extack, |
---|
| 954 | + "Nexthop configuration can not contain both GATEWAY and VIA"); |
---|
| 955 | + return -EINVAL; |
---|
| 956 | + } |
---|
| 957 | + |
---|
| 958 | + if (nla) { |
---|
| 959 | + __be32 gw; |
---|
| 960 | + |
---|
| 961 | + err = fib_gw_from_attr(&gw, nla, extack); |
---|
| 962 | + if (err) |
---|
| 963 | + return err; |
---|
| 964 | + |
---|
| 965 | + if (nh->fib_nh_gw_family != AF_INET || |
---|
| 966 | + gw != nh->fib_nh_gw4) |
---|
| 967 | + return 1; |
---|
| 968 | + } else if (nlav) { |
---|
| 969 | + struct fib_config cfg2; |
---|
| 970 | + |
---|
| 971 | + err = fib_gw_from_via(&cfg2, nlav, extack); |
---|
| 972 | + if (err) |
---|
| 973 | + return err; |
---|
| 974 | + |
---|
| 975 | + switch (nh->fib_nh_gw_family) { |
---|
| 976 | + case AF_INET: |
---|
| 977 | + if (cfg2.fc_gw_family != AF_INET || |
---|
| 978 | + cfg2.fc_gw4 != nh->fib_nh_gw4) |
---|
| 979 | + return 1; |
---|
| 980 | + break; |
---|
| 981 | + case AF_INET6: |
---|
| 982 | + if (cfg2.fc_gw_family != AF_INET6 || |
---|
| 983 | + ipv6_addr_cmp(&cfg2.fc_gw6, |
---|
| 984 | + &nh->fib_nh_gw6)) |
---|
| 985 | + return 1; |
---|
| 986 | + break; |
---|
| 987 | + } |
---|
| 988 | + } |
---|
| 989 | + |
---|
683 | 990 | #ifdef CONFIG_IP_ROUTE_CLASSID |
---|
684 | 991 | nla = nla_find(attrs, attrlen, RTA_FLOW); |
---|
685 | | - if (nla && nla_get_u32(nla) != nh->nh_tclassid) |
---|
686 | | - return 1; |
---|
| 992 | + if (nla) { |
---|
| 993 | + if (nla_len(nla) < sizeof(u32)) { |
---|
| 994 | + NL_SET_ERR_MSG(extack, "Invalid RTA_FLOW"); |
---|
| 995 | + return -EINVAL; |
---|
| 996 | + } |
---|
| 997 | + if (nla_get_u32(nla) != nh->nh_tclassid) |
---|
| 998 | + return 1; |
---|
| 999 | + } |
---|
687 | 1000 | #endif |
---|
688 | 1001 | } |
---|
689 | 1002 | |
---|
.. | .. |
---|
710 | 1023 | if (type > RTAX_MAX) |
---|
711 | 1024 | return false; |
---|
712 | 1025 | |
---|
| 1026 | + type = array_index_nospec(type, RTAX_MAX + 1); |
---|
713 | 1027 | if (type == RTAX_CC_ALGO) { |
---|
714 | 1028 | char tmp[TCP_CA_NAME_MAX]; |
---|
715 | 1029 | bool ecn_ca = false; |
---|
.. | .. |
---|
733 | 1047 | return true; |
---|
734 | 1048 | } |
---|
735 | 1049 | |
---|
| 1050 | +static int fib_check_nh_v6_gw(struct net *net, struct fib_nh *nh, |
---|
| 1051 | + u32 table, struct netlink_ext_ack *extack) |
---|
| 1052 | +{ |
---|
| 1053 | + struct fib6_config cfg = { |
---|
| 1054 | + .fc_table = table, |
---|
| 1055 | + .fc_flags = nh->fib_nh_flags | RTF_GATEWAY, |
---|
| 1056 | + .fc_ifindex = nh->fib_nh_oif, |
---|
| 1057 | + .fc_gateway = nh->fib_nh_gw6, |
---|
| 1058 | + }; |
---|
| 1059 | + struct fib6_nh fib6_nh = {}; |
---|
| 1060 | + int err; |
---|
| 1061 | + |
---|
| 1062 | + err = ipv6_stub->fib6_nh_init(net, &fib6_nh, &cfg, GFP_KERNEL, extack); |
---|
| 1063 | + if (!err) { |
---|
| 1064 | + nh->fib_nh_dev = fib6_nh.fib_nh_dev; |
---|
| 1065 | + dev_hold(nh->fib_nh_dev); |
---|
| 1066 | + nh->fib_nh_oif = nh->fib_nh_dev->ifindex; |
---|
| 1067 | + nh->fib_nh_scope = RT_SCOPE_LINK; |
---|
| 1068 | + |
---|
| 1069 | + ipv6_stub->fib6_nh_release(&fib6_nh); |
---|
| 1070 | + } |
---|
| 1071 | + |
---|
| 1072 | + return err; |
---|
| 1073 | +} |
---|
736 | 1074 | |
---|
737 | 1075 | /* |
---|
738 | 1076 | * Picture |
---|
.. | .. |
---|
777 | 1115 | * | |
---|
778 | 1116 | * |-> {local prefix} (terminal node) |
---|
779 | 1117 | */ |
---|
780 | | -static int fib_check_nh(struct fib_config *cfg, struct fib_nh *nh, |
---|
781 | | - struct netlink_ext_ack *extack) |
---|
| 1118 | +static int fib_check_nh_v4_gw(struct net *net, struct fib_nh *nh, u32 table, |
---|
| 1119 | + u8 scope, struct netlink_ext_ack *extack) |
---|
782 | 1120 | { |
---|
783 | | - int err = 0; |
---|
784 | | - struct net *net; |
---|
785 | 1121 | struct net_device *dev; |
---|
| 1122 | + struct fib_result res; |
---|
| 1123 | + int err = 0; |
---|
786 | 1124 | |
---|
787 | | - net = cfg->fc_nlinfo.nl_net; |
---|
788 | | - if (nh->nh_gw) { |
---|
789 | | - struct fib_result res; |
---|
| 1125 | + if (nh->fib_nh_flags & RTNH_F_ONLINK) { |
---|
| 1126 | + unsigned int addr_type; |
---|
790 | 1127 | |
---|
791 | | - if (nh->nh_flags & RTNH_F_ONLINK) { |
---|
792 | | - unsigned int addr_type; |
---|
793 | | - |
---|
794 | | - if (cfg->fc_scope >= RT_SCOPE_LINK) { |
---|
795 | | - NL_SET_ERR_MSG(extack, |
---|
796 | | - "Nexthop has invalid scope"); |
---|
797 | | - return -EINVAL; |
---|
798 | | - } |
---|
799 | | - dev = __dev_get_by_index(net, nh->nh_oif); |
---|
800 | | - if (!dev) |
---|
801 | | - return -ENODEV; |
---|
802 | | - if (!(dev->flags & IFF_UP)) { |
---|
803 | | - NL_SET_ERR_MSG(extack, |
---|
804 | | - "Nexthop device is not up"); |
---|
805 | | - return -ENETDOWN; |
---|
806 | | - } |
---|
807 | | - addr_type = inet_addr_type_dev_table(net, dev, nh->nh_gw); |
---|
808 | | - if (addr_type != RTN_UNICAST) { |
---|
809 | | - NL_SET_ERR_MSG(extack, |
---|
810 | | - "Nexthop has invalid gateway"); |
---|
811 | | - return -EINVAL; |
---|
812 | | - } |
---|
813 | | - if (!netif_carrier_ok(dev)) |
---|
814 | | - nh->nh_flags |= RTNH_F_LINKDOWN; |
---|
815 | | - nh->nh_dev = dev; |
---|
816 | | - dev_hold(dev); |
---|
817 | | - nh->nh_scope = RT_SCOPE_LINK; |
---|
818 | | - return 0; |
---|
| 1128 | + if (scope >= RT_SCOPE_LINK) { |
---|
| 1129 | + NL_SET_ERR_MSG(extack, "Nexthop has invalid scope"); |
---|
| 1130 | + return -EINVAL; |
---|
819 | 1131 | } |
---|
820 | | - rcu_read_lock(); |
---|
821 | | - { |
---|
822 | | - struct fib_table *tbl = NULL; |
---|
823 | | - struct flowi4 fl4 = { |
---|
824 | | - .daddr = nh->nh_gw, |
---|
825 | | - .flowi4_scope = cfg->fc_scope + 1, |
---|
826 | | - .flowi4_oif = nh->nh_oif, |
---|
827 | | - .flowi4_iif = LOOPBACK_IFINDEX, |
---|
828 | | - }; |
---|
829 | | - |
---|
830 | | - /* It is not necessary, but requires a bit of thinking */ |
---|
831 | | - if (fl4.flowi4_scope < RT_SCOPE_LINK) |
---|
832 | | - fl4.flowi4_scope = RT_SCOPE_LINK; |
---|
833 | | - |
---|
834 | | - if (cfg->fc_table && cfg->fc_table != RT_TABLE_MAIN) |
---|
835 | | - tbl = fib_get_table(net, cfg->fc_table); |
---|
836 | | - |
---|
837 | | - if (tbl) |
---|
838 | | - err = fib_table_lookup(tbl, &fl4, &res, |
---|
839 | | - FIB_LOOKUP_IGNORE_LINKSTATE | |
---|
840 | | - FIB_LOOKUP_NOREF); |
---|
841 | | - |
---|
842 | | - /* on error or if no table given do full lookup. This |
---|
843 | | - * is needed for example when nexthops are in the local |
---|
844 | | - * table rather than the given table |
---|
845 | | - */ |
---|
846 | | - if (!tbl || err) { |
---|
847 | | - err = fib_lookup(net, &fl4, &res, |
---|
848 | | - FIB_LOOKUP_IGNORE_LINKSTATE); |
---|
849 | | - } |
---|
850 | | - |
---|
851 | | - if (err) { |
---|
852 | | - NL_SET_ERR_MSG(extack, |
---|
853 | | - "Nexthop has invalid gateway"); |
---|
854 | | - rcu_read_unlock(); |
---|
855 | | - return err; |
---|
856 | | - } |
---|
| 1132 | + dev = __dev_get_by_index(net, nh->fib_nh_oif); |
---|
| 1133 | + if (!dev) { |
---|
| 1134 | + NL_SET_ERR_MSG(extack, "Nexthop device required for onlink"); |
---|
| 1135 | + return -ENODEV; |
---|
857 | 1136 | } |
---|
858 | | - err = -EINVAL; |
---|
859 | | - if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) { |
---|
| 1137 | + if (!(dev->flags & IFF_UP)) { |
---|
| 1138 | + NL_SET_ERR_MSG(extack, "Nexthop device is not up"); |
---|
| 1139 | + return -ENETDOWN; |
---|
| 1140 | + } |
---|
| 1141 | + addr_type = inet_addr_type_dev_table(net, dev, nh->fib_nh_gw4); |
---|
| 1142 | + if (addr_type != RTN_UNICAST) { |
---|
| 1143 | + NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway"); |
---|
| 1144 | + return -EINVAL; |
---|
| 1145 | + } |
---|
| 1146 | + if (!netif_carrier_ok(dev)) |
---|
| 1147 | + nh->fib_nh_flags |= RTNH_F_LINKDOWN; |
---|
| 1148 | + nh->fib_nh_dev = dev; |
---|
| 1149 | + dev_hold(dev); |
---|
| 1150 | + nh->fib_nh_scope = RT_SCOPE_LINK; |
---|
| 1151 | + return 0; |
---|
| 1152 | + } |
---|
| 1153 | + rcu_read_lock(); |
---|
| 1154 | + { |
---|
| 1155 | + struct fib_table *tbl = NULL; |
---|
| 1156 | + struct flowi4 fl4 = { |
---|
| 1157 | + .daddr = nh->fib_nh_gw4, |
---|
| 1158 | + .flowi4_scope = scope + 1, |
---|
| 1159 | + .flowi4_oif = nh->fib_nh_oif, |
---|
| 1160 | + .flowi4_iif = LOOPBACK_IFINDEX, |
---|
| 1161 | + }; |
---|
| 1162 | + |
---|
| 1163 | + /* It is not necessary, but requires a bit of thinking */ |
---|
| 1164 | + if (fl4.flowi4_scope < RT_SCOPE_LINK) |
---|
| 1165 | + fl4.flowi4_scope = RT_SCOPE_LINK; |
---|
| 1166 | + |
---|
| 1167 | + if (table && table != RT_TABLE_MAIN) |
---|
| 1168 | + tbl = fib_get_table(net, table); |
---|
| 1169 | + |
---|
| 1170 | + if (tbl) |
---|
| 1171 | + err = fib_table_lookup(tbl, &fl4, &res, |
---|
| 1172 | + FIB_LOOKUP_IGNORE_LINKSTATE | |
---|
| 1173 | + FIB_LOOKUP_NOREF); |
---|
| 1174 | + |
---|
| 1175 | + /* on error or if no table given do full lookup. This |
---|
| 1176 | + * is needed for example when nexthops are in the local |
---|
| 1177 | + * table rather than the given table |
---|
| 1178 | + */ |
---|
| 1179 | + if (!tbl || err) { |
---|
| 1180 | + err = fib_lookup(net, &fl4, &res, |
---|
| 1181 | + FIB_LOOKUP_IGNORE_LINKSTATE); |
---|
| 1182 | + } |
---|
| 1183 | + |
---|
| 1184 | + if (err) { |
---|
860 | 1185 | NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway"); |
---|
861 | 1186 | goto out; |
---|
862 | 1187 | } |
---|
863 | | - nh->nh_scope = res.scope; |
---|
864 | | - nh->nh_oif = FIB_RES_OIF(res); |
---|
865 | | - nh->nh_dev = dev = FIB_RES_DEV(res); |
---|
866 | | - if (!dev) { |
---|
867 | | - NL_SET_ERR_MSG(extack, |
---|
868 | | - "No egress device for nexthop gateway"); |
---|
869 | | - goto out; |
---|
870 | | - } |
---|
871 | | - dev_hold(dev); |
---|
872 | | - if (!netif_carrier_ok(dev)) |
---|
873 | | - nh->nh_flags |= RTNH_F_LINKDOWN; |
---|
874 | | - err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN; |
---|
875 | | - } else { |
---|
876 | | - struct in_device *in_dev; |
---|
877 | | - |
---|
878 | | - if (nh->nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK)) { |
---|
879 | | - NL_SET_ERR_MSG(extack, |
---|
880 | | - "Invalid flags for nexthop - PERVASIVE and ONLINK can not be set"); |
---|
881 | | - return -EINVAL; |
---|
882 | | - } |
---|
883 | | - rcu_read_lock(); |
---|
884 | | - err = -ENODEV; |
---|
885 | | - in_dev = inetdev_by_index(net, nh->nh_oif); |
---|
886 | | - if (!in_dev) |
---|
887 | | - goto out; |
---|
888 | | - err = -ENETDOWN; |
---|
889 | | - if (!(in_dev->dev->flags & IFF_UP)) { |
---|
890 | | - NL_SET_ERR_MSG(extack, "Device for nexthop is not up"); |
---|
891 | | - goto out; |
---|
892 | | - } |
---|
893 | | - nh->nh_dev = in_dev->dev; |
---|
894 | | - dev_hold(nh->nh_dev); |
---|
895 | | - nh->nh_scope = RT_SCOPE_HOST; |
---|
896 | | - if (!netif_carrier_ok(nh->nh_dev)) |
---|
897 | | - nh->nh_flags |= RTNH_F_LINKDOWN; |
---|
898 | | - err = 0; |
---|
899 | 1188 | } |
---|
| 1189 | + |
---|
| 1190 | + err = -EINVAL; |
---|
| 1191 | + if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) { |
---|
| 1192 | + NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway"); |
---|
| 1193 | + goto out; |
---|
| 1194 | + } |
---|
| 1195 | + nh->fib_nh_scope = res.scope; |
---|
| 1196 | + nh->fib_nh_oif = FIB_RES_OIF(res); |
---|
| 1197 | + nh->fib_nh_dev = dev = FIB_RES_DEV(res); |
---|
| 1198 | + if (!dev) { |
---|
| 1199 | + NL_SET_ERR_MSG(extack, |
---|
| 1200 | + "No egress device for nexthop gateway"); |
---|
| 1201 | + goto out; |
---|
| 1202 | + } |
---|
| 1203 | + dev_hold(dev); |
---|
| 1204 | + if (!netif_carrier_ok(dev)) |
---|
| 1205 | + nh->fib_nh_flags |= RTNH_F_LINKDOWN; |
---|
| 1206 | + err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN; |
---|
900 | 1207 | out: |
---|
901 | 1208 | rcu_read_unlock(); |
---|
| 1209 | + return err; |
---|
| 1210 | +} |
---|
| 1211 | + |
---|
| 1212 | +static int fib_check_nh_nongw(struct net *net, struct fib_nh *nh, |
---|
| 1213 | + struct netlink_ext_ack *extack) |
---|
| 1214 | +{ |
---|
| 1215 | + struct in_device *in_dev; |
---|
| 1216 | + int err; |
---|
| 1217 | + |
---|
| 1218 | + if (nh->fib_nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK)) { |
---|
| 1219 | + NL_SET_ERR_MSG(extack, |
---|
| 1220 | + "Invalid flags for nexthop - PERVASIVE and ONLINK can not be set"); |
---|
| 1221 | + return -EINVAL; |
---|
| 1222 | + } |
---|
| 1223 | + |
---|
| 1224 | + rcu_read_lock(); |
---|
| 1225 | + |
---|
| 1226 | + err = -ENODEV; |
---|
| 1227 | + in_dev = inetdev_by_index(net, nh->fib_nh_oif); |
---|
| 1228 | + if (!in_dev) |
---|
| 1229 | + goto out; |
---|
| 1230 | + err = -ENETDOWN; |
---|
| 1231 | + if (!(in_dev->dev->flags & IFF_UP)) { |
---|
| 1232 | + NL_SET_ERR_MSG(extack, "Device for nexthop is not up"); |
---|
| 1233 | + goto out; |
---|
| 1234 | + } |
---|
| 1235 | + |
---|
| 1236 | + nh->fib_nh_dev = in_dev->dev; |
---|
| 1237 | + dev_hold(nh->fib_nh_dev); |
---|
| 1238 | + nh->fib_nh_scope = RT_SCOPE_LINK; |
---|
| 1239 | + if (!netif_carrier_ok(nh->fib_nh_dev)) |
---|
| 1240 | + nh->fib_nh_flags |= RTNH_F_LINKDOWN; |
---|
| 1241 | + err = 0; |
---|
| 1242 | +out: |
---|
| 1243 | + rcu_read_unlock(); |
---|
| 1244 | + return err; |
---|
| 1245 | +} |
---|
| 1246 | + |
---|
| 1247 | +int fib_check_nh(struct net *net, struct fib_nh *nh, u32 table, u8 scope, |
---|
| 1248 | + struct netlink_ext_ack *extack) |
---|
| 1249 | +{ |
---|
| 1250 | + int err; |
---|
| 1251 | + |
---|
| 1252 | + if (nh->fib_nh_gw_family == AF_INET) |
---|
| 1253 | + err = fib_check_nh_v4_gw(net, nh, table, scope, extack); |
---|
| 1254 | + else if (nh->fib_nh_gw_family == AF_INET6) |
---|
| 1255 | + err = fib_check_nh_v6_gw(net, nh, table, extack); |
---|
| 1256 | + else |
---|
| 1257 | + err = fib_check_nh_nongw(net, nh, extack); |
---|
| 1258 | + |
---|
902 | 1259 | return err; |
---|
903 | 1260 | } |
---|
904 | 1261 | |
---|
.. | .. |
---|
984 | 1341 | fib_info_hash_free(old_laddrhash, bytes); |
---|
985 | 1342 | } |
---|
986 | 1343 | |
---|
987 | | -__be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh) |
---|
| 1344 | +__be32 fib_info_update_nhc_saddr(struct net *net, struct fib_nh_common *nhc, |
---|
| 1345 | + unsigned char scope) |
---|
988 | 1346 | { |
---|
989 | | - nh->nh_saddr = inet_select_addr(nh->nh_dev, |
---|
990 | | - nh->nh_gw, |
---|
991 | | - nh->nh_parent->fib_scope); |
---|
| 1347 | + struct fib_nh *nh; |
---|
| 1348 | + |
---|
| 1349 | + if (nhc->nhc_family != AF_INET) |
---|
| 1350 | + return inet_select_addr(nhc->nhc_dev, 0, scope); |
---|
| 1351 | + |
---|
| 1352 | + nh = container_of(nhc, struct fib_nh, nh_common); |
---|
| 1353 | + nh->nh_saddr = inet_select_addr(nh->fib_nh_dev, nh->fib_nh_gw4, scope); |
---|
992 | 1354 | nh->nh_saddr_genid = atomic_read(&net->ipv4.dev_addr_genid); |
---|
993 | 1355 | |
---|
994 | 1356 | return nh->nh_saddr; |
---|
| 1357 | +} |
---|
| 1358 | + |
---|
| 1359 | +__be32 fib_result_prefsrc(struct net *net, struct fib_result *res) |
---|
| 1360 | +{ |
---|
| 1361 | + struct fib_nh_common *nhc = res->nhc; |
---|
| 1362 | + |
---|
| 1363 | + if (res->fi->fib_prefsrc) |
---|
| 1364 | + return res->fi->fib_prefsrc; |
---|
| 1365 | + |
---|
| 1366 | + if (nhc->nhc_family == AF_INET) { |
---|
| 1367 | + struct fib_nh *nh; |
---|
| 1368 | + |
---|
| 1369 | + nh = container_of(nhc, struct fib_nh, nh_common); |
---|
| 1370 | + if (nh->nh_saddr_genid == atomic_read(&net->ipv4.dev_addr_genid)) |
---|
| 1371 | + return nh->nh_saddr; |
---|
| 1372 | + } |
---|
| 1373 | + |
---|
| 1374 | + return fib_info_update_nhc_saddr(net, nhc, res->fi->fib_scope); |
---|
995 | 1375 | } |
---|
996 | 1376 | |
---|
997 | 1377 | static bool fib_valid_prefsrc(struct fib_config *cfg, __be32 fib_prefsrc) |
---|
.. | .. |
---|
1018 | 1398 | return true; |
---|
1019 | 1399 | } |
---|
1020 | 1400 | |
---|
1021 | | -static int |
---|
1022 | | -fib_convert_metrics(struct fib_info *fi, const struct fib_config *cfg) |
---|
1023 | | -{ |
---|
1024 | | - return ip_metrics_convert(fi->fib_net, cfg->fc_mx, cfg->fc_mx_len, |
---|
1025 | | - fi->fib_metrics->metrics); |
---|
1026 | | -} |
---|
1027 | | - |
---|
1028 | 1401 | struct fib_info *fib_create_info(struct fib_config *cfg, |
---|
1029 | 1402 | struct netlink_ext_ack *extack) |
---|
1030 | 1403 | { |
---|
1031 | 1404 | int err; |
---|
1032 | 1405 | struct fib_info *fi = NULL; |
---|
| 1406 | + struct nexthop *nh = NULL; |
---|
1033 | 1407 | struct fib_info *ofi; |
---|
1034 | 1408 | int nhs = 1; |
---|
1035 | 1409 | struct net *net = cfg->fc_nlinfo.nl_net; |
---|
.. | .. |
---|
1049 | 1423 | goto err_inval; |
---|
1050 | 1424 | } |
---|
1051 | 1425 | |
---|
| 1426 | + if (cfg->fc_nh_id) { |
---|
| 1427 | + if (!cfg->fc_mx) { |
---|
| 1428 | + fi = fib_find_info_nh(net, cfg); |
---|
| 1429 | + if (fi) { |
---|
| 1430 | + fi->fib_treeref++; |
---|
| 1431 | + return fi; |
---|
| 1432 | + } |
---|
| 1433 | + } |
---|
| 1434 | + |
---|
| 1435 | + nh = nexthop_find_by_id(net, cfg->fc_nh_id); |
---|
| 1436 | + if (!nh) { |
---|
| 1437 | + NL_SET_ERR_MSG(extack, "Nexthop id does not exist"); |
---|
| 1438 | + goto err_inval; |
---|
| 1439 | + } |
---|
| 1440 | + nhs = 0; |
---|
| 1441 | + } |
---|
| 1442 | + |
---|
1052 | 1443 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
1053 | 1444 | if (cfg->fc_mp) { |
---|
1054 | 1445 | nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len, extack); |
---|
.. | .. |
---|
1058 | 1449 | #endif |
---|
1059 | 1450 | |
---|
1060 | 1451 | err = -ENOBUFS; |
---|
1061 | | - if (fib_info_cnt >= fib_info_hash_size) { |
---|
| 1452 | + |
---|
| 1453 | + /* Paired with WRITE_ONCE() in fib_release_info() */ |
---|
| 1454 | + if (READ_ONCE(fib_info_cnt) >= fib_info_hash_size) { |
---|
1062 | 1455 | unsigned int new_size = fib_info_hash_size << 1; |
---|
1063 | 1456 | struct hlist_head *new_info_hash; |
---|
1064 | 1457 | struct hlist_head *new_laddrhash; |
---|
.. | .. |
---|
1079 | 1472 | goto failure; |
---|
1080 | 1473 | } |
---|
1081 | 1474 | |
---|
1082 | | - fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); |
---|
| 1475 | + fi = kzalloc(struct_size(fi, fib_nh, nhs), GFP_KERNEL); |
---|
1083 | 1476 | if (!fi) |
---|
1084 | 1477 | goto failure; |
---|
1085 | | - if (cfg->fc_mx) { |
---|
1086 | | - fi->fib_metrics = kzalloc(sizeof(*fi->fib_metrics), GFP_KERNEL); |
---|
1087 | | - if (unlikely(!fi->fib_metrics)) { |
---|
1088 | | - kfree(fi); |
---|
1089 | | - return ERR_PTR(err); |
---|
1090 | | - } |
---|
1091 | | - refcount_set(&fi->fib_metrics->refcnt, 1); |
---|
1092 | | - } else { |
---|
1093 | | - fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; |
---|
| 1478 | + fi->fib_metrics = ip_fib_metrics_init(fi->fib_net, cfg->fc_mx, |
---|
| 1479 | + cfg->fc_mx_len, extack); |
---|
| 1480 | + if (IS_ERR(fi->fib_metrics)) { |
---|
| 1481 | + err = PTR_ERR(fi->fib_metrics); |
---|
| 1482 | + kfree(fi); |
---|
| 1483 | + return ERR_PTR(err); |
---|
1094 | 1484 | } |
---|
1095 | | - fib_info_cnt++; |
---|
| 1485 | + |
---|
1096 | 1486 | fi->fib_net = net; |
---|
1097 | 1487 | fi->fib_protocol = cfg->fc_protocol; |
---|
1098 | 1488 | fi->fib_scope = cfg->fc_scope; |
---|
.. | .. |
---|
1103 | 1493 | fi->fib_tb_id = cfg->fc_table; |
---|
1104 | 1494 | |
---|
1105 | 1495 | fi->fib_nhs = nhs; |
---|
1106 | | - change_nexthops(fi) { |
---|
1107 | | - nexthop_nh->nh_parent = fi; |
---|
1108 | | - nexthop_nh->nh_pcpu_rth_output = alloc_percpu(struct rtable __rcu *); |
---|
1109 | | - if (!nexthop_nh->nh_pcpu_rth_output) |
---|
1110 | | - goto failure; |
---|
1111 | | - } endfor_nexthops(fi) |
---|
1112 | | - |
---|
1113 | | - err = fib_convert_metrics(fi, cfg); |
---|
1114 | | - if (err) |
---|
1115 | | - goto failure; |
---|
1116 | | - |
---|
1117 | | - if (cfg->fc_mp) { |
---|
1118 | | -#ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
1119 | | - err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg, extack); |
---|
1120 | | - if (err != 0) |
---|
1121 | | - goto failure; |
---|
1122 | | - if (cfg->fc_oif && fi->fib_nh->nh_oif != cfg->fc_oif) { |
---|
1123 | | - NL_SET_ERR_MSG(extack, |
---|
1124 | | - "Nexthop device index does not match RTA_OIF"); |
---|
1125 | | - goto err_inval; |
---|
| 1496 | + if (nh) { |
---|
| 1497 | + if (!nexthop_get(nh)) { |
---|
| 1498 | + NL_SET_ERR_MSG(extack, "Nexthop has been deleted"); |
---|
| 1499 | + err = -EINVAL; |
---|
| 1500 | + } else { |
---|
| 1501 | + err = 0; |
---|
| 1502 | + fi->nh = nh; |
---|
1126 | 1503 | } |
---|
1127 | | - if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw) { |
---|
1128 | | - NL_SET_ERR_MSG(extack, |
---|
1129 | | - "Nexthop gateway does not match RTA_GATEWAY"); |
---|
1130 | | - goto err_inval; |
---|
1131 | | - } |
---|
1132 | | -#ifdef CONFIG_IP_ROUTE_CLASSID |
---|
1133 | | - if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow) { |
---|
1134 | | - NL_SET_ERR_MSG(extack, |
---|
1135 | | - "Nexthop class id does not match RTA_FLOW"); |
---|
1136 | | - goto err_inval; |
---|
1137 | | - } |
---|
1138 | | -#endif |
---|
1139 | | -#else |
---|
1140 | | - NL_SET_ERR_MSG(extack, |
---|
1141 | | - "Multipath support not enabled in kernel"); |
---|
1142 | | - goto err_inval; |
---|
1143 | | -#endif |
---|
1144 | 1504 | } else { |
---|
1145 | | - struct fib_nh *nh = fi->fib_nh; |
---|
| 1505 | + change_nexthops(fi) { |
---|
| 1506 | + nexthop_nh->nh_parent = fi; |
---|
| 1507 | + } endfor_nexthops(fi) |
---|
1146 | 1508 | |
---|
1147 | | - if (cfg->fc_encap) { |
---|
1148 | | - struct lwtunnel_state *lwtstate; |
---|
1149 | | - |
---|
1150 | | - if (cfg->fc_encap_type == LWTUNNEL_ENCAP_NONE) { |
---|
1151 | | - NL_SET_ERR_MSG(extack, |
---|
1152 | | - "LWT encap type not specified"); |
---|
1153 | | - goto err_inval; |
---|
1154 | | - } |
---|
1155 | | - err = lwtunnel_build_state(cfg->fc_encap_type, |
---|
1156 | | - cfg->fc_encap, AF_INET, cfg, |
---|
1157 | | - &lwtstate, extack); |
---|
1158 | | - if (err) |
---|
1159 | | - goto failure; |
---|
1160 | | - |
---|
1161 | | - nh->nh_lwtstate = lwtstate_get(lwtstate); |
---|
1162 | | - } |
---|
1163 | | - nh->nh_oif = cfg->fc_oif; |
---|
1164 | | - nh->nh_gw = cfg->fc_gw; |
---|
1165 | | - nh->nh_flags = cfg->fc_flags; |
---|
1166 | | -#ifdef CONFIG_IP_ROUTE_CLASSID |
---|
1167 | | - nh->nh_tclassid = cfg->fc_flow; |
---|
1168 | | - if (nh->nh_tclassid) |
---|
1169 | | - fi->fib_net->ipv4.fib_num_tclassid_users++; |
---|
1170 | | -#endif |
---|
1171 | | -#ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
1172 | | - nh->nh_weight = 1; |
---|
1173 | | -#endif |
---|
| 1509 | + if (cfg->fc_mp) |
---|
| 1510 | + err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg, |
---|
| 1511 | + extack); |
---|
| 1512 | + else |
---|
| 1513 | + err = fib_nh_init(net, fi->fib_nh, cfg, 1, extack); |
---|
1174 | 1514 | } |
---|
1175 | 1515 | |
---|
| 1516 | + if (err != 0) |
---|
| 1517 | + goto failure; |
---|
| 1518 | + |
---|
1176 | 1519 | if (fib_props[cfg->fc_type].error) { |
---|
1177 | | - if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) { |
---|
| 1520 | + if (cfg->fc_gw_family || cfg->fc_oif || cfg->fc_mp) { |
---|
1178 | 1521 | NL_SET_ERR_MSG(extack, |
---|
1179 | 1522 | "Gateway, device and multipath can not be specified for this route type"); |
---|
1180 | 1523 | goto err_inval; |
---|
.. | .. |
---|
1199 | 1542 | goto err_inval; |
---|
1200 | 1543 | } |
---|
1201 | 1544 | |
---|
1202 | | - if (cfg->fc_scope == RT_SCOPE_HOST) { |
---|
| 1545 | + if (fi->nh) { |
---|
| 1546 | + err = fib_check_nexthop(fi->nh, cfg->fc_scope, extack); |
---|
| 1547 | + if (err) |
---|
| 1548 | + goto failure; |
---|
| 1549 | + } else if (cfg->fc_scope == RT_SCOPE_HOST) { |
---|
1203 | 1550 | struct fib_nh *nh = fi->fib_nh; |
---|
1204 | 1551 | |
---|
1205 | 1552 | /* Local address is added. */ |
---|
.. | .. |
---|
1208 | 1555 | "Route with host scope can not have multiple nexthops"); |
---|
1209 | 1556 | goto err_inval; |
---|
1210 | 1557 | } |
---|
1211 | | - if (nh->nh_gw) { |
---|
| 1558 | + if (nh->fib_nh_gw_family) { |
---|
1212 | 1559 | NL_SET_ERR_MSG(extack, |
---|
1213 | 1560 | "Route with host scope can not have a gateway"); |
---|
1214 | 1561 | goto err_inval; |
---|
1215 | 1562 | } |
---|
1216 | | - nh->nh_scope = RT_SCOPE_NOWHERE; |
---|
1217 | | - nh->nh_dev = dev_get_by_index(net, fi->fib_nh->nh_oif); |
---|
| 1563 | + nh->fib_nh_scope = RT_SCOPE_NOWHERE; |
---|
| 1564 | + nh->fib_nh_dev = dev_get_by_index(net, nh->fib_nh_oif); |
---|
1218 | 1565 | err = -ENODEV; |
---|
1219 | | - if (!nh->nh_dev) |
---|
| 1566 | + if (!nh->fib_nh_dev) |
---|
1220 | 1567 | goto failure; |
---|
1221 | 1568 | } else { |
---|
1222 | 1569 | int linkdown = 0; |
---|
1223 | 1570 | |
---|
1224 | 1571 | change_nexthops(fi) { |
---|
1225 | | - err = fib_check_nh(cfg, nexthop_nh, extack); |
---|
| 1572 | + err = fib_check_nh(cfg->fc_nlinfo.nl_net, nexthop_nh, |
---|
| 1573 | + cfg->fc_table, cfg->fc_scope, |
---|
| 1574 | + extack); |
---|
1226 | 1575 | if (err != 0) |
---|
1227 | 1576 | goto failure; |
---|
1228 | | - if (nexthop_nh->nh_flags & RTNH_F_LINKDOWN) |
---|
| 1577 | + if (nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) |
---|
1229 | 1578 | linkdown++; |
---|
1230 | 1579 | } endfor_nexthops(fi) |
---|
1231 | 1580 | if (linkdown == fi->fib_nhs) |
---|
.. | .. |
---|
1237 | 1586 | goto err_inval; |
---|
1238 | 1587 | } |
---|
1239 | 1588 | |
---|
1240 | | - change_nexthops(fi) { |
---|
1241 | | - fib_info_update_nh_saddr(net, nexthop_nh); |
---|
1242 | | - } endfor_nexthops(fi) |
---|
| 1589 | + if (!fi->nh) { |
---|
| 1590 | + change_nexthops(fi) { |
---|
| 1591 | + fib_info_update_nhc_saddr(net, &nexthop_nh->nh_common, |
---|
| 1592 | + fi->fib_scope); |
---|
| 1593 | + if (nexthop_nh->fib_nh_gw_family == AF_INET6) |
---|
| 1594 | + fi->fib_nh_is_v6 = true; |
---|
| 1595 | + } endfor_nexthops(fi) |
---|
1243 | 1596 | |
---|
1244 | | - fib_rebalance(fi); |
---|
| 1597 | + fib_rebalance(fi); |
---|
| 1598 | + } |
---|
1245 | 1599 | |
---|
1246 | 1600 | link_it: |
---|
1247 | 1601 | ofi = fib_find_info(fi); |
---|
1248 | 1602 | if (ofi) { |
---|
| 1603 | + /* fib_table_lookup() should not see @fi yet. */ |
---|
1249 | 1604 | fi->fib_dead = 1; |
---|
1250 | 1605 | free_fib_info(fi); |
---|
1251 | 1606 | ofi->fib_treeref++; |
---|
.. | .. |
---|
1255 | 1610 | fi->fib_treeref++; |
---|
1256 | 1611 | refcount_set(&fi->fib_clntref, 1); |
---|
1257 | 1612 | spin_lock_bh(&fib_info_lock); |
---|
| 1613 | + fib_info_cnt++; |
---|
1258 | 1614 | hlist_add_head(&fi->fib_hash, |
---|
1259 | 1615 | &fib_info_hash[fib_info_hashfn(fi)]); |
---|
1260 | 1616 | if (fi->fib_prefsrc) { |
---|
.. | .. |
---|
1263 | 1619 | head = &fib_info_laddrhash[fib_laddr_hashfn(fi->fib_prefsrc)]; |
---|
1264 | 1620 | hlist_add_head(&fi->fib_lhash, head); |
---|
1265 | 1621 | } |
---|
1266 | | - change_nexthops(fi) { |
---|
1267 | | - struct hlist_head *head; |
---|
1268 | | - unsigned int hash; |
---|
| 1622 | + if (fi->nh) { |
---|
| 1623 | + list_add(&fi->nh_list, &nh->fi_list); |
---|
| 1624 | + } else { |
---|
| 1625 | + change_nexthops(fi) { |
---|
| 1626 | + struct hlist_head *head; |
---|
1269 | 1627 | |
---|
1270 | | - if (!nexthop_nh->nh_dev) |
---|
1271 | | - continue; |
---|
1272 | | - hash = fib_devindex_hashfn(nexthop_nh->nh_dev->ifindex); |
---|
1273 | | - head = &fib_info_devhash[hash]; |
---|
1274 | | - hlist_add_head(&nexthop_nh->nh_hash, head); |
---|
1275 | | - } endfor_nexthops(fi) |
---|
| 1628 | + if (!nexthop_nh->fib_nh_dev) |
---|
| 1629 | + continue; |
---|
| 1630 | + head = fib_info_devhash_bucket(nexthop_nh->fib_nh_dev); |
---|
| 1631 | + hlist_add_head(&nexthop_nh->nh_hash, head); |
---|
| 1632 | + } endfor_nexthops(fi) |
---|
| 1633 | + } |
---|
1276 | 1634 | spin_unlock_bh(&fib_info_lock); |
---|
1277 | 1635 | return fi; |
---|
1278 | 1636 | |
---|
.. | .. |
---|
1281 | 1639 | |
---|
1282 | 1640 | failure: |
---|
1283 | 1641 | if (fi) { |
---|
| 1642 | + /* fib_table_lookup() should not see @fi yet. */ |
---|
1284 | 1643 | fi->fib_dead = 1; |
---|
1285 | 1644 | free_fib_info(fi); |
---|
1286 | 1645 | } |
---|
.. | .. |
---|
1288 | 1647 | return ERR_PTR(err); |
---|
1289 | 1648 | } |
---|
1290 | 1649 | |
---|
1291 | | -int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, |
---|
1292 | | - u32 tb_id, u8 type, __be32 dst, int dst_len, u8 tos, |
---|
1293 | | - struct fib_info *fi, unsigned int flags) |
---|
| 1650 | +int fib_nexthop_info(struct sk_buff *skb, const struct fib_nh_common *nhc, |
---|
| 1651 | + u8 rt_family, unsigned char *flags, bool skip_oif) |
---|
1294 | 1652 | { |
---|
| 1653 | + if (nhc->nhc_flags & RTNH_F_DEAD) |
---|
| 1654 | + *flags |= RTNH_F_DEAD; |
---|
| 1655 | + |
---|
| 1656 | + if (nhc->nhc_flags & RTNH_F_LINKDOWN) { |
---|
| 1657 | + *flags |= RTNH_F_LINKDOWN; |
---|
| 1658 | + |
---|
| 1659 | + rcu_read_lock(); |
---|
| 1660 | + switch (nhc->nhc_family) { |
---|
| 1661 | + case AF_INET: |
---|
| 1662 | + if (ip_ignore_linkdown(nhc->nhc_dev)) |
---|
| 1663 | + *flags |= RTNH_F_DEAD; |
---|
| 1664 | + break; |
---|
| 1665 | + case AF_INET6: |
---|
| 1666 | + if (ip6_ignore_linkdown(nhc->nhc_dev)) |
---|
| 1667 | + *flags |= RTNH_F_DEAD; |
---|
| 1668 | + break; |
---|
| 1669 | + } |
---|
| 1670 | + rcu_read_unlock(); |
---|
| 1671 | + } |
---|
| 1672 | + |
---|
| 1673 | + switch (nhc->nhc_gw_family) { |
---|
| 1674 | + case AF_INET: |
---|
| 1675 | + if (nla_put_in_addr(skb, RTA_GATEWAY, nhc->nhc_gw.ipv4)) |
---|
| 1676 | + goto nla_put_failure; |
---|
| 1677 | + break; |
---|
| 1678 | + case AF_INET6: |
---|
| 1679 | + /* if gateway family does not match nexthop family |
---|
| 1680 | + * gateway is encoded as RTA_VIA |
---|
| 1681 | + */ |
---|
| 1682 | + if (rt_family != nhc->nhc_gw_family) { |
---|
| 1683 | + int alen = sizeof(struct in6_addr); |
---|
| 1684 | + struct nlattr *nla; |
---|
| 1685 | + struct rtvia *via; |
---|
| 1686 | + |
---|
| 1687 | + nla = nla_reserve(skb, RTA_VIA, alen + 2); |
---|
| 1688 | + if (!nla) |
---|
| 1689 | + goto nla_put_failure; |
---|
| 1690 | + |
---|
| 1691 | + via = nla_data(nla); |
---|
| 1692 | + via->rtvia_family = AF_INET6; |
---|
| 1693 | + memcpy(via->rtvia_addr, &nhc->nhc_gw.ipv6, alen); |
---|
| 1694 | + } else if (nla_put_in6_addr(skb, RTA_GATEWAY, |
---|
| 1695 | + &nhc->nhc_gw.ipv6) < 0) { |
---|
| 1696 | + goto nla_put_failure; |
---|
| 1697 | + } |
---|
| 1698 | + break; |
---|
| 1699 | + } |
---|
| 1700 | + |
---|
| 1701 | + *flags |= (nhc->nhc_flags & RTNH_F_ONLINK); |
---|
| 1702 | + if (nhc->nhc_flags & RTNH_F_OFFLOAD) |
---|
| 1703 | + *flags |= RTNH_F_OFFLOAD; |
---|
| 1704 | + |
---|
| 1705 | + if (!skip_oif && nhc->nhc_dev && |
---|
| 1706 | + nla_put_u32(skb, RTA_OIF, nhc->nhc_dev->ifindex)) |
---|
| 1707 | + goto nla_put_failure; |
---|
| 1708 | + |
---|
| 1709 | + if (nhc->nhc_lwtstate && |
---|
| 1710 | + lwtunnel_fill_encap(skb, nhc->nhc_lwtstate, |
---|
| 1711 | + RTA_ENCAP, RTA_ENCAP_TYPE) < 0) |
---|
| 1712 | + goto nla_put_failure; |
---|
| 1713 | + |
---|
| 1714 | + return 0; |
---|
| 1715 | + |
---|
| 1716 | +nla_put_failure: |
---|
| 1717 | + return -EMSGSIZE; |
---|
| 1718 | +} |
---|
| 1719 | +EXPORT_SYMBOL_GPL(fib_nexthop_info); |
---|
| 1720 | + |
---|
| 1721 | +#if IS_ENABLED(CONFIG_IP_ROUTE_MULTIPATH) || IS_ENABLED(CONFIG_IPV6) |
---|
| 1722 | +int fib_add_nexthop(struct sk_buff *skb, const struct fib_nh_common *nhc, |
---|
| 1723 | + int nh_weight, u8 rt_family, u32 nh_tclassid) |
---|
| 1724 | +{ |
---|
| 1725 | + const struct net_device *dev = nhc->nhc_dev; |
---|
| 1726 | + struct rtnexthop *rtnh; |
---|
| 1727 | + unsigned char flags = 0; |
---|
| 1728 | + |
---|
| 1729 | + rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); |
---|
| 1730 | + if (!rtnh) |
---|
| 1731 | + goto nla_put_failure; |
---|
| 1732 | + |
---|
| 1733 | + rtnh->rtnh_hops = nh_weight - 1; |
---|
| 1734 | + rtnh->rtnh_ifindex = dev ? dev->ifindex : 0; |
---|
| 1735 | + |
---|
| 1736 | + if (fib_nexthop_info(skb, nhc, rt_family, &flags, true) < 0) |
---|
| 1737 | + goto nla_put_failure; |
---|
| 1738 | + |
---|
| 1739 | + rtnh->rtnh_flags = flags; |
---|
| 1740 | + |
---|
| 1741 | + if (nh_tclassid && nla_put_u32(skb, RTA_FLOW, nh_tclassid)) |
---|
| 1742 | + goto nla_put_failure; |
---|
| 1743 | + |
---|
| 1744 | + /* length of rtnetlink header + attributes */ |
---|
| 1745 | + rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh; |
---|
| 1746 | + |
---|
| 1747 | + return 0; |
---|
| 1748 | + |
---|
| 1749 | +nla_put_failure: |
---|
| 1750 | + return -EMSGSIZE; |
---|
| 1751 | +} |
---|
| 1752 | +EXPORT_SYMBOL_GPL(fib_add_nexthop); |
---|
| 1753 | +#endif |
---|
| 1754 | + |
---|
| 1755 | +#ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
| 1756 | +static int fib_add_multipath(struct sk_buff *skb, struct fib_info *fi) |
---|
| 1757 | +{ |
---|
| 1758 | + struct nlattr *mp; |
---|
| 1759 | + |
---|
| 1760 | + mp = nla_nest_start_noflag(skb, RTA_MULTIPATH); |
---|
| 1761 | + if (!mp) |
---|
| 1762 | + goto nla_put_failure; |
---|
| 1763 | + |
---|
| 1764 | + if (unlikely(fi->nh)) { |
---|
| 1765 | + if (nexthop_mpath_fill_node(skb, fi->nh, AF_INET) < 0) |
---|
| 1766 | + goto nla_put_failure; |
---|
| 1767 | + goto mp_end; |
---|
| 1768 | + } |
---|
| 1769 | + |
---|
| 1770 | + for_nexthops(fi) { |
---|
| 1771 | + u32 nh_tclassid = 0; |
---|
| 1772 | +#ifdef CONFIG_IP_ROUTE_CLASSID |
---|
| 1773 | + nh_tclassid = nh->nh_tclassid; |
---|
| 1774 | +#endif |
---|
| 1775 | + if (fib_add_nexthop(skb, &nh->nh_common, nh->fib_nh_weight, |
---|
| 1776 | + AF_INET, nh_tclassid) < 0) |
---|
| 1777 | + goto nla_put_failure; |
---|
| 1778 | + } endfor_nexthops(fi); |
---|
| 1779 | + |
---|
| 1780 | +mp_end: |
---|
| 1781 | + nla_nest_end(skb, mp); |
---|
| 1782 | + |
---|
| 1783 | + return 0; |
---|
| 1784 | + |
---|
| 1785 | +nla_put_failure: |
---|
| 1786 | + return -EMSGSIZE; |
---|
| 1787 | +} |
---|
| 1788 | +#else |
---|
| 1789 | +static int fib_add_multipath(struct sk_buff *skb, struct fib_info *fi) |
---|
| 1790 | +{ |
---|
| 1791 | + return 0; |
---|
| 1792 | +} |
---|
| 1793 | +#endif |
---|
| 1794 | + |
---|
| 1795 | +int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, |
---|
| 1796 | + struct fib_rt_info *fri, unsigned int flags) |
---|
| 1797 | +{ |
---|
| 1798 | + unsigned int nhs = fib_info_num_path(fri->fi); |
---|
| 1799 | + struct fib_info *fi = fri->fi; |
---|
| 1800 | + u32 tb_id = fri->tb_id; |
---|
1295 | 1801 | struct nlmsghdr *nlh; |
---|
1296 | 1802 | struct rtmsg *rtm; |
---|
1297 | 1803 | |
---|
.. | .. |
---|
1301 | 1807 | |
---|
1302 | 1808 | rtm = nlmsg_data(nlh); |
---|
1303 | 1809 | rtm->rtm_family = AF_INET; |
---|
1304 | | - rtm->rtm_dst_len = dst_len; |
---|
| 1810 | + rtm->rtm_dst_len = fri->dst_len; |
---|
1305 | 1811 | rtm->rtm_src_len = 0; |
---|
1306 | | - rtm->rtm_tos = tos; |
---|
| 1812 | + rtm->rtm_tos = fri->tos; |
---|
1307 | 1813 | if (tb_id < 256) |
---|
1308 | 1814 | rtm->rtm_table = tb_id; |
---|
1309 | 1815 | else |
---|
1310 | 1816 | rtm->rtm_table = RT_TABLE_COMPAT; |
---|
1311 | 1817 | if (nla_put_u32(skb, RTA_TABLE, tb_id)) |
---|
1312 | 1818 | goto nla_put_failure; |
---|
1313 | | - rtm->rtm_type = type; |
---|
| 1819 | + rtm->rtm_type = fri->type; |
---|
1314 | 1820 | rtm->rtm_flags = fi->fib_flags; |
---|
1315 | 1821 | rtm->rtm_scope = fi->fib_scope; |
---|
1316 | 1822 | rtm->rtm_protocol = fi->fib_protocol; |
---|
1317 | 1823 | |
---|
1318 | 1824 | if (rtm->rtm_dst_len && |
---|
1319 | | - nla_put_in_addr(skb, RTA_DST, dst)) |
---|
| 1825 | + nla_put_in_addr(skb, RTA_DST, fri->dst)) |
---|
1320 | 1826 | goto nla_put_failure; |
---|
1321 | 1827 | if (fi->fib_priority && |
---|
1322 | 1828 | nla_put_u32(skb, RTA_PRIORITY, fi->fib_priority)) |
---|
.. | .. |
---|
1327 | 1833 | if (fi->fib_prefsrc && |
---|
1328 | 1834 | nla_put_in_addr(skb, RTA_PREFSRC, fi->fib_prefsrc)) |
---|
1329 | 1835 | goto nla_put_failure; |
---|
1330 | | - if (fi->fib_nhs == 1) { |
---|
1331 | | - if (fi->fib_nh->nh_gw && |
---|
1332 | | - nla_put_in_addr(skb, RTA_GATEWAY, fi->fib_nh->nh_gw)) |
---|
1333 | | - goto nla_put_failure; |
---|
1334 | | - if (fi->fib_nh->nh_oif && |
---|
1335 | | - nla_put_u32(skb, RTA_OIF, fi->fib_nh->nh_oif)) |
---|
1336 | | - goto nla_put_failure; |
---|
1337 | | - if (fi->fib_nh->nh_flags & RTNH_F_LINKDOWN) { |
---|
1338 | | - struct in_device *in_dev; |
---|
1339 | 1836 | |
---|
1340 | | - rcu_read_lock(); |
---|
1341 | | - in_dev = __in_dev_get_rcu(fi->fib_nh->nh_dev); |
---|
1342 | | - if (in_dev && |
---|
1343 | | - IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev)) |
---|
1344 | | - rtm->rtm_flags |= RTNH_F_DEAD; |
---|
1345 | | - rcu_read_unlock(); |
---|
1346 | | - } |
---|
1347 | | - if (fi->fib_nh->nh_flags & RTNH_F_OFFLOAD) |
---|
1348 | | - rtm->rtm_flags |= RTNH_F_OFFLOAD; |
---|
1349 | | -#ifdef CONFIG_IP_ROUTE_CLASSID |
---|
1350 | | - if (fi->fib_nh[0].nh_tclassid && |
---|
1351 | | - nla_put_u32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid)) |
---|
| 1837 | + if (fi->nh) { |
---|
| 1838 | + if (nla_put_u32(skb, RTA_NH_ID, fi->nh->id)) |
---|
1352 | 1839 | goto nla_put_failure; |
---|
1353 | | -#endif |
---|
1354 | | - if (fi->fib_nh->nh_lwtstate && |
---|
1355 | | - lwtunnel_fill_encap(skb, fi->fib_nh->nh_lwtstate) < 0) |
---|
1356 | | - goto nla_put_failure; |
---|
| 1840 | + if (nexthop_is_blackhole(fi->nh)) |
---|
| 1841 | + rtm->rtm_type = RTN_BLACKHOLE; |
---|
| 1842 | + if (!READ_ONCE(fi->fib_net->ipv4.sysctl_nexthop_compat_mode)) |
---|
| 1843 | + goto offload; |
---|
1357 | 1844 | } |
---|
1358 | | -#ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
1359 | | - if (fi->fib_nhs > 1) { |
---|
1360 | | - struct rtnexthop *rtnh; |
---|
1361 | | - struct nlattr *mp; |
---|
1362 | 1845 | |
---|
1363 | | - mp = nla_nest_start(skb, RTA_MULTIPATH); |
---|
1364 | | - if (!mp) |
---|
| 1846 | + if (nhs == 1) { |
---|
| 1847 | + const struct fib_nh_common *nhc = fib_info_nhc(fi, 0); |
---|
| 1848 | + unsigned char flags = 0; |
---|
| 1849 | + |
---|
| 1850 | + if (fib_nexthop_info(skb, nhc, AF_INET, &flags, false) < 0) |
---|
1365 | 1851 | goto nla_put_failure; |
---|
1366 | 1852 | |
---|
1367 | | - for_nexthops(fi) { |
---|
1368 | | - rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); |
---|
1369 | | - if (!rtnh) |
---|
1370 | | - goto nla_put_failure; |
---|
1371 | | - |
---|
1372 | | - rtnh->rtnh_flags = nh->nh_flags & 0xFF; |
---|
1373 | | - if (nh->nh_flags & RTNH_F_LINKDOWN) { |
---|
1374 | | - struct in_device *in_dev; |
---|
1375 | | - |
---|
1376 | | - rcu_read_lock(); |
---|
1377 | | - in_dev = __in_dev_get_rcu(nh->nh_dev); |
---|
1378 | | - if (in_dev && |
---|
1379 | | - IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev)) |
---|
1380 | | - rtnh->rtnh_flags |= RTNH_F_DEAD; |
---|
1381 | | - rcu_read_unlock(); |
---|
1382 | | - } |
---|
1383 | | - rtnh->rtnh_hops = nh->nh_weight - 1; |
---|
1384 | | - rtnh->rtnh_ifindex = nh->nh_oif; |
---|
1385 | | - |
---|
1386 | | - if (nh->nh_gw && |
---|
1387 | | - nla_put_in_addr(skb, RTA_GATEWAY, nh->nh_gw)) |
---|
1388 | | - goto nla_put_failure; |
---|
| 1853 | + rtm->rtm_flags = flags; |
---|
1389 | 1854 | #ifdef CONFIG_IP_ROUTE_CLASSID |
---|
| 1855 | + if (nhc->nhc_family == AF_INET) { |
---|
| 1856 | + struct fib_nh *nh; |
---|
| 1857 | + |
---|
| 1858 | + nh = container_of(nhc, struct fib_nh, nh_common); |
---|
1390 | 1859 | if (nh->nh_tclassid && |
---|
1391 | 1860 | nla_put_u32(skb, RTA_FLOW, nh->nh_tclassid)) |
---|
1392 | 1861 | goto nla_put_failure; |
---|
| 1862 | + } |
---|
1393 | 1863 | #endif |
---|
1394 | | - if (nh->nh_lwtstate && |
---|
1395 | | - lwtunnel_fill_encap(skb, nh->nh_lwtstate) < 0) |
---|
1396 | | - goto nla_put_failure; |
---|
1397 | | - |
---|
1398 | | - /* length of rtnetlink header + attributes */ |
---|
1399 | | - rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *) rtnh; |
---|
1400 | | - } endfor_nexthops(fi); |
---|
1401 | | - |
---|
1402 | | - nla_nest_end(skb, mp); |
---|
| 1864 | + } else { |
---|
| 1865 | + if (fib_add_multipath(skb, fi) < 0) |
---|
| 1866 | + goto nla_put_failure; |
---|
1403 | 1867 | } |
---|
1404 | | -#endif |
---|
| 1868 | + |
---|
| 1869 | +offload: |
---|
| 1870 | + if (fri->offload) |
---|
| 1871 | + rtm->rtm_flags |= RTM_F_OFFLOAD; |
---|
| 1872 | + if (fri->trap) |
---|
| 1873 | + rtm->rtm_flags |= RTM_F_TRAP; |
---|
| 1874 | + |
---|
1405 | 1875 | nlmsg_end(skb, nlh); |
---|
1406 | 1876 | return 0; |
---|
1407 | 1877 | |
---|
.. | .. |
---|
1440 | 1910 | return ret; |
---|
1441 | 1911 | } |
---|
1442 | 1912 | |
---|
1443 | | -static int call_fib_nh_notifiers(struct fib_nh *fib_nh, |
---|
| 1913 | +static int call_fib_nh_notifiers(struct fib_nh *nh, |
---|
1444 | 1914 | enum fib_event_type event_type) |
---|
1445 | 1915 | { |
---|
1446 | | - struct in_device *in_dev = __in_dev_get_rtnl(fib_nh->nh_dev); |
---|
| 1916 | + bool ignore_link_down = ip_ignore_linkdown(nh->fib_nh_dev); |
---|
1447 | 1917 | struct fib_nh_notifier_info info = { |
---|
1448 | | - .fib_nh = fib_nh, |
---|
| 1918 | + .fib_nh = nh, |
---|
1449 | 1919 | }; |
---|
1450 | 1920 | |
---|
1451 | 1921 | switch (event_type) { |
---|
1452 | 1922 | case FIB_EVENT_NH_ADD: |
---|
1453 | | - if (fib_nh->nh_flags & RTNH_F_DEAD) |
---|
| 1923 | + if (nh->fib_nh_flags & RTNH_F_DEAD) |
---|
1454 | 1924 | break; |
---|
1455 | | - if (IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && |
---|
1456 | | - fib_nh->nh_flags & RTNH_F_LINKDOWN) |
---|
| 1925 | + if (ignore_link_down && nh->fib_nh_flags & RTNH_F_LINKDOWN) |
---|
1457 | 1926 | break; |
---|
1458 | | - return call_fib4_notifiers(dev_net(fib_nh->nh_dev), event_type, |
---|
| 1927 | + return call_fib4_notifiers(dev_net(nh->fib_nh_dev), event_type, |
---|
1459 | 1928 | &info.info); |
---|
1460 | 1929 | case FIB_EVENT_NH_DEL: |
---|
1461 | | - if ((in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && |
---|
1462 | | - fib_nh->nh_flags & RTNH_F_LINKDOWN) || |
---|
1463 | | - (fib_nh->nh_flags & RTNH_F_DEAD)) |
---|
1464 | | - return call_fib4_notifiers(dev_net(fib_nh->nh_dev), |
---|
| 1930 | + if ((ignore_link_down && nh->fib_nh_flags & RTNH_F_LINKDOWN) || |
---|
| 1931 | + (nh->fib_nh_flags & RTNH_F_DEAD)) |
---|
| 1932 | + return call_fib4_notifiers(dev_net(nh->fib_nh_dev), |
---|
1465 | 1933 | event_type, &info.info); |
---|
1466 | 1934 | default: |
---|
1467 | 1935 | break; |
---|
.. | .. |
---|
1480 | 1948 | * - if the new MTU is greater than the PMTU, don't make any change |
---|
1481 | 1949 | * - otherwise, unlock and set PMTU |
---|
1482 | 1950 | */ |
---|
1483 | | -static void nh_update_mtu(struct fib_nh *nh, u32 new, u32 orig) |
---|
| 1951 | +void fib_nhc_update_mtu(struct fib_nh_common *nhc, u32 new, u32 orig) |
---|
1484 | 1952 | { |
---|
1485 | 1953 | struct fnhe_hash_bucket *bucket; |
---|
1486 | 1954 | int i; |
---|
1487 | 1955 | |
---|
1488 | | - bucket = rcu_dereference_protected(nh->nh_exceptions, 1); |
---|
| 1956 | + bucket = rcu_dereference_protected(nhc->nhc_exceptions, 1); |
---|
1489 | 1957 | if (!bucket) |
---|
1490 | 1958 | return; |
---|
1491 | 1959 | |
---|
.. | .. |
---|
1510 | 1978 | |
---|
1511 | 1979 | void fib_sync_mtu(struct net_device *dev, u32 orig_mtu) |
---|
1512 | 1980 | { |
---|
1513 | | - unsigned int hash = fib_devindex_hashfn(dev->ifindex); |
---|
1514 | | - struct hlist_head *head = &fib_info_devhash[hash]; |
---|
| 1981 | + struct hlist_head *head = fib_info_devhash_bucket(dev); |
---|
1515 | 1982 | struct fib_nh *nh; |
---|
1516 | 1983 | |
---|
1517 | 1984 | hlist_for_each_entry(nh, head, nh_hash) { |
---|
1518 | | - if (nh->nh_dev == dev) |
---|
1519 | | - nh_update_mtu(nh, dev->mtu, orig_mtu); |
---|
| 1985 | + if (nh->fib_nh_dev == dev) |
---|
| 1986 | + fib_nhc_update_mtu(&nh->nh_common, dev->mtu, orig_mtu); |
---|
1520 | 1987 | } |
---|
1521 | 1988 | } |
---|
1522 | 1989 | |
---|
.. | .. |
---|
1525 | 1992 | * NETDEV_DOWN 0 LINKDOWN|DEAD Link down, not for scope host |
---|
1526 | 1993 | * NETDEV_DOWN 1 LINKDOWN|DEAD Last address removed |
---|
1527 | 1994 | * NETDEV_UNREGISTER 1 LINKDOWN|DEAD Device removed |
---|
| 1995 | + * |
---|
| 1996 | + * only used when fib_nh is built into fib_info |
---|
1528 | 1997 | */ |
---|
1529 | 1998 | int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force) |
---|
1530 | 1999 | { |
---|
1531 | | - int ret = 0; |
---|
1532 | | - int scope = RT_SCOPE_NOWHERE; |
---|
| 2000 | + struct hlist_head *head = fib_info_devhash_bucket(dev); |
---|
1533 | 2001 | struct fib_info *prev_fi = NULL; |
---|
1534 | | - unsigned int hash = fib_devindex_hashfn(dev->ifindex); |
---|
1535 | | - struct hlist_head *head = &fib_info_devhash[hash]; |
---|
| 2002 | + int scope = RT_SCOPE_NOWHERE; |
---|
1536 | 2003 | struct fib_nh *nh; |
---|
| 2004 | + int ret = 0; |
---|
1537 | 2005 | |
---|
1538 | 2006 | if (force) |
---|
1539 | 2007 | scope = -1; |
---|
.. | .. |
---|
1543 | 2011 | int dead; |
---|
1544 | 2012 | |
---|
1545 | 2013 | BUG_ON(!fi->fib_nhs); |
---|
1546 | | - if (nh->nh_dev != dev || fi == prev_fi) |
---|
| 2014 | + if (nh->fib_nh_dev != dev || fi == prev_fi) |
---|
1547 | 2015 | continue; |
---|
1548 | 2016 | prev_fi = fi; |
---|
1549 | 2017 | dead = 0; |
---|
1550 | 2018 | change_nexthops(fi) { |
---|
1551 | | - if (nexthop_nh->nh_flags & RTNH_F_DEAD) |
---|
| 2019 | + if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) |
---|
1552 | 2020 | dead++; |
---|
1553 | | - else if (nexthop_nh->nh_dev == dev && |
---|
1554 | | - nexthop_nh->nh_scope != scope) { |
---|
| 2021 | + else if (nexthop_nh->fib_nh_dev == dev && |
---|
| 2022 | + nexthop_nh->fib_nh_scope != scope) { |
---|
1555 | 2023 | switch (event) { |
---|
1556 | 2024 | case NETDEV_DOWN: |
---|
1557 | 2025 | case NETDEV_UNREGISTER: |
---|
1558 | | - nexthop_nh->nh_flags |= RTNH_F_DEAD; |
---|
1559 | | - /* fall through */ |
---|
| 2026 | + nexthop_nh->fib_nh_flags |= RTNH_F_DEAD; |
---|
| 2027 | + fallthrough; |
---|
1560 | 2028 | case NETDEV_CHANGE: |
---|
1561 | | - nexthop_nh->nh_flags |= RTNH_F_LINKDOWN; |
---|
| 2029 | + nexthop_nh->fib_nh_flags |= RTNH_F_LINKDOWN; |
---|
1562 | 2030 | break; |
---|
1563 | 2031 | } |
---|
1564 | 2032 | call_fib_nh_notifiers(nexthop_nh, |
---|
.. | .. |
---|
1567 | 2035 | } |
---|
1568 | 2036 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
1569 | 2037 | if (event == NETDEV_UNREGISTER && |
---|
1570 | | - nexthop_nh->nh_dev == dev) { |
---|
| 2038 | + nexthop_nh->fib_nh_dev == dev) { |
---|
1571 | 2039 | dead = fi->fib_nhs; |
---|
1572 | 2040 | break; |
---|
1573 | 2041 | } |
---|
.. | .. |
---|
1578 | 2046 | case NETDEV_DOWN: |
---|
1579 | 2047 | case NETDEV_UNREGISTER: |
---|
1580 | 2048 | fi->fib_flags |= RTNH_F_DEAD; |
---|
1581 | | - /* fall through */ |
---|
| 2049 | + fallthrough; |
---|
1582 | 2050 | case NETDEV_CHANGE: |
---|
1583 | 2051 | fi->fib_flags |= RTNH_F_LINKDOWN; |
---|
1584 | 2052 | break; |
---|
.. | .. |
---|
1606 | 2074 | |
---|
1607 | 2075 | hlist_for_each_entry_rcu(fa, fa_head, fa_list) { |
---|
1608 | 2076 | struct fib_info *next_fi = fa->fa_info; |
---|
| 2077 | + struct fib_nh_common *nhc; |
---|
1609 | 2078 | |
---|
1610 | 2079 | if (fa->fa_slen != slen) |
---|
1611 | 2080 | continue; |
---|
.. | .. |
---|
1627 | 2096 | if (next_fi->fib_scope != res->scope || |
---|
1628 | 2097 | fa->fa_type != RTN_UNICAST) |
---|
1629 | 2098 | continue; |
---|
1630 | | - if (!next_fi->fib_nh[0].nh_gw || |
---|
1631 | | - next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK) |
---|
| 2099 | + |
---|
| 2100 | + nhc = fib_info_nhc(next_fi, 0); |
---|
| 2101 | + if (!nhc->nhc_gw_family || nhc->nhc_scope != RT_SCOPE_LINK) |
---|
1632 | 2102 | continue; |
---|
1633 | 2103 | |
---|
1634 | 2104 | fib_alias_accessed(fa); |
---|
.. | .. |
---|
1670 | 2140 | /* |
---|
1671 | 2141 | * Dead device goes up. We wake up dead nexthops. |
---|
1672 | 2142 | * It takes sense only on multipath routes. |
---|
| 2143 | + * |
---|
| 2144 | + * only used when fib_nh is built into fib_info |
---|
1673 | 2145 | */ |
---|
1674 | | -int fib_sync_up(struct net_device *dev, unsigned int nh_flags) |
---|
| 2146 | +int fib_sync_up(struct net_device *dev, unsigned char nh_flags) |
---|
1675 | 2147 | { |
---|
1676 | 2148 | struct fib_info *prev_fi; |
---|
1677 | | - unsigned int hash; |
---|
1678 | 2149 | struct hlist_head *head; |
---|
1679 | 2150 | struct fib_nh *nh; |
---|
1680 | 2151 | int ret; |
---|
.. | .. |
---|
1690 | 2161 | } |
---|
1691 | 2162 | |
---|
1692 | 2163 | prev_fi = NULL; |
---|
1693 | | - hash = fib_devindex_hashfn(dev->ifindex); |
---|
1694 | | - head = &fib_info_devhash[hash]; |
---|
| 2164 | + head = fib_info_devhash_bucket(dev); |
---|
1695 | 2165 | ret = 0; |
---|
1696 | 2166 | |
---|
1697 | 2167 | hlist_for_each_entry(nh, head, nh_hash) { |
---|
.. | .. |
---|
1699 | 2169 | int alive; |
---|
1700 | 2170 | |
---|
1701 | 2171 | BUG_ON(!fi->fib_nhs); |
---|
1702 | | - if (nh->nh_dev != dev || fi == prev_fi) |
---|
| 2172 | + if (nh->fib_nh_dev != dev || fi == prev_fi) |
---|
1703 | 2173 | continue; |
---|
1704 | 2174 | |
---|
1705 | 2175 | prev_fi = fi; |
---|
1706 | 2176 | alive = 0; |
---|
1707 | 2177 | change_nexthops(fi) { |
---|
1708 | | - if (!(nexthop_nh->nh_flags & nh_flags)) { |
---|
| 2178 | + if (!(nexthop_nh->fib_nh_flags & nh_flags)) { |
---|
1709 | 2179 | alive++; |
---|
1710 | 2180 | continue; |
---|
1711 | 2181 | } |
---|
1712 | | - if (!nexthop_nh->nh_dev || |
---|
1713 | | - !(nexthop_nh->nh_dev->flags & IFF_UP)) |
---|
| 2182 | + if (!nexthop_nh->fib_nh_dev || |
---|
| 2183 | + !(nexthop_nh->fib_nh_dev->flags & IFF_UP)) |
---|
1714 | 2184 | continue; |
---|
1715 | | - if (nexthop_nh->nh_dev != dev || |
---|
| 2185 | + if (nexthop_nh->fib_nh_dev != dev || |
---|
1716 | 2186 | !__in_dev_get_rtnl(dev)) |
---|
1717 | 2187 | continue; |
---|
1718 | 2188 | alive++; |
---|
1719 | | - nexthop_nh->nh_flags &= ~nh_flags; |
---|
| 2189 | + nexthop_nh->fib_nh_flags &= ~nh_flags; |
---|
1720 | 2190 | call_fib_nh_notifiers(nexthop_nh, FIB_EVENT_NH_ADD); |
---|
1721 | 2191 | } endfor_nexthops(fi) |
---|
1722 | 2192 | |
---|
.. | .. |
---|
1736 | 2206 | { |
---|
1737 | 2207 | int state = NUD_REACHABLE; |
---|
1738 | 2208 | |
---|
1739 | | - if (nh->nh_scope == RT_SCOPE_LINK) { |
---|
| 2209 | + if (nh->fib_nh_scope == RT_SCOPE_LINK) { |
---|
1740 | 2210 | struct neighbour *n; |
---|
1741 | 2211 | |
---|
1742 | 2212 | rcu_read_lock_bh(); |
---|
1743 | 2213 | |
---|
1744 | | - n = __ipv4_neigh_lookup_noref(nh->nh_dev, |
---|
1745 | | - (__force u32)nh->nh_gw); |
---|
| 2214 | + if (likely(nh->fib_nh_gw_family == AF_INET)) |
---|
| 2215 | + n = __ipv4_neigh_lookup_noref(nh->fib_nh_dev, |
---|
| 2216 | + (__force u32)nh->fib_nh_gw4); |
---|
| 2217 | + else if (nh->fib_nh_gw_family == AF_INET6) |
---|
| 2218 | + n = __ipv6_neigh_lookup_noref_stub(nh->fib_nh_dev, |
---|
| 2219 | + &nh->fib_nh_gw6); |
---|
| 2220 | + else |
---|
| 2221 | + n = NULL; |
---|
1746 | 2222 | if (n) |
---|
1747 | 2223 | state = n->nud_state; |
---|
1748 | 2224 | |
---|
.. | .. |
---|
1758 | 2234 | struct net *net = fi->fib_net; |
---|
1759 | 2235 | bool first = false; |
---|
1760 | 2236 | |
---|
1761 | | - for_nexthops(fi) { |
---|
1762 | | - if (net->ipv4.sysctl_fib_multipath_use_neigh) { |
---|
1763 | | - if (!fib_good_nh(nh)) |
---|
| 2237 | + if (unlikely(res->fi->nh)) { |
---|
| 2238 | + nexthop_path_fib_result(res, hash); |
---|
| 2239 | + return; |
---|
| 2240 | + } |
---|
| 2241 | + |
---|
| 2242 | + change_nexthops(fi) { |
---|
| 2243 | + if (READ_ONCE(net->ipv4.sysctl_fib_multipath_use_neigh)) { |
---|
| 2244 | + if (!fib_good_nh(nexthop_nh)) |
---|
1764 | 2245 | continue; |
---|
1765 | 2246 | if (!first) { |
---|
1766 | 2247 | res->nh_sel = nhsel; |
---|
| 2248 | + res->nhc = &nexthop_nh->nh_common; |
---|
1767 | 2249 | first = true; |
---|
1768 | 2250 | } |
---|
1769 | 2251 | } |
---|
1770 | 2252 | |
---|
1771 | | - if (hash > atomic_read(&nh->nh_upper_bound)) |
---|
| 2253 | + if (hash > atomic_read(&nexthop_nh->fib_nh_upper_bound)) |
---|
1772 | 2254 | continue; |
---|
1773 | 2255 | |
---|
1774 | 2256 | res->nh_sel = nhsel; |
---|
| 2257 | + res->nhc = &nexthop_nh->nh_common; |
---|
1775 | 2258 | return; |
---|
1776 | 2259 | } endfor_nexthops(fi); |
---|
1777 | 2260 | } |
---|
.. | .. |
---|
1784 | 2267 | goto check_saddr; |
---|
1785 | 2268 | |
---|
1786 | 2269 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
1787 | | - if (res->fi->fib_nhs > 1) { |
---|
| 2270 | + if (fib_info_num_path(res->fi) > 1) { |
---|
1788 | 2271 | int h = fib_multipath_hash(net, fl4, skb, NULL); |
---|
1789 | 2272 | |
---|
1790 | 2273 | fib_select_multipath(res, h); |
---|
.. | .. |
---|
1798 | 2281 | |
---|
1799 | 2282 | check_saddr: |
---|
1800 | 2283 | if (!fl4->saddr) |
---|
1801 | | - fl4->saddr = FIB_RES_PREFSRC(net, *res); |
---|
| 2284 | + fl4->saddr = fib_result_prefsrc(net, res); |
---|
1802 | 2285 | } |
---|