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> 44f21c7bc5SThomas Graf #include <net/netlink.h> 454e902c57SThomas Graf #include <net/nexthop.h> 46571e7226SRoopa Prabhu #include <net/lwtunnel.h> 4704b1d4e5SIdo Schimmel #include <net/fib_notifier.h> 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds #include "fib_lookup.h" 501da177e4SLinus Torvalds 51832b4c5eSStephen Hemminger static DEFINE_SPINLOCK(fib_info_lock); 521da177e4SLinus Torvalds static struct hlist_head *fib_info_hash; 531da177e4SLinus Torvalds static struct hlist_head *fib_info_laddrhash; 54123b9731SDavid S. Miller static unsigned int fib_info_hash_size; 551da177e4SLinus Torvalds static unsigned int fib_info_cnt; 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds #define DEVINDEX_HASHBITS 8 581da177e4SLinus Torvalds #define DEVINDEX_HASHSIZE (1U << DEVINDEX_HASHBITS) 591da177e4SLinus Torvalds static struct hlist_head fib_info_devhash[DEVINDEX_HASHSIZE]; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 621da177e4SLinus Torvalds 636a31d2a9SEric Dumazet #define for_nexthops(fi) { \ 646a31d2a9SEric Dumazet int nhsel; const struct fib_nh *nh; \ 656a31d2a9SEric Dumazet for (nhsel = 0, nh = (fi)->fib_nh; \ 666a31d2a9SEric Dumazet nhsel < (fi)->fib_nhs; \ 676a31d2a9SEric Dumazet nh++, nhsel++) 681da177e4SLinus Torvalds 696a31d2a9SEric Dumazet #define change_nexthops(fi) { \ 706a31d2a9SEric Dumazet int nhsel; struct fib_nh *nexthop_nh; \ 716a31d2a9SEric Dumazet for (nhsel = 0, nexthop_nh = (struct fib_nh *)((fi)->fib_nh); \ 726a31d2a9SEric Dumazet nhsel < (fi)->fib_nhs; \ 736a31d2a9SEric Dumazet nexthop_nh++, nhsel++) 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds #else /* CONFIG_IP_ROUTE_MULTIPATH */ 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds /* Hope, that gcc will optimize it to get rid of dummy loop */ 781da177e4SLinus Torvalds 796a31d2a9SEric Dumazet #define for_nexthops(fi) { \ 806a31d2a9SEric Dumazet int nhsel; const struct fib_nh *nh = (fi)->fib_nh; \ 811da177e4SLinus Torvalds for (nhsel = 0; nhsel < 1; nhsel++) 821da177e4SLinus Torvalds 836a31d2a9SEric Dumazet #define change_nexthops(fi) { \ 846a31d2a9SEric Dumazet int nhsel; \ 856a31d2a9SEric Dumazet struct fib_nh *nexthop_nh = (struct fib_nh *)((fi)->fib_nh); \ 861da177e4SLinus Torvalds for (nhsel = 0; nhsel < 1; nhsel++) 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds #endif /* CONFIG_IP_ROUTE_MULTIPATH */ 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds #define endfor_nexthops(fi) } 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds 933be0686bSDavid S. Miller const struct fib_prop fib_props[RTN_MAX + 1] = { 946a31d2a9SEric Dumazet [RTN_UNSPEC] = { 951da177e4SLinus Torvalds .error = 0, 961da177e4SLinus Torvalds .scope = RT_SCOPE_NOWHERE, 976a31d2a9SEric Dumazet }, 986a31d2a9SEric Dumazet [RTN_UNICAST] = { 991da177e4SLinus Torvalds .error = 0, 1001da177e4SLinus Torvalds .scope = RT_SCOPE_UNIVERSE, 1016a31d2a9SEric Dumazet }, 1026a31d2a9SEric Dumazet [RTN_LOCAL] = { 1031da177e4SLinus Torvalds .error = 0, 1041da177e4SLinus Torvalds .scope = RT_SCOPE_HOST, 1056a31d2a9SEric Dumazet }, 1066a31d2a9SEric Dumazet [RTN_BROADCAST] = { 1071da177e4SLinus Torvalds .error = 0, 1081da177e4SLinus Torvalds .scope = RT_SCOPE_LINK, 1096a31d2a9SEric Dumazet }, 1106a31d2a9SEric Dumazet [RTN_ANYCAST] = { 1111da177e4SLinus Torvalds .error = 0, 1121da177e4SLinus Torvalds .scope = RT_SCOPE_LINK, 1136a31d2a9SEric Dumazet }, 1146a31d2a9SEric Dumazet [RTN_MULTICAST] = { 1151da177e4SLinus Torvalds .error = 0, 1161da177e4SLinus Torvalds .scope = RT_SCOPE_UNIVERSE, 1176a31d2a9SEric Dumazet }, 1186a31d2a9SEric Dumazet [RTN_BLACKHOLE] = { 1191da177e4SLinus Torvalds .error = -EINVAL, 1201da177e4SLinus Torvalds .scope = RT_SCOPE_UNIVERSE, 1216a31d2a9SEric Dumazet }, 1226a31d2a9SEric Dumazet [RTN_UNREACHABLE] = { 1231da177e4SLinus Torvalds .error = -EHOSTUNREACH, 1241da177e4SLinus Torvalds .scope = RT_SCOPE_UNIVERSE, 1256a31d2a9SEric Dumazet }, 1266a31d2a9SEric Dumazet [RTN_PROHIBIT] = { 1271da177e4SLinus Torvalds .error = -EACCES, 1281da177e4SLinus Torvalds .scope = RT_SCOPE_UNIVERSE, 1296a31d2a9SEric Dumazet }, 1306a31d2a9SEric Dumazet [RTN_THROW] = { 1311da177e4SLinus Torvalds .error = -EAGAIN, 1321da177e4SLinus Torvalds .scope = RT_SCOPE_UNIVERSE, 1336a31d2a9SEric Dumazet }, 1346a31d2a9SEric Dumazet [RTN_NAT] = { 1351da177e4SLinus Torvalds .error = -EINVAL, 1361da177e4SLinus Torvalds .scope = RT_SCOPE_NOWHERE, 1376a31d2a9SEric Dumazet }, 1386a31d2a9SEric Dumazet [RTN_XRESOLVE] = { 1391da177e4SLinus Torvalds .error = -EINVAL, 1401da177e4SLinus Torvalds .scope = RT_SCOPE_NOWHERE, 1416a31d2a9SEric Dumazet }, 1421da177e4SLinus Torvalds }; 1431da177e4SLinus Torvalds 144c5038a83SDavid S. Miller static void rt_fibinfo_free(struct rtable __rcu **rtp) 14554764bb6SEric Dumazet { 14654764bb6SEric Dumazet struct rtable *rt = rcu_dereference_protected(*rtp, 1); 14754764bb6SEric Dumazet 14854764bb6SEric Dumazet if (!rt) 14954764bb6SEric Dumazet return; 15054764bb6SEric Dumazet 15154764bb6SEric Dumazet /* Not even needed : RCU_INIT_POINTER(*rtp, NULL); 15254764bb6SEric Dumazet * because we waited an RCU grace period before calling 15354764bb6SEric Dumazet * free_fib_info_rcu() 15454764bb6SEric Dumazet */ 15554764bb6SEric Dumazet 15695c47f9cSWei Wang dst_dev_put(&rt->dst); 157b838d5e1SWei Wang dst_release_immediate(&rt->dst); 15854764bb6SEric Dumazet } 15954764bb6SEric Dumazet 160c5038a83SDavid S. Miller static void free_nh_exceptions(struct fib_nh *nh) 161c5038a83SDavid S. Miller { 162caa41527SEric Dumazet struct fnhe_hash_bucket *hash; 163c5038a83SDavid S. Miller int i; 164c5038a83SDavid S. Miller 165caa41527SEric Dumazet hash = rcu_dereference_protected(nh->nh_exceptions, 1); 166caa41527SEric Dumazet if (!hash) 167caa41527SEric Dumazet return; 168c5038a83SDavid S. Miller for (i = 0; i < FNHE_HASH_SIZE; i++) { 169c5038a83SDavid S. Miller struct fib_nh_exception *fnhe; 170c5038a83SDavid S. Miller 171c5038a83SDavid S. Miller fnhe = rcu_dereference_protected(hash[i].chain, 1); 172c5038a83SDavid S. Miller while (fnhe) { 173c5038a83SDavid S. Miller struct fib_nh_exception *next; 174c5038a83SDavid S. Miller 175c5038a83SDavid S. Miller next = rcu_dereference_protected(fnhe->fnhe_next, 1); 176c5038a83SDavid S. Miller 1772ffae99dSTimo Teräs rt_fibinfo_free(&fnhe->fnhe_rth_input); 1782ffae99dSTimo Teräs rt_fibinfo_free(&fnhe->fnhe_rth_output); 179c5038a83SDavid S. Miller 180c5038a83SDavid S. Miller kfree(fnhe); 181c5038a83SDavid S. Miller 182c5038a83SDavid S. Miller fnhe = next; 183c5038a83SDavid S. Miller } 184c5038a83SDavid S. Miller } 185c5038a83SDavid S. Miller kfree(hash); 186c5038a83SDavid S. Miller } 187c5038a83SDavid S. Miller 188c5038a83SDavid S. Miller static void rt_fibinfo_free_cpus(struct rtable __rcu * __percpu *rtp) 189d26b3a7cSEric Dumazet { 190d26b3a7cSEric Dumazet int cpu; 191d26b3a7cSEric Dumazet 192d26b3a7cSEric Dumazet if (!rtp) 193d26b3a7cSEric Dumazet return; 194d26b3a7cSEric Dumazet 195d26b3a7cSEric Dumazet for_each_possible_cpu(cpu) { 196d26b3a7cSEric Dumazet struct rtable *rt; 197d26b3a7cSEric Dumazet 198d26b3a7cSEric Dumazet rt = rcu_dereference_protected(*per_cpu_ptr(rtp, cpu), 1); 1990830106cSWei Wang if (rt) { 20095c47f9cSWei Wang dst_dev_put(&rt->dst); 201b838d5e1SWei Wang dst_release_immediate(&rt->dst); 202d26b3a7cSEric Dumazet } 2030830106cSWei Wang } 204d26b3a7cSEric Dumazet free_percpu(rtp); 205d26b3a7cSEric Dumazet } 206d26b3a7cSEric Dumazet 207979e276eSDavid Ahern void fib_nh_common_release(struct fib_nh_common *nhc) 208979e276eSDavid Ahern { 209979e276eSDavid Ahern if (nhc->nhc_dev) 210979e276eSDavid Ahern dev_put(nhc->nhc_dev); 211979e276eSDavid Ahern 212979e276eSDavid Ahern lwtstate_put(nhc->nhc_lwtstate); 213979e276eSDavid Ahern } 214979e276eSDavid Ahern EXPORT_SYMBOL_GPL(fib_nh_common_release); 215979e276eSDavid Ahern 216faa041a4SDavid Ahern void fib_nh_release(struct net *net, struct fib_nh *fib_nh) 217faa041a4SDavid Ahern { 218faa041a4SDavid Ahern #ifdef CONFIG_IP_ROUTE_CLASSID 219faa041a4SDavid Ahern if (fib_nh->nh_tclassid) 220faa041a4SDavid Ahern net->ipv4.fib_num_tclassid_users--; 221faa041a4SDavid Ahern #endif 222979e276eSDavid Ahern fib_nh_common_release(&fib_nh->nh_common); 223faa041a4SDavid Ahern free_nh_exceptions(fib_nh); 224faa041a4SDavid Ahern rt_fibinfo_free_cpus(fib_nh->nh_pcpu_rth_output); 225faa041a4SDavid Ahern rt_fibinfo_free(&fib_nh->nh_rth_input); 226faa041a4SDavid Ahern } 227faa041a4SDavid Ahern 2281da177e4SLinus Torvalds /* Release a nexthop info record */ 22919c1ea14SYan, Zheng static void free_fib_info_rcu(struct rcu_head *head) 23019c1ea14SYan, Zheng { 23119c1ea14SYan, Zheng struct fib_info *fi = container_of(head, struct fib_info, rcu); 23219c1ea14SYan, Zheng 233e49cc0daSYanmin Zhang change_nexthops(fi) { 234faa041a4SDavid Ahern fib_nh_release(fi->fib_net, nexthop_nh); 235e49cc0daSYanmin Zhang } endfor_nexthops(fi); 236e49cc0daSYanmin Zhang 237cc5f0eb2SDavid Ahern ip_fib_metrics_put(fi->fib_metrics); 238cc5f0eb2SDavid Ahern 23919c1ea14SYan, Zheng kfree(fi); 24019c1ea14SYan, Zheng } 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds void free_fib_info(struct fib_info *fi) 2431da177e4SLinus Torvalds { 2441da177e4SLinus Torvalds if (fi->fib_dead == 0) { 245058bd4d2SJoe Perches pr_warn("Freeing alive fib_info %p\n", fi); 2461da177e4SLinus Torvalds return; 2471da177e4SLinus Torvalds } 2481da177e4SLinus Torvalds fib_info_cnt--; 249faa041a4SDavid Ahern 25019c1ea14SYan, Zheng call_rcu(&fi->rcu, free_fib_info_rcu); 2511da177e4SLinus Torvalds } 252b423cb10SIdo Schimmel EXPORT_SYMBOL_GPL(free_fib_info); 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds void fib_release_info(struct fib_info *fi) 2551da177e4SLinus Torvalds { 256832b4c5eSStephen Hemminger spin_lock_bh(&fib_info_lock); 2571da177e4SLinus Torvalds if (fi && --fi->fib_treeref == 0) { 2581da177e4SLinus Torvalds hlist_del(&fi->fib_hash); 2591da177e4SLinus Torvalds if (fi->fib_prefsrc) 2601da177e4SLinus Torvalds hlist_del(&fi->fib_lhash); 2611da177e4SLinus Torvalds change_nexthops(fi) { 262b75ed8b1SDavid Ahern if (!nexthop_nh->fib_nh_dev) 2631da177e4SLinus Torvalds continue; 26471fceff0SDavid S. Miller hlist_del(&nexthop_nh->nh_hash); 2651da177e4SLinus Torvalds } endfor_nexthops(fi) 2661da177e4SLinus Torvalds fi->fib_dead = 1; 2671da177e4SLinus Torvalds fib_info_put(fi); 2681da177e4SLinus Torvalds } 269832b4c5eSStephen Hemminger spin_unlock_bh(&fib_info_lock); 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds 2726a31d2a9SEric Dumazet static inline int nh_comp(const struct fib_info *fi, const struct fib_info *ofi) 2731da177e4SLinus Torvalds { 2741da177e4SLinus Torvalds const struct fib_nh *onh = ofi->fib_nh; 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds for_nexthops(fi) { 277b75ed8b1SDavid Ahern if (nh->fib_nh_oif != onh->fib_nh_oif || 278b75ed8b1SDavid Ahern nh->fib_nh_gw4 != onh->fib_nh_gw4 || 279b75ed8b1SDavid Ahern nh->fib_nh_scope != onh->fib_nh_scope || 2801da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 281b75ed8b1SDavid Ahern nh->fib_nh_weight != onh->fib_nh_weight || 2821da177e4SLinus Torvalds #endif 283c7066f70SPatrick McHardy #ifdef CONFIG_IP_ROUTE_CLASSID 2841da177e4SLinus Torvalds nh->nh_tclassid != onh->nh_tclassid || 2851da177e4SLinus Torvalds #endif 286b75ed8b1SDavid Ahern lwtunnel_cmp_encap(nh->fib_nh_lws, onh->fib_nh_lws) || 287b75ed8b1SDavid Ahern ((nh->fib_nh_flags ^ onh->fib_nh_flags) & ~RTNH_COMPARE_MASK)) 2881da177e4SLinus Torvalds return -1; 2891da177e4SLinus Torvalds onh++; 2901da177e4SLinus Torvalds } endfor_nexthops(fi); 2911da177e4SLinus Torvalds return 0; 2921da177e4SLinus Torvalds } 2931da177e4SLinus Torvalds 29488ebc72fSDavid S. Miller static inline unsigned int fib_devindex_hashfn(unsigned int val) 29588ebc72fSDavid S. Miller { 29688ebc72fSDavid S. Miller unsigned int mask = DEVINDEX_HASHSIZE - 1; 29788ebc72fSDavid S. Miller 29888ebc72fSDavid S. Miller return (val ^ 29988ebc72fSDavid S. Miller (val >> DEVINDEX_HASHBITS) ^ 30088ebc72fSDavid S. Miller (val >> (DEVINDEX_HASHBITS * 2))) & mask; 30188ebc72fSDavid S. Miller } 30288ebc72fSDavid S. Miller 3031da177e4SLinus Torvalds static inline unsigned int fib_info_hashfn(const struct fib_info *fi) 3041da177e4SLinus Torvalds { 305123b9731SDavid S. Miller unsigned int mask = (fib_info_hash_size - 1); 3061da177e4SLinus Torvalds unsigned int val = fi->fib_nhs; 3071da177e4SLinus Torvalds 30837e826c5SDavid S. Miller val ^= (fi->fib_protocol << 8) | fi->fib_scope; 30981f7bf6cSAl Viro val ^= (__force u32)fi->fib_prefsrc; 3101da177e4SLinus Torvalds val ^= fi->fib_priority; 31188ebc72fSDavid S. Miller for_nexthops(fi) { 312b75ed8b1SDavid Ahern val ^= fib_devindex_hashfn(nh->fib_nh_oif); 31388ebc72fSDavid S. Miller } endfor_nexthops(fi) 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds return (val ^ (val >> 7) ^ (val >> 12)) & mask; 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 3181da177e4SLinus Torvalds static struct fib_info *fib_find_info(const struct fib_info *nfi) 3191da177e4SLinus Torvalds { 3201da177e4SLinus Torvalds struct hlist_head *head; 3211da177e4SLinus Torvalds struct fib_info *fi; 3221da177e4SLinus Torvalds unsigned int hash; 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds hash = fib_info_hashfn(nfi); 3251da177e4SLinus Torvalds head = &fib_info_hash[hash]; 3261da177e4SLinus Torvalds 327b67bfe0dSSasha Levin hlist_for_each_entry(fi, head, fib_hash) { 32809ad9bc7SOctavian Purdila if (!net_eq(fi->fib_net, nfi->fib_net)) 3294814bdbdSDenis V. Lunev continue; 3301da177e4SLinus Torvalds if (fi->fib_nhs != nfi->fib_nhs) 3311da177e4SLinus Torvalds continue; 3321da177e4SLinus Torvalds if (nfi->fib_protocol == fi->fib_protocol && 33337e826c5SDavid S. Miller nfi->fib_scope == fi->fib_scope && 3341da177e4SLinus Torvalds nfi->fib_prefsrc == fi->fib_prefsrc && 3351da177e4SLinus Torvalds nfi->fib_priority == fi->fib_priority && 336f4ef85bbSEric Dumazet nfi->fib_type == fi->fib_type && 3371da177e4SLinus Torvalds memcmp(nfi->fib_metrics, fi->fib_metrics, 338fcd13f42SEric Dumazet sizeof(u32) * RTAX_MAX) == 0 && 3398a3d0316SAndy Gospodarek !((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_COMPARE_MASK) && 3401da177e4SLinus Torvalds (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0)) 3411da177e4SLinus Torvalds return fi; 3421da177e4SLinus Torvalds } 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds return NULL; 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds /* Check, that the gateway is already configured. 3486a31d2a9SEric Dumazet * Used only by redirect accept routine. 3491da177e4SLinus Torvalds */ 350d878e72eSAl Viro int ip_fib_check_default(__be32 gw, struct net_device *dev) 3511da177e4SLinus Torvalds { 3521da177e4SLinus Torvalds struct hlist_head *head; 3531da177e4SLinus Torvalds struct fib_nh *nh; 3541da177e4SLinus Torvalds unsigned int hash; 3551da177e4SLinus Torvalds 356832b4c5eSStephen Hemminger spin_lock(&fib_info_lock); 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds hash = fib_devindex_hashfn(dev->ifindex); 3591da177e4SLinus Torvalds head = &fib_info_devhash[hash]; 360b67bfe0dSSasha Levin hlist_for_each_entry(nh, head, nh_hash) { 361b75ed8b1SDavid Ahern if (nh->fib_nh_dev == dev && 362b75ed8b1SDavid Ahern nh->fib_nh_gw4 == gw && 363b75ed8b1SDavid Ahern !(nh->fib_nh_flags & RTNH_F_DEAD)) { 364832b4c5eSStephen Hemminger spin_unlock(&fib_info_lock); 3651da177e4SLinus Torvalds return 0; 3661da177e4SLinus Torvalds } 3671da177e4SLinus Torvalds } 3681da177e4SLinus Torvalds 369832b4c5eSStephen Hemminger spin_unlock(&fib_info_lock); 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds return -1; 3721da177e4SLinus Torvalds } 3731da177e4SLinus Torvalds 374339bf98fSThomas Graf static inline size_t fib_nlmsg_size(struct fib_info *fi) 375339bf98fSThomas Graf { 376339bf98fSThomas Graf size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg)) 377339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 378339bf98fSThomas Graf + nla_total_size(4) /* RTA_DST */ 379339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 380ea697639SDaniel Borkmann + nla_total_size(4) /* RTA_PREFSRC */ 381ea697639SDaniel Borkmann + nla_total_size(TCP_CA_NAME_MAX); /* RTAX_CC_ALGO */ 382339bf98fSThomas Graf 383339bf98fSThomas Graf /* space for nested metrics */ 384339bf98fSThomas Graf payload += nla_total_size((RTAX_MAX * nla_total_size(4))); 385339bf98fSThomas Graf 386339bf98fSThomas Graf if (fi->fib_nhs) { 387571e7226SRoopa Prabhu size_t nh_encapsize = 0; 388339bf98fSThomas Graf /* Also handles the special case fib_nhs == 1 */ 389339bf98fSThomas Graf 390339bf98fSThomas Graf /* each nexthop is packed in an attribute */ 391339bf98fSThomas Graf size_t nhsize = nla_total_size(sizeof(struct rtnexthop)); 392339bf98fSThomas Graf 393339bf98fSThomas Graf /* may contain flow and gateway attribute */ 394339bf98fSThomas Graf nhsize += 2 * nla_total_size(4); 395339bf98fSThomas Graf 396571e7226SRoopa Prabhu /* grab encap info */ 397571e7226SRoopa Prabhu for_nexthops(fi) { 398b75ed8b1SDavid Ahern if (nh->fib_nh_lws) { 399571e7226SRoopa Prabhu /* RTA_ENCAP_TYPE */ 400571e7226SRoopa Prabhu nh_encapsize += lwtunnel_get_encap_size( 401b75ed8b1SDavid Ahern nh->fib_nh_lws); 402571e7226SRoopa Prabhu /* RTA_ENCAP */ 403571e7226SRoopa Prabhu nh_encapsize += nla_total_size(2); 404571e7226SRoopa Prabhu } 405571e7226SRoopa Prabhu } endfor_nexthops(fi); 406571e7226SRoopa Prabhu 407339bf98fSThomas Graf /* all nexthops are packed in a nested attribute */ 408571e7226SRoopa Prabhu payload += nla_total_size((fi->fib_nhs * nhsize) + 409571e7226SRoopa Prabhu nh_encapsize); 410571e7226SRoopa Prabhu 411339bf98fSThomas Graf } 412339bf98fSThomas Graf 413339bf98fSThomas Graf return payload; 414339bf98fSThomas Graf } 415339bf98fSThomas Graf 41681f7bf6cSAl Viro void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, 4179877b253SJoe Perches int dst_len, u32 tb_id, const struct nl_info *info, 418b8f55831SMilan Kocian unsigned int nlm_flags) 4191da177e4SLinus Torvalds { 4201da177e4SLinus Torvalds struct sk_buff *skb; 4214e902c57SThomas Graf u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; 422f21c7bc5SThomas Graf int err = -ENOBUFS; 4231da177e4SLinus Torvalds 424339bf98fSThomas Graf skb = nlmsg_new(fib_nlmsg_size(fa->fa_info), GFP_KERNEL); 42551456b29SIan Morris if (!skb) 426f21c7bc5SThomas Graf goto errout; 4271da177e4SLinus Torvalds 42815e47304SEric W. Biederman err = fib_dump_info(skb, info->portid, seq, event, tb_id, 42937e826c5SDavid S. Miller fa->fa_type, key, dst_len, 430b8f55831SMilan Kocian fa->fa_tos, fa->fa_info, nlm_flags); 43126932566SPatrick McHardy if (err < 0) { 43226932566SPatrick McHardy /* -EMSGSIZE implies BUG in fib_nlmsg_size() */ 43326932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 43426932566SPatrick McHardy kfree_skb(skb); 43526932566SPatrick McHardy goto errout; 43626932566SPatrick McHardy } 43715e47304SEric W. Biederman rtnl_notify(skb, info->nl_net, info->portid, RTNLGRP_IPV4_ROUTE, 4384e902c57SThomas Graf info->nlh, GFP_KERNEL); 4391ce85fe4SPablo Neira Ayuso return; 440f21c7bc5SThomas Graf errout: 441f21c7bc5SThomas Graf if (err < 0) 4424d1169c1SDenis V. Lunev rtnl_set_sk_err(info->nl_net, RTNLGRP_IPV4_ROUTE, err); 4431da177e4SLinus Torvalds } 4441da177e4SLinus Torvalds 445c9cb6b6eSStephen Hemminger static int fib_detect_death(struct fib_info *fi, int order, 446c9cb6b6eSStephen Hemminger struct fib_info **last_resort, int *last_idx, 447c9cb6b6eSStephen Hemminger int dflt) 4481da177e4SLinus Torvalds { 4491da177e4SLinus Torvalds struct neighbour *n; 4501da177e4SLinus Torvalds int state = NUD_NONE; 4511da177e4SLinus Torvalds 452b75ed8b1SDavid Ahern n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].fib_nh_gw4, fi->fib_dev); 4531da177e4SLinus Torvalds if (n) { 4541da177e4SLinus Torvalds state = n->nud_state; 4551da177e4SLinus Torvalds neigh_release(n); 45688f64320SJulian Anastasov } else { 45788f64320SJulian Anastasov return 0; 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds if (state == NUD_REACHABLE) 4601da177e4SLinus Torvalds return 0; 461c17860a0SDenis V. Lunev if ((state & NUD_VALID) && order != dflt) 4621da177e4SLinus Torvalds return 0; 4631da177e4SLinus Torvalds if ((state & NUD_VALID) || 46488f64320SJulian Anastasov (*last_idx < 0 && order > dflt && state != NUD_INCOMPLETE)) { 4651da177e4SLinus Torvalds *last_resort = fi; 4661da177e4SLinus Torvalds *last_idx = order; 4671da177e4SLinus Torvalds } 4681da177e4SLinus Torvalds return 1; 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds 471979e276eSDavid Ahern int fib_nh_common_init(struct fib_nh_common *nhc, struct nlattr *encap, 472979e276eSDavid Ahern u16 encap_type, void *cfg, gfp_t gfp_flags, 473979e276eSDavid Ahern struct netlink_ext_ack *extack) 474979e276eSDavid Ahern { 475979e276eSDavid Ahern if (encap) { 476979e276eSDavid Ahern struct lwtunnel_state *lwtstate; 477979e276eSDavid Ahern int err; 478979e276eSDavid Ahern 479979e276eSDavid Ahern if (encap_type == LWTUNNEL_ENCAP_NONE) { 480979e276eSDavid Ahern NL_SET_ERR_MSG(extack, "LWT encap type not specified"); 481979e276eSDavid Ahern return -EINVAL; 482979e276eSDavid Ahern } 483979e276eSDavid Ahern err = lwtunnel_build_state(encap_type, encap, nhc->nhc_family, 484979e276eSDavid Ahern cfg, &lwtstate, extack); 485979e276eSDavid Ahern if (err) 486979e276eSDavid Ahern return err; 487979e276eSDavid Ahern 488979e276eSDavid Ahern nhc->nhc_lwtstate = lwtstate_get(lwtstate); 489979e276eSDavid Ahern } 490979e276eSDavid Ahern 491979e276eSDavid Ahern return 0; 492979e276eSDavid Ahern } 493979e276eSDavid Ahern EXPORT_SYMBOL_GPL(fib_nh_common_init); 494979e276eSDavid Ahern 495e4516ef6SDavid Ahern int fib_nh_init(struct net *net, struct fib_nh *nh, 496e4516ef6SDavid Ahern struct fib_config *cfg, int nh_weight, 497e4516ef6SDavid Ahern struct netlink_ext_ack *extack) 498e4516ef6SDavid Ahern { 499e4516ef6SDavid Ahern int err = -ENOMEM; 500e4516ef6SDavid Ahern 501f1741730SDavid Ahern nh->fib_nh_family = AF_INET; 502f1741730SDavid Ahern 503e4516ef6SDavid Ahern nh->nh_pcpu_rth_output = alloc_percpu(struct rtable __rcu *); 504e4516ef6SDavid Ahern if (!nh->nh_pcpu_rth_output) 505e4516ef6SDavid Ahern goto err_out; 506e4516ef6SDavid Ahern 507979e276eSDavid Ahern err = fib_nh_common_init(&nh->nh_common, cfg->fc_encap, 508979e276eSDavid Ahern cfg->fc_encap_type, cfg, GFP_KERNEL, extack); 509e4516ef6SDavid Ahern if (err) 510979e276eSDavid Ahern goto init_failure; 511e4516ef6SDavid Ahern 512b75ed8b1SDavid Ahern nh->fib_nh_oif = cfg->fc_oif; 513f1741730SDavid Ahern if (cfg->fc_gw) { 514b75ed8b1SDavid Ahern nh->fib_nh_gw4 = cfg->fc_gw; 515f1741730SDavid Ahern nh->fib_nh_has_gw = 1; 516f1741730SDavid Ahern } 517b75ed8b1SDavid Ahern nh->fib_nh_flags = cfg->fc_flags; 518e4516ef6SDavid Ahern 519e4516ef6SDavid Ahern #ifdef CONFIG_IP_ROUTE_CLASSID 520e4516ef6SDavid Ahern nh->nh_tclassid = cfg->fc_flow; 521e4516ef6SDavid Ahern if (nh->nh_tclassid) 522e4516ef6SDavid Ahern net->ipv4.fib_num_tclassid_users++; 523e4516ef6SDavid Ahern #endif 524e4516ef6SDavid Ahern #ifdef CONFIG_IP_ROUTE_MULTIPATH 525b75ed8b1SDavid Ahern nh->fib_nh_weight = nh_weight; 526e4516ef6SDavid Ahern #endif 527e4516ef6SDavid Ahern return 0; 528e4516ef6SDavid Ahern 529979e276eSDavid Ahern init_failure: 530e4516ef6SDavid Ahern rt_fibinfo_free_cpus(nh->nh_pcpu_rth_output); 531e4516ef6SDavid Ahern nh->nh_pcpu_rth_output = NULL; 532e4516ef6SDavid Ahern err_out: 533e4516ef6SDavid Ahern return err; 534e4516ef6SDavid Ahern } 535e4516ef6SDavid Ahern 5361da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 5371da177e4SLinus Torvalds 5386d8422a1SDavid Ahern static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining, 5396d8422a1SDavid Ahern struct netlink_ext_ack *extack) 5401da177e4SLinus Torvalds { 5411da177e4SLinus Torvalds int nhs = 0; 5421da177e4SLinus Torvalds 5434e902c57SThomas Graf while (rtnh_ok(rtnh, remaining)) { 5441da177e4SLinus Torvalds nhs++; 5454e902c57SThomas Graf rtnh = rtnh_next(rtnh, &remaining); 5461da177e4SLinus Torvalds } 5471da177e4SLinus Torvalds 5484e902c57SThomas Graf /* leftover implies invalid nexthop configuration, discard it */ 549c3ab2b4eSDavid Ahern if (remaining > 0) { 550c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 551c3ab2b4eSDavid Ahern "Invalid nexthop configuration - extra data after nexthops"); 552c3ab2b4eSDavid Ahern nhs = 0; 553c3ab2b4eSDavid Ahern } 554c3ab2b4eSDavid Ahern 555c3ab2b4eSDavid Ahern return nhs; 5564e902c57SThomas Graf } 5571da177e4SLinus Torvalds 5584e902c57SThomas Graf static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, 5596d8422a1SDavid Ahern int remaining, struct fib_config *cfg, 5606d8422a1SDavid Ahern struct netlink_ext_ack *extack) 5614e902c57SThomas Graf { 562e4516ef6SDavid Ahern struct net *net = fi->fib_net; 563e4516ef6SDavid Ahern struct fib_config fib_cfg; 564571e7226SRoopa Prabhu int ret; 565571e7226SRoopa Prabhu 5661da177e4SLinus Torvalds change_nexthops(fi) { 5674e902c57SThomas Graf int attrlen; 5684e902c57SThomas Graf 569e4516ef6SDavid Ahern memset(&fib_cfg, 0, sizeof(fib_cfg)); 570e4516ef6SDavid Ahern 571c3ab2b4eSDavid Ahern if (!rtnh_ok(rtnh, remaining)) { 572c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 573c3ab2b4eSDavid Ahern "Invalid nexthop configuration - extra data after nexthop"); 5741da177e4SLinus Torvalds return -EINVAL; 575c3ab2b4eSDavid Ahern } 5764e902c57SThomas Graf 577c3ab2b4eSDavid Ahern if (rtnh->rtnh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) { 578c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 579c3ab2b4eSDavid Ahern "Invalid flags for nexthop - can not contain DEAD or LINKDOWN"); 58080610229SJulian Anastasov return -EINVAL; 581c3ab2b4eSDavid Ahern } 58280610229SJulian Anastasov 583e4516ef6SDavid Ahern fib_cfg.fc_flags = (cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags; 584e4516ef6SDavid Ahern fib_cfg.fc_oif = rtnh->rtnh_ifindex; 5854e902c57SThomas Graf 5864e902c57SThomas Graf attrlen = rtnh_attrlen(rtnh); 5874e902c57SThomas Graf if (attrlen > 0) { 5884e902c57SThomas Graf struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 5894e902c57SThomas Graf 5904e902c57SThomas Graf nla = nla_find(attrs, attrlen, RTA_GATEWAY); 591e4516ef6SDavid Ahern if (nla) 592e4516ef6SDavid Ahern fib_cfg.fc_gw = nla_get_in_addr(nla); 593571e7226SRoopa Prabhu 594e4516ef6SDavid Ahern nla = nla_find(attrs, attrlen, RTA_FLOW); 595e4516ef6SDavid Ahern if (nla) 596e4516ef6SDavid Ahern fib_cfg.fc_flow = nla_get_u32(nla); 597e4516ef6SDavid Ahern 598e4516ef6SDavid Ahern fib_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 599e4516ef6SDavid Ahern nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 600e4516ef6SDavid Ahern if (nla) 601e4516ef6SDavid Ahern fib_cfg.fc_encap_type = nla_get_u16(nla); 602c3ab2b4eSDavid Ahern } 60330357d7dSDavid Ahern 604e4516ef6SDavid Ahern ret = fib_nh_init(net, nexthop_nh, &fib_cfg, 605e4516ef6SDavid Ahern rtnh->rtnh_hops + 1, extack); 606571e7226SRoopa Prabhu if (ret) 607571e7226SRoopa Prabhu goto errout; 6084e902c57SThomas Graf 6094e902c57SThomas Graf rtnh = rtnh_next(rtnh, &remaining); 6101da177e4SLinus Torvalds } endfor_nexthops(fi); 6114e902c57SThomas Graf 612571e7226SRoopa Prabhu ret = -EINVAL; 613b75ed8b1SDavid Ahern if (cfg->fc_oif && fi->fib_nh->fib_nh_oif != cfg->fc_oif) { 614e4516ef6SDavid Ahern NL_SET_ERR_MSG(extack, 615e4516ef6SDavid Ahern "Nexthop device index does not match RTA_OIF"); 616e4516ef6SDavid Ahern goto errout; 617e4516ef6SDavid Ahern } 618b75ed8b1SDavid Ahern if (cfg->fc_gw && fi->fib_nh->fib_nh_gw4 != cfg->fc_gw) { 619e4516ef6SDavid Ahern NL_SET_ERR_MSG(extack, 620e4516ef6SDavid Ahern "Nexthop gateway does not match RTA_GATEWAY"); 621e4516ef6SDavid Ahern goto errout; 622e4516ef6SDavid Ahern } 623e4516ef6SDavid Ahern #ifdef CONFIG_IP_ROUTE_CLASSID 624e4516ef6SDavid Ahern if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow) { 625e4516ef6SDavid Ahern NL_SET_ERR_MSG(extack, 626e4516ef6SDavid Ahern "Nexthop class id does not match RTA_FLOW"); 627e4516ef6SDavid Ahern goto errout; 628e4516ef6SDavid Ahern } 629e4516ef6SDavid Ahern #endif 630e4516ef6SDavid Ahern ret = 0; 631571e7226SRoopa Prabhu errout: 632571e7226SRoopa Prabhu return ret; 6331da177e4SLinus Torvalds } 6341da177e4SLinus Torvalds 6350e884c78SPeter Nørlund static void fib_rebalance(struct fib_info *fi) 6360e884c78SPeter Nørlund { 6370e884c78SPeter Nørlund int total; 6380e884c78SPeter Nørlund int w; 6390e884c78SPeter Nørlund 6400e884c78SPeter Nørlund if (fi->fib_nhs < 2) 6410e884c78SPeter Nørlund return; 6420e884c78SPeter Nørlund 6430e884c78SPeter Nørlund total = 0; 6440e884c78SPeter Nørlund for_nexthops(fi) { 645b75ed8b1SDavid Ahern if (nh->fib_nh_flags & RTNH_F_DEAD) 6460e884c78SPeter Nørlund continue; 6470e884c78SPeter Nørlund 648b75ed8b1SDavid Ahern if (ip_ignore_linkdown(nh->fib_nh_dev) && 649b75ed8b1SDavid Ahern nh->fib_nh_flags & RTNH_F_LINKDOWN) 6500e884c78SPeter Nørlund continue; 6510e884c78SPeter Nørlund 652b75ed8b1SDavid Ahern total += nh->fib_nh_weight; 6530e884c78SPeter Nørlund } endfor_nexthops(fi); 6540e884c78SPeter Nørlund 6550e884c78SPeter Nørlund w = 0; 6560e884c78SPeter Nørlund change_nexthops(fi) { 6570e884c78SPeter Nørlund int upper_bound; 6580e884c78SPeter Nørlund 659b75ed8b1SDavid Ahern if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) { 6600e884c78SPeter Nørlund upper_bound = -1; 661b75ed8b1SDavid Ahern } else if (ip_ignore_linkdown(nexthop_nh->fib_nh_dev) && 662b75ed8b1SDavid Ahern nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) { 6630e884c78SPeter Nørlund upper_bound = -1; 6640e884c78SPeter Nørlund } else { 665b75ed8b1SDavid Ahern w += nexthop_nh->fib_nh_weight; 6660a837fe4SPeter Nørlund upper_bound = DIV_ROUND_CLOSEST_ULL((u64)w << 31, 6670e884c78SPeter Nørlund total) - 1; 6680e884c78SPeter Nørlund } 6690e884c78SPeter Nørlund 670b75ed8b1SDavid Ahern atomic_set(&nexthop_nh->fib_nh_upper_bound, upper_bound); 6710e884c78SPeter Nørlund } endfor_nexthops(fi); 6720e884c78SPeter Nørlund } 6730e884c78SPeter Nørlund #else /* CONFIG_IP_ROUTE_MULTIPATH */ 6740e884c78SPeter Nørlund 6758373c6c8SDavid Ahern static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, 6768373c6c8SDavid Ahern int remaining, struct fib_config *cfg, 6778373c6c8SDavid Ahern struct netlink_ext_ack *extack) 6788373c6c8SDavid Ahern { 6798373c6c8SDavid Ahern NL_SET_ERR_MSG(extack, "Multipath support not enabled in kernel"); 6808373c6c8SDavid Ahern 6818373c6c8SDavid Ahern return -EINVAL; 6828373c6c8SDavid Ahern } 6838373c6c8SDavid Ahern 6840e884c78SPeter Nørlund #define fib_rebalance(fi) do { } while (0) 6850e884c78SPeter Nørlund 6860e884c78SPeter Nørlund #endif /* CONFIG_IP_ROUTE_MULTIPATH */ 6871da177e4SLinus Torvalds 68830357d7dSDavid Ahern static int fib_encap_match(u16 encap_type, 689571e7226SRoopa Prabhu struct nlattr *encap, 69030357d7dSDavid Ahern const struct fib_nh *nh, 6919ae28727SDavid Ahern const struct fib_config *cfg, 6929ae28727SDavid Ahern struct netlink_ext_ack *extack) 693571e7226SRoopa Prabhu { 694571e7226SRoopa Prabhu struct lwtunnel_state *lwtstate; 695df383e62SJiri Benc int ret, result = 0; 696571e7226SRoopa Prabhu 697571e7226SRoopa Prabhu if (encap_type == LWTUNNEL_ENCAP_NONE) 698571e7226SRoopa Prabhu return 0; 699571e7226SRoopa Prabhu 7009ae28727SDavid Ahern ret = lwtunnel_build_state(encap_type, encap, AF_INET, 7019ae28727SDavid Ahern cfg, &lwtstate, extack); 702df383e62SJiri Benc if (!ret) { 703b75ed8b1SDavid Ahern result = lwtunnel_cmp_encap(lwtstate, nh->fib_nh_lws); 704df383e62SJiri Benc lwtstate_free(lwtstate); 705df383e62SJiri Benc } 706571e7226SRoopa Prabhu 707df383e62SJiri Benc return result; 708571e7226SRoopa Prabhu } 709571e7226SRoopa Prabhu 7109ae28727SDavid Ahern int fib_nh_match(struct fib_config *cfg, struct fib_info *fi, 7119ae28727SDavid Ahern struct netlink_ext_ack *extack) 7121da177e4SLinus Torvalds { 7131da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 7144e902c57SThomas Graf struct rtnexthop *rtnh; 7154e902c57SThomas Graf int remaining; 7161da177e4SLinus Torvalds #endif 7171da177e4SLinus Torvalds 7184e902c57SThomas Graf if (cfg->fc_priority && cfg->fc_priority != fi->fib_priority) 7191da177e4SLinus Torvalds return 1; 7201da177e4SLinus Torvalds 7214e902c57SThomas Graf if (cfg->fc_oif || cfg->fc_gw) { 722571e7226SRoopa Prabhu if (cfg->fc_encap) { 7239ae28727SDavid Ahern if (fib_encap_match(cfg->fc_encap_type, cfg->fc_encap, 7249ae28727SDavid Ahern fi->fib_nh, cfg, extack)) 725571e7226SRoopa Prabhu return 1; 726571e7226SRoopa Prabhu } 727a8c6db1dSStefano Brivio #ifdef CONFIG_IP_ROUTE_CLASSID 728a8c6db1dSStefano Brivio if (cfg->fc_flow && 729a8c6db1dSStefano Brivio cfg->fc_flow != fi->fib_nh->nh_tclassid) 730a8c6db1dSStefano Brivio return 1; 731a8c6db1dSStefano Brivio #endif 732b75ed8b1SDavid Ahern if ((!cfg->fc_oif || cfg->fc_oif == fi->fib_nh->fib_nh_oif) && 733b75ed8b1SDavid Ahern (!cfg->fc_gw || cfg->fc_gw == fi->fib_nh->fib_nh_gw4)) 7341da177e4SLinus Torvalds return 0; 7351da177e4SLinus Torvalds return 1; 7361da177e4SLinus Torvalds } 7371da177e4SLinus Torvalds 7381da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 73951456b29SIan Morris if (!cfg->fc_mp) 7401da177e4SLinus Torvalds return 0; 7414e902c57SThomas Graf 7424e902c57SThomas Graf rtnh = cfg->fc_mp; 7434e902c57SThomas Graf remaining = cfg->fc_mp_len; 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds for_nexthops(fi) { 7464e902c57SThomas Graf int attrlen; 7471da177e4SLinus Torvalds 7484e902c57SThomas Graf if (!rtnh_ok(rtnh, remaining)) 7491da177e4SLinus Torvalds return -EINVAL; 7504e902c57SThomas Graf 751b75ed8b1SDavid Ahern if (rtnh->rtnh_ifindex && rtnh->rtnh_ifindex != nh->fib_nh_oif) 7521da177e4SLinus Torvalds return 1; 7534e902c57SThomas Graf 7544e902c57SThomas Graf attrlen = rtnh_attrlen(rtnh); 755f76936d0SJiri Pirko if (attrlen > 0) { 7564e902c57SThomas Graf struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 7574e902c57SThomas Graf 7584e902c57SThomas Graf nla = nla_find(attrs, attrlen, RTA_GATEWAY); 759b75ed8b1SDavid Ahern if (nla && nla_get_in_addr(nla) != nh->fib_nh_gw4) 7601da177e4SLinus Torvalds return 1; 761c7066f70SPatrick McHardy #ifdef CONFIG_IP_ROUTE_CLASSID 7624e902c57SThomas Graf nla = nla_find(attrs, attrlen, RTA_FLOW); 7634e902c57SThomas Graf if (nla && nla_get_u32(nla) != nh->nh_tclassid) 7641da177e4SLinus Torvalds return 1; 7651da177e4SLinus Torvalds #endif 7661da177e4SLinus Torvalds } 7674e902c57SThomas Graf 7684e902c57SThomas Graf rtnh = rtnh_next(rtnh, &remaining); 7691da177e4SLinus Torvalds } endfor_nexthops(fi); 7701da177e4SLinus Torvalds #endif 7711da177e4SLinus Torvalds return 0; 7721da177e4SLinus Torvalds } 7731da177e4SLinus Torvalds 7745f9ae3d9SXin Long bool fib_metrics_match(struct fib_config *cfg, struct fib_info *fi) 7755f9ae3d9SXin Long { 7765f9ae3d9SXin Long struct nlattr *nla; 7775f9ae3d9SXin Long int remaining; 7785f9ae3d9SXin Long 7795f9ae3d9SXin Long if (!cfg->fc_mx) 7805f9ae3d9SXin Long return true; 7815f9ae3d9SXin Long 7825f9ae3d9SXin Long nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 7835f9ae3d9SXin Long int type = nla_type(nla); 784d03a4557SPhil Sutter u32 fi_val, val; 7855f9ae3d9SXin Long 7865f9ae3d9SXin Long if (!type) 7875f9ae3d9SXin Long continue; 7885f9ae3d9SXin Long if (type > RTAX_MAX) 7895f9ae3d9SXin Long return false; 7905f9ae3d9SXin Long 7915f9ae3d9SXin Long if (type == RTAX_CC_ALGO) { 7925f9ae3d9SXin Long char tmp[TCP_CA_NAME_MAX]; 7935f9ae3d9SXin Long bool ecn_ca = false; 7945f9ae3d9SXin Long 7955f9ae3d9SXin Long nla_strlcpy(tmp, nla, sizeof(tmp)); 7966670e152SStephen Hemminger val = tcp_ca_get_key_by_name(fi->fib_net, tmp, &ecn_ca); 7975f9ae3d9SXin Long } else { 7985b5e7a0dSEric Dumazet if (nla_len(nla) != sizeof(u32)) 7995b5e7a0dSEric Dumazet return false; 8005f9ae3d9SXin Long val = nla_get_u32(nla); 8015f9ae3d9SXin Long } 8025f9ae3d9SXin Long 803d03a4557SPhil Sutter fi_val = fi->fib_metrics->metrics[type - 1]; 804d03a4557SPhil Sutter if (type == RTAX_FEATURES) 805d03a4557SPhil Sutter fi_val &= ~DST_FEATURE_ECN_CA; 806d03a4557SPhil Sutter 807d03a4557SPhil Sutter if (fi_val != val) 8085f9ae3d9SXin Long return false; 8095f9ae3d9SXin Long } 8105f9ae3d9SXin Long 8115f9ae3d9SXin Long return true; 8125f9ae3d9SXin Long } 8135f9ae3d9SXin Long 8141da177e4SLinus Torvalds 8151da177e4SLinus Torvalds /* 8166a31d2a9SEric Dumazet * Picture 8176a31d2a9SEric Dumazet * ------- 8186a31d2a9SEric Dumazet * 8196a31d2a9SEric Dumazet * Semantics of nexthop is very messy by historical reasons. 8206a31d2a9SEric Dumazet * We have to take into account, that: 8216a31d2a9SEric Dumazet * a) gateway can be actually local interface address, 8226a31d2a9SEric Dumazet * so that gatewayed route is direct. 8236a31d2a9SEric Dumazet * b) gateway must be on-link address, possibly 8246a31d2a9SEric Dumazet * described not by an ifaddr, but also by a direct route. 8256a31d2a9SEric Dumazet * c) If both gateway and interface are specified, they should not 8266a31d2a9SEric Dumazet * contradict. 8276a31d2a9SEric Dumazet * d) If we use tunnel routes, gateway could be not on-link. 8286a31d2a9SEric Dumazet * 8296a31d2a9SEric Dumazet * Attempt to reconcile all of these (alas, self-contradictory) conditions 8306a31d2a9SEric Dumazet * results in pretty ugly and hairy code with obscure logic. 8316a31d2a9SEric Dumazet * 8326a31d2a9SEric Dumazet * I chose to generalized it instead, so that the size 8336a31d2a9SEric Dumazet * of code does not increase practically, but it becomes 8346a31d2a9SEric Dumazet * much more general. 8356a31d2a9SEric Dumazet * Every prefix is assigned a "scope" value: "host" is local address, 8366a31d2a9SEric Dumazet * "link" is direct route, 8376a31d2a9SEric Dumazet * [ ... "site" ... "interior" ... ] 8386a31d2a9SEric Dumazet * and "universe" is true gateway route with global meaning. 8396a31d2a9SEric Dumazet * 8406a31d2a9SEric Dumazet * Every prefix refers to a set of "nexthop"s (gw, oif), 8416a31d2a9SEric Dumazet * where gw must have narrower scope. This recursion stops 8426a31d2a9SEric Dumazet * when gw has LOCAL scope or if "nexthop" is declared ONLINK, 8436a31d2a9SEric Dumazet * which means that gw is forced to be on link. 8446a31d2a9SEric Dumazet * 8456a31d2a9SEric Dumazet * Code is still hairy, but now it is apparently logically 8466a31d2a9SEric Dumazet * consistent and very flexible. F.e. as by-product it allows 8476a31d2a9SEric Dumazet * to co-exists in peace independent exterior and interior 8486a31d2a9SEric Dumazet * routing processes. 8496a31d2a9SEric Dumazet * 8506a31d2a9SEric Dumazet * Normally it looks as following. 8516a31d2a9SEric Dumazet * 8526a31d2a9SEric Dumazet * {universe prefix} -> (gw, oif) [scope link] 8536a31d2a9SEric Dumazet * | 8546a31d2a9SEric Dumazet * |-> {link prefix} -> (gw, oif) [scope local] 8556a31d2a9SEric Dumazet * | 8566a31d2a9SEric Dumazet * |-> {local prefix} (terminal node) 8571da177e4SLinus Torvalds */ 858fa8fefaaSDavid Ahern static int fib_check_nh(struct fib_config *cfg, struct fib_nh *nh, 859fa8fefaaSDavid Ahern struct netlink_ext_ack *extack) 8601da177e4SLinus Torvalds { 861127eb7cdSTom Herbert int err = 0; 86286167a37SDenis V. Lunev struct net *net; 8636a31d2a9SEric Dumazet struct net_device *dev; 8641da177e4SLinus Torvalds 86586167a37SDenis V. Lunev net = cfg->fc_nlinfo.nl_net; 866b75ed8b1SDavid Ahern if (nh->fib_nh_gw4) { 8671da177e4SLinus Torvalds struct fib_result res; 8681da177e4SLinus Torvalds 869b75ed8b1SDavid Ahern if (nh->fib_nh_flags & RTNH_F_ONLINK) { 87030bbaa19SDavid Ahern unsigned int addr_type; 8711da177e4SLinus Torvalds 872c3ab2b4eSDavid Ahern if (cfg->fc_scope >= RT_SCOPE_LINK) { 873c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 874c3ab2b4eSDavid Ahern "Nexthop has invalid scope"); 8751da177e4SLinus Torvalds return -EINVAL; 876c3ab2b4eSDavid Ahern } 877b75ed8b1SDavid Ahern dev = __dev_get_by_index(net, nh->fib_nh_oif); 878066b1030SDavid Ahern if (!dev) { 879066b1030SDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop device required for onlink"); 8801da177e4SLinus Torvalds return -ENODEV; 881066b1030SDavid Ahern } 882c3ab2b4eSDavid Ahern if (!(dev->flags & IFF_UP)) { 883c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 884c3ab2b4eSDavid Ahern "Nexthop device is not up"); 8851da177e4SLinus Torvalds return -ENETDOWN; 886c3ab2b4eSDavid Ahern } 887b75ed8b1SDavid Ahern addr_type = inet_addr_type_dev_table(net, dev, 888b75ed8b1SDavid Ahern nh->fib_nh_gw4); 889c3ab2b4eSDavid Ahern if (addr_type != RTN_UNICAST) { 890c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 891c3ab2b4eSDavid Ahern "Nexthop has invalid gateway"); 89230bbaa19SDavid Ahern return -EINVAL; 893c3ab2b4eSDavid Ahern } 8948a3d0316SAndy Gospodarek if (!netif_carrier_ok(dev)) 895b75ed8b1SDavid Ahern nh->fib_nh_flags |= RTNH_F_LINKDOWN; 896b75ed8b1SDavid Ahern nh->fib_nh_dev = dev; 8971da177e4SLinus Torvalds dev_hold(dev); 898b75ed8b1SDavid Ahern nh->fib_nh_scope = RT_SCOPE_LINK; 8991da177e4SLinus Torvalds return 0; 9001da177e4SLinus Torvalds } 901ebc0ffaeSEric Dumazet rcu_read_lock(); 9021da177e4SLinus Torvalds { 9033bfd8472SDavid Ahern struct fib_table *tbl = NULL; 9049ade2286SDavid S. Miller struct flowi4 fl4 = { 905b75ed8b1SDavid Ahern .daddr = nh->fib_nh_gw4, 9069ade2286SDavid S. Miller .flowi4_scope = cfg->fc_scope + 1, 907b75ed8b1SDavid Ahern .flowi4_oif = nh->fib_nh_oif, 9086a662719SCong Wang .flowi4_iif = LOOPBACK_IFINDEX, 9094e902c57SThomas Graf }; 9101da177e4SLinus Torvalds 9111da177e4SLinus Torvalds /* It is not necessary, but requires a bit of thinking */ 9129ade2286SDavid S. Miller if (fl4.flowi4_scope < RT_SCOPE_LINK) 9139ade2286SDavid S. Miller fl4.flowi4_scope = RT_SCOPE_LINK; 9143bfd8472SDavid Ahern 9153bfd8472SDavid Ahern if (cfg->fc_table) 9163bfd8472SDavid Ahern tbl = fib_get_table(net, cfg->fc_table); 9173bfd8472SDavid Ahern 9183bfd8472SDavid Ahern if (tbl) 9193bfd8472SDavid Ahern err = fib_table_lookup(tbl, &fl4, &res, 9201e313678SEric Dumazet FIB_LOOKUP_IGNORE_LINKSTATE | 9211e313678SEric Dumazet FIB_LOOKUP_NOREF); 9224c9bcd11SDavid Ahern 9234c9bcd11SDavid Ahern /* on error or if no table given do full lookup. This 9244c9bcd11SDavid Ahern * is needed for example when nexthops are in the local 9254c9bcd11SDavid Ahern * table rather than the given table 9264c9bcd11SDavid Ahern */ 9274c9bcd11SDavid Ahern if (!tbl || err) { 9280eeb075fSAndy Gospodarek err = fib_lookup(net, &fl4, &res, 9290eeb075fSAndy Gospodarek FIB_LOOKUP_IGNORE_LINKSTATE); 9304c9bcd11SDavid Ahern } 9314c9bcd11SDavid Ahern 932ebc0ffaeSEric Dumazet if (err) { 933c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 934c3ab2b4eSDavid Ahern "Nexthop has invalid gateway"); 935ebc0ffaeSEric Dumazet rcu_read_unlock(); 9361da177e4SLinus Torvalds return err; 9371da177e4SLinus Torvalds } 938ebc0ffaeSEric Dumazet } 9391da177e4SLinus Torvalds err = -EINVAL; 940c3ab2b4eSDavid Ahern if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) { 941c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway"); 9421da177e4SLinus Torvalds goto out; 943c3ab2b4eSDavid Ahern } 944b75ed8b1SDavid Ahern nh->fib_nh_scope = res.scope; 945b75ed8b1SDavid Ahern nh->fib_nh_oif = FIB_RES_OIF(res); 946b75ed8b1SDavid Ahern nh->fib_nh_dev = dev = FIB_RES_DEV(res); 947c3ab2b4eSDavid Ahern if (!dev) { 948c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 949c3ab2b4eSDavid Ahern "No egress device for nexthop gateway"); 9501da177e4SLinus Torvalds goto out; 951c3ab2b4eSDavid Ahern } 9526a31d2a9SEric Dumazet dev_hold(dev); 9538a3d0316SAndy Gospodarek if (!netif_carrier_ok(dev)) 954b75ed8b1SDavid Ahern nh->fib_nh_flags |= RTNH_F_LINKDOWN; 9558723e1b4SEric Dumazet err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN; 9561da177e4SLinus Torvalds } else { 9571da177e4SLinus Torvalds struct in_device *in_dev; 9581da177e4SLinus Torvalds 959b75ed8b1SDavid Ahern if (nh->fib_nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK)) { 960c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 961c3ab2b4eSDavid Ahern "Invalid flags for nexthop - PERVASIVE and ONLINK can not be set"); 9621da177e4SLinus Torvalds return -EINVAL; 963c3ab2b4eSDavid Ahern } 9648723e1b4SEric Dumazet rcu_read_lock(); 9658723e1b4SEric Dumazet err = -ENODEV; 966b75ed8b1SDavid Ahern in_dev = inetdev_by_index(net, nh->fib_nh_oif); 96751456b29SIan Morris if (!in_dev) 9688723e1b4SEric Dumazet goto out; 9698723e1b4SEric Dumazet err = -ENETDOWN; 970c3ab2b4eSDavid Ahern if (!(in_dev->dev->flags & IFF_UP)) { 971c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, "Device for nexthop is not up"); 9728723e1b4SEric Dumazet goto out; 973c3ab2b4eSDavid Ahern } 974b75ed8b1SDavid Ahern nh->fib_nh_dev = in_dev->dev; 975b75ed8b1SDavid Ahern dev_hold(nh->fib_nh_dev); 976b75ed8b1SDavid Ahern nh->fib_nh_scope = RT_SCOPE_HOST; 977b75ed8b1SDavid Ahern if (!netif_carrier_ok(nh->fib_nh_dev)) 978b75ed8b1SDavid Ahern nh->fib_nh_flags |= RTNH_F_LINKDOWN; 9798723e1b4SEric Dumazet err = 0; 9801da177e4SLinus Torvalds } 9818723e1b4SEric Dumazet out: 9828723e1b4SEric Dumazet rcu_read_unlock(); 9838723e1b4SEric Dumazet return err; 9841da177e4SLinus Torvalds } 9851da177e4SLinus Torvalds 98681f7bf6cSAl Viro static inline unsigned int fib_laddr_hashfn(__be32 val) 9871da177e4SLinus Torvalds { 988123b9731SDavid S. Miller unsigned int mask = (fib_info_hash_size - 1); 9891da177e4SLinus Torvalds 9906a31d2a9SEric Dumazet return ((__force u32)val ^ 9916a31d2a9SEric Dumazet ((__force u32)val >> 7) ^ 9926a31d2a9SEric Dumazet ((__force u32)val >> 14)) & mask; 9931da177e4SLinus Torvalds } 9941da177e4SLinus Torvalds 995123b9731SDavid S. Miller static struct hlist_head *fib_info_hash_alloc(int bytes) 9961da177e4SLinus Torvalds { 9971da177e4SLinus Torvalds if (bytes <= PAGE_SIZE) 99888f83491SJoonwoo Park return kzalloc(bytes, GFP_KERNEL); 9991da177e4SLinus Torvalds else 10001da177e4SLinus Torvalds return (struct hlist_head *) 10016a31d2a9SEric Dumazet __get_free_pages(GFP_KERNEL | __GFP_ZERO, 10026a31d2a9SEric Dumazet get_order(bytes)); 10031da177e4SLinus Torvalds } 10041da177e4SLinus Torvalds 1005123b9731SDavid S. Miller static void fib_info_hash_free(struct hlist_head *hash, int bytes) 10061da177e4SLinus Torvalds { 10071da177e4SLinus Torvalds if (!hash) 10081da177e4SLinus Torvalds return; 10091da177e4SLinus Torvalds 10101da177e4SLinus Torvalds if (bytes <= PAGE_SIZE) 10111da177e4SLinus Torvalds kfree(hash); 10121da177e4SLinus Torvalds else 10131da177e4SLinus Torvalds free_pages((unsigned long) hash, get_order(bytes)); 10141da177e4SLinus Torvalds } 10151da177e4SLinus Torvalds 1016123b9731SDavid S. Miller static void fib_info_hash_move(struct hlist_head *new_info_hash, 10171da177e4SLinus Torvalds struct hlist_head *new_laddrhash, 10181da177e4SLinus Torvalds unsigned int new_size) 10191da177e4SLinus Torvalds { 1020b7656e7fSDavid S. Miller struct hlist_head *old_info_hash, *old_laddrhash; 1021123b9731SDavid S. Miller unsigned int old_size = fib_info_hash_size; 1022b7656e7fSDavid S. Miller unsigned int i, bytes; 10231da177e4SLinus Torvalds 1024832b4c5eSStephen Hemminger spin_lock_bh(&fib_info_lock); 1025b7656e7fSDavid S. Miller old_info_hash = fib_info_hash; 1026b7656e7fSDavid S. Miller old_laddrhash = fib_info_laddrhash; 1027123b9731SDavid S. Miller fib_info_hash_size = new_size; 10281da177e4SLinus Torvalds 10291da177e4SLinus Torvalds for (i = 0; i < old_size; i++) { 10301da177e4SLinus Torvalds struct hlist_head *head = &fib_info_hash[i]; 1031b67bfe0dSSasha Levin struct hlist_node *n; 10321da177e4SLinus Torvalds struct fib_info *fi; 10331da177e4SLinus Torvalds 1034b67bfe0dSSasha Levin hlist_for_each_entry_safe(fi, n, head, fib_hash) { 10351da177e4SLinus Torvalds struct hlist_head *dest; 10361da177e4SLinus Torvalds unsigned int new_hash; 10371da177e4SLinus Torvalds 10381da177e4SLinus Torvalds new_hash = fib_info_hashfn(fi); 10391da177e4SLinus Torvalds dest = &new_info_hash[new_hash]; 10401da177e4SLinus Torvalds hlist_add_head(&fi->fib_hash, dest); 10411da177e4SLinus Torvalds } 10421da177e4SLinus Torvalds } 10431da177e4SLinus Torvalds fib_info_hash = new_info_hash; 10441da177e4SLinus Torvalds 10451da177e4SLinus Torvalds for (i = 0; i < old_size; i++) { 10461da177e4SLinus Torvalds struct hlist_head *lhead = &fib_info_laddrhash[i]; 1047b67bfe0dSSasha Levin struct hlist_node *n; 10481da177e4SLinus Torvalds struct fib_info *fi; 10491da177e4SLinus Torvalds 1050b67bfe0dSSasha Levin hlist_for_each_entry_safe(fi, n, lhead, fib_lhash) { 10511da177e4SLinus Torvalds struct hlist_head *ldest; 10521da177e4SLinus Torvalds unsigned int new_hash; 10531da177e4SLinus Torvalds 10541da177e4SLinus Torvalds new_hash = fib_laddr_hashfn(fi->fib_prefsrc); 10551da177e4SLinus Torvalds ldest = &new_laddrhash[new_hash]; 10561da177e4SLinus Torvalds hlist_add_head(&fi->fib_lhash, ldest); 10571da177e4SLinus Torvalds } 10581da177e4SLinus Torvalds } 10591da177e4SLinus Torvalds fib_info_laddrhash = new_laddrhash; 10601da177e4SLinus Torvalds 1061832b4c5eSStephen Hemminger spin_unlock_bh(&fib_info_lock); 1062b7656e7fSDavid S. Miller 1063b7656e7fSDavid S. Miller bytes = old_size * sizeof(struct hlist_head *); 1064123b9731SDavid S. Miller fib_info_hash_free(old_info_hash, bytes); 1065123b9731SDavid S. Miller fib_info_hash_free(old_laddrhash, bytes); 10661da177e4SLinus Torvalds } 10671da177e4SLinus Torvalds 1068436c3b66SDavid S. Miller __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh) 1069436c3b66SDavid S. Miller { 1070b75ed8b1SDavid Ahern nh->nh_saddr = inet_select_addr(nh->fib_nh_dev, 1071b75ed8b1SDavid Ahern nh->fib_nh_gw4, 107237e826c5SDavid S. Miller nh->nh_parent->fib_scope); 1073436c3b66SDavid S. Miller nh->nh_saddr_genid = atomic_read(&net->ipv4.dev_addr_genid); 1074436c3b66SDavid S. Miller 1075436c3b66SDavid S. Miller return nh->nh_saddr; 1076436c3b66SDavid S. Miller } 1077436c3b66SDavid S. Miller 1078eba618abSDavid Ahern __be32 fib_result_prefsrc(struct net *net, struct fib_result *res) 1079eba618abSDavid Ahern { 1080eba618abSDavid Ahern struct fib_nh_common *nhc = res->nhc; 1081eba618abSDavid Ahern struct fib_nh *nh; 1082eba618abSDavid Ahern 1083eba618abSDavid Ahern if (res->fi->fib_prefsrc) 1084eba618abSDavid Ahern return res->fi->fib_prefsrc; 1085eba618abSDavid Ahern 1086eba618abSDavid Ahern nh = container_of(nhc, struct fib_nh, nh_common); 1087eba618abSDavid Ahern if (nh->nh_saddr_genid == atomic_read(&net->ipv4.dev_addr_genid)) 1088eba618abSDavid Ahern return nh->nh_saddr; 1089eba618abSDavid Ahern 1090eba618abSDavid Ahern return fib_info_update_nh_saddr(net, nh); 1091eba618abSDavid Ahern } 1092eba618abSDavid Ahern 1093021dd3b8SDavid Ahern static bool fib_valid_prefsrc(struct fib_config *cfg, __be32 fib_prefsrc) 1094021dd3b8SDavid Ahern { 1095021dd3b8SDavid Ahern if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || 1096021dd3b8SDavid Ahern fib_prefsrc != cfg->fc_dst) { 10979b8ff518SDavid Ahern u32 tb_id = cfg->fc_table; 1098e1b8d903SDavid Ahern int rc; 1099021dd3b8SDavid Ahern 1100021dd3b8SDavid Ahern if (tb_id == RT_TABLE_MAIN) 1101021dd3b8SDavid Ahern tb_id = RT_TABLE_LOCAL; 1102021dd3b8SDavid Ahern 1103e1b8d903SDavid Ahern rc = inet_addr_type_table(cfg->fc_nlinfo.nl_net, 1104e1b8d903SDavid Ahern fib_prefsrc, tb_id); 1105e1b8d903SDavid Ahern 1106e1b8d903SDavid Ahern if (rc != RTN_LOCAL && tb_id != RT_TABLE_LOCAL) { 1107e1b8d903SDavid Ahern rc = inet_addr_type_table(cfg->fc_nlinfo.nl_net, 1108e1b8d903SDavid Ahern fib_prefsrc, RT_TABLE_LOCAL); 1109021dd3b8SDavid Ahern } 1110e1b8d903SDavid Ahern 1111e1b8d903SDavid Ahern if (rc != RTN_LOCAL) 1112e1b8d903SDavid Ahern return false; 1113021dd3b8SDavid Ahern } 1114021dd3b8SDavid Ahern return true; 1115021dd3b8SDavid Ahern } 1116021dd3b8SDavid Ahern 11176d8422a1SDavid Ahern struct fib_info *fib_create_info(struct fib_config *cfg, 11186d8422a1SDavid Ahern struct netlink_ext_ack *extack) 11191da177e4SLinus Torvalds { 11201da177e4SLinus Torvalds int err; 11211da177e4SLinus Torvalds struct fib_info *fi = NULL; 11221da177e4SLinus Torvalds struct fib_info *ofi; 11231da177e4SLinus Torvalds int nhs = 1; 11247462bd74SDenis V. Lunev struct net *net = cfg->fc_nlinfo.nl_net; 11251da177e4SLinus Torvalds 11264c8237cdSDavid S. Miller if (cfg->fc_type > RTN_MAX) 11274c8237cdSDavid S. Miller goto err_inval; 11284c8237cdSDavid S. Miller 11291da177e4SLinus Torvalds /* Fast check to catch the most weird cases */ 1130c3ab2b4eSDavid Ahern if (fib_props[cfg->fc_type].scope > cfg->fc_scope) { 1131c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid scope"); 11321da177e4SLinus Torvalds goto err_inval; 1133c3ab2b4eSDavid Ahern } 11341da177e4SLinus Torvalds 1135c3ab2b4eSDavid Ahern if (cfg->fc_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) { 1136c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 1137c3ab2b4eSDavid Ahern "Invalid rtm_flags - can not contain DEAD or LINKDOWN"); 113880610229SJulian Anastasov goto err_inval; 1139c3ab2b4eSDavid Ahern } 114080610229SJulian Anastasov 11411da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 11424e902c57SThomas Graf if (cfg->fc_mp) { 11436d8422a1SDavid Ahern nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len, extack); 11441da177e4SLinus Torvalds if (nhs == 0) 11451da177e4SLinus Torvalds goto err_inval; 11461da177e4SLinus Torvalds } 11471da177e4SLinus Torvalds #endif 11481da177e4SLinus Torvalds 11491da177e4SLinus Torvalds err = -ENOBUFS; 1150123b9731SDavid S. Miller if (fib_info_cnt >= fib_info_hash_size) { 1151123b9731SDavid S. Miller unsigned int new_size = fib_info_hash_size << 1; 11521da177e4SLinus Torvalds struct hlist_head *new_info_hash; 11531da177e4SLinus Torvalds struct hlist_head *new_laddrhash; 11541da177e4SLinus Torvalds unsigned int bytes; 11551da177e4SLinus Torvalds 11561da177e4SLinus Torvalds if (!new_size) 1157d94ce9b2SEric Dumazet new_size = 16; 11581da177e4SLinus Torvalds bytes = new_size * sizeof(struct hlist_head *); 1159123b9731SDavid S. Miller new_info_hash = fib_info_hash_alloc(bytes); 1160123b9731SDavid S. Miller new_laddrhash = fib_info_hash_alloc(bytes); 11611da177e4SLinus Torvalds if (!new_info_hash || !new_laddrhash) { 1162123b9731SDavid S. Miller fib_info_hash_free(new_info_hash, bytes); 1163123b9731SDavid S. Miller fib_info_hash_free(new_laddrhash, bytes); 116488f83491SJoonwoo Park } else 1165123b9731SDavid S. Miller fib_info_hash_move(new_info_hash, new_laddrhash, new_size); 11661da177e4SLinus Torvalds 1167123b9731SDavid S. Miller if (!fib_info_hash_size) 11681da177e4SLinus Torvalds goto failure; 11691da177e4SLinus Torvalds } 11701da177e4SLinus Torvalds 11711f533ba6SGustavo A. R. Silva fi = kzalloc(struct_size(fi, fib_nh, nhs), GFP_KERNEL); 117251456b29SIan Morris if (!fi) 11731da177e4SLinus Torvalds goto failure; 1174767a2217SDavid Ahern fi->fib_metrics = ip_fib_metrics_init(fi->fib_net, cfg->fc_mx, 1175d7e774f3SDavid Ahern cfg->fc_mx_len, extack); 1176767a2217SDavid Ahern if (unlikely(IS_ERR(fi->fib_metrics))) { 1177767a2217SDavid Ahern err = PTR_ERR(fi->fib_metrics); 1178187e5b3aSEric Dumazet kfree(fi); 1179187e5b3aSEric Dumazet return ERR_PTR(err); 1180187e5b3aSEric Dumazet } 1181767a2217SDavid Ahern 1182187e5b3aSEric Dumazet fib_info_cnt++; 1183efd7ef1cSEric W. Biederman fi->fib_net = net; 11844e902c57SThomas Graf fi->fib_protocol = cfg->fc_protocol; 118537e826c5SDavid S. Miller fi->fib_scope = cfg->fc_scope; 11864e902c57SThomas Graf fi->fib_flags = cfg->fc_flags; 11874e902c57SThomas Graf fi->fib_priority = cfg->fc_priority; 11884e902c57SThomas Graf fi->fib_prefsrc = cfg->fc_prefsrc; 1189f4ef85bbSEric Dumazet fi->fib_type = cfg->fc_type; 11905a56a0b3SMark Tomlinson fi->fib_tb_id = cfg->fc_table; 11911da177e4SLinus Torvalds 11921da177e4SLinus Torvalds fi->fib_nhs = nhs; 11931da177e4SLinus Torvalds change_nexthops(fi) { 119471fceff0SDavid S. Miller nexthop_nh->nh_parent = fi; 11951da177e4SLinus Torvalds } endfor_nexthops(fi) 11961da177e4SLinus Torvalds 1197e4516ef6SDavid Ahern if (cfg->fc_mp) 11986d8422a1SDavid Ahern err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg, extack); 1199e4516ef6SDavid Ahern else 1200e4516ef6SDavid Ahern err = fib_nh_init(net, fi->fib_nh, cfg, 1, extack); 1201e4516ef6SDavid Ahern 12024e902c57SThomas Graf if (err != 0) 12031da177e4SLinus Torvalds goto failure; 12041da177e4SLinus Torvalds 12054e902c57SThomas Graf if (fib_props[cfg->fc_type].error) { 1206c3ab2b4eSDavid Ahern if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) { 1207c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 1208c3ab2b4eSDavid Ahern "Gateway, device and multipath can not be specified for this route type"); 12091da177e4SLinus Torvalds goto err_inval; 1210c3ab2b4eSDavid Ahern } 12111da177e4SLinus Torvalds goto link_it; 12124c8237cdSDavid S. Miller } else { 12134c8237cdSDavid S. Miller switch (cfg->fc_type) { 12144c8237cdSDavid S. Miller case RTN_UNICAST: 12154c8237cdSDavid S. Miller case RTN_LOCAL: 12164c8237cdSDavid S. Miller case RTN_BROADCAST: 12174c8237cdSDavid S. Miller case RTN_ANYCAST: 12184c8237cdSDavid S. Miller case RTN_MULTICAST: 12194c8237cdSDavid S. Miller break; 12204c8237cdSDavid S. Miller default: 1221c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid route type"); 12224c8237cdSDavid S. Miller goto err_inval; 12234c8237cdSDavid S. Miller } 12241da177e4SLinus Torvalds } 12251da177e4SLinus Torvalds 1226c3ab2b4eSDavid Ahern if (cfg->fc_scope > RT_SCOPE_HOST) { 1227c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid scope"); 12281da177e4SLinus Torvalds goto err_inval; 1229c3ab2b4eSDavid Ahern } 12301da177e4SLinus Torvalds 12314e902c57SThomas Graf if (cfg->fc_scope == RT_SCOPE_HOST) { 12321da177e4SLinus Torvalds struct fib_nh *nh = fi->fib_nh; 12331da177e4SLinus Torvalds 12341da177e4SLinus Torvalds /* Local address is added. */ 1235c3ab2b4eSDavid Ahern if (nhs != 1) { 1236c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 1237c3ab2b4eSDavid Ahern "Route with host scope can not have multiple nexthops"); 12386d8422a1SDavid Ahern goto err_inval; 1239c3ab2b4eSDavid Ahern } 1240b75ed8b1SDavid Ahern if (nh->fib_nh_gw4) { 1241c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, 1242c3ab2b4eSDavid Ahern "Route with host scope can not have a gateway"); 12431da177e4SLinus Torvalds goto err_inval; 1244c3ab2b4eSDavid Ahern } 1245b75ed8b1SDavid Ahern nh->fib_nh_scope = RT_SCOPE_NOWHERE; 1246b75ed8b1SDavid Ahern nh->fib_nh_dev = dev_get_by_index(net, fi->fib_nh->fib_nh_oif); 12471da177e4SLinus Torvalds err = -ENODEV; 1248b75ed8b1SDavid Ahern if (!nh->fib_nh_dev) 12491da177e4SLinus Torvalds goto failure; 12501da177e4SLinus Torvalds } else { 12518a3d0316SAndy Gospodarek int linkdown = 0; 12528a3d0316SAndy Gospodarek 12531da177e4SLinus Torvalds change_nexthops(fi) { 1254fa8fefaaSDavid Ahern err = fib_check_nh(cfg, nexthop_nh, extack); 12556a31d2a9SEric Dumazet if (err != 0) 12561da177e4SLinus Torvalds goto failure; 1257b75ed8b1SDavid Ahern if (nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) 12588a3d0316SAndy Gospodarek linkdown++; 12591da177e4SLinus Torvalds } endfor_nexthops(fi) 12608a3d0316SAndy Gospodarek if (linkdown == fi->fib_nhs) 12618a3d0316SAndy Gospodarek fi->fib_flags |= RTNH_F_LINKDOWN; 12621da177e4SLinus Torvalds } 12631da177e4SLinus Torvalds 1264c3ab2b4eSDavid Ahern if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) { 1265c3ab2b4eSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid prefsrc address"); 12661da177e4SLinus Torvalds goto err_inval; 1267c3ab2b4eSDavid Ahern } 12681da177e4SLinus Torvalds 12691fc050a1SDavid S. Miller change_nexthops(fi) { 1270436c3b66SDavid S. Miller fib_info_update_nh_saddr(net, nexthop_nh); 12711fc050a1SDavid S. Miller } endfor_nexthops(fi) 12721fc050a1SDavid S. Miller 12730e884c78SPeter Nørlund fib_rebalance(fi); 12740e884c78SPeter Nørlund 12751da177e4SLinus Torvalds link_it: 12766a31d2a9SEric Dumazet ofi = fib_find_info(fi); 12776a31d2a9SEric Dumazet if (ofi) { 12781da177e4SLinus Torvalds fi->fib_dead = 1; 12791da177e4SLinus Torvalds free_fib_info(fi); 12801da177e4SLinus Torvalds ofi->fib_treeref++; 12811da177e4SLinus Torvalds return ofi; 12821da177e4SLinus Torvalds } 12831da177e4SLinus Torvalds 12841da177e4SLinus Torvalds fi->fib_treeref++; 12850029c0deSReshetova, Elena refcount_set(&fi->fib_clntref, 1); 1286832b4c5eSStephen Hemminger spin_lock_bh(&fib_info_lock); 12871da177e4SLinus Torvalds hlist_add_head(&fi->fib_hash, 12881da177e4SLinus Torvalds &fib_info_hash[fib_info_hashfn(fi)]); 12891da177e4SLinus Torvalds if (fi->fib_prefsrc) { 12901da177e4SLinus Torvalds struct hlist_head *head; 12911da177e4SLinus Torvalds 12921da177e4SLinus Torvalds head = &fib_info_laddrhash[fib_laddr_hashfn(fi->fib_prefsrc)]; 12931da177e4SLinus Torvalds hlist_add_head(&fi->fib_lhash, head); 12941da177e4SLinus Torvalds } 12951da177e4SLinus Torvalds change_nexthops(fi) { 12961da177e4SLinus Torvalds struct hlist_head *head; 12971da177e4SLinus Torvalds unsigned int hash; 12981da177e4SLinus Torvalds 1299b75ed8b1SDavid Ahern if (!nexthop_nh->fib_nh_dev) 13001da177e4SLinus Torvalds continue; 1301b75ed8b1SDavid Ahern hash = fib_devindex_hashfn(nexthop_nh->fib_nh_dev->ifindex); 13021da177e4SLinus Torvalds head = &fib_info_devhash[hash]; 130371fceff0SDavid S. Miller hlist_add_head(&nexthop_nh->nh_hash, head); 13041da177e4SLinus Torvalds } endfor_nexthops(fi) 1305832b4c5eSStephen Hemminger spin_unlock_bh(&fib_info_lock); 13061da177e4SLinus Torvalds return fi; 13071da177e4SLinus Torvalds 13081da177e4SLinus Torvalds err_inval: 13091da177e4SLinus Torvalds err = -EINVAL; 13101da177e4SLinus Torvalds 13111da177e4SLinus Torvalds failure: 13121da177e4SLinus Torvalds if (fi) { 13131da177e4SLinus Torvalds fi->fib_dead = 1; 13141da177e4SLinus Torvalds free_fib_info(fi); 13151da177e4SLinus Torvalds } 13164e902c57SThomas Graf 13174e902c57SThomas Graf return ERR_PTR(err); 13181da177e4SLinus Torvalds } 13191da177e4SLinus Torvalds 1320*b0f60193SDavid Ahern static int fib_nexthop_info(struct sk_buff *skb, const struct fib_nh *nh, 1321*b0f60193SDavid Ahern unsigned int *flags, bool skip_oif) 1322*b0f60193SDavid Ahern { 1323*b0f60193SDavid Ahern if (nh->fib_nh_flags & RTNH_F_DEAD) 1324*b0f60193SDavid Ahern *flags |= RTNH_F_DEAD; 1325*b0f60193SDavid Ahern 1326*b0f60193SDavid Ahern if (nh->fib_nh_flags & RTNH_F_LINKDOWN) { 1327*b0f60193SDavid Ahern *flags |= RTNH_F_LINKDOWN; 1328*b0f60193SDavid Ahern 1329*b0f60193SDavid Ahern rcu_read_lock(); 1330*b0f60193SDavid Ahern if (ip_ignore_linkdown(nh->fib_nh_dev)) 1331*b0f60193SDavid Ahern *flags |= RTNH_F_DEAD; 1332*b0f60193SDavid Ahern rcu_read_unlock(); 1333*b0f60193SDavid Ahern } 1334*b0f60193SDavid Ahern 1335*b0f60193SDavid Ahern if (nh->fib_nh_gw4 && 1336*b0f60193SDavid Ahern nla_put_in_addr(skb, RTA_GATEWAY, nh->fib_nh_gw4)) 1337*b0f60193SDavid Ahern goto nla_put_failure; 1338*b0f60193SDavid Ahern 1339*b0f60193SDavid Ahern *flags |= (nh->fib_nh_flags & RTNH_F_ONLINK); 1340*b0f60193SDavid Ahern if (nh->fib_nh_flags & RTNH_F_OFFLOAD) 1341*b0f60193SDavid Ahern *flags |= RTNH_F_OFFLOAD; 1342*b0f60193SDavid Ahern 1343*b0f60193SDavid Ahern if (!skip_oif && nh->fib_nh_dev && 1344*b0f60193SDavid Ahern nla_put_u32(skb, RTA_OIF, nh->fib_nh_dev->ifindex)) 1345*b0f60193SDavid Ahern goto nla_put_failure; 1346*b0f60193SDavid Ahern 1347*b0f60193SDavid Ahern if (nh->fib_nh_lws && 1348*b0f60193SDavid Ahern lwtunnel_fill_encap(skb, nh->fib_nh_lws) < 0) 1349*b0f60193SDavid Ahern goto nla_put_failure; 1350*b0f60193SDavid Ahern 1351*b0f60193SDavid Ahern return 0; 1352*b0f60193SDavid Ahern 1353*b0f60193SDavid Ahern nla_put_failure: 1354*b0f60193SDavid Ahern return -EMSGSIZE; 1355*b0f60193SDavid Ahern } 1356*b0f60193SDavid Ahern 1357*b0f60193SDavid Ahern #ifdef CONFIG_IP_ROUTE_MULTIPATH 1358*b0f60193SDavid Ahern static int fib_add_nexthop(struct sk_buff *skb, const struct fib_nh *nh) 1359*b0f60193SDavid Ahern { 1360*b0f60193SDavid Ahern const struct net_device *dev = nh->fib_nh_dev; 1361*b0f60193SDavid Ahern struct rtnexthop *rtnh; 1362*b0f60193SDavid Ahern unsigned int flags = 0; 1363*b0f60193SDavid Ahern 1364*b0f60193SDavid Ahern rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); 1365*b0f60193SDavid Ahern if (!rtnh) 1366*b0f60193SDavid Ahern goto nla_put_failure; 1367*b0f60193SDavid Ahern 1368*b0f60193SDavid Ahern rtnh->rtnh_hops = nh->fib_nh_weight - 1; 1369*b0f60193SDavid Ahern rtnh->rtnh_ifindex = dev ? dev->ifindex : 0; 1370*b0f60193SDavid Ahern 1371*b0f60193SDavid Ahern if (fib_nexthop_info(skb, nh, &flags, true) < 0) 1372*b0f60193SDavid Ahern goto nla_put_failure; 1373*b0f60193SDavid Ahern 1374*b0f60193SDavid Ahern rtnh->rtnh_flags = flags; 1375*b0f60193SDavid Ahern 1376*b0f60193SDavid Ahern /* length of rtnetlink header + attributes */ 1377*b0f60193SDavid Ahern rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh; 1378*b0f60193SDavid Ahern 1379*b0f60193SDavid Ahern return 0; 1380*b0f60193SDavid Ahern 1381*b0f60193SDavid Ahern nla_put_failure: 1382*b0f60193SDavid Ahern return -EMSGSIZE; 1383*b0f60193SDavid Ahern } 1384*b0f60193SDavid Ahern 1385*b0f60193SDavid Ahern static int fib_add_multipath(struct sk_buff *skb, struct fib_info *fi) 1386*b0f60193SDavid Ahern { 1387*b0f60193SDavid Ahern struct nlattr *mp; 1388*b0f60193SDavid Ahern 1389*b0f60193SDavid Ahern mp = nla_nest_start(skb, RTA_MULTIPATH); 1390*b0f60193SDavid Ahern if (!mp) 1391*b0f60193SDavid Ahern goto nla_put_failure; 1392*b0f60193SDavid Ahern 1393*b0f60193SDavid Ahern for_nexthops(fi) { 1394*b0f60193SDavid Ahern if (fib_add_nexthop(skb, nh) < 0) 1395*b0f60193SDavid Ahern goto nla_put_failure; 1396*b0f60193SDavid Ahern #ifdef CONFIG_IP_ROUTE_CLASSID 1397*b0f60193SDavid Ahern if (nh->nh_tclassid && 1398*b0f60193SDavid Ahern nla_put_u32(skb, RTA_FLOW, nh->nh_tclassid)) 1399*b0f60193SDavid Ahern goto nla_put_failure; 1400*b0f60193SDavid Ahern #endif 1401*b0f60193SDavid Ahern } endfor_nexthops(fi); 1402*b0f60193SDavid Ahern 1403*b0f60193SDavid Ahern nla_nest_end(skb, mp); 1404*b0f60193SDavid Ahern 1405*b0f60193SDavid Ahern return 0; 1406*b0f60193SDavid Ahern 1407*b0f60193SDavid Ahern nla_put_failure: 1408*b0f60193SDavid Ahern return -EMSGSIZE; 1409*b0f60193SDavid Ahern } 1410*b0f60193SDavid Ahern #else 1411*b0f60193SDavid Ahern static int fib_add_multipath(struct sk_buff *skb, struct fib_info *fi) 1412*b0f60193SDavid Ahern { 1413*b0f60193SDavid Ahern return 0; 1414*b0f60193SDavid Ahern } 1415*b0f60193SDavid Ahern #endif 1416*b0f60193SDavid Ahern 141715e47304SEric W. Biederman int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, 141837e826c5SDavid S. Miller u32 tb_id, u8 type, __be32 dst, int dst_len, u8 tos, 1419b6544c0bSJamal Hadi Salim struct fib_info *fi, unsigned int flags) 14201da177e4SLinus Torvalds { 14211da177e4SLinus Torvalds struct nlmsghdr *nlh; 1422be403ea1SThomas Graf struct rtmsg *rtm; 14231da177e4SLinus Torvalds 142415e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags); 142551456b29SIan Morris if (!nlh) 142626932566SPatrick McHardy return -EMSGSIZE; 1427be403ea1SThomas Graf 1428be403ea1SThomas Graf rtm = nlmsg_data(nlh); 14291da177e4SLinus Torvalds rtm->rtm_family = AF_INET; 14301da177e4SLinus Torvalds rtm->rtm_dst_len = dst_len; 14311da177e4SLinus Torvalds rtm->rtm_src_len = 0; 14321da177e4SLinus Torvalds rtm->rtm_tos = tos; 1433709772e6SKrzysztof Piotr Oledzki if (tb_id < 256) 14341da177e4SLinus Torvalds rtm->rtm_table = tb_id; 1435709772e6SKrzysztof Piotr Oledzki else 1436709772e6SKrzysztof Piotr Oledzki rtm->rtm_table = RT_TABLE_COMPAT; 1437f3756b79SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, tb_id)) 1438f3756b79SDavid S. Miller goto nla_put_failure; 14391da177e4SLinus Torvalds rtm->rtm_type = type; 14401da177e4SLinus Torvalds rtm->rtm_flags = fi->fib_flags; 144137e826c5SDavid S. Miller rtm->rtm_scope = fi->fib_scope; 14421da177e4SLinus Torvalds rtm->rtm_protocol = fi->fib_protocol; 1443be403ea1SThomas Graf 1444f3756b79SDavid S. Miller if (rtm->rtm_dst_len && 1445930345eaSJiri Benc nla_put_in_addr(skb, RTA_DST, dst)) 1446f3756b79SDavid S. Miller goto nla_put_failure; 1447f3756b79SDavid S. Miller if (fi->fib_priority && 1448f3756b79SDavid S. Miller nla_put_u32(skb, RTA_PRIORITY, fi->fib_priority)) 1449f3756b79SDavid S. Miller goto nla_put_failure; 14503fb07dafSEric Dumazet if (rtnetlink_put_metrics(skb, fi->fib_metrics->metrics) < 0) 1451be403ea1SThomas Graf goto nla_put_failure; 1452be403ea1SThomas Graf 1453f3756b79SDavid S. Miller if (fi->fib_prefsrc && 1454930345eaSJiri Benc nla_put_in_addr(skb, RTA_PREFSRC, fi->fib_prefsrc)) 1455f3756b79SDavid S. Miller goto nla_put_failure; 14561da177e4SLinus Torvalds if (fi->fib_nhs == 1) { 1457*b0f60193SDavid Ahern struct fib_nh *nh = &fi->fib_nh[0]; 1458*b0f60193SDavid Ahern unsigned int flags = 0; 1459be403ea1SThomas Graf 1460*b0f60193SDavid Ahern if (fib_nexthop_info(skb, nh, &flags, false) < 0) 1461be403ea1SThomas Graf goto nla_put_failure; 14621da177e4SLinus Torvalds 1463*b0f60193SDavid Ahern rtm->rtm_flags = flags; 1464c7066f70SPatrick McHardy #ifdef CONFIG_IP_ROUTE_CLASSID 1465f3756b79SDavid S. Miller if (nh->nh_tclassid && 1466f3756b79SDavid S. Miller nla_put_u32(skb, RTA_FLOW, nh->nh_tclassid)) 1467f3756b79SDavid S. Miller goto nla_put_failure; 14688265abc0SPatrick McHardy #endif 1469*b0f60193SDavid Ahern } else { 1470*b0f60193SDavid Ahern if (fib_add_multipath(skb, fi) < 0) 1471ea7a8085SDavid Ahern goto nla_put_failure; 14721da177e4SLinus Torvalds } 1473*b0f60193SDavid Ahern 1474053c095aSJohannes Berg nlmsg_end(skb, nlh); 1475053c095aSJohannes Berg return 0; 14761da177e4SLinus Torvalds 1477be403ea1SThomas Graf nla_put_failure: 147826932566SPatrick McHardy nlmsg_cancel(skb, nlh); 147926932566SPatrick McHardy return -EMSGSIZE; 14801da177e4SLinus Torvalds } 14811da177e4SLinus Torvalds 14821da177e4SLinus Torvalds /* 14836a31d2a9SEric Dumazet * Update FIB if: 14846a31d2a9SEric Dumazet * - local address disappeared -> we must delete all the entries 14856a31d2a9SEric Dumazet * referring to it. 14866a31d2a9SEric Dumazet * - device went down -> we must shutdown all nexthops going via it. 14871da177e4SLinus Torvalds */ 14885a56a0b3SMark Tomlinson int fib_sync_down_addr(struct net_device *dev, __be32 local) 14891da177e4SLinus Torvalds { 14901da177e4SLinus Torvalds int ret = 0; 14911da177e4SLinus Torvalds unsigned int hash = fib_laddr_hashfn(local); 14921da177e4SLinus Torvalds struct hlist_head *head = &fib_info_laddrhash[hash]; 14935a56a0b3SMark Tomlinson struct net *net = dev_net(dev); 14945a56a0b3SMark Tomlinson int tb_id = l3mdev_fib_table(dev); 14951da177e4SLinus Torvalds struct fib_info *fi; 14961da177e4SLinus Torvalds 149751456b29SIan Morris if (!fib_info_laddrhash || local == 0) 149885326fa5SDenis V. Lunev return 0; 149985326fa5SDenis V. Lunev 1500b67bfe0dSSasha Levin hlist_for_each_entry(fi, head, fib_lhash) { 15015a56a0b3SMark Tomlinson if (!net_eq(fi->fib_net, net) || 15025a56a0b3SMark Tomlinson fi->fib_tb_id != tb_id) 15034814bdbdSDenis V. Lunev continue; 15041da177e4SLinus Torvalds if (fi->fib_prefsrc == local) { 15051da177e4SLinus Torvalds fi->fib_flags |= RTNH_F_DEAD; 15061da177e4SLinus Torvalds ret++; 15071da177e4SLinus Torvalds } 15081da177e4SLinus Torvalds } 150985326fa5SDenis V. Lunev return ret; 15101da177e4SLinus Torvalds } 15111da177e4SLinus Torvalds 1512b75ed8b1SDavid Ahern static int call_fib_nh_notifiers(struct fib_nh *nh, 1513982acb97SIdo Schimmel enum fib_event_type event_type) 1514982acb97SIdo Schimmel { 1515b75ed8b1SDavid Ahern bool ignore_link_down = ip_ignore_linkdown(nh->fib_nh_dev); 1516982acb97SIdo Schimmel struct fib_nh_notifier_info info = { 1517b75ed8b1SDavid Ahern .fib_nh = nh, 1518982acb97SIdo Schimmel }; 1519982acb97SIdo Schimmel 1520982acb97SIdo Schimmel switch (event_type) { 1521982acb97SIdo Schimmel case FIB_EVENT_NH_ADD: 1522b75ed8b1SDavid Ahern if (nh->fib_nh_flags & RTNH_F_DEAD) 1523982acb97SIdo Schimmel break; 1524b75ed8b1SDavid Ahern if (ignore_link_down && nh->fib_nh_flags & RTNH_F_LINKDOWN) 1525982acb97SIdo Schimmel break; 1526b75ed8b1SDavid Ahern return call_fib4_notifiers(dev_net(nh->fib_nh_dev), event_type, 1527982acb97SIdo Schimmel &info.info); 1528982acb97SIdo Schimmel case FIB_EVENT_NH_DEL: 1529b75ed8b1SDavid Ahern if ((ignore_link_down && nh->fib_nh_flags & RTNH_F_LINKDOWN) || 1530b75ed8b1SDavid Ahern (nh->fib_nh_flags & RTNH_F_DEAD)) 1531b75ed8b1SDavid Ahern return call_fib4_notifiers(dev_net(nh->fib_nh_dev), 1532982acb97SIdo Schimmel event_type, &info.info); 1533982acb97SIdo Schimmel default: 1534982acb97SIdo Schimmel break; 1535982acb97SIdo Schimmel } 1536982acb97SIdo Schimmel 1537982acb97SIdo Schimmel return NOTIFY_DONE; 1538982acb97SIdo Schimmel } 1539982acb97SIdo Schimmel 1540af7d6cceSSabrina Dubroca /* Update the PMTU of exceptions when: 1541af7d6cceSSabrina Dubroca * - the new MTU of the first hop becomes smaller than the PMTU 1542af7d6cceSSabrina Dubroca * - the old MTU was the same as the PMTU, and it limited discovery of 1543af7d6cceSSabrina Dubroca * larger MTUs on the path. With that limit raised, we can now 1544af7d6cceSSabrina Dubroca * discover larger MTUs 1545af7d6cceSSabrina Dubroca * A special case is locked exceptions, for which the PMTU is smaller 1546af7d6cceSSabrina Dubroca * than the minimal accepted PMTU: 1547af7d6cceSSabrina Dubroca * - if the new MTU is greater than the PMTU, don't make any change 1548af7d6cceSSabrina Dubroca * - otherwise, unlock and set PMTU 1549af7d6cceSSabrina Dubroca */ 1550af7d6cceSSabrina Dubroca static void nh_update_mtu(struct fib_nh *nh, u32 new, u32 orig) 1551af7d6cceSSabrina Dubroca { 1552af7d6cceSSabrina Dubroca struct fnhe_hash_bucket *bucket; 1553af7d6cceSSabrina Dubroca int i; 1554af7d6cceSSabrina Dubroca 1555af7d6cceSSabrina Dubroca bucket = rcu_dereference_protected(nh->nh_exceptions, 1); 1556af7d6cceSSabrina Dubroca if (!bucket) 1557af7d6cceSSabrina Dubroca return; 1558af7d6cceSSabrina Dubroca 1559af7d6cceSSabrina Dubroca for (i = 0; i < FNHE_HASH_SIZE; i++) { 1560af7d6cceSSabrina Dubroca struct fib_nh_exception *fnhe; 1561af7d6cceSSabrina Dubroca 1562af7d6cceSSabrina Dubroca for (fnhe = rcu_dereference_protected(bucket[i].chain, 1); 1563af7d6cceSSabrina Dubroca fnhe; 1564af7d6cceSSabrina Dubroca fnhe = rcu_dereference_protected(fnhe->fnhe_next, 1)) { 1565af7d6cceSSabrina Dubroca if (fnhe->fnhe_mtu_locked) { 1566af7d6cceSSabrina Dubroca if (new <= fnhe->fnhe_pmtu) { 1567af7d6cceSSabrina Dubroca fnhe->fnhe_pmtu = new; 1568af7d6cceSSabrina Dubroca fnhe->fnhe_mtu_locked = false; 1569af7d6cceSSabrina Dubroca } 1570af7d6cceSSabrina Dubroca } else if (new < fnhe->fnhe_pmtu || 1571af7d6cceSSabrina Dubroca orig == fnhe->fnhe_pmtu) { 1572af7d6cceSSabrina Dubroca fnhe->fnhe_pmtu = new; 1573af7d6cceSSabrina Dubroca } 1574af7d6cceSSabrina Dubroca } 1575af7d6cceSSabrina Dubroca } 1576af7d6cceSSabrina Dubroca } 1577af7d6cceSSabrina Dubroca 1578af7d6cceSSabrina Dubroca void fib_sync_mtu(struct net_device *dev, u32 orig_mtu) 1579af7d6cceSSabrina Dubroca { 1580af7d6cceSSabrina Dubroca unsigned int hash = fib_devindex_hashfn(dev->ifindex); 1581af7d6cceSSabrina Dubroca struct hlist_head *head = &fib_info_devhash[hash]; 1582af7d6cceSSabrina Dubroca struct fib_nh *nh; 1583af7d6cceSSabrina Dubroca 1584af7d6cceSSabrina Dubroca hlist_for_each_entry(nh, head, nh_hash) { 1585b75ed8b1SDavid Ahern if (nh->fib_nh_dev == dev) 1586af7d6cceSSabrina Dubroca nh_update_mtu(nh, dev->mtu, orig_mtu); 1587af7d6cceSSabrina Dubroca } 1588af7d6cceSSabrina Dubroca } 1589af7d6cceSSabrina Dubroca 15904f823defSJulian Anastasov /* Event force Flags Description 15914f823defSJulian Anastasov * NETDEV_CHANGE 0 LINKDOWN Carrier OFF, not for scope host 15924f823defSJulian Anastasov * NETDEV_DOWN 0 LINKDOWN|DEAD Link down, not for scope host 15934f823defSJulian Anastasov * NETDEV_DOWN 1 LINKDOWN|DEAD Last address removed 15944f823defSJulian Anastasov * NETDEV_UNREGISTER 1 LINKDOWN|DEAD Device removed 15954f823defSJulian Anastasov */ 15964f823defSJulian Anastasov int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force) 159785326fa5SDenis V. Lunev { 159885326fa5SDenis V. Lunev int ret = 0; 159985326fa5SDenis V. Lunev int scope = RT_SCOPE_NOWHERE; 16001da177e4SLinus Torvalds struct fib_info *prev_fi = NULL; 16011da177e4SLinus Torvalds unsigned int hash = fib_devindex_hashfn(dev->ifindex); 16021da177e4SLinus Torvalds struct hlist_head *head = &fib_info_devhash[hash]; 16031da177e4SLinus Torvalds struct fib_nh *nh; 16041da177e4SLinus Torvalds 16054f823defSJulian Anastasov if (force) 160685326fa5SDenis V. Lunev scope = -1; 160785326fa5SDenis V. Lunev 1608b67bfe0dSSasha Levin hlist_for_each_entry(nh, head, nh_hash) { 16091da177e4SLinus Torvalds struct fib_info *fi = nh->nh_parent; 16101da177e4SLinus Torvalds int dead; 16111da177e4SLinus Torvalds 16121da177e4SLinus Torvalds BUG_ON(!fi->fib_nhs); 1613b75ed8b1SDavid Ahern if (nh->fib_nh_dev != dev || fi == prev_fi) 16141da177e4SLinus Torvalds continue; 16151da177e4SLinus Torvalds prev_fi = fi; 16161da177e4SLinus Torvalds dead = 0; 16171da177e4SLinus Torvalds change_nexthops(fi) { 1618b75ed8b1SDavid Ahern if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) 16191da177e4SLinus Torvalds dead++; 1620b75ed8b1SDavid Ahern else if (nexthop_nh->fib_nh_dev == dev && 1621b75ed8b1SDavid Ahern nexthop_nh->fib_nh_scope != scope) { 16228a3d0316SAndy Gospodarek switch (event) { 16238a3d0316SAndy Gospodarek case NETDEV_DOWN: 16248a3d0316SAndy Gospodarek case NETDEV_UNREGISTER: 1625b75ed8b1SDavid Ahern nexthop_nh->fib_nh_flags |= RTNH_F_DEAD; 16268a3d0316SAndy Gospodarek /* fall through */ 16278a3d0316SAndy Gospodarek case NETDEV_CHANGE: 1628b75ed8b1SDavid Ahern nexthop_nh->fib_nh_flags |= RTNH_F_LINKDOWN; 16298a3d0316SAndy Gospodarek break; 16308a3d0316SAndy Gospodarek } 1631982acb97SIdo Schimmel call_fib_nh_notifiers(nexthop_nh, 1632982acb97SIdo Schimmel FIB_EVENT_NH_DEL); 16331da177e4SLinus Torvalds dead++; 16341da177e4SLinus Torvalds } 16351da177e4SLinus Torvalds #ifdef CONFIG_IP_ROUTE_MULTIPATH 16368a3d0316SAndy Gospodarek if (event == NETDEV_UNREGISTER && 1637b75ed8b1SDavid Ahern nexthop_nh->fib_nh_dev == dev) { 16381da177e4SLinus Torvalds dead = fi->fib_nhs; 16391da177e4SLinus Torvalds break; 16401da177e4SLinus Torvalds } 16411da177e4SLinus Torvalds #endif 16421da177e4SLinus Torvalds } endfor_nexthops(fi) 16431da177e4SLinus Torvalds if (dead == fi->fib_nhs) { 16448a3d0316SAndy Gospodarek switch (event) { 16458a3d0316SAndy Gospodarek case NETDEV_DOWN: 16468a3d0316SAndy Gospodarek case NETDEV_UNREGISTER: 16471da177e4SLinus Torvalds fi->fib_flags |= RTNH_F_DEAD; 16488a3d0316SAndy Gospodarek /* fall through */ 16498a3d0316SAndy Gospodarek case NETDEV_CHANGE: 16508a3d0316SAndy Gospodarek fi->fib_flags |= RTNH_F_LINKDOWN; 16518a3d0316SAndy Gospodarek break; 16528a3d0316SAndy Gospodarek } 16531da177e4SLinus Torvalds ret++; 16541da177e4SLinus Torvalds } 16550e884c78SPeter Nørlund 16560e884c78SPeter Nørlund fib_rebalance(fi); 16571da177e4SLinus Torvalds } 16581da177e4SLinus Torvalds 16591da177e4SLinus Torvalds return ret; 16601da177e4SLinus Torvalds } 16611da177e4SLinus Torvalds 16620c838ff1SDavid S. Miller /* Must be invoked inside of an RCU protected region. */ 1663c7b371e3SDavid Ahern static void fib_select_default(const struct flowi4 *flp, struct fib_result *res) 16640c838ff1SDavid S. Miller { 16650c838ff1SDavid S. Miller struct fib_info *fi = NULL, *last_resort = NULL; 166656315f9eSAlexander Duyck struct hlist_head *fa_head = res->fa_head; 16670c838ff1SDavid S. Miller struct fib_table *tb = res->table; 166818a912e9SJulian Anastasov u8 slen = 32 - res->prefixlen; 16690c838ff1SDavid S. Miller int order = -1, last_idx = -1; 16702392debcSJulian Anastasov struct fib_alias *fa, *fa1 = NULL; 16712392debcSJulian Anastasov u32 last_prio = res->fi->fib_priority; 16722392debcSJulian Anastasov u8 last_tos = 0; 16730c838ff1SDavid S. Miller 167456315f9eSAlexander Duyck hlist_for_each_entry_rcu(fa, fa_head, fa_list) { 16750c838ff1SDavid S. Miller struct fib_info *next_fi = fa->fa_info; 16760c838ff1SDavid S. Miller 167718a912e9SJulian Anastasov if (fa->fa_slen != slen) 167818a912e9SJulian Anastasov continue; 16792392debcSJulian Anastasov if (fa->fa_tos && fa->fa_tos != flp->flowi4_tos) 16802392debcSJulian Anastasov continue; 168118a912e9SJulian Anastasov if (fa->tb_id != tb->tb_id) 168218a912e9SJulian Anastasov continue; 16832392debcSJulian Anastasov if (next_fi->fib_priority > last_prio && 16842392debcSJulian Anastasov fa->fa_tos == last_tos) { 16852392debcSJulian Anastasov if (last_tos) 16862392debcSJulian Anastasov continue; 16872392debcSJulian Anastasov break; 16882392debcSJulian Anastasov } 16892392debcSJulian Anastasov if (next_fi->fib_flags & RTNH_F_DEAD) 16902392debcSJulian Anastasov continue; 16912392debcSJulian Anastasov last_tos = fa->fa_tos; 16922392debcSJulian Anastasov last_prio = next_fi->fib_priority; 16932392debcSJulian Anastasov 169437e826c5SDavid S. Miller if (next_fi->fib_scope != res->scope || 16950c838ff1SDavid S. Miller fa->fa_type != RTN_UNICAST) 16960c838ff1SDavid S. Miller continue; 1697b75ed8b1SDavid Ahern if (!next_fi->fib_nh[0].fib_nh_gw4 || 1698b75ed8b1SDavid Ahern next_fi->fib_nh[0].fib_nh_scope != RT_SCOPE_LINK) 16990c838ff1SDavid S. Miller continue; 17000c838ff1SDavid S. Miller 17010c838ff1SDavid S. Miller fib_alias_accessed(fa); 17020c838ff1SDavid S. Miller 170351456b29SIan Morris if (!fi) { 17040c838ff1SDavid S. Miller if (next_fi != res->fi) 17050c838ff1SDavid S. Miller break; 17062392debcSJulian Anastasov fa1 = fa; 17070c838ff1SDavid S. Miller } else if (!fib_detect_death(fi, order, &last_resort, 17082392debcSJulian Anastasov &last_idx, fa1->fa_default)) { 17090c838ff1SDavid S. Miller fib_result_assign(res, fi); 17102392debcSJulian Anastasov fa1->fa_default = order; 17110c838ff1SDavid S. Miller goto out; 17120c838ff1SDavid S. Miller } 17130c838ff1SDavid S. Miller fi = next_fi; 17140c838ff1SDavid S. Miller order++; 17150c838ff1SDavid S. Miller } 17160c838ff1SDavid S. Miller 171751456b29SIan Morris if (order <= 0 || !fi) { 17182392debcSJulian Anastasov if (fa1) 17192392debcSJulian Anastasov fa1->fa_default = -1; 17200c838ff1SDavid S. Miller goto out; 17210c838ff1SDavid S. Miller } 17220c838ff1SDavid S. Miller 17230c838ff1SDavid S. Miller if (!fib_detect_death(fi, order, &last_resort, &last_idx, 17242392debcSJulian Anastasov fa1->fa_default)) { 17250c838ff1SDavid S. Miller fib_result_assign(res, fi); 17262392debcSJulian Anastasov fa1->fa_default = order; 17270c838ff1SDavid S. Miller goto out; 17280c838ff1SDavid S. Miller } 17290c838ff1SDavid S. Miller 17300c838ff1SDavid S. Miller if (last_idx >= 0) 17310c838ff1SDavid S. Miller fib_result_assign(res, last_resort); 17322392debcSJulian Anastasov fa1->fa_default = last_idx; 17330c838ff1SDavid S. Miller out: 173431d40937SEric Dumazet return; 17350c838ff1SDavid S. Miller } 17360c838ff1SDavid S. Miller 17371da177e4SLinus Torvalds /* 17386a31d2a9SEric Dumazet * Dead device goes up. We wake up dead nexthops. 17396a31d2a9SEric Dumazet * It takes sense only on multipath routes. 17401da177e4SLinus Torvalds */ 17418a3d0316SAndy Gospodarek int fib_sync_up(struct net_device *dev, unsigned int nh_flags) 17421da177e4SLinus Torvalds { 17431da177e4SLinus Torvalds struct fib_info *prev_fi; 17441da177e4SLinus Torvalds unsigned int hash; 17451da177e4SLinus Torvalds struct hlist_head *head; 17461da177e4SLinus Torvalds struct fib_nh *nh; 17471da177e4SLinus Torvalds int ret; 17481da177e4SLinus Torvalds 17491da177e4SLinus Torvalds if (!(dev->flags & IFF_UP)) 17501da177e4SLinus Torvalds return 0; 17511da177e4SLinus Torvalds 1752c9b3292eSJulian Anastasov if (nh_flags & RTNH_F_DEAD) { 1753c9b3292eSJulian Anastasov unsigned int flags = dev_get_flags(dev); 1754c9b3292eSJulian Anastasov 1755c9b3292eSJulian Anastasov if (flags & (IFF_RUNNING | IFF_LOWER_UP)) 1756c9b3292eSJulian Anastasov nh_flags |= RTNH_F_LINKDOWN; 1757c9b3292eSJulian Anastasov } 1758c9b3292eSJulian Anastasov 17591da177e4SLinus Torvalds prev_fi = NULL; 17601da177e4SLinus Torvalds hash = fib_devindex_hashfn(dev->ifindex); 17611da177e4SLinus Torvalds head = &fib_info_devhash[hash]; 17621da177e4SLinus Torvalds ret = 0; 17631da177e4SLinus Torvalds 1764b67bfe0dSSasha Levin hlist_for_each_entry(nh, head, nh_hash) { 17651da177e4SLinus Torvalds struct fib_info *fi = nh->nh_parent; 17661da177e4SLinus Torvalds int alive; 17671da177e4SLinus Torvalds 17681da177e4SLinus Torvalds BUG_ON(!fi->fib_nhs); 1769b75ed8b1SDavid Ahern if (nh->fib_nh_dev != dev || fi == prev_fi) 17701da177e4SLinus Torvalds continue; 17711da177e4SLinus Torvalds 17721da177e4SLinus Torvalds prev_fi = fi; 17731da177e4SLinus Torvalds alive = 0; 17741da177e4SLinus Torvalds change_nexthops(fi) { 1775b75ed8b1SDavid Ahern if (!(nexthop_nh->fib_nh_flags & nh_flags)) { 17761da177e4SLinus Torvalds alive++; 17771da177e4SLinus Torvalds continue; 17781da177e4SLinus Torvalds } 1779b75ed8b1SDavid Ahern if (!nexthop_nh->fib_nh_dev || 1780b75ed8b1SDavid Ahern !(nexthop_nh->fib_nh_dev->flags & IFF_UP)) 17811da177e4SLinus Torvalds continue; 1782b75ed8b1SDavid Ahern if (nexthop_nh->fib_nh_dev != dev || 178371fceff0SDavid S. Miller !__in_dev_get_rtnl(dev)) 17841da177e4SLinus Torvalds continue; 17851da177e4SLinus Torvalds alive++; 1786b75ed8b1SDavid Ahern nexthop_nh->fib_nh_flags &= ~nh_flags; 1787982acb97SIdo Schimmel call_fib_nh_notifiers(nexthop_nh, FIB_EVENT_NH_ADD); 17881da177e4SLinus Torvalds } endfor_nexthops(fi) 17891da177e4SLinus Torvalds 17901da177e4SLinus Torvalds if (alive > 0) { 17918a3d0316SAndy Gospodarek fi->fib_flags &= ~nh_flags; 17921da177e4SLinus Torvalds ret++; 17931da177e4SLinus Torvalds } 17940e884c78SPeter Nørlund 17950e884c78SPeter Nørlund fib_rebalance(fi); 17961da177e4SLinus Torvalds } 17971da177e4SLinus Torvalds 17981da177e4SLinus Torvalds return ret; 17991da177e4SLinus Torvalds } 18001da177e4SLinus Torvalds 18018a3d0316SAndy Gospodarek #ifdef CONFIG_IP_ROUTE_MULTIPATH 1802a6db4494SDavid Ahern static bool fib_good_nh(const struct fib_nh *nh) 1803a6db4494SDavid Ahern { 1804a6db4494SDavid Ahern int state = NUD_REACHABLE; 1805a6db4494SDavid Ahern 1806b75ed8b1SDavid Ahern if (nh->fib_nh_scope == RT_SCOPE_LINK) { 1807a6db4494SDavid Ahern struct neighbour *n; 1808a6db4494SDavid Ahern 1809a6db4494SDavid Ahern rcu_read_lock_bh(); 1810a6db4494SDavid Ahern 1811b75ed8b1SDavid Ahern n = __ipv4_neigh_lookup_noref(nh->fib_nh_dev, 1812b75ed8b1SDavid Ahern (__force u32)nh->fib_nh_gw4); 1813a6db4494SDavid Ahern if (n) 1814a6db4494SDavid Ahern state = n->nud_state; 1815a6db4494SDavid Ahern 1816a6db4494SDavid Ahern rcu_read_unlock_bh(); 1817a6db4494SDavid Ahern } 1818a6db4494SDavid Ahern 1819a6db4494SDavid Ahern return !!(state & NUD_VALID); 1820a6db4494SDavid Ahern } 18218a3d0316SAndy Gospodarek 18220e884c78SPeter Nørlund void fib_select_multipath(struct fib_result *res, int hash) 18231da177e4SLinus Torvalds { 18241da177e4SLinus Torvalds struct fib_info *fi = res->fi; 1825a6db4494SDavid Ahern struct net *net = fi->fib_net; 1826a6db4494SDavid Ahern bool first = false; 18271da177e4SLinus Torvalds 1828eba618abSDavid Ahern change_nexthops(fi) { 18296174a30dSXin Long if (net->ipv4.sysctl_fib_multipath_use_neigh) { 1830eba618abSDavid Ahern if (!fib_good_nh(nexthop_nh)) 18310eeb075fSAndy Gospodarek continue; 1832a6db4494SDavid Ahern if (!first) { 1833a6db4494SDavid Ahern res->nh_sel = nhsel; 1834eba618abSDavid Ahern res->nhc = &nexthop_nh->nh_common; 1835a6db4494SDavid Ahern first = true; 1836a6db4494SDavid Ahern } 18376174a30dSXin Long } 18386174a30dSXin Long 1839eba618abSDavid Ahern if (hash > atomic_read(&nexthop_nh->fib_nh_upper_bound)) 18406174a30dSXin Long continue; 18416174a30dSXin Long 18426174a30dSXin Long res->nh_sel = nhsel; 1843eba618abSDavid Ahern res->nhc = &nexthop_nh->nh_common; 18446174a30dSXin Long return; 18451da177e4SLinus Torvalds } endfor_nexthops(fi); 18461da177e4SLinus Torvalds } 18471da177e4SLinus Torvalds #endif 18483ce58d84SDavid Ahern 18493ce58d84SDavid Ahern void fib_select_path(struct net *net, struct fib_result *res, 1850bf4e0a3dSNikolay Aleksandrov struct flowi4 *fl4, const struct sk_buff *skb) 18513ce58d84SDavid Ahern { 18520d876f2cSDavid Ahern if (fl4->flowi4_oif && !(fl4->flowi4_flags & FLOWI_FLAG_SKIP_NH_OIF)) 18530d876f2cSDavid Ahern goto check_saddr; 18547a18c5b9SDavid Ahern 18553ce58d84SDavid Ahern #ifdef CONFIG_IP_ROUTE_MULTIPATH 18560d876f2cSDavid Ahern if (res->fi->fib_nhs > 1) { 18577efc0b6bSDavid Ahern int h = fib_multipath_hash(net, fl4, skb, NULL); 18589920e48bSPaolo Abeni 1859bf4e0a3dSNikolay Aleksandrov fib_select_multipath(res, h); 18603ce58d84SDavid Ahern } 18613ce58d84SDavid Ahern else 18623ce58d84SDavid Ahern #endif 18633ce58d84SDavid Ahern if (!res->prefixlen && 18643ce58d84SDavid Ahern res->table->tb_num_default > 1 && 18650d876f2cSDavid Ahern res->type == RTN_UNICAST) 18663ce58d84SDavid Ahern fib_select_default(fl4, res); 18673ce58d84SDavid Ahern 18680d876f2cSDavid Ahern check_saddr: 18693ce58d84SDavid Ahern if (!fl4->saddr) 1870eba618abSDavid Ahern fl4->saddr = fib_result_prefsrc(net, res); 18713ce58d84SDavid Ahern } 1872