11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * INET An implementation of the TCP/IP protocol suite for the LINUX 31da177e4SLinus Torvalds * operating system. INET is implemented using the BSD Socket 41da177e4SLinus Torvalds * interface as the means of communication with the user level. 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * IPv4 Forwarding Information Base: semantics. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 111da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 121da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 131da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 141da177e4SLinus Torvalds */ 151da177e4SLinus Torvalds 167c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 171da177e4SLinus Torvalds #include <linux/bitops.h> 181da177e4SLinus Torvalds #include <linux/types.h> 191da177e4SLinus Torvalds #include <linux/kernel.h> 201da177e4SLinus Torvalds #include <linux/jiffies.h> 211da177e4SLinus Torvalds #include <linux/mm.h> 221da177e4SLinus Torvalds #include <linux/string.h> 231da177e4SLinus Torvalds #include <linux/socket.h> 241da177e4SLinus Torvalds #include <linux/sockios.h> 251da177e4SLinus Torvalds #include <linux/errno.h> 261da177e4SLinus Torvalds #include <linux/in.h> 271da177e4SLinus Torvalds #include <linux/inet.h> 2814c85021SArnaldo Carvalho de Melo #include <linux/inetdevice.h> 291da177e4SLinus Torvalds #include <linux/netdevice.h> 301da177e4SLinus Torvalds #include <linux/if_arp.h> 311da177e4SLinus Torvalds #include <linux/proc_fs.h> 321da177e4SLinus Torvalds #include <linux/skbuff.h> 331da177e4SLinus Torvalds #include <linux/init.h> 345a0e3ad6STejun Heo #include <linux/slab.h> 35c3ab2b4eSDavid Ahern #include <linux/netlink.h> 361da177e4SLinus Torvalds 3714c85021SArnaldo Carvalho de Melo #include <net/arp.h> 381da177e4SLinus Torvalds #include <net/ip.h> 391da177e4SLinus Torvalds #include <net/protocol.h> 401da177e4SLinus Torvalds #include <net/route.h> 411da177e4SLinus Torvalds #include <net/tcp.h> 421da177e4SLinus Torvalds #include <net/sock.h> 431da177e4SLinus Torvalds #include <net/ip_fib.h> 44*717a8f5bSDavid Ahern #include <net/ip6_fib.h> 45f21c7bc5SThomas Graf #include <net/netlink.h> 464e902c57SThomas Graf #include <net/nexthop.h> 47571e7226SRoopa Prabhu #include <net/lwtunnel.h> 4804b1d4e5SIdo Schimmel #include <net/fib_notifier.h> 49c0a72077SDavid Ahern #include <net/addrconf.h> 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds #include "fib_lookup.h" 521da177e4SLinus Torvalds 53832b4c5eSStephen Hemminger static DEFINE_SPINLOCK(fib_info_lock); 541da177e4SLinus Torvalds static struct hlist_head *fib_info_hash; 551da177e4SLinus Torvalds static struct hlist_head *fib_info_laddrhash; 56123b9731SDavid S. Miller static unsigned int fib_info_hash_size; 571da177e4SLinus Torvalds static unsigned int fib_info_cnt; 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds #define DEVINDEX_HASHBITS 8 601da177e4SLinus Torvalds #define DEVINDEX_HASHSIZE (1U << DEVINDEX_HASHBITS) 611da177e4SLinus Torvalds static struct hlist_head fib_info_devhash[DEVINDEX_HASHSIZE]; 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 641da177e4SLinus Torvalds 656a31d2a9SEric Dumazet #define for_nexthops(fi) { \ 666a31d2a9SEric Dumazet int nhsel; const struct fib_nh *nh; \ 676a31d2a9SEric Dumazet for (nhsel = 0, nh = (fi)->fib_nh; \ 686a31d2a9SEric Dumazet nhsel < (fi)->fib_nhs; \ 696a31d2a9SEric Dumazet nh++, nhsel++) 701da177e4SLinus Torvalds 716a31d2a9SEric Dumazet #define change_nexthops(fi) { \ 726a31d2a9SEric Dumazet int nhsel; struct fib_nh *nexthop_nh; \ 736a31d2a9SEric Dumazet for (nhsel = 0, nexthop_nh = (struct fib_nh *)((fi)->fib_nh); \ 746a31d2a9SEric Dumazet nhsel < (fi)->fib_nhs; \ 756a31d2a9SEric Dumazet nexthop_nh++, nhsel++) 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds #else /* CONFIG_IP_ROUTE_MULTIPATH */ 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds /* Hope, that gcc will optimize it to get rid of dummy loop */ 801da177e4SLinus Torvalds 816a31d2a9SEric Dumazet #define for_nexthops(fi) { \ 826a31d2a9SEric Dumazet int nhsel; const struct fib_nh *nh = (fi)->fib_nh; \ 831da177e4SLinus Torvalds for (nhsel = 0; nhsel < 1; nhsel++) 841da177e4SLinus Torvalds 856a31d2a9SEric Dumazet #define change_nexthops(fi) { \ 866a31d2a9SEric Dumazet int nhsel; \ 876a31d2a9SEric Dumazet struct fib_nh *nexthop_nh = (struct fib_nh *)((fi)->fib_nh); \ 881da177e4SLinus Torvalds for (nhsel = 0; nhsel < 1; nhsel++) 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds #endif /* CONFIG_IP_ROUTE_MULTIPATH */ 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds #define endfor_nexthops(fi) } 931da177e4SLinus Torvalds 941da177e4SLinus Torvalds 953be0686bSDavid S. Miller const struct fib_prop fib_props[RTN_MAX + 1] = { 966a31d2a9SEric Dumazet [RTN_UNSPEC] = { 971da177e4SLinus Torvalds .error = 0, 981da177e4SLinus Torvalds .scope = RT_SCOPE_NOWHERE, 996a31d2a9SEric Dumazet }, 1006a31d2a9SEric Dumazet [RTN_UNICAST] = { 1011da177e4SLinus Torvalds .error = 0, 1021da177e4SLinus Torvalds .scope = RT_SCOPE_UNIVERSE, 1036a31d2a9SEric Dumazet }, 1046a31d2a9SEric Dumazet [RTN_LOCAL] = { 1051da177e4SLinus Torvalds .error = 0, 1061da177e4SLinus Torvalds .scope = RT_SCOPE_HOST, 1076a31d2a9SEric Dumazet }, 1086a31d2a9SEric Dumazet [RTN_BROADCAST] = { 1091da177e4SLinus Torvalds .error = 0, 1101da177e4SLinus Torvalds .scope = RT_SCOPE_LINK, 1116a31d2a9SEric Dumazet }, 1126a31d2a9SEric Dumazet [RTN_ANYCAST] = { 1131da177e4SLinus Torvalds .error = 0, 1141da177e4SLinus Torvalds .scope = RT_SCOPE_LINK, 1156a31d2a9SEric Dumazet }, 1166a31d2a9SEric Dumazet [RTN_MULTICAST] = { 1171da177e4SLinus Torvalds .error = 0, 1181da177e4SLinus Torvalds .scope = RT_SCOPE_UNIVERSE, 1196a31d2a9SEric Dumazet }, 1206a31d2a9SEric Dumazet [RTN_BLACKHOLE] = { 1211da177e4SLinus Torvalds .error = -EINVAL, 1221da177e4SLinus Torvalds .scope = RT_SCOPE_UNIVERSE, 1236a31d2a9SEric Dumazet }, 1246a31d2a9SEric Dumazet [RTN_UNREACHABLE] = { 1251da177e4SLinus Torvalds .error = -EHOSTUNREACH, 1261da177e4SLinus Torvalds .scope = RT_SCOPE_UNIVERSE, 1276a31d2a9SEric Dumazet }, 1286a31d2a9SEric Dumazet [RTN_PROHIBIT] = { 1291da177e4SLinus Torvalds .error = -EACCES, 1301da177e4SLinus Torvalds .scope = RT_SCOPE_UNIVERSE, 1316a31d2a9SEric Dumazet }, 1326a31d2a9SEric Dumazet [RTN_THROW] = { 1331da177e4SLinus Torvalds .error = -EAGAIN, 1341da177e4SLinus Torvalds .scope = RT_SCOPE_UNIVERSE, 1356a31d2a9SEric Dumazet }, 1366a31d2a9SEric Dumazet [RTN_NAT] = { 1371da177e4SLinus Torvalds .error = -EINVAL, 1381da177e4SLinus Torvalds .scope = RT_SCOPE_NOWHERE, 1396a31d2a9SEric Dumazet }, 1406a31d2a9SEric Dumazet [RTN_XRESOLVE] = { 1411da177e4SLinus Torvalds .error = -EINVAL, 1421da177e4SLinus Torvalds .scope = RT_SCOPE_NOWHERE, 1436a31d2a9SEric Dumazet }, 1441da177e4SLinus Torvalds }; 1451da177e4SLinus Torvalds 146c5038a83SDavid S. Miller static void rt_fibinfo_free(struct rtable __rcu **rtp) 14754764bb6SEric Dumazet { 14854764bb6SEric Dumazet struct rtable *rt = rcu_dereference_protected(*rtp, 1); 14954764bb6SEric Dumazet 15054764bb6SEric Dumazet if (!rt) 15154764bb6SEric Dumazet return; 15254764bb6SEric Dumazet 15354764bb6SEric Dumazet /* Not even needed : RCU_INIT_POINTER(*rtp, NULL); 15454764bb6SEric Dumazet * because we waited an RCU grace period before calling 15554764bb6SEric Dumazet * free_fib_info_rcu() 15654764bb6SEric Dumazet */ 15754764bb6SEric Dumazet 15895c47f9cSWei Wang dst_dev_put(&rt->dst); 159b838d5e1SWei Wang dst_release_immediate(&rt->dst); 16054764bb6SEric Dumazet } 16154764bb6SEric Dumazet 162c5038a83SDavid S. Miller static void free_nh_exceptions(struct fib_nh *nh) 163c5038a83SDavid S. Miller { 164caa41527SEric Dumazet struct fnhe_hash_bucket *hash; 165c5038a83SDavid S. Miller int i; 166c5038a83SDavid S. Miller 167caa41527SEric Dumazet hash = rcu_dereference_protected(nh->nh_exceptions, 1); 168caa41527SEric Dumazet if (!hash) 169caa41527SEric Dumazet return; 170c5038a83SDavid S. Miller for (i = 0; i < FNHE_HASH_SIZE; i++) { 171c5038a83SDavid S. Miller struct fib_nh_exception *fnhe; 172c5038a83SDavid S. Miller 173c5038a83SDavid S. Miller fnhe = rcu_dereference_protected(hash[i].chain, 1); 174c5038a83SDavid S. Miller while (fnhe) { 175c5038a83SDavid S. Miller struct fib_nh_exception *next; 176c5038a83SDavid S. Miller 177c5038a83SDavid S. Miller next = rcu_dereference_protected(fnhe->fnhe_next, 1); 178c5038a83SDavid S. Miller 1792ffae99dSTimo Teräs rt_fibinfo_free(&fnhe->fnhe_rth_input); 1802ffae99dSTimo Teräs rt_fibinfo_free(&fnhe->fnhe_rth_output); 181c5038a83SDavid S. Miller 182c5038a83SDavid S. Miller kfree(fnhe); 183c5038a83SDavid S. Miller 184c5038a83SDavid S. Miller fnhe = next; 185c5038a83SDavid S. Miller } 186c5038a83SDavid S. Miller } 187c5038a83SDavid S. Miller kfree(hash); 188c5038a83SDavid S. Miller } 189c5038a83SDavid S. Miller 190c5038a83SDavid S. Miller static void rt_fibinfo_free_cpus(struct rtable __rcu * __percpu *rtp) 191d26b3a7cSEric Dumazet { 192d26b3a7cSEric Dumazet int cpu; 193d26b3a7cSEric Dumazet 194d26b3a7cSEric Dumazet if (!rtp) 195d26b3a7cSEric Dumazet return; 196d26b3a7cSEric Dumazet 197d26b3a7cSEric Dumazet for_each_possible_cpu(cpu) { 198d26b3a7cSEric Dumazet struct rtable *rt; 199d26b3a7cSEric Dumazet 200d26b3a7cSEric Dumazet rt = rcu_dereference_protected(*per_cpu_ptr(rtp, cpu), 1); 2010830106cSWei Wang if (rt) { 20295c47f9cSWei Wang dst_dev_put(&rt->dst); 203b838d5e1SWei Wang dst_release_immediate(&rt->dst); 204d26b3a7cSEric Dumazet } 2050830106cSWei Wang } 206d26b3a7cSEric Dumazet free_percpu(rtp); 207d26b3a7cSEric Dumazet } 208d26b3a7cSEric Dumazet 209979e276eSDavid Ahern void fib_nh_common_release(struct fib_nh_common *nhc) 210979e276eSDavid Ahern { 211979e276eSDavid Ahern if (nhc->nhc_dev) 212979e276eSDavid Ahern dev_put(nhc->nhc_dev); 213979e276eSDavid Ahern 214979e276eSDavid Ahern lwtstate_put(nhc->nhc_lwtstate); 215979e276eSDavid Ahern } 216979e276eSDavid Ahern EXPORT_SYMBOL_GPL(fib_nh_common_release); 217979e276eSDavid Ahern 218faa041a4SDavid Ahern void fib_nh_release(struct net *net, struct fib_nh *fib_nh) 219faa041a4SDavid Ahern { 220faa041a4SDavid Ahern #ifdef CONFIG_IP_ROUTE_CLASSID 221faa041a4SDavid Ahern if (fib_nh->nh_tclassid) 222faa041a4SDavid Ahern net->ipv4.fib_num_tclassid_users--; 223faa041a4SDavid Ahern #endif 224979e276eSDavid Ahern fib_nh_common_release(&fib_nh->nh_common); 225faa041a4SDavid Ahern free_nh_exceptions(fib_nh); 226faa041a4SDavid Ahern rt_fibinfo_free_cpus(fib_nh->nh_pcpu_rth_output); 227faa041a4SDavid Ahern rt_fibinfo_free(&fib_nh->nh_rth_input); 228faa041a4SDavid Ahern } 229faa041a4SDavid Ahern 2301da177e4SLinus Torvalds /* Release a nexthop info record */ 23119c1ea14SYan, Zheng static void free_fib_info_rcu(struct rcu_head *head) 23219c1ea14SYan, Zheng { 23319c1ea14SYan, Zheng struct fib_info *fi = container_of(head, struct fib_info, rcu); 23419c1ea14SYan, Zheng 235e49cc0daSYanmin Zhang change_nexthops(fi) { 236faa041a4SDavid Ahern fib_nh_release(fi->fib_net, nexthop_nh); 237e49cc0daSYanmin Zhang } endfor_nexthops(fi); 238e49cc0daSYanmin Zhang 239cc5f0eb2SDavid Ahern ip_fib_metrics_put(fi->fib_metrics); 240cc5f0eb2SDavid Ahern 24119c1ea14SYan, Zheng kfree(fi); 24219c1ea14SYan, Zheng } 2431da177e4SLinus Torvalds 2441da177e4SLinus Torvalds void free_fib_info(struct fib_info *fi) 2451da177e4SLinus Torvalds { 2461da177e4SLinus Torvalds if (fi->fib_dead == 0) { 247058bd4d2SJoe Perches pr_warn("Freeing alive fib_info %p\n", fi); 2481da177e4SLinus Torvalds return; 2491da177e4SLinus Torvalds } 2501da177e4SLinus Torvalds fib_info_cnt--; 251faa041a4SDavid Ahern 25219c1ea14SYan, Zheng call_rcu(&fi->rcu, free_fib_info_rcu); 2531da177e4SLinus Torvalds } 254b423cb10SIdo Schimmel EXPORT_SYMBOL_GPL(free_fib_info); 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds void fib_release_info(struct fib_info *fi) 2571da177e4SLinus Torvalds { 258832b4c5eSStephen Hemminger spin_lock_bh(&fib_info_lock); 2591da177e4SLinus Torvalds if (fi && --fi->fib_treeref == 0) { 2601da177e4SLinus Torvalds hlist_del(&fi->fib_hash); 2611da177e4SLinus Torvalds if (fi->fib_prefsrc) 2621da177e4SLinus Torvalds hlist_del(&fi->fib_lhash); 2631da177e4SLinus Torvalds change_nexthops(fi) { 264b75ed8b1SDavid Ahern if (!nexthop_nh->fib_nh_dev) 2651da177e4SLinus Torvalds continue; 26671fceff0SDavid S. Miller hlist_del(&nexthop_nh->nh_hash); 2671da177e4SLinus Torvalds } endfor_nexthops(fi) 2681da177e4SLinus Torvalds fi->fib_dead = 1; 2691da177e4SLinus Torvalds fib_info_put(fi); 2701da177e4SLinus Torvalds } 271832b4c5eSStephen Hemminger spin_unlock_bh(&fib_info_lock); 2721da177e4SLinus Torvalds } 2731da177e4SLinus Torvalds 2746a31d2a9SEric Dumazet static inline int nh_comp(const struct fib_info *fi, const struct fib_info *ofi) 2751da177e4SLinus Torvalds { 2761da177e4SLinus Torvalds const struct fib_nh *onh = ofi->fib_nh; 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds for_nexthops(fi) { 279b75ed8b1SDavid Ahern if (nh->fib_nh_oif != onh->fib_nh_oif || 280a4ea5d43SDavid Ahern nh->fib_nh_gw_family != onh->fib_nh_gw_family || 281b75ed8b1SDavid Ahern nh->fib_nh_scope != onh->fib_nh_scope || 2821da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 283b75ed8b1SDavid Ahern nh->fib_nh_weight != onh->fib_nh_weight || 2841da177e4SLinus Torvalds #endif 285c7066f70SPatrick McHardy #ifdef CONFIG_IP_ROUTE_CLASSID 2861da177e4SLinus Torvalds nh->nh_tclassid != onh->nh_tclassid || 2871da177e4SLinus Torvalds #endif 288b75ed8b1SDavid Ahern lwtunnel_cmp_encap(nh->fib_nh_lws, onh->fib_nh_lws) || 289b75ed8b1SDavid Ahern ((nh->fib_nh_flags ^ onh->fib_nh_flags) & ~RTNH_COMPARE_MASK)) 2901da177e4SLinus Torvalds return -1; 291a4ea5d43SDavid Ahern 292a4ea5d43SDavid Ahern if (nh->fib_nh_gw_family == AF_INET && 293a4ea5d43SDavid Ahern nh->fib_nh_gw4 != onh->fib_nh_gw4) 294a4ea5d43SDavid Ahern return -1; 295a4ea5d43SDavid Ahern 296a4ea5d43SDavid Ahern if (nh->fib_nh_gw_family == AF_INET6 && 297a4ea5d43SDavid Ahern ipv6_addr_cmp(&nh->fib_nh_gw6, &onh->fib_nh_gw6)) 298a4ea5d43SDavid Ahern return -1; 299a4ea5d43SDavid Ahern 3001da177e4SLinus Torvalds onh++; 3011da177e4SLinus Torvalds } endfor_nexthops(fi); 3021da177e4SLinus Torvalds return 0; 3031da177e4SLinus Torvalds } 3041da177e4SLinus Torvalds 30588ebc72fSDavid S. Miller static inline unsigned int fib_devindex_hashfn(unsigned int val) 30688ebc72fSDavid S. Miller { 30788ebc72fSDavid S. Miller unsigned int mask = DEVINDEX_HASHSIZE - 1; 30888ebc72fSDavid S. Miller 30988ebc72fSDavid S. Miller return (val ^ 31088ebc72fSDavid S. Miller (val >> DEVINDEX_HASHBITS) ^ 31188ebc72fSDavid S. Miller (val >> (DEVINDEX_HASHBITS * 2))) & mask; 31288ebc72fSDavid S. Miller } 31388ebc72fSDavid S. Miller 3141da177e4SLinus Torvalds static inline unsigned int fib_info_hashfn(const struct fib_info *fi) 3151da177e4SLinus Torvalds { 316123b9731SDavid S. Miller unsigned int mask = (fib_info_hash_size - 1); 3171da177e4SLinus Torvalds unsigned int val = fi->fib_nhs; 3181da177e4SLinus Torvalds 31937e826c5SDavid S. Miller val ^= (fi->fib_protocol << 8) | fi->fib_scope; 32081f7bf6cSAl Viro val ^= (__force u32)fi->fib_prefsrc; 3211da177e4SLinus Torvalds val ^= fi->fib_priority; 32288ebc72fSDavid S. Miller for_nexthops(fi) { 323b75ed8b1SDavid Ahern val ^= fib_devindex_hashfn(nh->fib_nh_oif); 32488ebc72fSDavid S. Miller } endfor_nexthops(fi) 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds return (val ^ (val >> 7) ^ (val >> 12)) & mask; 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds static struct fib_info *fib_find_info(const struct fib_info *nfi) 3301da177e4SLinus Torvalds { 3311da177e4SLinus Torvalds struct hlist_head *head; 3321da177e4SLinus Torvalds struct fib_info *fi; 3331da177e4SLinus Torvalds unsigned int hash; 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds hash = fib_info_hashfn(nfi); 3361da177e4SLinus Torvalds head = &fib_info_hash[hash]; 3371da177e4SLinus Torvalds 338b67bfe0dSSasha Levin hlist_for_each_entry(fi, head, fib_hash) { 33909ad9bc7SOctavian Purdila if (!net_eq(fi->fib_net, nfi->fib_net)) 3404814bdbdSDenis V. Lunev continue; 3411da177e4SLinus Torvalds if (fi->fib_nhs != nfi->fib_nhs) 3421da177e4SLinus Torvalds continue; 3431da177e4SLinus Torvalds if (nfi->fib_protocol == fi->fib_protocol && 34437e826c5SDavid S. Miller nfi->fib_scope == fi->fib_scope && 3451da177e4SLinus Torvalds nfi->fib_prefsrc == fi->fib_prefsrc && 3461da177e4SLinus Torvalds nfi->fib_priority == fi->fib_priority && 347f4ef85bbSEric Dumazet nfi->fib_type == fi->fib_type && 3481da177e4SLinus Torvalds memcmp(nfi->fib_metrics, fi->fib_metrics, 349fcd13f42SEric Dumazet sizeof(u32) * RTAX_MAX) == 0 && 3508a3d0316SAndy Gospodarek !((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_COMPARE_MASK) && 3511da177e4SLinus Torvalds (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0)) 3521da177e4SLinus Torvalds return fi; 3531da177e4SLinus Torvalds } 3541da177e4SLinus Torvalds 3551da177e4SLinus Torvalds return NULL; 3561da177e4SLinus Torvalds } 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds /* Check, that the gateway is already configured. 3596a31d2a9SEric Dumazet * Used only by redirect accept routine. 3601da177e4SLinus Torvalds */ 361d878e72eSAl Viro int ip_fib_check_default(__be32 gw, struct net_device *dev) 3621da177e4SLinus Torvalds { 3631da177e4SLinus Torvalds struct hlist_head *head; 3641da177e4SLinus Torvalds struct fib_nh *nh; 3651da177e4SLinus Torvalds unsigned int hash; 3661da177e4SLinus Torvalds 367832b4c5eSStephen Hemminger spin_lock(&fib_info_lock); 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds hash = fib_devindex_hashfn(dev->ifindex); 3701da177e4SLinus Torvalds head = &fib_info_devhash[hash]; 371b67bfe0dSSasha Levin hlist_for_each_entry(nh, head, nh_hash) { 372b75ed8b1SDavid Ahern if (nh->fib_nh_dev == dev && 373b75ed8b1SDavid Ahern nh->fib_nh_gw4 == gw && 374b75ed8b1SDavid Ahern !(nh->fib_nh_flags & RTNH_F_DEAD)) { 375832b4c5eSStephen Hemminger spin_unlock(&fib_info_lock); 3761da177e4SLinus Torvalds return 0; 3771da177e4SLinus Torvalds } 3781da177e4SLinus Torvalds } 3791da177e4SLinus Torvalds 380832b4c5eSStephen Hemminger spin_unlock(&fib_info_lock); 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds return -1; 3831da177e4SLinus Torvalds } 3841da177e4SLinus Torvalds 385339bf98fSThomas Graf static inline size_t fib_nlmsg_size(struct fib_info *fi) 386339bf98fSThomas Graf { 387339bf98fSThomas Graf size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg)) 388339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 389339bf98fSThomas Graf + nla_total_size(4) /* RTA_DST */ 390339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 391ea697639SDaniel Borkmann + nla_total_size(4) /* RTA_PREFSRC */ 392ea697639SDaniel Borkmann + nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */ 393339bf98fSThomas Graf 394339bf98fSThomas Graf /* space for nested metrics */ 395339bf98fSThomas Graf payload += nla_total_size((RTAX_MAX * nla_total_size(4))); 396339bf98fSThomas Graf 397339bf98fSThomas Graf if (fi->fib_nhs) { 398571e7226SRoopa Prabhu size_t nh_encapsize = 0; 399339bf98fSThomas Graf /* Also handles the special case fib_nhs == 1 */ 400339bf98fSThomas Graf 401339bf98fSThomas Graf /* each nexthop is packed in an attribute */ 402339bf98fSThomas Graf size_t nhsize = nla_total_size(sizeof(struct rtnexthop)); 403339bf98fSThomas Graf 404339bf98fSThomas Graf /* may contain flow and gateway attribute */ 405339bf98fSThomas Graf nhsize += 2 * nla_total_size(4); 406339bf98fSThomas Graf 407571e7226SRoopa Prabhu /* grab encap info */ 408571e7226SRoopa Prabhu for_nexthops(fi) { 409b75ed8b1SDavid Ahern if (nh->fib_nh_lws) { 410571e7226SRoopa Prabhu /* RTA_ENCAP_TYPE */ 411571e7226SRoopa Prabhu nh_encapsize += lwtunnel_get_encap_size( 412b75ed8b1SDavid Ahern nh->fib_nh_lws); 413571e7226SRoopa Prabhu /* RTA_ENCAP */ 414571e7226SRoopa Prabhu nh_encapsize += nla_total_size(2); 415571e7226SRoopa Prabhu } 416571e7226SRoopa Prabhu } endfor_nexthops(fi); 417571e7226SRoopa Prabhu 418339bf98fSThomas Graf /* all nexthops are packed in a nested attribute */ 419571e7226SRoopa Prabhu payload += nla_total_size((fi->fib_nhs * nhsize) + 420571e7226SRoopa Prabhu nh_encapsize); 421571e7226SRoopa Prabhu 422339bf98fSThomas Graf } 423339bf98fSThomas Graf 424339bf98fSThomas Graf return payload; 425339bf98fSThomas Graf } 426339bf98fSThomas Graf 42781f7bf6cSAl Viro void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, 4289877b253SJoe Perches int dst_len, u32 tb_id, const struct nl_info *info, 429b8f55831SMilan Kocian unsigned int nlm_flags) 4301da177e4SLinus Torvalds { 4311da177e4SLinus Torvalds struct sk_buff *skb; 4324e902c57SThomas Graf u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; 433f21c7bc5SThomas Graf int err = -ENOBUFS; 4341da177e4SLinus Torvalds 435339bf98fSThomas Graf skb = nlmsg_new(fib_nlmsg_size(fa->fa_info), GFP_KERNEL); 43651456b29SIan Morris if (!skb) 437f21c7bc5SThomas Graf goto errout; 4381da177e4SLinus Torvalds 43915e47304SEric W. Biederman err = fib_dump_info(skb, info->portid, seq, event, tb_id, 44037e826c5SDavid S. Miller fa->fa_type, key, dst_len, 441b8f55831SMilan Kocian fa->fa_tos, fa->fa_info, nlm_flags); 44226932566SPatrick McHardy if (err < 0) { 44326932566SPatrick McHardy /* -EMSGSIZE implies BUG in fib_nlmsg_size() */ 44426932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 44526932566SPatrick McHardy kfree_skb(skb); 44626932566SPatrick McHardy goto errout; 44726932566SPatrick McHardy } 44815e47304SEric W. Biederman rtnl_notify(skb, info->nl_net, info->portid, RTNLGRP_IPV4_ROUTE, 4494e902c57SThomas Graf info->nlh, GFP_KERNEL); 4501ce85fe4SPablo Neira Ayuso return; 451f21c7bc5SThomas Graf errout: 452f21c7bc5SThomas Graf if (err < 0) 4534d1169c1SDenis V. Lunev rtnl_set_sk_err(info->nl_net, RTNLGRP_IPV4_ROUTE, err); 4541da177e4SLinus Torvalds } 4551da177e4SLinus Torvalds 456c9cb6b6eSStephen Hemminger static int fib_detect_death(struct fib_info *fi, int order, 457c9cb6b6eSStephen Hemminger struct fib_info **last_resort, int *last_idx, 458c9cb6b6eSStephen Hemminger int dflt) 4591da177e4SLinus Torvalds { 4601da177e4SLinus Torvalds struct neighbour *n; 4611da177e4SLinus Torvalds int state = NUD_NONE; 4621da177e4SLinus Torvalds 463b75ed8b1SDavid Ahern n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].fib_nh_gw4, fi->fib_dev); 4641da177e4SLinus Torvalds if (n) { 4651da177e4SLinus Torvalds state = n->nud_state; 4661da177e4SLinus Torvalds neigh_release(n); 46788f64320SJulian Anastasov } else { 46888f64320SJulian Anastasov return 0; 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds if (state == NUD_REACHABLE) 4711da177e4SLinus Torvalds return 0; 472c17860a0SDenis V. Lunev if ((state & NUD_VALID) && order != dflt) 4731da177e4SLinus Torvalds return 0; 4741da177e4SLinus Torvalds if ((state & NUD_VALID) || 47588f64320SJulian Anastasov (*last_idx < 0 && order > dflt && state != NUD_INCOMPLETE)) { 4761da177e4SLinus Torvalds *last_resort = fi; 4771da177e4SLinus Torvalds *last_idx = order; 4781da177e4SLinus Torvalds } 4791da177e4SLinus Torvalds return 1; 4801da177e4SLinus Torvalds } 4811da177e4SLinus Torvalds 482979e276eSDavid Ahern int fib_nh_common_init(struct fib_nh_common *nhc, struct nlattr *encap, 483979e276eSDavid Ahern u16 encap_type, void *cfg, gfp_t gfp_flags, 484979e276eSDavid Ahern struct netlink_ext_ack *extack) 485979e276eSDavid Ahern { 486979e276eSDavid Ahern if (encap) { 487979e276eSDavid Ahern struct lwtunnel_state *lwtstate; 488979e276eSDavid Ahern int err; 489979e276eSDavid Ahern 490979e276eSDavid Ahern if (encap_type == LWTUNNEL_ENCAP_NONE) { 491979e276eSDavid Ahern NL_SET_ERR_MSG(extack, "LWT encap type not specified"); 492979e276eSDavid Ahern return -EINVAL; 493979e276eSDavid Ahern } 494979e276eSDavid Ahern err = lwtunnel_build_state(encap_type, encap, nhc->nhc_family, 495979e276eSDavid Ahern cfg, &lwtstate, extack); 496979e276eSDavid Ahern if (err) 497979e276eSDavid Ahern return err; 498979e276eSDavid Ahern 499979e276eSDavid Ahern nhc->nhc_lwtstate = lwtstate_get(lwtstate); 500979e276eSDavid Ahern } 501979e276eSDavid Ahern 502979e276eSDavid Ahern return 0; 503979e276eSDavid Ahern } 504979e276eSDavid Ahern EXPORT_SYMBOL_GPL(fib_nh_common_init); 505979e276eSDavid Ahern 506e4516ef6SDavid Ahern int fib_nh_init(struct net *net, struct fib_nh *nh, 507e4516ef6SDavid Ahern struct fib_config *cfg, int nh_weight, 508e4516ef6SDavid Ahern struct netlink_ext_ack *extack) 509e4516ef6SDavid Ahern { 510e4516ef6SDavid Ahern int err = -ENOMEM; 511e4516ef6SDavid Ahern 512f1741730SDavid Ahern nh->fib_nh_family = AF_INET; 513f1741730SDavid Ahern 514e4516ef6SDavid Ahern nh->nh_pcpu_rth_output = alloc_percpu(struct rtable __rcu *); 515e4516ef6SDavid Ahern if (!nh->nh_pcpu_rth_output) 516e4516ef6SDavid Ahern goto err_out; 517e4516ef6SDavid Ahern 518979e276eSDavid Ahern err = fib_nh_common_init(&nh->nh_common, cfg->fc_encap, 519979e276eSDavid Ahern cfg->fc_encap_type, cfg, GFP_KERNEL, extack); 520e4516ef6SDavid Ahern if (err) 521979e276eSDavid Ahern goto init_failure; 522e4516ef6SDavid Ahern 523b75ed8b1SDavid Ahern nh->fib_nh_oif = cfg->fc_oif; 524a4ea5d43SDavid Ahern nh->fib_nh_gw_family = cfg->fc_gw_family; 525a4ea5d43SDavid Ahern if (cfg->fc_gw_family == AF_INET) 526f35b794bSDavid Ahern nh->fib_nh_gw4 = cfg->fc_gw4; 527a4ea5d43SDavid Ahern else if (cfg->fc_gw_family == AF_INET6) 528a4ea5d43SDavid Ahern nh->fib_nh_gw6 = cfg->fc_gw6; 529a4ea5d43SDavid Ahern 530b75ed8b1SDavid Ahern nh->fib_nh_flags = cfg->fc_flags; 531e4516ef6SDavid Ahern 532e4516ef6SDavid Ahern #ifdef CONFIG_IP_ROUTE_CLASSID 533e4516ef6SDavid Ahern nh->nh_tclassid = cfg->fc_flow; 534e4516ef6SDavid Ahern if (nh->nh_tclassid) 535e4516ef6SDavid Ahern net->ipv4.fib_num_tclassid_users++; 536e4516ef6SDavid Ahern #endif 537e4516ef6SDavid Ahern #ifdef CONFIG_IP_ROUTE_MULTIPATH 538b75ed8b1SDavid Ahern nh->fib_nh_weight = nh_weight; 539e4516ef6SDavid Ahern #endif 540e4516ef6SDavid Ahern return 0; 541e4516ef6SDavid Ahern 542979e276eSDavid Ahern init_failure: 543e4516ef6SDavid Ahern rt_fibinfo_free_cpus(nh->nh_pcpu_rth_output); 544e4516ef6SDavid Ahern nh->nh_pcpu_rth_output = NULL; 545e4516ef6SDavid Ahern err_out: 546e4516ef6SDavid Ahern return err; 547e4516ef6SDavid Ahern } 548e4516ef6SDavid Ahern 5491da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 5501da177e4SLinus Torvalds 5516d8422a1SDavid Ahern static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining, 5526d8422a1SDavid Ahern struct netlink_ext_ack *extack) 5531da177e4SLinus Torvalds { 5541da177e4SLinus Torvalds int nhs = 0; 5551da177e4SLinus Torvalds 5564e902c57SThomas Graf while (rtnh_ok(rtnh, remaining)) { 5571da177e4SLinus Torvalds nhs++; 5584e902c57SThomas Graf rtnh = rtnh_next(rtnh, &remaining); 5591da177e4SLinus Torvalds } 5601da177e4SLinus Torvalds 5614e902c57SThomas Graf /* leftover implies invalid nexthop configuration, discard it */ 562c3ab2b4eSDavid Ahern if (remaining > 0) { 563c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 564c3ab2b4eSDavid Ahern "Invalid nexthop configuration - extra data after nexthops"); 565c3ab2b4eSDavid Ahern nhs = 0; 566c3ab2b4eSDavid Ahern } 567c3ab2b4eSDavid Ahern 568c3ab2b4eSDavid Ahern return nhs; 5694e902c57SThomas Graf } 5701da177e4SLinus Torvalds 5714e902c57SThomas Graf static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, 5726d8422a1SDavid Ahern int remaining, struct fib_config *cfg, 5736d8422a1SDavid Ahern struct netlink_ext_ack *extack) 5744e902c57SThomas Graf { 575e4516ef6SDavid Ahern struct net *net = fi->fib_net; 576e4516ef6SDavid Ahern struct fib_config fib_cfg; 577571e7226SRoopa Prabhu int ret; 578571e7226SRoopa Prabhu 5791da177e4SLinus Torvalds change_nexthops(fi) { 5804e902c57SThomas Graf int attrlen; 5814e902c57SThomas Graf 582e4516ef6SDavid Ahern memset(&fib_cfg, 0, sizeof(fib_cfg)); 583e4516ef6SDavid Ahern 584c3ab2b4eSDavid Ahern if (!rtnh_ok(rtnh, remaining)) { 585c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 586c3ab2b4eSDavid Ahern "Invalid nexthop configuration - extra data after nexthop"); 5871da177e4SLinus Torvalds return -EINVAL; 588c3ab2b4eSDavid Ahern } 5894e902c57SThomas Graf 590c3ab2b4eSDavid Ahern if (rtnh->rtnh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) { 591c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 592c3ab2b4eSDavid Ahern "Invalid flags for nexthop - can not contain DEAD or LINKDOWN"); 59380610229SJulian Anastasov return -EINVAL; 594c3ab2b4eSDavid Ahern } 59580610229SJulian Anastasov 596e4516ef6SDavid Ahern fib_cfg.fc_flags = (cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags; 597e4516ef6SDavid Ahern fib_cfg.fc_oif = rtnh->rtnh_ifindex; 5984e902c57SThomas Graf 5994e902c57SThomas Graf attrlen = rtnh_attrlen(rtnh); 6004e902c57SThomas Graf if (attrlen > 0) { 6014e902c57SThomas Graf struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 6024e902c57SThomas Graf 6034e902c57SThomas Graf nla = nla_find(attrs, attrlen, RTA_GATEWAY); 604f35b794bSDavid Ahern if (nla) { 605f35b794bSDavid Ahern fib_cfg.fc_gw_family = AF_INET; 606f35b794bSDavid Ahern fib_cfg.fc_gw4 = nla_get_in_addr(nla); 607f35b794bSDavid Ahern } 608571e7226SRoopa Prabhu 609e4516ef6SDavid Ahern nla = nla_find(attrs, attrlen, RTA_FLOW); 610e4516ef6SDavid Ahern if (nla) 611e4516ef6SDavid Ahern fib_cfg.fc_flow = nla_get_u32(nla); 612e4516ef6SDavid Ahern 613e4516ef6SDavid Ahern fib_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 614e4516ef6SDavid Ahern nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 615e4516ef6SDavid Ahern if (nla) 616e4516ef6SDavid Ahern fib_cfg.fc_encap_type = nla_get_u16(nla); 617c3ab2b4eSDavid Ahern } 61830357d7dSDavid Ahern 619e4516ef6SDavid Ahern ret = fib_nh_init(net, nexthop_nh, &fib_cfg, 620e4516ef6SDavid Ahern rtnh->rtnh_hops + 1, extack); 621571e7226SRoopa Prabhu if (ret) 622571e7226SRoopa Prabhu goto errout; 6234e902c57SThomas Graf 6244e902c57SThomas Graf rtnh = rtnh_next(rtnh, &remaining); 6251da177e4SLinus Torvalds } endfor_nexthops(fi); 6264e902c57SThomas Graf 627571e7226SRoopa Prabhu ret = -EINVAL; 628b75ed8b1SDavid Ahern if (cfg->fc_oif && fi->fib_nh->fib_nh_oif != cfg->fc_oif) { 629e4516ef6SDavid Ahern NL_SET_ERR_MSG(extack, 630e4516ef6SDavid Ahern "Nexthop device index does not match RTA_OIF"); 631e4516ef6SDavid Ahern goto errout; 632e4516ef6SDavid Ahern } 633f35b794bSDavid Ahern if (cfg->fc_gw_family) { 634f35b794bSDavid Ahern if (cfg->fc_gw_family != fi->fib_nh->fib_nh_gw_family || 635f35b794bSDavid Ahern (cfg->fc_gw_family == AF_INET && 636a4ea5d43SDavid Ahern fi->fib_nh->fib_nh_gw4 != cfg->fc_gw4) || 637a4ea5d43SDavid Ahern (cfg->fc_gw_family == AF_INET6 && 638a4ea5d43SDavid Ahern ipv6_addr_cmp(&fi->fib_nh->fib_nh_gw6, &cfg->fc_gw6))) { 639e4516ef6SDavid Ahern NL_SET_ERR_MSG(extack, 640a4ea5d43SDavid Ahern "Nexthop gateway does not match RTA_GATEWAY or RTA_VIA"); 641e4516ef6SDavid Ahern goto errout; 642e4516ef6SDavid Ahern } 643f35b794bSDavid Ahern } 644e4516ef6SDavid Ahern #ifdef CONFIG_IP_ROUTE_CLASSID 645e4516ef6SDavid Ahern if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow) { 646e4516ef6SDavid Ahern NL_SET_ERR_MSG(extack, 647e4516ef6SDavid Ahern "Nexthop class id does not match RTA_FLOW"); 648e4516ef6SDavid Ahern goto errout; 649e4516ef6SDavid Ahern } 650e4516ef6SDavid Ahern #endif 651e4516ef6SDavid Ahern ret = 0; 652571e7226SRoopa Prabhu errout: 653571e7226SRoopa Prabhu return ret; 6541da177e4SLinus Torvalds } 6551da177e4SLinus Torvalds 6560e884c78SPeter Nørlund static void fib_rebalance(struct fib_info *fi) 6570e884c78SPeter Nørlund { 6580e884c78SPeter Nørlund int total; 6590e884c78SPeter Nørlund int w; 6600e884c78SPeter Nørlund 6610e884c78SPeter Nørlund if (fi->fib_nhs < 2) 6620e884c78SPeter Nørlund return; 6630e884c78SPeter Nørlund 6640e884c78SPeter Nørlund total = 0; 6650e884c78SPeter Nørlund for_nexthops(fi) { 666b75ed8b1SDavid Ahern if (nh->fib_nh_flags & RTNH_F_DEAD) 6670e884c78SPeter Nørlund continue; 6680e884c78SPeter Nørlund 669b75ed8b1SDavid Ahern if (ip_ignore_linkdown(nh->fib_nh_dev) && 670b75ed8b1SDavid Ahern nh->fib_nh_flags & RTNH_F_LINKDOWN) 6710e884c78SPeter Nørlund continue; 6720e884c78SPeter Nørlund 673b75ed8b1SDavid Ahern total += nh->fib_nh_weight; 6740e884c78SPeter Nørlund } endfor_nexthops(fi); 6750e884c78SPeter Nørlund 6760e884c78SPeter Nørlund w = 0; 6770e884c78SPeter Nørlund change_nexthops(fi) { 6780e884c78SPeter Nørlund int upper_bound; 6790e884c78SPeter Nørlund 680b75ed8b1SDavid Ahern if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) { 6810e884c78SPeter Nørlund upper_bound = -1; 682b75ed8b1SDavid Ahern } else if (ip_ignore_linkdown(nexthop_nh->fib_nh_dev) && 683b75ed8b1SDavid Ahern nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) { 6840e884c78SPeter Nørlund upper_bound = -1; 6850e884c78SPeter Nørlund } else { 686b75ed8b1SDavid Ahern w += nexthop_nh->fib_nh_weight; 6870a837fe4SPeter Nørlund upper_bound = DIV_ROUND_CLOSEST_ULL((u64)w << 31, 6880e884c78SPeter Nørlund total) - 1; 6890e884c78SPeter Nørlund } 6900e884c78SPeter Nørlund 691b75ed8b1SDavid Ahern atomic_set(&nexthop_nh->fib_nh_upper_bound, upper_bound); 6920e884c78SPeter Nørlund } endfor_nexthops(fi); 6930e884c78SPeter Nørlund } 6940e884c78SPeter Nørlund #else /* CONFIG_IP_ROUTE_MULTIPATH */ 6950e884c78SPeter Nørlund 6968373c6c8SDavid Ahern static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, 6978373c6c8SDavid Ahern int remaining, struct fib_config *cfg, 6988373c6c8SDavid Ahern struct netlink_ext_ack *extack) 6998373c6c8SDavid Ahern { 7008373c6c8SDavid Ahern NL_SET_ERR_MSG(extack, "Multipath support not enabled in kernel"); 7018373c6c8SDavid Ahern 7028373c6c8SDavid Ahern return -EINVAL; 7038373c6c8SDavid Ahern } 7048373c6c8SDavid Ahern 7050e884c78SPeter Nørlund #define fib_rebalance(fi) do { } while (0) 7060e884c78SPeter Nørlund 7070e884c78SPeter Nørlund #endif /* CONFIG_IP_ROUTE_MULTIPATH */ 7081da177e4SLinus Torvalds 70930357d7dSDavid Ahern static int fib_encap_match(u16 encap_type, 710571e7226SRoopa Prabhu struct nlattr *encap, 71130357d7dSDavid Ahern const struct fib_nh *nh, 7129ae28727SDavid Ahern const struct fib_config *cfg, 7139ae28727SDavid Ahern struct netlink_ext_ack *extack) 714571e7226SRoopa Prabhu { 715571e7226SRoopa Prabhu struct lwtunnel_state *lwtstate; 716df383e62SJiri Benc int ret, result = 0; 717571e7226SRoopa Prabhu 718571e7226SRoopa Prabhu if (encap_type == LWTUNNEL_ENCAP_NONE) 719571e7226SRoopa Prabhu return 0; 720571e7226SRoopa Prabhu 7219ae28727SDavid Ahern ret = lwtunnel_build_state(encap_type, encap, AF_INET, 7229ae28727SDavid Ahern cfg, &lwtstate, extack); 723df383e62SJiri Benc if (!ret) { 724b75ed8b1SDavid Ahern result = lwtunnel_cmp_encap(lwtstate, nh->fib_nh_lws); 725df383e62SJiri Benc lwtstate_free(lwtstate); 726df383e62SJiri Benc } 727571e7226SRoopa Prabhu 728df383e62SJiri Benc return result; 729571e7226SRoopa Prabhu } 730571e7226SRoopa Prabhu 7319ae28727SDavid Ahern int fib_nh_match(struct fib_config *cfg, struct fib_info *fi, 7329ae28727SDavid Ahern struct netlink_ext_ack *extack) 7331da177e4SLinus Torvalds { 7341da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 7354e902c57SThomas Graf struct rtnexthop *rtnh; 7364e902c57SThomas Graf int remaining; 7371da177e4SLinus Torvalds #endif 7381da177e4SLinus Torvalds 7394e902c57SThomas Graf if (cfg->fc_priority && cfg->fc_priority != fi->fib_priority) 7401da177e4SLinus Torvalds return 1; 7411da177e4SLinus Torvalds 742f35b794bSDavid Ahern if (cfg->fc_oif || cfg->fc_gw_family) { 743571e7226SRoopa Prabhu if (cfg->fc_encap) { 7449ae28727SDavid Ahern if (fib_encap_match(cfg->fc_encap_type, cfg->fc_encap, 7459ae28727SDavid Ahern fi->fib_nh, cfg, extack)) 746571e7226SRoopa Prabhu return 1; 747571e7226SRoopa Prabhu } 748a8c6db1dSStefano Brivio #ifdef CONFIG_IP_ROUTE_CLASSID 749a8c6db1dSStefano Brivio if (cfg->fc_flow && 750a8c6db1dSStefano Brivio cfg->fc_flow != fi->fib_nh->nh_tclassid) 751a8c6db1dSStefano Brivio return 1; 752a8c6db1dSStefano Brivio #endif 753f35b794bSDavid Ahern if ((cfg->fc_oif && cfg->fc_oif != fi->fib_nh->fib_nh_oif) || 754f35b794bSDavid Ahern (cfg->fc_gw_family && 755f35b794bSDavid Ahern cfg->fc_gw_family != fi->fib_nh->fib_nh_gw_family)) 7561da177e4SLinus Torvalds return 1; 757f35b794bSDavid Ahern 758f35b794bSDavid Ahern if (cfg->fc_gw_family == AF_INET && 759f35b794bSDavid Ahern cfg->fc_gw4 != fi->fib_nh->fib_nh_gw4) 760f35b794bSDavid Ahern return 1; 761f35b794bSDavid Ahern 762a4ea5d43SDavid Ahern if (cfg->fc_gw_family == AF_INET6 && 763a4ea5d43SDavid Ahern ipv6_addr_cmp(&cfg->fc_gw6, &fi->fib_nh->fib_nh_gw6)) 764a4ea5d43SDavid Ahern return 1; 765a4ea5d43SDavid Ahern 766f35b794bSDavid Ahern return 0; 7671da177e4SLinus Torvalds } 7681da177e4SLinus Torvalds 7691da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 77051456b29SIan Morris if (!cfg->fc_mp) 7711da177e4SLinus Torvalds return 0; 7724e902c57SThomas Graf 7734e902c57SThomas Graf rtnh = cfg->fc_mp; 7744e902c57SThomas Graf remaining = cfg->fc_mp_len; 7751da177e4SLinus Torvalds 7761da177e4SLinus Torvalds for_nexthops(fi) { 7774e902c57SThomas Graf int attrlen; 7781da177e4SLinus Torvalds 7794e902c57SThomas Graf if (!rtnh_ok(rtnh, remaining)) 7801da177e4SLinus Torvalds return -EINVAL; 7814e902c57SThomas Graf 782b75ed8b1SDavid Ahern if (rtnh->rtnh_ifindex && rtnh->rtnh_ifindex != nh->fib_nh_oif) 7831da177e4SLinus Torvalds return 1; 7844e902c57SThomas Graf 7854e902c57SThomas Graf attrlen = rtnh_attrlen(rtnh); 786f76936d0SJiri Pirko if (attrlen > 0) { 7874e902c57SThomas Graf struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 7884e902c57SThomas Graf 7894e902c57SThomas Graf nla = nla_find(attrs, attrlen, RTA_GATEWAY); 790b75ed8b1SDavid Ahern if (nla && nla_get_in_addr(nla) != nh->fib_nh_gw4) 7911da177e4SLinus Torvalds return 1; 792c7066f70SPatrick McHardy #ifdef CONFIG_IP_ROUTE_CLASSID 7934e902c57SThomas Graf nla = nla_find(attrs, attrlen, RTA_FLOW); 7944e902c57SThomas Graf if (nla && nla_get_u32(nla) != nh->nh_tclassid) 7951da177e4SLinus Torvalds return 1; 7961da177e4SLinus Torvalds #endif 7971da177e4SLinus Torvalds } 7984e902c57SThomas Graf 7994e902c57SThomas Graf rtnh = rtnh_next(rtnh, &remaining); 8001da177e4SLinus Torvalds } endfor_nexthops(fi); 8011da177e4SLinus Torvalds #endif 8021da177e4SLinus Torvalds return 0; 8031da177e4SLinus Torvalds } 8041da177e4SLinus Torvalds 8055f9ae3d9SXin Long bool fib_metrics_match(struct fib_config *cfg, struct fib_info *fi) 8065f9ae3d9SXin Long { 8075f9ae3d9SXin Long struct nlattr *nla; 8085f9ae3d9SXin Long int remaining; 8095f9ae3d9SXin Long 8105f9ae3d9SXin Long if (!cfg->fc_mx) 8115f9ae3d9SXin Long return true; 8125f9ae3d9SXin Long 8135f9ae3d9SXin Long nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 8145f9ae3d9SXin Long int type = nla_type(nla); 815d03a4557SPhil Sutter u32 fi_val, val; 8165f9ae3d9SXin Long 8175f9ae3d9SXin Long if (!type) 8185f9ae3d9SXin Long continue; 8195f9ae3d9SXin Long if (type > RTAX_MAX) 8205f9ae3d9SXin Long return false; 8215f9ae3d9SXin Long 8225f9ae3d9SXin Long if (type == RTAX_CC_ALGO) { 8235f9ae3d9SXin Long char tmp[TCP_CA_NAME_MAX]; 8245f9ae3d9SXin Long bool ecn_ca = false; 8255f9ae3d9SXin Long 8265f9ae3d9SXin Long nla_strlcpy(tmp, nla, sizeof(tmp)); 8276670e152SStephen Hemminger val = tcp_ca_get_key_by_name(fi->fib_net, tmp, &ecn_ca); 8285f9ae3d9SXin Long } else { 8295b5e7a0dSEric Dumazet if (nla_len(nla) != sizeof(u32)) 8305b5e7a0dSEric Dumazet return false; 8315f9ae3d9SXin Long val = nla_get_u32(nla); 8325f9ae3d9SXin Long } 8335f9ae3d9SXin Long 834d03a4557SPhil Sutter fi_val = fi->fib_metrics->metrics[type - 1]; 835d03a4557SPhil Sutter if (type == RTAX_FEATURES) 836d03a4557SPhil Sutter fi_val &= ~DST_FEATURE_ECN_CA; 837d03a4557SPhil Sutter 838d03a4557SPhil Sutter if (fi_val != val) 8395f9ae3d9SXin Long return false; 8405f9ae3d9SXin Long } 8415f9ae3d9SXin Long 8425f9ae3d9SXin Long return true; 8435f9ae3d9SXin Long } 8445f9ae3d9SXin Long 845*717a8f5bSDavid Ahern static int fib_check_nh_v6_gw(struct net *net, struct fib_nh *nh, 846*717a8f5bSDavid Ahern u32 table, struct netlink_ext_ack *extack) 847*717a8f5bSDavid Ahern { 848*717a8f5bSDavid Ahern struct fib6_config cfg = { 849*717a8f5bSDavid Ahern .fc_table = table, 850*717a8f5bSDavid Ahern .fc_flags = nh->fib_nh_flags | RTF_GATEWAY, 851*717a8f5bSDavid Ahern .fc_ifindex = nh->fib_nh_oif, 852*717a8f5bSDavid Ahern .fc_gateway = nh->fib_nh_gw6, 853*717a8f5bSDavid Ahern }; 854*717a8f5bSDavid Ahern struct fib6_nh fib6_nh = {}; 855*717a8f5bSDavid Ahern int err; 856*717a8f5bSDavid Ahern 857*717a8f5bSDavid Ahern err = ipv6_stub->fib6_nh_init(net, &fib6_nh, &cfg, GFP_KERNEL, extack); 858*717a8f5bSDavid Ahern if (!err) { 859*717a8f5bSDavid Ahern nh->fib_nh_dev = fib6_nh.fib_nh_dev; 860*717a8f5bSDavid Ahern dev_hold(nh->fib_nh_dev); 861*717a8f5bSDavid Ahern nh->fib_nh_oif = nh->fib_nh_dev->ifindex; 862*717a8f5bSDavid Ahern nh->fib_nh_scope = RT_SCOPE_LINK; 863*717a8f5bSDavid Ahern 864*717a8f5bSDavid Ahern ipv6_stub->fib6_nh_release(&fib6_nh); 865*717a8f5bSDavid Ahern } 866*717a8f5bSDavid Ahern 867*717a8f5bSDavid Ahern return err; 868*717a8f5bSDavid Ahern } 8691da177e4SLinus Torvalds 8701da177e4SLinus Torvalds /* 8716a31d2a9SEric Dumazet * Picture 8726a31d2a9SEric Dumazet * ------- 8736a31d2a9SEric Dumazet * 8746a31d2a9SEric Dumazet * Semantics of nexthop is very messy by historical reasons. 8756a31d2a9SEric Dumazet * We have to take into account, that: 8766a31d2a9SEric Dumazet * a) gateway can be actually local interface address, 8776a31d2a9SEric Dumazet * so that gatewayed route is direct. 8786a31d2a9SEric Dumazet * b) gateway must be on-link address, possibly 8796a31d2a9SEric Dumazet * described not by an ifaddr, but also by a direct route. 8806a31d2a9SEric Dumazet * c) If both gateway and interface are specified, they should not 8816a31d2a9SEric Dumazet * contradict. 8826a31d2a9SEric Dumazet * d) If we use tunnel routes, gateway could be not on-link. 8836a31d2a9SEric Dumazet * 8846a31d2a9SEric Dumazet * Attempt to reconcile all of these (alas, self-contradictory) conditions 8856a31d2a9SEric Dumazet * results in pretty ugly and hairy code with obscure logic. 8866a31d2a9SEric Dumazet * 8876a31d2a9SEric Dumazet * I chose to generalized it instead, so that the size 8886a31d2a9SEric Dumazet * of code does not increase practically, but it becomes 8896a31d2a9SEric Dumazet * much more general. 8906a31d2a9SEric Dumazet * Every prefix is assigned a "scope" value: "host" is local address, 8916a31d2a9SEric Dumazet * "link" is direct route, 8926a31d2a9SEric Dumazet * [ ... "site" ... "interior" ... ] 8936a31d2a9SEric Dumazet * and "universe" is true gateway route with global meaning. 8946a31d2a9SEric Dumazet * 8956a31d2a9SEric Dumazet * Every prefix refers to a set of "nexthop"s (gw, oif), 8966a31d2a9SEric Dumazet * where gw must have narrower scope. This recursion stops 8976a31d2a9SEric Dumazet * when gw has LOCAL scope or if "nexthop" is declared ONLINK, 8986a31d2a9SEric Dumazet * which means that gw is forced to be on link. 8996a31d2a9SEric Dumazet * 9006a31d2a9SEric Dumazet * Code is still hairy, but now it is apparently logically 9016a31d2a9SEric Dumazet * consistent and very flexible. F.e. as by-product it allows 9026a31d2a9SEric Dumazet * to co-exists in peace independent exterior and interior 9036a31d2a9SEric Dumazet * routing processes. 9046a31d2a9SEric Dumazet * 9056a31d2a9SEric Dumazet * Normally it looks as following. 9066a31d2a9SEric Dumazet * 9076a31d2a9SEric Dumazet * {universe prefix} -> (gw, oif) [scope link] 9086a31d2a9SEric Dumazet * | 9096a31d2a9SEric Dumazet * |-> {link prefix} -> (gw, oif) [scope local] 9106a31d2a9SEric Dumazet * | 9116a31d2a9SEric Dumazet * |-> {local prefix} (terminal node) 9121da177e4SLinus Torvalds */ 913448d7248SDavid Ahern static int fib_check_nh_v4_gw(struct net *net, struct fib_nh *nh, u32 table, 914448d7248SDavid Ahern u8 scope, struct netlink_ext_ack *extack) 9151da177e4SLinus Torvalds { 9166a31d2a9SEric Dumazet struct net_device *dev; 9171da177e4SLinus Torvalds struct fib_result res; 918448d7248SDavid Ahern int err; 9191da177e4SLinus Torvalds 920b75ed8b1SDavid Ahern if (nh->fib_nh_flags & RTNH_F_ONLINK) { 92130bbaa19SDavid Ahern unsigned int addr_type; 9221da177e4SLinus Torvalds 923448d7248SDavid Ahern if (scope >= RT_SCOPE_LINK) { 924448d7248SDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop has invalid scope"); 9251da177e4SLinus Torvalds return -EINVAL; 926c3ab2b4eSDavid Ahern } 927b75ed8b1SDavid Ahern dev = __dev_get_by_index(net, nh->fib_nh_oif); 928066b1030SDavid Ahern if (!dev) { 929066b1030SDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop device required for onlink"); 9301da177e4SLinus Torvalds return -ENODEV; 931066b1030SDavid Ahern } 932c3ab2b4eSDavid Ahern if (!(dev->flags & IFF_UP)) { 933448d7248SDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop device is not up"); 9341da177e4SLinus Torvalds return -ENETDOWN; 935c3ab2b4eSDavid Ahern } 936448d7248SDavid Ahern addr_type = inet_addr_type_dev_table(net, dev, nh->fib_nh_gw4); 937c3ab2b4eSDavid Ahern if (addr_type != RTN_UNICAST) { 938448d7248SDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway"); 93930bbaa19SDavid Ahern return -EINVAL; 940c3ab2b4eSDavid Ahern } 9418a3d0316SAndy Gospodarek if (!netif_carrier_ok(dev)) 942b75ed8b1SDavid Ahern nh->fib_nh_flags |= RTNH_F_LINKDOWN; 943b75ed8b1SDavid Ahern nh->fib_nh_dev = dev; 9441da177e4SLinus Torvalds dev_hold(dev); 945b75ed8b1SDavid Ahern nh->fib_nh_scope = RT_SCOPE_LINK; 9461da177e4SLinus Torvalds return 0; 9471da177e4SLinus Torvalds } 948ebc0ffaeSEric Dumazet rcu_read_lock(); 9491da177e4SLinus Torvalds { 9503bfd8472SDavid Ahern struct fib_table *tbl = NULL; 9519ade2286SDavid S. Miller struct flowi4 fl4 = { 952b75ed8b1SDavid Ahern .daddr = nh->fib_nh_gw4, 953448d7248SDavid Ahern .flowi4_scope = scope + 1, 954b75ed8b1SDavid Ahern .flowi4_oif = nh->fib_nh_oif, 9556a662719SCong Wang .flowi4_iif = LOOPBACK_IFINDEX, 9564e902c57SThomas Graf }; 9571da177e4SLinus Torvalds 9581da177e4SLinus Torvalds /* It is not necessary, but requires a bit of thinking */ 9599ade2286SDavid S. Miller if (fl4.flowi4_scope < RT_SCOPE_LINK) 9609ade2286SDavid S. Miller fl4.flowi4_scope = RT_SCOPE_LINK; 9613bfd8472SDavid Ahern 962448d7248SDavid Ahern if (table) 963448d7248SDavid Ahern tbl = fib_get_table(net, table); 9643bfd8472SDavid Ahern 9653bfd8472SDavid Ahern if (tbl) 9663bfd8472SDavid Ahern err = fib_table_lookup(tbl, &fl4, &res, 9671e313678SEric Dumazet FIB_LOOKUP_IGNORE_LINKSTATE | 9681e313678SEric Dumazet FIB_LOOKUP_NOREF); 9694c9bcd11SDavid Ahern 9704c9bcd11SDavid Ahern /* on error or if no table given do full lookup. This 9714c9bcd11SDavid Ahern * is needed for example when nexthops are in the local 9724c9bcd11SDavid Ahern * table rather than the given table 9734c9bcd11SDavid Ahern */ 9744c9bcd11SDavid Ahern if (!tbl || err) { 9750eeb075fSAndy Gospodarek err = fib_lookup(net, &fl4, &res, 9760eeb075fSAndy Gospodarek FIB_LOOKUP_IGNORE_LINKSTATE); 9774c9bcd11SDavid Ahern } 9784c9bcd11SDavid Ahern 979ebc0ffaeSEric Dumazet if (err) { 980448d7248SDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway"); 981448d7248SDavid Ahern goto out; 9821da177e4SLinus Torvalds } 983ebc0ffaeSEric Dumazet } 984448d7248SDavid Ahern 9851da177e4SLinus Torvalds err = -EINVAL; 986c3ab2b4eSDavid Ahern if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) { 987c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway"); 9881da177e4SLinus Torvalds goto out; 989c3ab2b4eSDavid Ahern } 990b75ed8b1SDavid Ahern nh->fib_nh_scope = res.scope; 991b75ed8b1SDavid Ahern nh->fib_nh_oif = FIB_RES_OIF(res); 992b75ed8b1SDavid Ahern nh->fib_nh_dev = dev = FIB_RES_DEV(res); 993c3ab2b4eSDavid Ahern if (!dev) { 994c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 995c3ab2b4eSDavid Ahern "No egress device for nexthop gateway"); 9961da177e4SLinus Torvalds goto out; 997c3ab2b4eSDavid Ahern } 9986a31d2a9SEric Dumazet dev_hold(dev); 9998a3d0316SAndy Gospodarek if (!netif_carrier_ok(dev)) 1000b75ed8b1SDavid Ahern nh->fib_nh_flags |= RTNH_F_LINKDOWN; 10018723e1b4SEric Dumazet err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN; 1002448d7248SDavid Ahern out: 1003448d7248SDavid Ahern rcu_read_unlock(); 1004448d7248SDavid Ahern return err; 1005448d7248SDavid Ahern } 1006448d7248SDavid Ahern 1007448d7248SDavid Ahern static int fib_check_nh_nongw(struct net *net, struct fib_nh *nh, 1008448d7248SDavid Ahern struct netlink_ext_ack *extack) 1009448d7248SDavid Ahern { 10101da177e4SLinus Torvalds struct in_device *in_dev; 1011448d7248SDavid Ahern int err; 10121da177e4SLinus Torvalds 1013b75ed8b1SDavid Ahern if (nh->fib_nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK)) { 1014c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 1015c3ab2b4eSDavid Ahern "Invalid flags for nexthop - PERVASIVE and ONLINK can not be set"); 10161da177e4SLinus Torvalds return -EINVAL; 1017c3ab2b4eSDavid Ahern } 1018448d7248SDavid Ahern 10198723e1b4SEric Dumazet rcu_read_lock(); 1020448d7248SDavid Ahern 10218723e1b4SEric Dumazet err = -ENODEV; 1022b75ed8b1SDavid Ahern in_dev = inetdev_by_index(net, nh->fib_nh_oif); 102351456b29SIan Morris if (!in_dev) 10248723e1b4SEric Dumazet goto out; 10258723e1b4SEric Dumazet err = -ENETDOWN; 1026c3ab2b4eSDavid Ahern if (!(in_dev->dev->flags & IFF_UP)) { 1027c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, "Device for nexthop is not up"); 10288723e1b4SEric Dumazet goto out; 1029c3ab2b4eSDavid Ahern } 1030448d7248SDavid Ahern 1031b75ed8b1SDavid Ahern nh->fib_nh_dev = in_dev->dev; 1032b75ed8b1SDavid Ahern dev_hold(nh->fib_nh_dev); 1033b75ed8b1SDavid Ahern nh->fib_nh_scope = RT_SCOPE_HOST; 1034b75ed8b1SDavid Ahern if (!netif_carrier_ok(nh->fib_nh_dev)) 1035b75ed8b1SDavid Ahern nh->fib_nh_flags |= RTNH_F_LINKDOWN; 10368723e1b4SEric Dumazet err = 0; 10378723e1b4SEric Dumazet out: 10388723e1b4SEric Dumazet rcu_read_unlock(); 10398723e1b4SEric Dumazet return err; 10401da177e4SLinus Torvalds } 10411da177e4SLinus Torvalds 1042448d7248SDavid Ahern static int fib_check_nh(struct fib_config *cfg, struct fib_nh *nh, 1043448d7248SDavid Ahern struct netlink_ext_ack *extack) 1044448d7248SDavid Ahern { 1045448d7248SDavid Ahern struct net *net = cfg->fc_nlinfo.nl_net; 1046448d7248SDavid Ahern u32 table = cfg->fc_table; 1047448d7248SDavid Ahern int err; 1048448d7248SDavid Ahern 1049448d7248SDavid Ahern if (nh->fib_nh_gw_family == AF_INET) 1050448d7248SDavid Ahern err = fib_check_nh_v4_gw(net, nh, table, cfg->fc_scope, extack); 1051*717a8f5bSDavid Ahern else if (nh->fib_nh_gw_family == AF_INET6) 1052*717a8f5bSDavid Ahern err = fib_check_nh_v6_gw(net, nh, table, extack); 1053448d7248SDavid Ahern else 1054448d7248SDavid Ahern err = fib_check_nh_nongw(net, nh, extack); 1055448d7248SDavid Ahern 1056448d7248SDavid Ahern return err; 1057448d7248SDavid Ahern } 1058448d7248SDavid Ahern 105981f7bf6cSAl Viro static inline unsigned int fib_laddr_hashfn(__be32 val) 10601da177e4SLinus Torvalds { 1061123b9731SDavid S. Miller unsigned int mask = (fib_info_hash_size - 1); 10621da177e4SLinus Torvalds 10636a31d2a9SEric Dumazet return ((__force u32)val ^ 10646a31d2a9SEric Dumazet ((__force u32)val >> 7) ^ 10656a31d2a9SEric Dumazet ((__force u32)val >> 14)) & mask; 10661da177e4SLinus Torvalds } 10671da177e4SLinus Torvalds 1068123b9731SDavid S. Miller static struct hlist_head *fib_info_hash_alloc(int bytes) 10691da177e4SLinus Torvalds { 10701da177e4SLinus Torvalds if (bytes <= PAGE_SIZE) 107188f83491SJoonwoo Park return kzalloc(bytes, GFP_KERNEL); 10721da177e4SLinus Torvalds else 10731da177e4SLinus Torvalds return (struct hlist_head *) 10746a31d2a9SEric Dumazet __get_free_pages(GFP_KERNEL | __GFP_ZERO, 10756a31d2a9SEric Dumazet get_order(bytes)); 10761da177e4SLinus Torvalds } 10771da177e4SLinus Torvalds 1078123b9731SDavid S. Miller static void fib_info_hash_free(struct hlist_head *hash, int bytes) 10791da177e4SLinus Torvalds { 10801da177e4SLinus Torvalds if (!hash) 10811da177e4SLinus Torvalds return; 10821da177e4SLinus Torvalds 10831da177e4SLinus Torvalds if (bytes <= PAGE_SIZE) 10841da177e4SLinus Torvalds kfree(hash); 10851da177e4SLinus Torvalds else 10861da177e4SLinus Torvalds free_pages((unsigned long) hash, get_order(bytes)); 10871da177e4SLinus Torvalds } 10881da177e4SLinus Torvalds 1089123b9731SDavid S. Miller static void fib_info_hash_move(struct hlist_head *new_info_hash, 10901da177e4SLinus Torvalds struct hlist_head *new_laddrhash, 10911da177e4SLinus Torvalds unsigned int new_size) 10921da177e4SLinus Torvalds { 1093b7656e7fSDavid S. Miller struct hlist_head *old_info_hash, *old_laddrhash; 1094123b9731SDavid S. Miller unsigned int old_size = fib_info_hash_size; 1095b7656e7fSDavid S. Miller unsigned int i, bytes; 10961da177e4SLinus Torvalds 1097832b4c5eSStephen Hemminger spin_lock_bh(&fib_info_lock); 1098b7656e7fSDavid S. Miller old_info_hash = fib_info_hash; 1099b7656e7fSDavid S. Miller old_laddrhash = fib_info_laddrhash; 1100123b9731SDavid S. Miller fib_info_hash_size = new_size; 11011da177e4SLinus Torvalds 11021da177e4SLinus Torvalds for (i = 0; i < old_size; i++) { 11031da177e4SLinus Torvalds struct hlist_head *head = &fib_info_hash[i]; 1104b67bfe0dSSasha Levin struct hlist_node *n; 11051da177e4SLinus Torvalds struct fib_info *fi; 11061da177e4SLinus Torvalds 1107b67bfe0dSSasha Levin hlist_for_each_entry_safe(fi, n, head, fib_hash) { 11081da177e4SLinus Torvalds struct hlist_head *dest; 11091da177e4SLinus Torvalds unsigned int new_hash; 11101da177e4SLinus Torvalds 11111da177e4SLinus Torvalds new_hash = fib_info_hashfn(fi); 11121da177e4SLinus Torvalds dest = &new_info_hash[new_hash]; 11131da177e4SLinus Torvalds hlist_add_head(&fi->fib_hash, dest); 11141da177e4SLinus Torvalds } 11151da177e4SLinus Torvalds } 11161da177e4SLinus Torvalds fib_info_hash = new_info_hash; 11171da177e4SLinus Torvalds 11181da177e4SLinus Torvalds for (i = 0; i < old_size; i++) { 11191da177e4SLinus Torvalds struct hlist_head *lhead = &fib_info_laddrhash[i]; 1120b67bfe0dSSasha Levin struct hlist_node *n; 11211da177e4SLinus Torvalds struct fib_info *fi; 11221da177e4SLinus Torvalds 1123b67bfe0dSSasha Levin hlist_for_each_entry_safe(fi, n, lhead, fib_lhash) { 11241da177e4SLinus Torvalds struct hlist_head *ldest; 11251da177e4SLinus Torvalds unsigned int new_hash; 11261da177e4SLinus Torvalds 11271da177e4SLinus Torvalds new_hash = fib_laddr_hashfn(fi->fib_prefsrc); 11281da177e4SLinus Torvalds ldest = &new_laddrhash[new_hash]; 11291da177e4SLinus Torvalds hlist_add_head(&fi->fib_lhash, ldest); 11301da177e4SLinus Torvalds } 11311da177e4SLinus Torvalds } 11321da177e4SLinus Torvalds fib_info_laddrhash = new_laddrhash; 11331da177e4SLinus Torvalds 1134832b4c5eSStephen Hemminger spin_unlock_bh(&fib_info_lock); 1135b7656e7fSDavid S. Miller 1136b7656e7fSDavid S. Miller bytes = old_size * sizeof(struct hlist_head *); 1137123b9731SDavid S. Miller fib_info_hash_free(old_info_hash, bytes); 1138123b9731SDavid S. Miller fib_info_hash_free(old_laddrhash, bytes); 11391da177e4SLinus Torvalds } 11401da177e4SLinus Torvalds 1141436c3b66SDavid S. Miller __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh) 1142436c3b66SDavid S. Miller { 1143b75ed8b1SDavid Ahern nh->nh_saddr = inet_select_addr(nh->fib_nh_dev, 1144b75ed8b1SDavid Ahern nh->fib_nh_gw4, 114537e826c5SDavid S. Miller nh->nh_parent->fib_scope); 1146436c3b66SDavid S. Miller nh->nh_saddr_genid = atomic_read(&net->ipv4.dev_addr_genid); 1147436c3b66SDavid S. Miller 1148436c3b66SDavid S. Miller return nh->nh_saddr; 1149436c3b66SDavid S. Miller } 1150436c3b66SDavid S. Miller 1151eba618abSDavid Ahern __be32 fib_result_prefsrc(struct net *net, struct fib_result *res) 1152eba618abSDavid Ahern { 1153eba618abSDavid Ahern struct fib_nh_common *nhc = res->nhc; 1154eba618abSDavid Ahern struct fib_nh *nh; 1155eba618abSDavid Ahern 1156eba618abSDavid Ahern if (res->fi->fib_prefsrc) 1157eba618abSDavid Ahern return res->fi->fib_prefsrc; 1158eba618abSDavid Ahern 1159eba618abSDavid Ahern nh = container_of(nhc, struct fib_nh, nh_common); 1160eba618abSDavid Ahern if (nh->nh_saddr_genid == atomic_read(&net->ipv4.dev_addr_genid)) 1161eba618abSDavid Ahern return nh->nh_saddr; 1162eba618abSDavid Ahern 1163eba618abSDavid Ahern return fib_info_update_nh_saddr(net, nh); 1164eba618abSDavid Ahern } 1165eba618abSDavid Ahern 1166021dd3b8SDavid Ahern static bool fib_valid_prefsrc(struct fib_config *cfg, __be32 fib_prefsrc) 1167021dd3b8SDavid Ahern { 1168021dd3b8SDavid Ahern if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || 1169021dd3b8SDavid Ahern fib_prefsrc != cfg->fc_dst) { 11709b8ff518SDavid Ahern u32 tb_id = cfg->fc_table; 1171e1b8d903SDavid Ahern int rc; 1172021dd3b8SDavid Ahern 1173021dd3b8SDavid Ahern if (tb_id == RT_TABLE_MAIN) 1174021dd3b8SDavid Ahern tb_id = RT_TABLE_LOCAL; 1175021dd3b8SDavid Ahern 1176e1b8d903SDavid Ahern rc = inet_addr_type_table(cfg->fc_nlinfo.nl_net, 1177e1b8d903SDavid Ahern fib_prefsrc, tb_id); 1178e1b8d903SDavid Ahern 1179e1b8d903SDavid Ahern if (rc != RTN_LOCAL && tb_id != RT_TABLE_LOCAL) { 1180e1b8d903SDavid Ahern rc = inet_addr_type_table(cfg->fc_nlinfo.nl_net, 1181e1b8d903SDavid Ahern fib_prefsrc, RT_TABLE_LOCAL); 1182021dd3b8SDavid Ahern } 1183e1b8d903SDavid Ahern 1184e1b8d903SDavid Ahern if (rc != RTN_LOCAL) 1185e1b8d903SDavid Ahern return false; 1186021dd3b8SDavid Ahern } 1187021dd3b8SDavid Ahern return true; 1188021dd3b8SDavid Ahern } 1189021dd3b8SDavid Ahern 11906d8422a1SDavid Ahern struct fib_info *fib_create_info(struct fib_config *cfg, 11916d8422a1SDavid Ahern struct netlink_ext_ack *extack) 11921da177e4SLinus Torvalds { 11931da177e4SLinus Torvalds int err; 11941da177e4SLinus Torvalds struct fib_info *fi = NULL; 11951da177e4SLinus Torvalds struct fib_info *ofi; 11961da177e4SLinus Torvalds int nhs = 1; 11977462bd74SDenis V. Lunev struct net *net = cfg->fc_nlinfo.nl_net; 11981da177e4SLinus Torvalds 11994c8237cdSDavid S. Miller if (cfg->fc_type > RTN_MAX) 12004c8237cdSDavid S. Miller goto err_inval; 12014c8237cdSDavid S. Miller 12021da177e4SLinus Torvalds /* Fast check to catch the most weird cases */ 1203c3ab2b4eSDavid Ahern if (fib_props[cfg->fc_type].scope > cfg->fc_scope) { 1204c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid scope"); 12051da177e4SLinus Torvalds goto err_inval; 1206c3ab2b4eSDavid Ahern } 12071da177e4SLinus Torvalds 1208c3ab2b4eSDavid Ahern if (cfg->fc_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) { 1209c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 1210c3ab2b4eSDavid Ahern "Invalid rtm_flags - can not contain DEAD or LINKDOWN"); 121180610229SJulian Anastasov goto err_inval; 1212c3ab2b4eSDavid Ahern } 121380610229SJulian Anastasov 12141da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 12154e902c57SThomas Graf if (cfg->fc_mp) { 12166d8422a1SDavid Ahern nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len, extack); 12171da177e4SLinus Torvalds if (nhs == 0) 12181da177e4SLinus Torvalds goto err_inval; 12191da177e4SLinus Torvalds } 12201da177e4SLinus Torvalds #endif 12211da177e4SLinus Torvalds 12221da177e4SLinus Torvalds err = -ENOBUFS; 1223123b9731SDavid S. Miller if (fib_info_cnt >= fib_info_hash_size) { 1224123b9731SDavid S. Miller unsigned int new_size = fib_info_hash_size << 1; 12251da177e4SLinus Torvalds struct hlist_head *new_info_hash; 12261da177e4SLinus Torvalds struct hlist_head *new_laddrhash; 12271da177e4SLinus Torvalds unsigned int bytes; 12281da177e4SLinus Torvalds 12291da177e4SLinus Torvalds if (!new_size) 1230d94ce9b2SEric Dumazet new_size = 16; 12311da177e4SLinus Torvalds bytes = new_size * sizeof(struct hlist_head *); 1232123b9731SDavid S. Miller new_info_hash = fib_info_hash_alloc(bytes); 1233123b9731SDavid S. Miller new_laddrhash = fib_info_hash_alloc(bytes); 12341da177e4SLinus Torvalds if (!new_info_hash || !new_laddrhash) { 1235123b9731SDavid S. Miller fib_info_hash_free(new_info_hash, bytes); 1236123b9731SDavid S. Miller fib_info_hash_free(new_laddrhash, bytes); 123788f83491SJoonwoo Park } else 1238123b9731SDavid S. Miller fib_info_hash_move(new_info_hash, new_laddrhash, new_size); 12391da177e4SLinus Torvalds 1240123b9731SDavid S. Miller if (!fib_info_hash_size) 12411da177e4SLinus Torvalds goto failure; 12421da177e4SLinus Torvalds } 12431da177e4SLinus Torvalds 12441f533ba6SGustavo A. R. Silva fi = kzalloc(struct_size(fi, fib_nh, nhs), GFP_KERNEL); 124551456b29SIan Morris if (!fi) 12461da177e4SLinus Torvalds goto failure; 1247767a2217SDavid Ahern fi->fib_metrics = ip_fib_metrics_init(fi->fib_net, cfg->fc_mx, 1248d7e774f3SDavid Ahern cfg->fc_mx_len, extack); 1249767a2217SDavid Ahern if (unlikely(IS_ERR(fi->fib_metrics))) { 1250767a2217SDavid Ahern err = PTR_ERR(fi->fib_metrics); 1251187e5b3aSEric Dumazet kfree(fi); 1252187e5b3aSEric Dumazet return ERR_PTR(err); 1253187e5b3aSEric Dumazet } 1254767a2217SDavid Ahern 1255187e5b3aSEric Dumazet fib_info_cnt++; 1256efd7ef1cSEric W. Biederman fi->fib_net = net; 12574e902c57SThomas Graf fi->fib_protocol = cfg->fc_protocol; 125837e826c5SDavid S. Miller fi->fib_scope = cfg->fc_scope; 12594e902c57SThomas Graf fi->fib_flags = cfg->fc_flags; 12604e902c57SThomas Graf fi->fib_priority = cfg->fc_priority; 12614e902c57SThomas Graf fi->fib_prefsrc = cfg->fc_prefsrc; 1262f4ef85bbSEric Dumazet fi->fib_type = cfg->fc_type; 12635a56a0b3SMark Tomlinson fi->fib_tb_id = cfg->fc_table; 12641da177e4SLinus Torvalds 12651da177e4SLinus Torvalds fi->fib_nhs = nhs; 12661da177e4SLinus Torvalds change_nexthops(fi) { 126771fceff0SDavid S. Miller nexthop_nh->nh_parent = fi; 12681da177e4SLinus Torvalds } endfor_nexthops(fi) 12691da177e4SLinus Torvalds 1270e4516ef6SDavid Ahern if (cfg->fc_mp) 12716d8422a1SDavid Ahern err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg, extack); 1272e4516ef6SDavid Ahern else 1273e4516ef6SDavid Ahern err = fib_nh_init(net, fi->fib_nh, cfg, 1, extack); 1274e4516ef6SDavid Ahern 12754e902c57SThomas Graf if (err != 0) 12761da177e4SLinus Torvalds goto failure; 12771da177e4SLinus Torvalds 12784e902c57SThomas Graf if (fib_props[cfg->fc_type].error) { 1279f35b794bSDavid Ahern if (cfg->fc_gw_family || cfg->fc_oif || cfg->fc_mp) { 1280c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 1281c3ab2b4eSDavid Ahern "Gateway, device and multipath can not be specified for this route type"); 12821da177e4SLinus Torvalds goto err_inval; 1283c3ab2b4eSDavid Ahern } 12841da177e4SLinus Torvalds goto link_it; 12854c8237cdSDavid S. Miller } else { 12864c8237cdSDavid S. Miller switch (cfg->fc_type) { 12874c8237cdSDavid S. Miller case RTN_UNICAST: 12884c8237cdSDavid S. Miller case RTN_LOCAL: 12894c8237cdSDavid S. Miller case RTN_BROADCAST: 12904c8237cdSDavid S. Miller case RTN_ANYCAST: 12914c8237cdSDavid S. Miller case RTN_MULTICAST: 12924c8237cdSDavid S. Miller break; 12934c8237cdSDavid S. Miller default: 1294c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid route type"); 12954c8237cdSDavid S. Miller goto err_inval; 12964c8237cdSDavid S. Miller } 12971da177e4SLinus Torvalds } 12981da177e4SLinus Torvalds 1299c3ab2b4eSDavid Ahern if (cfg->fc_scope > RT_SCOPE_HOST) { 1300c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid scope"); 13011da177e4SLinus Torvalds goto err_inval; 1302c3ab2b4eSDavid Ahern } 13031da177e4SLinus Torvalds 13044e902c57SThomas Graf if (cfg->fc_scope == RT_SCOPE_HOST) { 13051da177e4SLinus Torvalds struct fib_nh *nh = fi->fib_nh; 13061da177e4SLinus Torvalds 13071da177e4SLinus Torvalds /* Local address is added. */ 1308c3ab2b4eSDavid Ahern if (nhs != 1) { 1309c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 1310c3ab2b4eSDavid Ahern "Route with host scope can not have multiple nexthops"); 13116d8422a1SDavid Ahern goto err_inval; 1312c3ab2b4eSDavid Ahern } 1313bdf00467SDavid Ahern if (nh->fib_nh_gw_family) { 1314c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 1315c3ab2b4eSDavid Ahern "Route with host scope can not have a gateway"); 13161da177e4SLinus Torvalds goto err_inval; 1317c3ab2b4eSDavid Ahern } 1318b75ed8b1SDavid Ahern nh->fib_nh_scope = RT_SCOPE_NOWHERE; 1319b75ed8b1SDavid Ahern nh->fib_nh_dev = dev_get_by_index(net, fi->fib_nh->fib_nh_oif); 13201da177e4SLinus Torvalds err = -ENODEV; 1321b75ed8b1SDavid Ahern if (!nh->fib_nh_dev) 13221da177e4SLinus Torvalds goto failure; 13231da177e4SLinus Torvalds } else { 13248a3d0316SAndy Gospodarek int linkdown = 0; 13258a3d0316SAndy Gospodarek 13261da177e4SLinus Torvalds change_nexthops(fi) { 1327fa8fefaaSDavid Ahern err = fib_check_nh(cfg, nexthop_nh, extack); 13286a31d2a9SEric Dumazet if (err != 0) 13291da177e4SLinus Torvalds goto failure; 1330b75ed8b1SDavid Ahern if (nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) 13318a3d0316SAndy Gospodarek linkdown++; 13321da177e4SLinus Torvalds } endfor_nexthops(fi) 13338a3d0316SAndy Gospodarek if (linkdown == fi->fib_nhs) 13348a3d0316SAndy Gospodarek fi->fib_flags |= RTNH_F_LINKDOWN; 13351da177e4SLinus Torvalds } 13361da177e4SLinus Torvalds 1337c3ab2b4eSDavid Ahern if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) { 1338c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid prefsrc address"); 13391da177e4SLinus Torvalds goto err_inval; 1340c3ab2b4eSDavid Ahern } 13411da177e4SLinus Torvalds 13421fc050a1SDavid S. Miller change_nexthops(fi) { 1343436c3b66SDavid S. Miller fib_info_update_nh_saddr(net, nexthop_nh); 13441fc050a1SDavid S. Miller } endfor_nexthops(fi) 13451fc050a1SDavid S. Miller 13460e884c78SPeter Nørlund fib_rebalance(fi); 13470e884c78SPeter Nørlund 13481da177e4SLinus Torvalds link_it: 13496a31d2a9SEric Dumazet ofi = fib_find_info(fi); 13506a31d2a9SEric Dumazet if (ofi) { 13511da177e4SLinus Torvalds fi->fib_dead = 1; 13521da177e4SLinus Torvalds free_fib_info(fi); 13531da177e4SLinus Torvalds ofi->fib_treeref++; 13541da177e4SLinus Torvalds return ofi; 13551da177e4SLinus Torvalds } 13561da177e4SLinus Torvalds 13571da177e4SLinus Torvalds fi->fib_treeref++; 13580029c0deSReshetova, Elena refcount_set(&fi->fib_clntref, 1); 1359832b4c5eSStephen Hemminger spin_lock_bh(&fib_info_lock); 13601da177e4SLinus Torvalds hlist_add_head(&fi->fib_hash, 13611da177e4SLinus Torvalds &fib_info_hash[fib_info_hashfn(fi)]); 13621da177e4SLinus Torvalds if (fi->fib_prefsrc) { 13631da177e4SLinus Torvalds struct hlist_head *head; 13641da177e4SLinus Torvalds 13651da177e4SLinus Torvalds head = &fib_info_laddrhash[fib_laddr_hashfn(fi->fib_prefsrc)]; 13661da177e4SLinus Torvalds hlist_add_head(&fi->fib_lhash, head); 13671da177e4SLinus Torvalds } 13681da177e4SLinus Torvalds change_nexthops(fi) { 13691da177e4SLinus Torvalds struct hlist_head *head; 13701da177e4SLinus Torvalds unsigned int hash; 13711da177e4SLinus Torvalds 1372b75ed8b1SDavid Ahern if (!nexthop_nh->fib_nh_dev) 13731da177e4SLinus Torvalds continue; 1374b75ed8b1SDavid Ahern hash = fib_devindex_hashfn(nexthop_nh->fib_nh_dev->ifindex); 13751da177e4SLinus Torvalds head = &fib_info_devhash[hash]; 137671fceff0SDavid S. Miller hlist_add_head(&nexthop_nh->nh_hash, head); 13771da177e4SLinus Torvalds } endfor_nexthops(fi) 1378832b4c5eSStephen Hemminger spin_unlock_bh(&fib_info_lock); 13791da177e4SLinus Torvalds return fi; 13801da177e4SLinus Torvalds 13811da177e4SLinus Torvalds err_inval: 13821da177e4SLinus Torvalds err = -EINVAL; 13831da177e4SLinus Torvalds 13841da177e4SLinus Torvalds failure: 13851da177e4SLinus Torvalds if (fi) { 13861da177e4SLinus Torvalds fi->fib_dead = 1; 13871da177e4SLinus Torvalds free_fib_info(fi); 13881da177e4SLinus Torvalds } 13894e902c57SThomas Graf 13904e902c57SThomas Graf return ERR_PTR(err); 13911da177e4SLinus Torvalds } 13921da177e4SLinus Torvalds 1393c0a72077SDavid Ahern int fib_nexthop_info(struct sk_buff *skb, const struct fib_nh_common *nhc, 1394b0f60193SDavid Ahern unsigned int *flags, bool skip_oif) 1395b0f60193SDavid Ahern { 1396c2364199SDavid Ahern if (nhc->nhc_flags & RTNH_F_DEAD) 1397b0f60193SDavid Ahern *flags |= RTNH_F_DEAD; 1398b0f60193SDavid Ahern 1399c2364199SDavid Ahern if (nhc->nhc_flags & RTNH_F_LINKDOWN) { 1400b0f60193SDavid Ahern *flags |= RTNH_F_LINKDOWN; 1401b0f60193SDavid Ahern 1402b0f60193SDavid Ahern rcu_read_lock(); 1403c2364199SDavid Ahern switch (nhc->nhc_family) { 1404c2364199SDavid Ahern case AF_INET: 1405c2364199SDavid Ahern if (ip_ignore_linkdown(nhc->nhc_dev)) 1406b0f60193SDavid Ahern *flags |= RTNH_F_DEAD; 1407c2364199SDavid Ahern break; 1408c0a72077SDavid Ahern case AF_INET6: 1409c0a72077SDavid Ahern if (ip6_ignore_linkdown(nhc->nhc_dev)) 1410c0a72077SDavid Ahern *flags |= RTNH_F_DEAD; 1411c0a72077SDavid Ahern break; 1412c2364199SDavid Ahern } 1413b0f60193SDavid Ahern rcu_read_unlock(); 1414b0f60193SDavid Ahern } 1415b0f60193SDavid Ahern 1416bdf00467SDavid Ahern switch (nhc->nhc_gw_family) { 1417c2364199SDavid Ahern case AF_INET: 1418c2364199SDavid Ahern if (nla_put_in_addr(skb, RTA_GATEWAY, nhc->nhc_gw.ipv4)) 1419b0f60193SDavid Ahern goto nla_put_failure; 1420c2364199SDavid Ahern break; 1421c0a72077SDavid Ahern case AF_INET6: 1422bdf00467SDavid Ahern if (nla_put_in6_addr(skb, RTA_GATEWAY, &nhc->nhc_gw.ipv6) < 0) 1423c0a72077SDavid Ahern goto nla_put_failure; 1424c0a72077SDavid Ahern break; 1425c2364199SDavid Ahern } 1426b0f60193SDavid Ahern 1427c2364199SDavid Ahern *flags |= (nhc->nhc_flags & RTNH_F_ONLINK); 1428c2364199SDavid Ahern if (nhc->nhc_flags & RTNH_F_OFFLOAD) 1429b0f60193SDavid Ahern *flags |= RTNH_F_OFFLOAD; 1430b0f60193SDavid Ahern 1431c2364199SDavid Ahern if (!skip_oif && nhc->nhc_dev && 1432c2364199SDavid Ahern nla_put_u32(skb, RTA_OIF, nhc->nhc_dev->ifindex)) 1433b0f60193SDavid Ahern goto nla_put_failure; 1434b0f60193SDavid Ahern 1435c2364199SDavid Ahern if (nhc->nhc_lwtstate && 1436c2364199SDavid Ahern lwtunnel_fill_encap(skb, nhc->nhc_lwtstate) < 0) 1437b0f60193SDavid Ahern goto nla_put_failure; 1438b0f60193SDavid Ahern 1439b0f60193SDavid Ahern return 0; 1440b0f60193SDavid Ahern 1441b0f60193SDavid Ahern nla_put_failure: 1442b0f60193SDavid Ahern return -EMSGSIZE; 1443b0f60193SDavid Ahern } 1444c0a72077SDavid Ahern EXPORT_SYMBOL_GPL(fib_nexthop_info); 1445b0f60193SDavid Ahern 1446c0a72077SDavid Ahern #if IS_ENABLED(CONFIG_IP_ROUTE_MULTIPATH) || IS_ENABLED(CONFIG_IPV6) 1447c0a72077SDavid Ahern int fib_add_nexthop(struct sk_buff *skb, const struct fib_nh_common *nhc, 1448c2364199SDavid Ahern int nh_weight) 1449b0f60193SDavid Ahern { 1450c2364199SDavid Ahern const struct net_device *dev = nhc->nhc_dev; 1451b0f60193SDavid Ahern struct rtnexthop *rtnh; 1452b0f60193SDavid Ahern unsigned int flags = 0; 1453b0f60193SDavid Ahern 1454b0f60193SDavid Ahern rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); 1455b0f60193SDavid Ahern if (!rtnh) 1456b0f60193SDavid Ahern goto nla_put_failure; 1457b0f60193SDavid Ahern 1458c2364199SDavid Ahern rtnh->rtnh_hops = nh_weight - 1; 1459b0f60193SDavid Ahern rtnh->rtnh_ifindex = dev ? dev->ifindex : 0; 1460b0f60193SDavid Ahern 1461c2364199SDavid Ahern if (fib_nexthop_info(skb, nhc, &flags, true) < 0) 1462b0f60193SDavid Ahern goto nla_put_failure; 1463b0f60193SDavid Ahern 1464b0f60193SDavid Ahern rtnh->rtnh_flags = flags; 1465b0f60193SDavid Ahern 1466b0f60193SDavid Ahern /* length of rtnetlink header + attributes */ 1467b0f60193SDavid Ahern rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh; 1468b0f60193SDavid Ahern 1469b0f60193SDavid Ahern return 0; 1470b0f60193SDavid Ahern 1471b0f60193SDavid Ahern nla_put_failure: 1472b0f60193SDavid Ahern return -EMSGSIZE; 1473b0f60193SDavid Ahern } 1474c0a72077SDavid Ahern EXPORT_SYMBOL_GPL(fib_add_nexthop); 1475c2364199SDavid Ahern #endif 1476b0f60193SDavid Ahern 1477c2364199SDavid Ahern #ifdef CONFIG_IP_ROUTE_MULTIPATH 1478b0f60193SDavid Ahern static int fib_add_multipath(struct sk_buff *skb, struct fib_info *fi) 1479b0f60193SDavid Ahern { 1480b0f60193SDavid Ahern struct nlattr *mp; 1481b0f60193SDavid Ahern 1482b0f60193SDavid Ahern mp = nla_nest_start(skb, RTA_MULTIPATH); 1483b0f60193SDavid Ahern if (!mp) 1484b0f60193SDavid Ahern goto nla_put_failure; 1485b0f60193SDavid Ahern 1486b0f60193SDavid Ahern for_nexthops(fi) { 1487c2364199SDavid Ahern if (fib_add_nexthop(skb, &nh->nh_common, nh->fib_nh_weight) < 0) 1488b0f60193SDavid Ahern goto nla_put_failure; 1489b0f60193SDavid Ahern #ifdef CONFIG_IP_ROUTE_CLASSID 1490b0f60193SDavid Ahern if (nh->nh_tclassid && 1491b0f60193SDavid Ahern nla_put_u32(skb, RTA_FLOW, nh->nh_tclassid)) 1492b0f60193SDavid Ahern goto nla_put_failure; 1493b0f60193SDavid Ahern #endif 1494b0f60193SDavid Ahern } endfor_nexthops(fi); 1495b0f60193SDavid Ahern 1496b0f60193SDavid Ahern nla_nest_end(skb, mp); 1497b0f60193SDavid Ahern 1498b0f60193SDavid Ahern return 0; 1499b0f60193SDavid Ahern 1500b0f60193SDavid Ahern nla_put_failure: 1501b0f60193SDavid Ahern return -EMSGSIZE; 1502b0f60193SDavid Ahern } 1503b0f60193SDavid Ahern #else 1504b0f60193SDavid Ahern static int fib_add_multipath(struct sk_buff *skb, struct fib_info *fi) 1505b0f60193SDavid Ahern { 1506b0f60193SDavid Ahern return 0; 1507b0f60193SDavid Ahern } 1508b0f60193SDavid Ahern #endif 1509b0f60193SDavid Ahern 151015e47304SEric W. Biederman int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, 151137e826c5SDavid S. Miller u32 tb_id, u8 type, __be32 dst, int dst_len, u8 tos, 1512b6544c0bSJamal Hadi Salim struct fib_info *fi, unsigned int flags) 15131da177e4SLinus Torvalds { 15141da177e4SLinus Torvalds struct nlmsghdr *nlh; 1515be403ea1SThomas Graf struct rtmsg *rtm; 15161da177e4SLinus Torvalds 151715e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags); 151851456b29SIan Morris if (!nlh) 151926932566SPatrick McHardy return -EMSGSIZE; 1520be403ea1SThomas Graf 1521be403ea1SThomas Graf rtm = nlmsg_data(nlh); 15221da177e4SLinus Torvalds rtm->rtm_family = AF_INET; 15231da177e4SLinus Torvalds rtm->rtm_dst_len = dst_len; 15241da177e4SLinus Torvalds rtm->rtm_src_len = 0; 15251da177e4SLinus Torvalds rtm->rtm_tos = tos; 1526709772e6SKrzysztof Piotr Oledzki if (tb_id < 256) 15271da177e4SLinus Torvalds rtm->rtm_table = tb_id; 1528709772e6SKrzysztof Piotr Oledzki else 1529709772e6SKrzysztof Piotr Oledzki rtm->rtm_table = RT_TABLE_COMPAT; 1530f3756b79SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, tb_id)) 1531f3756b79SDavid S. Miller goto nla_put_failure; 15321da177e4SLinus Torvalds rtm->rtm_type = type; 15331da177e4SLinus Torvalds rtm->rtm_flags = fi->fib_flags; 153437e826c5SDavid S. Miller rtm->rtm_scope = fi->fib_scope; 15351da177e4SLinus Torvalds rtm->rtm_protocol = fi->fib_protocol; 1536be403ea1SThomas Graf 1537f3756b79SDavid S. Miller if (rtm->rtm_dst_len && 1538930345eaSJiri Benc nla_put_in_addr(skb, RTA_DST, dst)) 1539f3756b79SDavid S. Miller goto nla_put_failure; 1540f3756b79SDavid S. Miller if (fi->fib_priority && 1541f3756b79SDavid S. Miller nla_put_u32(skb, RTA_PRIORITY, fi->fib_priority)) 1542f3756b79SDavid S. Miller goto nla_put_failure; 15433fb07dafSEric Dumazet if (rtnetlink_put_metrics(skb, fi->fib_metrics->metrics) < 0) 1544be403ea1SThomas Graf goto nla_put_failure; 1545be403ea1SThomas Graf 1546f3756b79SDavid S. Miller if (fi->fib_prefsrc && 1547930345eaSJiri Benc nla_put_in_addr(skb, RTA_PREFSRC, fi->fib_prefsrc)) 1548f3756b79SDavid S. Miller goto nla_put_failure; 15491da177e4SLinus Torvalds if (fi->fib_nhs == 1) { 1550b0f60193SDavid Ahern struct fib_nh *nh = &fi->fib_nh[0]; 1551b0f60193SDavid Ahern unsigned int flags = 0; 1552be403ea1SThomas Graf 1553c2364199SDavid Ahern if (fib_nexthop_info(skb, &nh->nh_common, &flags, false) < 0) 1554be403ea1SThomas Graf goto nla_put_failure; 15551da177e4SLinus Torvalds 1556b0f60193SDavid Ahern rtm->rtm_flags = flags; 1557c7066f70SPatrick McHardy #ifdef CONFIG_IP_ROUTE_CLASSID 1558f3756b79SDavid S. Miller if (nh->nh_tclassid && 1559f3756b79SDavid S. Miller nla_put_u32(skb, RTA_FLOW, nh->nh_tclassid)) 1560f3756b79SDavid S. Miller goto nla_put_failure; 15618265abc0SPatrick McHardy #endif 1562b0f60193SDavid Ahern } else { 1563b0f60193SDavid Ahern if (fib_add_multipath(skb, fi) < 0) 1564ea7a8085SDavid Ahern goto nla_put_failure; 15651da177e4SLinus Torvalds } 1566b0f60193SDavid Ahern 1567053c095aSJohannes Berg nlmsg_end(skb, nlh); 1568053c095aSJohannes Berg return 0; 15691da177e4SLinus Torvalds 1570be403ea1SThomas Graf nla_put_failure: 157126932566SPatrick McHardy nlmsg_cancel(skb, nlh); 157226932566SPatrick McHardy return -EMSGSIZE; 15731da177e4SLinus Torvalds } 15741da177e4SLinus Torvalds 15751da177e4SLinus Torvalds /* 15766a31d2a9SEric Dumazet * Update FIB if: 15776a31d2a9SEric Dumazet * - local address disappeared -> we must delete all the entries 15786a31d2a9SEric Dumazet * referring to it. 15796a31d2a9SEric Dumazet * - device went down -> we must shutdown all nexthops going via it. 15801da177e4SLinus Torvalds */ 15815a56a0b3SMark Tomlinson int fib_sync_down_addr(struct net_device *dev, __be32 local) 15821da177e4SLinus Torvalds { 15831da177e4SLinus Torvalds int ret = 0; 15841da177e4SLinus Torvalds unsigned int hash = fib_laddr_hashfn(local); 15851da177e4SLinus Torvalds struct hlist_head *head = &fib_info_laddrhash[hash]; 15865a56a0b3SMark Tomlinson struct net *net = dev_net(dev); 15875a56a0b3SMark Tomlinson int tb_id = l3mdev_fib_table(dev); 15881da177e4SLinus Torvalds struct fib_info *fi; 15891da177e4SLinus Torvalds 159051456b29SIan Morris if (!fib_info_laddrhash || local == 0) 159185326fa5SDenis V. Lunev return 0; 159285326fa5SDenis V. Lunev 1593b67bfe0dSSasha Levin hlist_for_each_entry(fi, head, fib_lhash) { 15945a56a0b3SMark Tomlinson if (!net_eq(fi->fib_net, net) || 15955a56a0b3SMark Tomlinson fi->fib_tb_id != tb_id) 15964814bdbdSDenis V. Lunev continue; 15971da177e4SLinus Torvalds if (fi->fib_prefsrc == local) { 15981da177e4SLinus Torvalds fi->fib_flags |= RTNH_F_DEAD; 15991da177e4SLinus Torvalds ret++; 16001da177e4SLinus Torvalds } 16011da177e4SLinus Torvalds } 160285326fa5SDenis V. Lunev return ret; 16031da177e4SLinus Torvalds } 16041da177e4SLinus Torvalds 1605b75ed8b1SDavid Ahern static int call_fib_nh_notifiers(struct fib_nh *nh, 1606982acb97SIdo Schimmel enum fib_event_type event_type) 1607982acb97SIdo Schimmel { 1608b75ed8b1SDavid Ahern bool ignore_link_down = ip_ignore_linkdown(nh->fib_nh_dev); 1609982acb97SIdo Schimmel struct fib_nh_notifier_info info = { 1610b75ed8b1SDavid Ahern .fib_nh = nh, 1611982acb97SIdo Schimmel }; 1612982acb97SIdo Schimmel 1613982acb97SIdo Schimmel switch (event_type) { 1614982acb97SIdo Schimmel case FIB_EVENT_NH_ADD: 1615b75ed8b1SDavid Ahern if (nh->fib_nh_flags & RTNH_F_DEAD) 1616982acb97SIdo Schimmel break; 1617b75ed8b1SDavid Ahern if (ignore_link_down && nh->fib_nh_flags & RTNH_F_LINKDOWN) 1618982acb97SIdo Schimmel break; 1619b75ed8b1SDavid Ahern return call_fib4_notifiers(dev_net(nh->fib_nh_dev), event_type, 1620982acb97SIdo Schimmel &info.info); 1621982acb97SIdo Schimmel case FIB_EVENT_NH_DEL: 1622b75ed8b1SDavid Ahern if ((ignore_link_down && nh->fib_nh_flags & RTNH_F_LINKDOWN) || 1623b75ed8b1SDavid Ahern (nh->fib_nh_flags & RTNH_F_DEAD)) 1624b75ed8b1SDavid Ahern return call_fib4_notifiers(dev_net(nh->fib_nh_dev), 1625982acb97SIdo Schimmel event_type, &info.info); 1626982acb97SIdo Schimmel default: 1627982acb97SIdo Schimmel break; 1628982acb97SIdo Schimmel } 1629982acb97SIdo Schimmel 1630982acb97SIdo Schimmel return NOTIFY_DONE; 1631982acb97SIdo Schimmel } 1632982acb97SIdo Schimmel 1633af7d6cceSSabrina Dubroca /* Update the PMTU of exceptions when: 1634af7d6cceSSabrina Dubroca * - the new MTU of the first hop becomes smaller than the PMTU 1635af7d6cceSSabrina Dubroca * - the old MTU was the same as the PMTU, and it limited discovery of 1636af7d6cceSSabrina Dubroca * larger MTUs on the path. With that limit raised, we can now 1637af7d6cceSSabrina Dubroca * discover larger MTUs 1638af7d6cceSSabrina Dubroca * A special case is locked exceptions, for which the PMTU is smaller 1639af7d6cceSSabrina Dubroca * than the minimal accepted PMTU: 1640af7d6cceSSabrina Dubroca * - if the new MTU is greater than the PMTU, don't make any change 1641af7d6cceSSabrina Dubroca * - otherwise, unlock and set PMTU 1642af7d6cceSSabrina Dubroca */ 1643af7d6cceSSabrina Dubroca static void nh_update_mtu(struct fib_nh *nh, u32 new, u32 orig) 1644af7d6cceSSabrina Dubroca { 1645af7d6cceSSabrina Dubroca struct fnhe_hash_bucket *bucket; 1646af7d6cceSSabrina Dubroca int i; 1647af7d6cceSSabrina Dubroca 1648af7d6cceSSabrina Dubroca bucket = rcu_dereference_protected(nh->nh_exceptions, 1); 1649af7d6cceSSabrina Dubroca if (!bucket) 1650af7d6cceSSabrina Dubroca return; 1651af7d6cceSSabrina Dubroca 1652af7d6cceSSabrina Dubroca for (i = 0; i < FNHE_HASH_SIZE; i++) { 1653af7d6cceSSabrina Dubroca struct fib_nh_exception *fnhe; 1654af7d6cceSSabrina Dubroca 1655af7d6cceSSabrina Dubroca for (fnhe = rcu_dereference_protected(bucket[i].chain, 1); 1656af7d6cceSSabrina Dubroca fnhe; 1657af7d6cceSSabrina Dubroca fnhe = rcu_dereference_protected(fnhe->fnhe_next, 1)) { 1658af7d6cceSSabrina Dubroca if (fnhe->fnhe_mtu_locked) { 1659af7d6cceSSabrina Dubroca if (new <= fnhe->fnhe_pmtu) { 1660af7d6cceSSabrina Dubroca fnhe->fnhe_pmtu = new; 1661af7d6cceSSabrina Dubroca fnhe->fnhe_mtu_locked = false; 1662af7d6cceSSabrina Dubroca } 1663af7d6cceSSabrina Dubroca } else if (new < fnhe->fnhe_pmtu || 1664af7d6cceSSabrina Dubroca orig == fnhe->fnhe_pmtu) { 1665af7d6cceSSabrina Dubroca fnhe->fnhe_pmtu = new; 1666af7d6cceSSabrina Dubroca } 1667af7d6cceSSabrina Dubroca } 1668af7d6cceSSabrina Dubroca } 1669af7d6cceSSabrina Dubroca } 1670af7d6cceSSabrina Dubroca 1671af7d6cceSSabrina Dubroca void fib_sync_mtu(struct net_device *dev, u32 orig_mtu) 1672af7d6cceSSabrina Dubroca { 1673af7d6cceSSabrina Dubroca unsigned int hash = fib_devindex_hashfn(dev->ifindex); 1674af7d6cceSSabrina Dubroca struct hlist_head *head = &fib_info_devhash[hash]; 1675af7d6cceSSabrina Dubroca struct fib_nh *nh; 1676af7d6cceSSabrina Dubroca 1677af7d6cceSSabrina Dubroca hlist_for_each_entry(nh, head, nh_hash) { 1678b75ed8b1SDavid Ahern if (nh->fib_nh_dev == dev) 1679af7d6cceSSabrina Dubroca nh_update_mtu(nh, dev->mtu, orig_mtu); 1680af7d6cceSSabrina Dubroca } 1681af7d6cceSSabrina Dubroca } 1682af7d6cceSSabrina Dubroca 16834f823defSJulian Anastasov /* Event force Flags Description 16844f823defSJulian Anastasov * NETDEV_CHANGE 0 LINKDOWN Carrier OFF, not for scope host 16854f823defSJulian Anastasov * NETDEV_DOWN 0 LINKDOWN|DEAD Link down, not for scope host 16864f823defSJulian Anastasov * NETDEV_DOWN 1 LINKDOWN|DEAD Last address removed 16874f823defSJulian Anastasov * NETDEV_UNREGISTER 1 LINKDOWN|DEAD Device removed 16884f823defSJulian Anastasov */ 16894f823defSJulian Anastasov int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force) 169085326fa5SDenis V. Lunev { 169185326fa5SDenis V. Lunev int ret = 0; 169285326fa5SDenis V. Lunev int scope = RT_SCOPE_NOWHERE; 16931da177e4SLinus Torvalds struct fib_info *prev_fi = NULL; 16941da177e4SLinus Torvalds unsigned int hash = fib_devindex_hashfn(dev->ifindex); 16951da177e4SLinus Torvalds struct hlist_head *head = &fib_info_devhash[hash]; 16961da177e4SLinus Torvalds struct fib_nh *nh; 16971da177e4SLinus Torvalds 16984f823defSJulian Anastasov if (force) 169985326fa5SDenis V. Lunev scope = -1; 170085326fa5SDenis V. Lunev 1701b67bfe0dSSasha Levin hlist_for_each_entry(nh, head, nh_hash) { 17021da177e4SLinus Torvalds struct fib_info *fi = nh->nh_parent; 17031da177e4SLinus Torvalds int dead; 17041da177e4SLinus Torvalds 17051da177e4SLinus Torvalds BUG_ON(!fi->fib_nhs); 1706b75ed8b1SDavid Ahern if (nh->fib_nh_dev != dev || fi == prev_fi) 17071da177e4SLinus Torvalds continue; 17081da177e4SLinus Torvalds prev_fi = fi; 17091da177e4SLinus Torvalds dead = 0; 17101da177e4SLinus Torvalds change_nexthops(fi) { 1711b75ed8b1SDavid Ahern if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) 17121da177e4SLinus Torvalds dead++; 1713b75ed8b1SDavid Ahern else if (nexthop_nh->fib_nh_dev == dev && 1714b75ed8b1SDavid Ahern nexthop_nh->fib_nh_scope != scope) { 17158a3d0316SAndy Gospodarek switch (event) { 17168a3d0316SAndy Gospodarek case NETDEV_DOWN: 17178a3d0316SAndy Gospodarek case NETDEV_UNREGISTER: 1718b75ed8b1SDavid Ahern nexthop_nh->fib_nh_flags |= RTNH_F_DEAD; 17198a3d0316SAndy Gospodarek /* fall through */ 17208a3d0316SAndy Gospodarek case NETDEV_CHANGE: 1721b75ed8b1SDavid Ahern nexthop_nh->fib_nh_flags |= RTNH_F_LINKDOWN; 17228a3d0316SAndy Gospodarek break; 17238a3d0316SAndy Gospodarek } 1724982acb97SIdo Schimmel call_fib_nh_notifiers(nexthop_nh, 1725982acb97SIdo Schimmel FIB_EVENT_NH_DEL); 17261da177e4SLinus Torvalds dead++; 17271da177e4SLinus Torvalds } 17281da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 17298a3d0316SAndy Gospodarek if (event == NETDEV_UNREGISTER && 1730b75ed8b1SDavid Ahern nexthop_nh->fib_nh_dev == dev) { 17311da177e4SLinus Torvalds dead = fi->fib_nhs; 17321da177e4SLinus Torvalds break; 17331da177e4SLinus Torvalds } 17341da177e4SLinus Torvalds #endif 17351da177e4SLinus Torvalds } endfor_nexthops(fi) 17361da177e4SLinus Torvalds if (dead == fi->fib_nhs) { 17378a3d0316SAndy Gospodarek switch (event) { 17388a3d0316SAndy Gospodarek case NETDEV_DOWN: 17398a3d0316SAndy Gospodarek case NETDEV_UNREGISTER: 17401da177e4SLinus Torvalds fi->fib_flags |= RTNH_F_DEAD; 17418a3d0316SAndy Gospodarek /* fall through */ 17428a3d0316SAndy Gospodarek case NETDEV_CHANGE: 17438a3d0316SAndy Gospodarek fi->fib_flags |= RTNH_F_LINKDOWN; 17448a3d0316SAndy Gospodarek break; 17458a3d0316SAndy Gospodarek } 17461da177e4SLinus Torvalds ret++; 17471da177e4SLinus Torvalds } 17480e884c78SPeter Nørlund 17490e884c78SPeter Nørlund fib_rebalance(fi); 17501da177e4SLinus Torvalds } 17511da177e4SLinus Torvalds 17521da177e4SLinus Torvalds return ret; 17531da177e4SLinus Torvalds } 17541da177e4SLinus Torvalds 17550c838ff1SDavid S. Miller /* Must be invoked inside of an RCU protected region. */ 1756c7b371e3SDavid Ahern static void fib_select_default(const struct flowi4 *flp, struct fib_result *res) 17570c838ff1SDavid S. Miller { 17580c838ff1SDavid S. Miller struct fib_info *fi = NULL, *last_resort = NULL; 175956315f9eSAlexander Duyck struct hlist_head *fa_head = res->fa_head; 17600c838ff1SDavid S. Miller struct fib_table *tb = res->table; 176118a912e9SJulian Anastasov u8 slen = 32 - res->prefixlen; 17620c838ff1SDavid S. Miller int order = -1, last_idx = -1; 17632392debcSJulian Anastasov struct fib_alias *fa, *fa1 = NULL; 17642392debcSJulian Anastasov u32 last_prio = res->fi->fib_priority; 17652392debcSJulian Anastasov u8 last_tos = 0; 17660c838ff1SDavid S. Miller 176756315f9eSAlexander Duyck hlist_for_each_entry_rcu(fa, fa_head, fa_list) { 17680c838ff1SDavid S. Miller struct fib_info *next_fi = fa->fa_info; 17690c838ff1SDavid S. Miller 177018a912e9SJulian Anastasov if (fa->fa_slen != slen) 177118a912e9SJulian Anastasov continue; 17722392debcSJulian Anastasov if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos) 17732392debcSJulian Anastasov continue; 177418a912e9SJulian Anastasov if (fa->tb_id != tb->tb_id) 177518a912e9SJulian Anastasov continue; 17762392debcSJulian Anastasov if (next_fi->fib_priority > last_prio && 17772392debcSJulian Anastasov fa->fa_tos == last_tos) { 17782392debcSJulian Anastasov if (last_tos) 17792392debcSJulian Anastasov continue; 17802392debcSJulian Anastasov break; 17812392debcSJulian Anastasov } 17822392debcSJulian Anastasov if (next_fi->fib_flags & RTNH_F_DEAD) 17832392debcSJulian Anastasov continue; 17842392debcSJulian Anastasov last_tos = fa->fa_tos; 17852392debcSJulian Anastasov last_prio = next_fi->fib_priority; 17862392debcSJulian Anastasov 178737e826c5SDavid S. Miller if (next_fi->fib_scope != res->scope || 17880c838ff1SDavid S. Miller fa->fa_type != RTN_UNICAST) 17890c838ff1SDavid S. Miller continue; 1790b75ed8b1SDavid Ahern if (!next_fi->fib_nh[0].fib_nh_gw4 || 1791b75ed8b1SDavid Ahern next_fi->fib_nh[0].fib_nh_scope != RT_SCOPE_LINK) 17920c838ff1SDavid S. Miller continue; 17930c838ff1SDavid S. Miller 17940c838ff1SDavid S. Miller fib_alias_accessed(fa); 17950c838ff1SDavid S. Miller 179651456b29SIan Morris if (!fi) { 17970c838ff1SDavid S. Miller if (next_fi != res->fi) 17980c838ff1SDavid S. Miller break; 17992392debcSJulian Anastasov fa1 = fa; 18000c838ff1SDavid S. Miller } else if (!fib_detect_death(fi, order, &last_resort, 18012392debcSJulian Anastasov &last_idx, fa1->fa_default)) { 18020c838ff1SDavid S. Miller fib_result_assign(res, fi); 18032392debcSJulian Anastasov fa1->fa_default = order; 18040c838ff1SDavid S. Miller goto out; 18050c838ff1SDavid S. Miller } 18060c838ff1SDavid S. Miller fi = next_fi; 18070c838ff1SDavid S. Miller order++; 18080c838ff1SDavid S. Miller } 18090c838ff1SDavid S. Miller 181051456b29SIan Morris if (order <= 0 || !fi) { 18112392debcSJulian Anastasov if (fa1) 18122392debcSJulian Anastasov fa1->fa_default = -1; 18130c838ff1SDavid S. Miller goto out; 18140c838ff1SDavid S. Miller } 18150c838ff1SDavid S. Miller 18160c838ff1SDavid S. Miller if (!fib_detect_death(fi, order, &last_resort, &last_idx, 18172392debcSJulian Anastasov fa1->fa_default)) { 18180c838ff1SDavid S. Miller fib_result_assign(res, fi); 18192392debcSJulian Anastasov fa1->fa_default = order; 18200c838ff1SDavid S. Miller goto out; 18210c838ff1SDavid S. Miller } 18220c838ff1SDavid S. Miller 18230c838ff1SDavid S. Miller if (last_idx >= 0) 18240c838ff1SDavid S. Miller fib_result_assign(res, last_resort); 18252392debcSJulian Anastasov fa1->fa_default = last_idx; 18260c838ff1SDavid S. Miller out: 182731d40937SEric Dumazet return; 18280c838ff1SDavid S. Miller } 18290c838ff1SDavid S. Miller 18301da177e4SLinus Torvalds /* 18316a31d2a9SEric Dumazet * Dead device goes up. We wake up dead nexthops. 18326a31d2a9SEric Dumazet * It takes sense only on multipath routes. 18331da177e4SLinus Torvalds */ 18348a3d0316SAndy Gospodarek int fib_sync_up(struct net_device *dev, unsigned int nh_flags) 18351da177e4SLinus Torvalds { 18361da177e4SLinus Torvalds struct fib_info *prev_fi; 18371da177e4SLinus Torvalds unsigned int hash; 18381da177e4SLinus Torvalds struct hlist_head *head; 18391da177e4SLinus Torvalds struct fib_nh *nh; 18401da177e4SLinus Torvalds int ret; 18411da177e4SLinus Torvalds 18421da177e4SLinus Torvalds if (!(dev->flags & IFF_UP)) 18431da177e4SLinus Torvalds return 0; 18441da177e4SLinus Torvalds 1845c9b3292eSJulian Anastasov if (nh_flags & RTNH_F_DEAD) { 1846c9b3292eSJulian Anastasov unsigned int flags = dev_get_flags(dev); 1847c9b3292eSJulian Anastasov 1848c9b3292eSJulian Anastasov if (flags & (IFF_RUNNING | IFF_LOWER_UP)) 1849c9b3292eSJulian Anastasov nh_flags |= RTNH_F_LINKDOWN; 1850c9b3292eSJulian Anastasov } 1851c9b3292eSJulian Anastasov 18521da177e4SLinus Torvalds prev_fi = NULL; 18531da177e4SLinus Torvalds hash = fib_devindex_hashfn(dev->ifindex); 18541da177e4SLinus Torvalds head = &fib_info_devhash[hash]; 18551da177e4SLinus Torvalds ret = 0; 18561da177e4SLinus Torvalds 1857b67bfe0dSSasha Levin hlist_for_each_entry(nh, head, nh_hash) { 18581da177e4SLinus Torvalds struct fib_info *fi = nh->nh_parent; 18591da177e4SLinus Torvalds int alive; 18601da177e4SLinus Torvalds 18611da177e4SLinus Torvalds BUG_ON(!fi->fib_nhs); 1862b75ed8b1SDavid Ahern if (nh->fib_nh_dev != dev || fi == prev_fi) 18631da177e4SLinus Torvalds continue; 18641da177e4SLinus Torvalds 18651da177e4SLinus Torvalds prev_fi = fi; 18661da177e4SLinus Torvalds alive = 0; 18671da177e4SLinus Torvalds change_nexthops(fi) { 1868b75ed8b1SDavid Ahern if (!(nexthop_nh->fib_nh_flags & nh_flags)) { 18691da177e4SLinus Torvalds alive++; 18701da177e4SLinus Torvalds continue; 18711da177e4SLinus Torvalds } 1872b75ed8b1SDavid Ahern if (!nexthop_nh->fib_nh_dev || 1873b75ed8b1SDavid Ahern !(nexthop_nh->fib_nh_dev->flags & IFF_UP)) 18741da177e4SLinus Torvalds continue; 1875b75ed8b1SDavid Ahern if (nexthop_nh->fib_nh_dev != dev || 187671fceff0SDavid S. Miller !__in_dev_get_rtnl(dev)) 18771da177e4SLinus Torvalds continue; 18781da177e4SLinus Torvalds alive++; 1879b75ed8b1SDavid Ahern nexthop_nh->fib_nh_flags &= ~nh_flags; 1880982acb97SIdo Schimmel call_fib_nh_notifiers(nexthop_nh, FIB_EVENT_NH_ADD); 18811da177e4SLinus Torvalds } endfor_nexthops(fi) 18821da177e4SLinus Torvalds 18831da177e4SLinus Torvalds if (alive > 0) { 18848a3d0316SAndy Gospodarek fi->fib_flags &= ~nh_flags; 18851da177e4SLinus Torvalds ret++; 18861da177e4SLinus Torvalds } 18870e884c78SPeter Nørlund 18880e884c78SPeter Nørlund fib_rebalance(fi); 18891da177e4SLinus Torvalds } 18901da177e4SLinus Torvalds 18911da177e4SLinus Torvalds return ret; 18921da177e4SLinus Torvalds } 18931da177e4SLinus Torvalds 18948a3d0316SAndy Gospodarek #ifdef CONFIG_IP_ROUTE_MULTIPATH 1895a6db4494SDavid Ahern static bool fib_good_nh(const struct fib_nh *nh) 1896a6db4494SDavid Ahern { 1897a6db4494SDavid Ahern int state = NUD_REACHABLE; 1898a6db4494SDavid Ahern 1899b75ed8b1SDavid Ahern if (nh->fib_nh_scope == RT_SCOPE_LINK) { 1900a6db4494SDavid Ahern struct neighbour *n; 1901a6db4494SDavid Ahern 1902a6db4494SDavid Ahern rcu_read_lock_bh(); 1903a6db4494SDavid Ahern 1904b75ed8b1SDavid Ahern n = __ipv4_neigh_lookup_noref(nh->fib_nh_dev, 1905b75ed8b1SDavid Ahern (__force u32)nh->fib_nh_gw4); 1906a6db4494SDavid Ahern if (n) 1907a6db4494SDavid Ahern state = n->nud_state; 1908a6db4494SDavid Ahern 1909a6db4494SDavid Ahern rcu_read_unlock_bh(); 1910a6db4494SDavid Ahern } 1911a6db4494SDavid Ahern 1912a6db4494SDavid Ahern return !!(state & NUD_VALID); 1913a6db4494SDavid Ahern } 19148a3d0316SAndy Gospodarek 19150e884c78SPeter Nørlund void fib_select_multipath(struct fib_result *res, int hash) 19161da177e4SLinus Torvalds { 19171da177e4SLinus Torvalds struct fib_info *fi = res->fi; 1918a6db4494SDavid Ahern struct net *net = fi->fib_net; 1919a6db4494SDavid Ahern bool first = false; 19201da177e4SLinus Torvalds 1921eba618abSDavid Ahern change_nexthops(fi) { 19226174a30dSXin Long if (net->ipv4.sysctl_fib_multipath_use_neigh) { 1923eba618abSDavid Ahern if (!fib_good_nh(nexthop_nh)) 19240eeb075fSAndy Gospodarek continue; 1925a6db4494SDavid Ahern if (!first) { 1926a6db4494SDavid Ahern res->nh_sel = nhsel; 1927eba618abSDavid Ahern res->nhc = &nexthop_nh->nh_common; 1928a6db4494SDavid Ahern first = true; 1929a6db4494SDavid Ahern } 19306174a30dSXin Long } 19316174a30dSXin Long 1932eba618abSDavid Ahern if (hash > atomic_read(&nexthop_nh->fib_nh_upper_bound)) 19336174a30dSXin Long continue; 19346174a30dSXin Long 19356174a30dSXin Long res->nh_sel = nhsel; 1936eba618abSDavid Ahern res->nhc = &nexthop_nh->nh_common; 19376174a30dSXin Long return; 19381da177e4SLinus Torvalds } endfor_nexthops(fi); 19391da177e4SLinus Torvalds } 19401da177e4SLinus Torvalds #endif 19413ce58d84SDavid Ahern 19423ce58d84SDavid Ahern void fib_select_path(struct net *net, struct fib_result *res, 1943bf4e0a3dSNikolay Aleksandrov struct flowi4 *fl4, const struct sk_buff *skb) 19443ce58d84SDavid Ahern { 19450d876f2cSDavid Ahern if (fl4->flowi4_oif && !(fl4->flowi4_flags & FLOWI_FLAG_SKIP_NH_OIF)) 19460d876f2cSDavid Ahern goto check_saddr; 19477a18c5b9SDavid Ahern 19483ce58d84SDavid Ahern #ifdef CONFIG_IP_ROUTE_MULTIPATH 19490d876f2cSDavid Ahern if (res->fi->fib_nhs > 1) { 19507efc0b6bSDavid Ahern int h = fib_multipath_hash(net, fl4, skb, NULL); 19519920e48bSPaolo Abeni 1952bf4e0a3dSNikolay Aleksandrov fib_select_multipath(res, h); 19533ce58d84SDavid Ahern } 19543ce58d84SDavid Ahern else 19553ce58d84SDavid Ahern #endif 19563ce58d84SDavid Ahern if (!res->prefixlen && 19573ce58d84SDavid Ahern res->table->tb_num_default > 1 && 19580d876f2cSDavid Ahern res->type == RTN_UNICAST) 19593ce58d84SDavid Ahern fib_select_default(fl4, res); 19603ce58d84SDavid Ahern 19610d876f2cSDavid Ahern check_saddr: 19623ce58d84SDavid Ahern if (!fl4->saddr) 1963eba618abSDavid Ahern fl4->saddr = fib_result_prefsrc(net, res); 19643ce58d84SDavid Ahern } 1965