| .. | .. | 
|---|
|  | 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 | } | 
|---|