11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Linux INET6 implementation 31da177e4SLinus Torvalds * FIB front-end. 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Authors: 61da177e4SLinus Torvalds * Pedro Roque <roque@di.fc.ul.pt> 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 91da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 101da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 111da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds /* Changes: 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * YOSHIFUJI Hideaki @USAGI 171da177e4SLinus Torvalds * reworked default router selection. 181da177e4SLinus Torvalds * - respect outgoing interface 191da177e4SLinus Torvalds * - select from (probably) reachable routers (i.e. 201da177e4SLinus Torvalds * routers in REACHABLE, STALE, DELAY or PROBE states). 211da177e4SLinus Torvalds * - always select the same router if it is (probably) 221da177e4SLinus Torvalds * reachable. otherwise, round-robin the list. 23c0bece9fSYOSHIFUJI Hideaki * Ville Nuorvala 24c0bece9fSYOSHIFUJI Hideaki * Fixed routing subtrees. 251da177e4SLinus Torvalds */ 261da177e4SLinus Torvalds 27f3213831SJoe Perches #define pr_fmt(fmt) "IPv6: " fmt 28f3213831SJoe Perches 294fc268d2SRandy Dunlap #include <linux/capability.h> 301da177e4SLinus Torvalds #include <linux/errno.h> 31bc3b2d7fSPaul Gortmaker #include <linux/export.h> 321da177e4SLinus Torvalds #include <linux/types.h> 331da177e4SLinus Torvalds #include <linux/times.h> 341da177e4SLinus Torvalds #include <linux/socket.h> 351da177e4SLinus Torvalds #include <linux/sockios.h> 361da177e4SLinus Torvalds #include <linux/net.h> 371da177e4SLinus Torvalds #include <linux/route.h> 381da177e4SLinus Torvalds #include <linux/netdevice.h> 391da177e4SLinus Torvalds #include <linux/in6.h> 407bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h> 411da177e4SLinus Torvalds #include <linux/init.h> 421da177e4SLinus Torvalds #include <linux/if_arp.h> 431da177e4SLinus Torvalds #include <linux/proc_fs.h> 441da177e4SLinus Torvalds #include <linux/seq_file.h> 455b7c931dSDaniel Lezcano #include <linux/nsproxy.h> 465a0e3ad6STejun Heo #include <linux/slab.h> 4735732d01SWei Wang #include <linux/jhash.h> 48457c4cbcSEric W. Biederman #include <net/net_namespace.h> 491da177e4SLinus Torvalds #include <net/snmp.h> 501da177e4SLinus Torvalds #include <net/ipv6.h> 511da177e4SLinus Torvalds #include <net/ip6_fib.h> 521da177e4SLinus Torvalds #include <net/ip6_route.h> 531da177e4SLinus Torvalds #include <net/ndisc.h> 541da177e4SLinus Torvalds #include <net/addrconf.h> 551da177e4SLinus Torvalds #include <net/tcp.h> 561da177e4SLinus Torvalds #include <linux/rtnetlink.h> 571da177e4SLinus Torvalds #include <net/dst.h> 58904af04dSJiri Benc #include <net/dst_metadata.h> 591da177e4SLinus Torvalds #include <net/xfrm.h> 608d71740cSTom Tucker #include <net/netevent.h> 6121713ebcSThomas Graf #include <net/netlink.h> 6251ebd318SNicolas Dichtel #include <net/nexthop.h> 6319e42e45SRoopa Prabhu #include <net/lwtunnel.h> 64904af04dSJiri Benc #include <net/ip_tunnels.h> 65ca254490SDavid Ahern #include <net/l3mdev.h> 66b811580dSDavid Ahern #include <trace/events/fib6.h> 671da177e4SLinus Torvalds 687c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 711da177e4SLinus Torvalds #include <linux/sysctl.h> 721da177e4SLinus Torvalds #endif 731da177e4SLinus Torvalds 74afc154e9SHannes Frederic Sowa enum rt6_nud_state { 757e980569SJiri Benc RT6_NUD_FAIL_HARD = -3, 767e980569SJiri Benc RT6_NUD_FAIL_PROBE = -2, 777e980569SJiri Benc RT6_NUD_FAIL_DO_RR = -1, 78afc154e9SHannes Frederic Sowa RT6_NUD_SUCCEED = 1 79afc154e9SHannes Frederic Sowa }; 80afc154e9SHannes Frederic Sowa 811da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 820dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst); 83ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst); 841da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *); 851da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *); 861da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *, 871da177e4SLinus Torvalds struct net_device *dev, int how); 88569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops); 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds static int ip6_pkt_discard(struct sk_buff *skb); 91ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); 927150aedeSKamala R static int ip6_pkt_prohibit(struct sk_buff *skb); 93ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb); 941da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb); 956700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 966700c270SDavid S. Miller struct sk_buff *skb, u32 mtu); 976700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, 986700c270SDavid S. Miller struct sk_buff *skb); 994b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt); 10052bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict); 10116a16cd3SDavid Ahern static size_t rt6_nlmsg_size(struct rt6_info *rt); 10216a16cd3SDavid Ahern static int rt6_fill_node(struct net *net, 10316a16cd3SDavid Ahern struct sk_buff *skb, struct rt6_info *rt, 10416a16cd3SDavid Ahern struct in6_addr *dst, struct in6_addr *src, 10516a16cd3SDavid Ahern int iif, int type, u32 portid, u32 seq, 10616a16cd3SDavid Ahern unsigned int flags); 10735732d01SWei Wang static struct rt6_info *rt6_find_cached_rt(struct rt6_info *rt, 10835732d01SWei Wang struct in6_addr *daddr, 10935732d01SWei Wang struct in6_addr *saddr); 1101da177e4SLinus Torvalds 11170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 112efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 113b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 114830218c1SDavid Ahern const struct in6_addr *gwaddr, 115830218c1SDavid Ahern struct net_device *dev, 11695c96174SEric Dumazet unsigned int pref); 117efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 118b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 119830218c1SDavid Ahern const struct in6_addr *gwaddr, 120830218c1SDavid Ahern struct net_device *dev); 12170ceb4f5SYOSHIFUJI Hideaki #endif 12270ceb4f5SYOSHIFUJI Hideaki 1238d0b94afSMartin KaFai Lau struct uncached_list { 1248d0b94afSMartin KaFai Lau spinlock_t lock; 1258d0b94afSMartin KaFai Lau struct list_head head; 1268d0b94afSMartin KaFai Lau }; 1278d0b94afSMartin KaFai Lau 1288d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list); 1298d0b94afSMartin KaFai Lau 130510c321bSXin Long void rt6_uncached_list_add(struct rt6_info *rt) 1318d0b94afSMartin KaFai Lau { 1328d0b94afSMartin KaFai Lau struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list); 1338d0b94afSMartin KaFai Lau 1348d0b94afSMartin KaFai Lau rt->rt6i_uncached_list = ul; 1358d0b94afSMartin KaFai Lau 1368d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1378d0b94afSMartin KaFai Lau list_add_tail(&rt->rt6i_uncached, &ul->head); 1388d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1398d0b94afSMartin KaFai Lau } 1408d0b94afSMartin KaFai Lau 141510c321bSXin Long void rt6_uncached_list_del(struct rt6_info *rt) 1428d0b94afSMartin KaFai Lau { 1438d0b94afSMartin KaFai Lau if (!list_empty(&rt->rt6i_uncached)) { 1448d0b94afSMartin KaFai Lau struct uncached_list *ul = rt->rt6i_uncached_list; 14581eb8447SWei Wang struct net *net = dev_net(rt->dst.dev); 1468d0b94afSMartin KaFai Lau 1478d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1488d0b94afSMartin KaFai Lau list_del(&rt->rt6i_uncached); 14981eb8447SWei Wang atomic_dec(&net->ipv6.rt6_stats->fib_rt_uncache); 1508d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1518d0b94afSMartin KaFai Lau } 1528d0b94afSMartin KaFai Lau } 1538d0b94afSMartin KaFai Lau 1548d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) 1558d0b94afSMartin KaFai Lau { 1568d0b94afSMartin KaFai Lau struct net_device *loopback_dev = net->loopback_dev; 1578d0b94afSMartin KaFai Lau int cpu; 1588d0b94afSMartin KaFai Lau 159e332bc67SEric W. Biederman if (dev == loopback_dev) 160e332bc67SEric W. Biederman return; 161e332bc67SEric W. Biederman 1628d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 1638d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 1648d0b94afSMartin KaFai Lau struct rt6_info *rt; 1658d0b94afSMartin KaFai Lau 1668d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1678d0b94afSMartin KaFai Lau list_for_each_entry(rt, &ul->head, rt6i_uncached) { 1688d0b94afSMartin KaFai Lau struct inet6_dev *rt_idev = rt->rt6i_idev; 1698d0b94afSMartin KaFai Lau struct net_device *rt_dev = rt->dst.dev; 1708d0b94afSMartin KaFai Lau 171e332bc67SEric W. Biederman if (rt_idev->dev == dev) { 1728d0b94afSMartin KaFai Lau rt->rt6i_idev = in6_dev_get(loopback_dev); 1738d0b94afSMartin KaFai Lau in6_dev_put(rt_idev); 1748d0b94afSMartin KaFai Lau } 1758d0b94afSMartin KaFai Lau 176e332bc67SEric W. Biederman if (rt_dev == dev) { 1778d0b94afSMartin KaFai Lau rt->dst.dev = loopback_dev; 1788d0b94afSMartin KaFai Lau dev_hold(rt->dst.dev); 1798d0b94afSMartin KaFai Lau dev_put(rt_dev); 1808d0b94afSMartin KaFai Lau } 1818d0b94afSMartin KaFai Lau } 1828d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1838d0b94afSMartin KaFai Lau } 1848d0b94afSMartin KaFai Lau } 1858d0b94afSMartin KaFai Lau 186d52d3997SMartin KaFai Lau static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt) 187d52d3997SMartin KaFai Lau { 1883a2232e9SDavid Miller return dst_metrics_write_ptr(&rt->from->dst); 189d52d3997SMartin KaFai Lau } 190d52d3997SMartin KaFai Lau 19106582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) 19206582540SDavid S. Miller { 19306582540SDavid S. Miller struct rt6_info *rt = (struct rt6_info *)dst; 19406582540SDavid S. Miller 195d52d3997SMartin KaFai Lau if (rt->rt6i_flags & RTF_PCPU) 196d52d3997SMartin KaFai Lau return rt6_pcpu_cow_metrics(rt); 197d52d3997SMartin KaFai Lau else if (rt->rt6i_flags & RTF_CACHE) 1984b32b5adSMartin KaFai Lau return NULL; 1994b32b5adSMartin KaFai Lau else 2003b471175SMartin KaFai Lau return dst_cow_metrics_generic(dst, old); 20106582540SDavid S. Miller } 20206582540SDavid S. Miller 203f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, 204f894cbf8SDavid S. Miller struct sk_buff *skb, 205f894cbf8SDavid S. Miller const void *daddr) 20639232973SDavid S. Miller { 20739232973SDavid S. Miller struct in6_addr *p = &rt->rt6i_gateway; 20839232973SDavid S. Miller 209a7563f34SDavid S. Miller if (!ipv6_addr_any(p)) 21039232973SDavid S. Miller return (const void *) p; 211f894cbf8SDavid S. Miller else if (skb) 212f894cbf8SDavid S. Miller return &ipv6_hdr(skb)->daddr; 21339232973SDavid S. Miller return daddr; 21439232973SDavid S. Miller } 21539232973SDavid S. Miller 216f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, 217f894cbf8SDavid S. Miller struct sk_buff *skb, 218f894cbf8SDavid S. Miller const void *daddr) 219d3aaeb38SDavid S. Miller { 22039232973SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 22139232973SDavid S. Miller struct neighbour *n; 22239232973SDavid S. Miller 223f894cbf8SDavid S. Miller daddr = choose_neigh_daddr(rt, skb, daddr); 2248e022ee6SYOSHIFUJI Hideaki / 吉藤英明 n = __ipv6_neigh_lookup(dst->dev, daddr); 225f83c7790SDavid S. Miller if (n) 226f83c7790SDavid S. Miller return n; 227f83c7790SDavid S. Miller return neigh_create(&nd_tbl, daddr, dst->dev); 228f83c7790SDavid S. Miller } 229f83c7790SDavid S. Miller 23063fca65dSJulian Anastasov static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr) 23163fca65dSJulian Anastasov { 23263fca65dSJulian Anastasov struct net_device *dev = dst->dev; 23363fca65dSJulian Anastasov struct rt6_info *rt = (struct rt6_info *)dst; 23463fca65dSJulian Anastasov 23563fca65dSJulian Anastasov daddr = choose_neigh_daddr(rt, NULL, daddr); 23663fca65dSJulian Anastasov if (!daddr) 23763fca65dSJulian Anastasov return; 23863fca65dSJulian Anastasov if (dev->flags & (IFF_NOARP | IFF_LOOPBACK)) 23963fca65dSJulian Anastasov return; 24063fca65dSJulian Anastasov if (ipv6_addr_is_multicast((const struct in6_addr *)daddr)) 24163fca65dSJulian Anastasov return; 24263fca65dSJulian Anastasov __ipv6_confirm_neigh(dev, daddr); 24363fca65dSJulian Anastasov } 24463fca65dSJulian Anastasov 2459a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 2461da177e4SLinus Torvalds .family = AF_INET6, 2471da177e4SLinus Torvalds .gc = ip6_dst_gc, 2481da177e4SLinus Torvalds .gc_thresh = 1024, 2491da177e4SLinus Torvalds .check = ip6_dst_check, 2500dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 251ebb762f2SSteffen Klassert .mtu = ip6_mtu, 25206582540SDavid S. Miller .cow_metrics = ipv6_cow_metrics, 2531da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 2541da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 2551da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 2561da177e4SLinus Torvalds .link_failure = ip6_link_failure, 2571da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 2586e157b6aSDavid S. Miller .redirect = rt6_do_redirect, 2599f8955ccSEric W. Biederman .local_out = __ip6_local_out, 260d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 26163fca65dSJulian Anastasov .confirm_neigh = ip6_confirm_neigh, 2621da177e4SLinus Torvalds }; 2631da177e4SLinus Torvalds 264ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 265ec831ea7SRoland Dreier { 266618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 267618f9bc7SSteffen Klassert 268618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 269ec831ea7SRoland Dreier } 270ec831ea7SRoland Dreier 2716700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, 2726700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 27314e50e57SDavid S. Miller { 27414e50e57SDavid S. Miller } 27514e50e57SDavid S. Miller 2766700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, 2776700c270SDavid S. Miller struct sk_buff *skb) 278b587ee3bSDavid S. Miller { 279b587ee3bSDavid S. Miller } 280b587ee3bSDavid S. Miller 28114e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 28214e50e57SDavid S. Miller .family = AF_INET6, 28314e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 28414e50e57SDavid S. Miller .check = ip6_dst_check, 285ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 286214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 28714e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 288b587ee3bSDavid S. Miller .redirect = ip6_rt_blackhole_redirect, 2890a1f5962SMartin KaFai Lau .cow_metrics = dst_cow_metrics_generic, 290d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 29114e50e57SDavid S. Miller }; 29214e50e57SDavid S. Miller 29362fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 29414edd87dSLi RongQing [RTAX_HOPLIMIT - 1] = 0, 29562fa8a84SDavid S. Miller }; 29662fa8a84SDavid S. Miller 297fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = { 2981da177e4SLinus Torvalds .dst = { 2991da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 3001da177e4SLinus Torvalds .__use = 1, 3012c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 3021da177e4SLinus Torvalds .error = -ENETUNREACH, 3031da177e4SLinus Torvalds .input = ip6_pkt_discard, 3041da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 3051da177e4SLinus Torvalds }, 3061da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 3074f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 3081da177e4SLinus Torvalds .rt6i_metric = ~(u32) 0, 3091da177e4SLinus Torvalds .rt6i_ref = ATOMIC_INIT(1), 310e8478e80SDavid Ahern .fib6_type = RTN_UNREACHABLE, 3111da177e4SLinus Torvalds }; 3121da177e4SLinus Torvalds 313101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 314101367c2SThomas Graf 315fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = { 316101367c2SThomas Graf .dst = { 317101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 318101367c2SThomas Graf .__use = 1, 3192c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 320101367c2SThomas Graf .error = -EACCES, 3219ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 3229ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 323101367c2SThomas Graf }, 324101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 3254f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 326101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 327101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 328e8478e80SDavid Ahern .fib6_type = RTN_PROHIBIT, 329101367c2SThomas Graf }; 330101367c2SThomas Graf 331fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = { 332101367c2SThomas Graf .dst = { 333101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 334101367c2SThomas Graf .__use = 1, 3352c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 336101367c2SThomas Graf .error = -EINVAL, 337352e512cSHerbert Xu .input = dst_discard, 338ede2059dSEric W. Biederman .output = dst_discard_out, 339101367c2SThomas Graf }, 340101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 3414f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 342101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 343101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 344e8478e80SDavid Ahern .fib6_type = RTN_BLACKHOLE, 345101367c2SThomas Graf }; 346101367c2SThomas Graf 347101367c2SThomas Graf #endif 348101367c2SThomas Graf 349ebfa45f0SMartin KaFai Lau static void rt6_info_init(struct rt6_info *rt) 350ebfa45f0SMartin KaFai Lau { 351ebfa45f0SMartin KaFai Lau struct dst_entry *dst = &rt->dst; 352ebfa45f0SMartin KaFai Lau 353ebfa45f0SMartin KaFai Lau memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 354ebfa45f0SMartin KaFai Lau INIT_LIST_HEAD(&rt->rt6i_siblings); 355ebfa45f0SMartin KaFai Lau INIT_LIST_HEAD(&rt->rt6i_uncached); 356ebfa45f0SMartin KaFai Lau } 357ebfa45f0SMartin KaFai Lau 3581da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 359d52d3997SMartin KaFai Lau static struct rt6_info *__ip6_dst_alloc(struct net *net, 360957c665fSDavid S. Miller struct net_device *dev, 361ad706862SMartin KaFai Lau int flags) 3621da177e4SLinus Torvalds { 36397bab73fSDavid S. Miller struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 364b2a9c0edSWei Wang 1, DST_OBSOLETE_FORCE_CHK, flags); 365cf911662SDavid S. Miller 36681eb8447SWei Wang if (rt) { 367ebfa45f0SMartin KaFai Lau rt6_info_init(rt); 36881eb8447SWei Wang atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc); 36981eb8447SWei Wang } 3708104891bSSteffen Klassert 371cf911662SDavid S. Miller return rt; 3721da177e4SLinus Torvalds } 3731da177e4SLinus Torvalds 3749ab179d8SDavid Ahern struct rt6_info *ip6_dst_alloc(struct net *net, 375d52d3997SMartin KaFai Lau struct net_device *dev, 376ad706862SMartin KaFai Lau int flags) 377d52d3997SMartin KaFai Lau { 378ad706862SMartin KaFai Lau struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags); 379d52d3997SMartin KaFai Lau 380d52d3997SMartin KaFai Lau if (rt) { 381d52d3997SMartin KaFai Lau rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC); 382bfd8e5a4SEric Dumazet if (!rt->rt6i_pcpu) { 383587fea74SWei Wang dst_release_immediate(&rt->dst); 384d52d3997SMartin KaFai Lau return NULL; 385d52d3997SMartin KaFai Lau } 386d52d3997SMartin KaFai Lau } 387d52d3997SMartin KaFai Lau 388d52d3997SMartin KaFai Lau return rt; 389d52d3997SMartin KaFai Lau } 3909ab179d8SDavid Ahern EXPORT_SYMBOL(ip6_dst_alloc); 391d52d3997SMartin KaFai Lau 3921da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 3931da177e4SLinus Torvalds { 3941da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 39535732d01SWei Wang struct rt6_exception_bucket *bucket; 3963a2232e9SDavid Miller struct rt6_info *from = rt->from; 3978d0b94afSMartin KaFai Lau struct inet6_dev *idev; 3981da177e4SLinus Torvalds 3998e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 400d52d3997SMartin KaFai Lau free_percpu(rt->rt6i_pcpu); 4018d0b94afSMartin KaFai Lau rt6_uncached_list_del(rt); 4028d0b94afSMartin KaFai Lau 4038d0b94afSMartin KaFai Lau idev = rt->rt6i_idev; 40438308473SDavid S. Miller if (idev) { 4051da177e4SLinus Torvalds rt->rt6i_idev = NULL; 4061da177e4SLinus Torvalds in6_dev_put(idev); 4071da177e4SLinus Torvalds } 40835732d01SWei Wang bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, 1); 40935732d01SWei Wang if (bucket) { 41035732d01SWei Wang rt->rt6i_exception_bucket = NULL; 41135732d01SWei Wang kfree(bucket); 41235732d01SWei Wang } 4131716a961SGao feng 4143a2232e9SDavid Miller rt->from = NULL; 4153a2232e9SDavid Miller dst_release(&from->dst); 416b3419363SDavid S. Miller } 417b3419363SDavid S. Miller 4181da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 4191da177e4SLinus Torvalds int how) 4201da177e4SLinus Torvalds { 4211da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 4221da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 4235a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 424c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 4251da177e4SLinus Torvalds 426e5645f51SWei Wang if (idev && idev->dev != loopback_dev) { 427e5645f51SWei Wang struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev); 42838308473SDavid S. Miller if (loopback_idev) { 4291da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 4301da177e4SLinus Torvalds in6_dev_put(idev); 4311da177e4SLinus Torvalds } 4321da177e4SLinus Torvalds } 43397cac082SDavid S. Miller } 4341da177e4SLinus Torvalds 4355973fb1eSMartin KaFai Lau static bool __rt6_check_expired(const struct rt6_info *rt) 4365973fb1eSMartin KaFai Lau { 4375973fb1eSMartin KaFai Lau if (rt->rt6i_flags & RTF_EXPIRES) 4385973fb1eSMartin KaFai Lau return time_after(jiffies, rt->dst.expires); 4395973fb1eSMartin KaFai Lau else 4405973fb1eSMartin KaFai Lau return false; 4415973fb1eSMartin KaFai Lau } 4425973fb1eSMartin KaFai Lau 443a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt) 4441da177e4SLinus Torvalds { 4451716a961SGao feng if (rt->rt6i_flags & RTF_EXPIRES) { 4461716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 447a50feda5SEric Dumazet return true; 4483a2232e9SDavid Miller } else if (rt->from) { 4491e2ea8adSXin Long return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK || 4503a2232e9SDavid Miller rt6_check_expired(rt->from); 4511716a961SGao feng } 452a50feda5SEric Dumazet return false; 4531da177e4SLinus Torvalds } 4541da177e4SLinus Torvalds 455b4bac172SDavid Ahern static struct rt6_info *rt6_multipath_select(const struct net *net, 456b4bac172SDavid Ahern struct rt6_info *match, 45752bd4c0cSNicolas Dichtel struct flowi6 *fl6, int oif, 458b75cc8f9SDavid Ahern const struct sk_buff *skb, 45952bd4c0cSNicolas Dichtel int strict) 46051ebd318SNicolas Dichtel { 46151ebd318SNicolas Dichtel struct rt6_info *sibling, *next_sibling; 46251ebd318SNicolas Dichtel 463b673d6ccSJakub Sitnicki /* We might have already computed the hash for ICMPv6 errors. In such 464b673d6ccSJakub Sitnicki * case it will always be non-zero. Otherwise now is the time to do it. 465b673d6ccSJakub Sitnicki */ 466b673d6ccSJakub Sitnicki if (!fl6->mp_hash) 467b4bac172SDavid Ahern fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL); 468b673d6ccSJakub Sitnicki 4695e670d84SDavid Ahern if (fl6->mp_hash <= atomic_read(&match->fib6_nh.nh_upper_bound)) 4703d709f69SIdo Schimmel return match; 471bbfcd776SIdo Schimmel 4723d709f69SIdo Schimmel list_for_each_entry_safe(sibling, next_sibling, &match->rt6i_siblings, 4733d709f69SIdo Schimmel rt6i_siblings) { 4745e670d84SDavid Ahern int nh_upper_bound; 4755e670d84SDavid Ahern 4765e670d84SDavid Ahern nh_upper_bound = atomic_read(&sibling->fib6_nh.nh_upper_bound); 4775e670d84SDavid Ahern if (fl6->mp_hash > nh_upper_bound) 4783d709f69SIdo Schimmel continue; 47952bd4c0cSNicolas Dichtel if (rt6_score_route(sibling, oif, strict) < 0) 48052bd4c0cSNicolas Dichtel break; 48151ebd318SNicolas Dichtel match = sibling; 48251ebd318SNicolas Dichtel break; 48351ebd318SNicolas Dichtel } 4843d709f69SIdo Schimmel 48551ebd318SNicolas Dichtel return match; 48651ebd318SNicolas Dichtel } 48751ebd318SNicolas Dichtel 4881da177e4SLinus Torvalds /* 48966f5d6ceSWei Wang * Route lookup. rcu_read_lock() should be held. 4901da177e4SLinus Torvalds */ 4911da177e4SLinus Torvalds 4928ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 4938ed67789SDaniel Lezcano struct rt6_info *rt, 494b71d1d42SEric Dumazet const struct in6_addr *saddr, 4951da177e4SLinus Torvalds int oif, 496d420895eSYOSHIFUJI Hideaki int flags) 4971da177e4SLinus Torvalds { 4981da177e4SLinus Torvalds struct rt6_info *local = NULL; 4991da177e4SLinus Torvalds struct rt6_info *sprt; 5001da177e4SLinus Torvalds 5015e670d84SDavid Ahern if (!oif && ipv6_addr_any(saddr) && 5025e670d84SDavid Ahern !(rt->fib6_nh.nh_flags & RTNH_F_DEAD)) 5038067bb8cSIdo Schimmel return rt; 504dd3abc4eSYOSHIFUJI Hideaki 505071fb37eSDavid Miller for (sprt = rt; sprt; sprt = rcu_dereference(sprt->rt6_next)) { 5065e670d84SDavid Ahern const struct net_device *dev = sprt->fib6_nh.nh_dev; 507dd3abc4eSYOSHIFUJI Hideaki 5085e670d84SDavid Ahern if (sprt->fib6_nh.nh_flags & RTNH_F_DEAD) 5098067bb8cSIdo Schimmel continue; 5108067bb8cSIdo Schimmel 511dd3abc4eSYOSHIFUJI Hideaki if (oif) { 5121da177e4SLinus Torvalds if (dev->ifindex == oif) 5131da177e4SLinus Torvalds return sprt; 5141da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 51538308473SDavid S. Miller if (!sprt->rt6i_idev || 5161da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 51717fb0b2bSDavid Ahern if (flags & RT6_LOOKUP_F_IFACE) 5181da177e4SLinus Torvalds continue; 51917fb0b2bSDavid Ahern if (local && 52017fb0b2bSDavid Ahern local->rt6i_idev->dev->ifindex == oif) 5211da177e4SLinus Torvalds continue; 5221da177e4SLinus Torvalds } 5231da177e4SLinus Torvalds local = sprt; 5241da177e4SLinus Torvalds } 525dd3abc4eSYOSHIFUJI Hideaki } else { 526dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 527dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 528dd3abc4eSYOSHIFUJI Hideaki return sprt; 529dd3abc4eSYOSHIFUJI Hideaki } 5301da177e4SLinus Torvalds } 5311da177e4SLinus Torvalds 532dd3abc4eSYOSHIFUJI Hideaki if (oif) { 5331da177e4SLinus Torvalds if (local) 5341da177e4SLinus Torvalds return local; 5351da177e4SLinus Torvalds 536d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 5378ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 5381da177e4SLinus Torvalds } 5398067bb8cSIdo Schimmel 5405e670d84SDavid Ahern return rt->fib6_nh.nh_flags & RTNH_F_DEAD ? net->ipv6.ip6_null_entry : rt; 5411da177e4SLinus Torvalds } 5421da177e4SLinus Torvalds 54327097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 544c2f17e82SHannes Frederic Sowa struct __rt6_probe_work { 545c2f17e82SHannes Frederic Sowa struct work_struct work; 546c2f17e82SHannes Frederic Sowa struct in6_addr target; 547c2f17e82SHannes Frederic Sowa struct net_device *dev; 548c2f17e82SHannes Frederic Sowa }; 549c2f17e82SHannes Frederic Sowa 550c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w) 551c2f17e82SHannes Frederic Sowa { 552c2f17e82SHannes Frederic Sowa struct in6_addr mcaddr; 553c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work = 554c2f17e82SHannes Frederic Sowa container_of(w, struct __rt6_probe_work, work); 555c2f17e82SHannes Frederic Sowa 556c2f17e82SHannes Frederic Sowa addrconf_addr_solict_mult(&work->target, &mcaddr); 557adc176c5SErik Nordmark ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0); 558c2f17e82SHannes Frederic Sowa dev_put(work->dev); 559662f5533SMichael Büsch kfree(work); 560c2f17e82SHannes Frederic Sowa } 561c2f17e82SHannes Frederic Sowa 56227097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 56327097255SYOSHIFUJI Hideaki { 564990edb42SMartin KaFai Lau struct __rt6_probe_work *work; 5655e670d84SDavid Ahern const struct in6_addr *nh_gw; 566f2c31e32SEric Dumazet struct neighbour *neigh; 5675e670d84SDavid Ahern struct net_device *dev; 5685e670d84SDavid Ahern 56927097255SYOSHIFUJI Hideaki /* 57027097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 57127097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 57227097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 57327097255SYOSHIFUJI Hideaki * 57427097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 57527097255SYOSHIFUJI Hideaki * to no more than one per minute. 57627097255SYOSHIFUJI Hideaki */ 5772152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!rt || !(rt->rt6i_flags & RTF_GATEWAY)) 578fdd6681dSAmerigo Wang return; 5795e670d84SDavid Ahern 5805e670d84SDavid Ahern nh_gw = &rt->fib6_nh.nh_gw; 5815e670d84SDavid Ahern dev = rt->fib6_nh.nh_dev; 5822152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 5835e670d84SDavid Ahern neigh = __ipv6_neigh_lookup_noref(dev, nh_gw); 5842152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 5858d6c31bfSMartin KaFai Lau if (neigh->nud_state & NUD_VALID) 5868d6c31bfSMartin KaFai Lau goto out; 5878d6c31bfSMartin KaFai Lau 588990edb42SMartin KaFai Lau work = NULL; 5892152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_lock(&neigh->lock); 590990edb42SMartin KaFai Lau if (!(neigh->nud_state & NUD_VALID) && 591990edb42SMartin KaFai Lau time_after(jiffies, 592990edb42SMartin KaFai Lau neigh->updated + 593990edb42SMartin KaFai Lau rt->rt6i_idev->cnf.rtr_probe_interval)) { 594c2f17e82SHannes Frederic Sowa work = kmalloc(sizeof(*work), GFP_ATOMIC); 595990edb42SMartin KaFai Lau if (work) 5967e980569SJiri Benc __neigh_set_probe_once(neigh); 597990edb42SMartin KaFai Lau } 598c2f17e82SHannes Frederic Sowa write_unlock(&neigh->lock); 599990edb42SMartin KaFai Lau } else { 600990edb42SMartin KaFai Lau work = kmalloc(sizeof(*work), GFP_ATOMIC); 601990edb42SMartin KaFai Lau } 602c2f17e82SHannes Frederic Sowa 603c2f17e82SHannes Frederic Sowa if (work) { 604c2f17e82SHannes Frederic Sowa INIT_WORK(&work->work, rt6_probe_deferred); 6055e670d84SDavid Ahern work->target = *nh_gw; 6065e670d84SDavid Ahern dev_hold(dev); 6075e670d84SDavid Ahern work->dev = dev; 608c2f17e82SHannes Frederic Sowa schedule_work(&work->work); 609c2f17e82SHannes Frederic Sowa } 610990edb42SMartin KaFai Lau 6118d6c31bfSMartin KaFai Lau out: 6122152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 613f2c31e32SEric Dumazet } 61427097255SYOSHIFUJI Hideaki #else 61527097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 61627097255SYOSHIFUJI Hideaki { 61727097255SYOSHIFUJI Hideaki } 61827097255SYOSHIFUJI Hideaki #endif 61927097255SYOSHIFUJI Hideaki 6201da177e4SLinus Torvalds /* 621554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 6221da177e4SLinus Torvalds */ 623b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 6241da177e4SLinus Torvalds { 6255e670d84SDavid Ahern const struct net_device *dev = rt->fib6_nh.nh_dev; 6265e670d84SDavid Ahern 627161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 628554cfb7eSYOSHIFUJI Hideaki return 2; 629161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 630161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 631161980f4SDavid S. Miller return 1; 632554cfb7eSYOSHIFUJI Hideaki return 0; 6331da177e4SLinus Torvalds } 6341da177e4SLinus Torvalds 635afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt) 6361da177e4SLinus Torvalds { 637afc154e9SHannes Frederic Sowa enum rt6_nud_state ret = RT6_NUD_FAIL_HARD; 6385e670d84SDavid Ahern struct neighbour *neigh; 639f2c31e32SEric Dumazet 6404d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 6414d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 642afc154e9SHannes Frederic Sowa return RT6_NUD_SUCCEED; 643145a3621SYOSHIFUJI Hideaki / 吉藤英明 644145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 6455e670d84SDavid Ahern neigh = __ipv6_neigh_lookup_noref(rt->fib6_nh.nh_dev, 6465e670d84SDavid Ahern &rt->fib6_nh.nh_gw); 647145a3621SYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 648145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_lock(&neigh->lock); 649554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 650afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 651398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 652a5a81f0bSPaul Marks else if (!(neigh->nud_state & NUD_FAILED)) 653afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 6547e980569SJiri Benc else 6557e980569SJiri Benc ret = RT6_NUD_FAIL_PROBE; 656398bcbebSYOSHIFUJI Hideaki #endif 657145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_unlock(&neigh->lock); 658afc154e9SHannes Frederic Sowa } else { 659afc154e9SHannes Frederic Sowa ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 6607e980569SJiri Benc RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; 661a5a81f0bSPaul Marks } 662145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 663145a3621SYOSHIFUJI Hideaki / 吉藤英明 664a5a81f0bSPaul Marks return ret; 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds 667554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 668554cfb7eSYOSHIFUJI Hideaki int strict) 669554cfb7eSYOSHIFUJI Hideaki { 670a5a81f0bSPaul Marks int m; 6714d0c5911SYOSHIFUJI Hideaki 6724d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 67377d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 674afc154e9SHannes Frederic Sowa return RT6_NUD_FAIL_HARD; 675ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 676ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 677ebacaaa0SYOSHIFUJI Hideaki #endif 678afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) { 679afc154e9SHannes Frederic Sowa int n = rt6_check_neigh(rt); 680afc154e9SHannes Frederic Sowa if (n < 0) 681afc154e9SHannes Frederic Sowa return n; 682afc154e9SHannes Frederic Sowa } 683554cfb7eSYOSHIFUJI Hideaki return m; 684554cfb7eSYOSHIFUJI Hideaki } 685554cfb7eSYOSHIFUJI Hideaki 686f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 687afc154e9SHannes Frederic Sowa int *mpri, struct rt6_info *match, 688afc154e9SHannes Frederic Sowa bool *do_rr) 689554cfb7eSYOSHIFUJI Hideaki { 690554cfb7eSYOSHIFUJI Hideaki int m; 691afc154e9SHannes Frederic Sowa bool match_do_rr = false; 69235103d11SAndy Gospodarek struct inet6_dev *idev = rt->rt6i_idev; 69335103d11SAndy Gospodarek 6945e670d84SDavid Ahern if (rt->fib6_nh.nh_flags & RTNH_F_DEAD) 6958067bb8cSIdo Schimmel goto out; 6968067bb8cSIdo Schimmel 69714c5206cSIdo Schimmel if (idev->cnf.ignore_routes_with_linkdown && 6985e670d84SDavid Ahern rt->fib6_nh.nh_flags & RTNH_F_LINKDOWN && 699d5d32e4bSDavid Ahern !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE)) 70035103d11SAndy Gospodarek goto out; 701554cfb7eSYOSHIFUJI Hideaki 702554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 703f11e6659SDavid S. Miller goto out; 704554cfb7eSYOSHIFUJI Hideaki 705554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 7067e980569SJiri Benc if (m == RT6_NUD_FAIL_DO_RR) { 707afc154e9SHannes Frederic Sowa match_do_rr = true; 708afc154e9SHannes Frederic Sowa m = 0; /* lowest valid score */ 7097e980569SJiri Benc } else if (m == RT6_NUD_FAIL_HARD) { 710f11e6659SDavid S. Miller goto out; 7111da177e4SLinus Torvalds } 712f11e6659SDavid S. Miller 713afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) 714afc154e9SHannes Frederic Sowa rt6_probe(rt); 715afc154e9SHannes Frederic Sowa 7167e980569SJiri Benc /* note that m can be RT6_NUD_FAIL_PROBE at this point */ 717afc154e9SHannes Frederic Sowa if (m > *mpri) { 718afc154e9SHannes Frederic Sowa *do_rr = match_do_rr; 719afc154e9SHannes Frederic Sowa *mpri = m; 720afc154e9SHannes Frederic Sowa match = rt; 721afc154e9SHannes Frederic Sowa } 722f11e6659SDavid S. Miller out: 723f11e6659SDavid S. Miller return match; 7241da177e4SLinus Torvalds } 7251da177e4SLinus Torvalds 726f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 7278d1040e8SWei Wang struct rt6_info *leaf, 728f11e6659SDavid S. Miller struct rt6_info *rr_head, 729afc154e9SHannes Frederic Sowa u32 metric, int oif, int strict, 730afc154e9SHannes Frederic Sowa bool *do_rr) 731f11e6659SDavid S. Miller { 7329fbdcfafSSteffen Klassert struct rt6_info *rt, *match, *cont; 733f11e6659SDavid S. Miller int mpri = -1; 734f11e6659SDavid S. Miller 735f11e6659SDavid S. Miller match = NULL; 7369fbdcfafSSteffen Klassert cont = NULL; 737071fb37eSDavid Miller for (rt = rr_head; rt; rt = rcu_dereference(rt->rt6_next)) { 7389fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 7399fbdcfafSSteffen Klassert cont = rt; 7409fbdcfafSSteffen Klassert break; 7419fbdcfafSSteffen Klassert } 7429fbdcfafSSteffen Klassert 743afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 7449fbdcfafSSteffen Klassert } 7459fbdcfafSSteffen Klassert 74666f5d6ceSWei Wang for (rt = leaf; rt && rt != rr_head; 747071fb37eSDavid Miller rt = rcu_dereference(rt->rt6_next)) { 7489fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 7499fbdcfafSSteffen Klassert cont = rt; 7509fbdcfafSSteffen Klassert break; 7519fbdcfafSSteffen Klassert } 7529fbdcfafSSteffen Klassert 7539fbdcfafSSteffen Klassert match = find_match(rt, oif, strict, &mpri, match, do_rr); 7549fbdcfafSSteffen Klassert } 7559fbdcfafSSteffen Klassert 7569fbdcfafSSteffen Klassert if (match || !cont) 7579fbdcfafSSteffen Klassert return match; 7589fbdcfafSSteffen Klassert 759071fb37eSDavid Miller for (rt = cont; rt; rt = rcu_dereference(rt->rt6_next)) 760afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 761f11e6659SDavid S. Miller 762f11e6659SDavid S. Miller return match; 763f11e6659SDavid S. Miller } 764f11e6659SDavid S. Miller 7658d1040e8SWei Wang static struct rt6_info *rt6_select(struct net *net, struct fib6_node *fn, 7668d1040e8SWei Wang int oif, int strict) 767f11e6659SDavid S. Miller { 76866f5d6ceSWei Wang struct rt6_info *leaf = rcu_dereference(fn->leaf); 769f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 770afc154e9SHannes Frederic Sowa bool do_rr = false; 77117ecf590SWei Wang int key_plen; 772f11e6659SDavid S. Miller 77387b1af8dSWei Wang if (!leaf || leaf == net->ipv6.ip6_null_entry) 7748d1040e8SWei Wang return net->ipv6.ip6_null_entry; 7758d1040e8SWei Wang 77666f5d6ceSWei Wang rt0 = rcu_dereference(fn->rr_ptr); 777f11e6659SDavid S. Miller if (!rt0) 77866f5d6ceSWei Wang rt0 = leaf; 779f11e6659SDavid S. Miller 78017ecf590SWei Wang /* Double check to make sure fn is not an intermediate node 78117ecf590SWei Wang * and fn->leaf does not points to its child's leaf 78217ecf590SWei Wang * (This might happen if all routes under fn are deleted from 78317ecf590SWei Wang * the tree and fib6_repair_tree() is called on the node.) 78417ecf590SWei Wang */ 78517ecf590SWei Wang key_plen = rt0->rt6i_dst.plen; 78617ecf590SWei Wang #ifdef CONFIG_IPV6_SUBTREES 78717ecf590SWei Wang if (rt0->rt6i_src.plen) 78817ecf590SWei Wang key_plen = rt0->rt6i_src.plen; 78917ecf590SWei Wang #endif 79017ecf590SWei Wang if (fn->fn_bit != key_plen) 79117ecf590SWei Wang return net->ipv6.ip6_null_entry; 79217ecf590SWei Wang 7938d1040e8SWei Wang match = find_rr_leaf(fn, leaf, rt0, rt0->rt6i_metric, oif, strict, 794afc154e9SHannes Frederic Sowa &do_rr); 795f11e6659SDavid S. Miller 796afc154e9SHannes Frederic Sowa if (do_rr) { 797071fb37eSDavid Miller struct rt6_info *next = rcu_dereference(rt0->rt6_next); 798f11e6659SDavid S. Miller 799554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 800f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 8018d1040e8SWei Wang next = leaf; 802f11e6659SDavid S. Miller 80366f5d6ceSWei Wang if (next != rt0) { 80466f5d6ceSWei Wang spin_lock_bh(&leaf->rt6i_table->tb6_lock); 80566f5d6ceSWei Wang /* make sure next is not being deleted from the tree */ 80666f5d6ceSWei Wang if (next->rt6i_node) 80766f5d6ceSWei Wang rcu_assign_pointer(fn->rr_ptr, next); 80866f5d6ceSWei Wang spin_unlock_bh(&leaf->rt6i_table->tb6_lock); 80966f5d6ceSWei Wang } 810554cfb7eSYOSHIFUJI Hideaki } 811554cfb7eSYOSHIFUJI Hideaki 812a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 8131da177e4SLinus Torvalds } 8141da177e4SLinus Torvalds 8158b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt) 8168b9df265SMartin KaFai Lau { 8178b9df265SMartin KaFai Lau return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY)); 8188b9df265SMartin KaFai Lau } 8198b9df265SMartin KaFai Lau 82070ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 82170ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 822b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 82370ceb4f5SYOSHIFUJI Hideaki { 824c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 82570ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 82670ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 82770ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 8284bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 82970ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 83070ceb4f5SYOSHIFUJI Hideaki 83170ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 83270ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 83370ceb4f5SYOSHIFUJI Hideaki } 83470ceb4f5SYOSHIFUJI Hideaki 83570ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 83670ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 83770ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 83870ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 83970ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 84070ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 84170ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 84270ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 84370ceb4f5SYOSHIFUJI Hideaki } 84470ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 84570ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 84670ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 84770ceb4f5SYOSHIFUJI Hideaki } 84870ceb4f5SYOSHIFUJI Hideaki } 84970ceb4f5SYOSHIFUJI Hideaki 85070ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 85170ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 8523933fc95SJens Rosenboom return -EINVAL; 85370ceb4f5SYOSHIFUJI Hideaki 8544bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 85570ceb4f5SYOSHIFUJI Hideaki 85670ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 85770ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 85870ceb4f5SYOSHIFUJI Hideaki else { 85970ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 86070ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 86170ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 86270ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 86370ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 86470ceb4f5SYOSHIFUJI Hideaki } 86570ceb4f5SYOSHIFUJI Hideaki 866f104a567SDuan Jiong if (rinfo->prefix_len == 0) 867afb1d4b5SDavid Ahern rt = rt6_get_dflt_router(net, gwaddr, dev); 868f104a567SDuan Jiong else 869f104a567SDuan Jiong rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, 870830218c1SDavid Ahern gwaddr, dev); 87170ceb4f5SYOSHIFUJI Hideaki 87270ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 873afb1d4b5SDavid Ahern ip6_del_rt(net, rt); 87470ceb4f5SYOSHIFUJI Hideaki rt = NULL; 87570ceb4f5SYOSHIFUJI Hideaki } 87670ceb4f5SYOSHIFUJI Hideaki 87770ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 878830218c1SDavid Ahern rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, 879830218c1SDavid Ahern dev, pref); 88070ceb4f5SYOSHIFUJI Hideaki else if (rt) 88170ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 88270ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 88370ceb4f5SYOSHIFUJI Hideaki 88470ceb4f5SYOSHIFUJI Hideaki if (rt) { 8851716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 8861716a961SGao feng rt6_clean_expires(rt); 8871716a961SGao feng else 8881716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 8891716a961SGao feng 89094e187c0SAmerigo Wang ip6_rt_put(rt); 89170ceb4f5SYOSHIFUJI Hideaki } 89270ceb4f5SYOSHIFUJI Hideaki return 0; 89370ceb4f5SYOSHIFUJI Hideaki } 89470ceb4f5SYOSHIFUJI Hideaki #endif 89570ceb4f5SYOSHIFUJI Hideaki 896ae90d867SDavid Ahern /* 897ae90d867SDavid Ahern * Misc support functions 898ae90d867SDavid Ahern */ 899ae90d867SDavid Ahern 900ae90d867SDavid Ahern /* called with rcu_lock held */ 901ae90d867SDavid Ahern static struct net_device *ip6_rt_get_dev_rcu(struct rt6_info *rt) 902ae90d867SDavid Ahern { 9035e670d84SDavid Ahern struct net_device *dev = rt->fib6_nh.nh_dev; 904ae90d867SDavid Ahern 905ae90d867SDavid Ahern if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) { 906ae90d867SDavid Ahern /* for copies of local routes, dst->dev needs to be the 907ae90d867SDavid Ahern * device if it is a master device, the master device if 908ae90d867SDavid Ahern * device is enslaved, and the loopback as the default 909ae90d867SDavid Ahern */ 910ae90d867SDavid Ahern if (netif_is_l3_slave(dev) && 911ae90d867SDavid Ahern !rt6_need_strict(&rt->rt6i_dst.addr)) 912ae90d867SDavid Ahern dev = l3mdev_master_dev_rcu(dev); 913ae90d867SDavid Ahern else if (!netif_is_l3_master(dev)) 914ae90d867SDavid Ahern dev = dev_net(dev)->loopback_dev; 915ae90d867SDavid Ahern /* last case is netif_is_l3_master(dev) is true in which 916ae90d867SDavid Ahern * case we want dev returned to be dev 917ae90d867SDavid Ahern */ 918ae90d867SDavid Ahern } 919ae90d867SDavid Ahern 920ae90d867SDavid Ahern return dev; 921ae90d867SDavid Ahern } 922ae90d867SDavid Ahern 923*6edb3c96SDavid Ahern static const int fib6_prop[RTN_MAX + 1] = { 924*6edb3c96SDavid Ahern [RTN_UNSPEC] = 0, 925*6edb3c96SDavid Ahern [RTN_UNICAST] = 0, 926*6edb3c96SDavid Ahern [RTN_LOCAL] = 0, 927*6edb3c96SDavid Ahern [RTN_BROADCAST] = 0, 928*6edb3c96SDavid Ahern [RTN_ANYCAST] = 0, 929*6edb3c96SDavid Ahern [RTN_MULTICAST] = 0, 930*6edb3c96SDavid Ahern [RTN_BLACKHOLE] = -EINVAL, 931*6edb3c96SDavid Ahern [RTN_UNREACHABLE] = -EHOSTUNREACH, 932*6edb3c96SDavid Ahern [RTN_PROHIBIT] = -EACCES, 933*6edb3c96SDavid Ahern [RTN_THROW] = -EAGAIN, 934*6edb3c96SDavid Ahern [RTN_NAT] = -EINVAL, 935*6edb3c96SDavid Ahern [RTN_XRESOLVE] = -EINVAL, 936*6edb3c96SDavid Ahern }; 937*6edb3c96SDavid Ahern 938*6edb3c96SDavid Ahern static int ip6_rt_type_to_error(u8 fib6_type) 939*6edb3c96SDavid Ahern { 940*6edb3c96SDavid Ahern return fib6_prop[fib6_type]; 941*6edb3c96SDavid Ahern } 942*6edb3c96SDavid Ahern 943*6edb3c96SDavid Ahern static void ip6_rt_init_dst_reject(struct rt6_info *rt, struct rt6_info *ort) 944*6edb3c96SDavid Ahern { 945*6edb3c96SDavid Ahern rt->dst.error = ip6_rt_type_to_error(ort->fib6_type); 946*6edb3c96SDavid Ahern 947*6edb3c96SDavid Ahern switch (ort->fib6_type) { 948*6edb3c96SDavid Ahern case RTN_BLACKHOLE: 949*6edb3c96SDavid Ahern rt->dst.output = dst_discard_out; 950*6edb3c96SDavid Ahern rt->dst.input = dst_discard; 951*6edb3c96SDavid Ahern break; 952*6edb3c96SDavid Ahern case RTN_PROHIBIT: 953*6edb3c96SDavid Ahern rt->dst.output = ip6_pkt_prohibit_out; 954*6edb3c96SDavid Ahern rt->dst.input = ip6_pkt_prohibit; 955*6edb3c96SDavid Ahern break; 956*6edb3c96SDavid Ahern case RTN_THROW: 957*6edb3c96SDavid Ahern case RTN_UNREACHABLE: 958*6edb3c96SDavid Ahern default: 959*6edb3c96SDavid Ahern rt->dst.output = ip6_pkt_discard_out; 960*6edb3c96SDavid Ahern rt->dst.input = ip6_pkt_discard; 961*6edb3c96SDavid Ahern break; 962*6edb3c96SDavid Ahern } 963*6edb3c96SDavid Ahern } 964*6edb3c96SDavid Ahern 965*6edb3c96SDavid Ahern static void ip6_rt_init_dst(struct rt6_info *rt, struct rt6_info *ort) 966*6edb3c96SDavid Ahern { 967*6edb3c96SDavid Ahern if (ort->rt6i_flags & RTF_REJECT) { 968*6edb3c96SDavid Ahern ip6_rt_init_dst_reject(rt, ort); 969*6edb3c96SDavid Ahern return; 970*6edb3c96SDavid Ahern } 971*6edb3c96SDavid Ahern 972*6edb3c96SDavid Ahern rt->dst.error = 0; 973*6edb3c96SDavid Ahern rt->dst.output = ip6_output; 974*6edb3c96SDavid Ahern 975*6edb3c96SDavid Ahern if (ort->fib6_type == RTN_LOCAL) { 976*6edb3c96SDavid Ahern rt->dst.flags |= DST_HOST; 977*6edb3c96SDavid Ahern rt->dst.input = ip6_input; 978*6edb3c96SDavid Ahern } else if (ipv6_addr_type(&ort->rt6i_dst.addr) & IPV6_ADDR_MULTICAST) { 979*6edb3c96SDavid Ahern rt->dst.input = ip6_mc_input; 980*6edb3c96SDavid Ahern } else { 981*6edb3c96SDavid Ahern rt->dst.input = ip6_forward; 982*6edb3c96SDavid Ahern } 983*6edb3c96SDavid Ahern 984*6edb3c96SDavid Ahern if (ort->fib6_nh.nh_lwtstate) { 985*6edb3c96SDavid Ahern rt->dst.lwtstate = lwtstate_get(ort->fib6_nh.nh_lwtstate); 986*6edb3c96SDavid Ahern lwtunnel_set_redirect(&rt->dst); 987*6edb3c96SDavid Ahern } 988*6edb3c96SDavid Ahern 989*6edb3c96SDavid Ahern rt->dst.lastuse = jiffies; 990*6edb3c96SDavid Ahern } 991*6edb3c96SDavid Ahern 992ae90d867SDavid Ahern static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) 993ae90d867SDavid Ahern { 994ae90d867SDavid Ahern BUG_ON(from->from); 995ae90d867SDavid Ahern 996ae90d867SDavid Ahern rt->rt6i_flags &= ~RTF_EXPIRES; 997ae90d867SDavid Ahern dst_hold(&from->dst); 998ae90d867SDavid Ahern rt->from = from; 999ae90d867SDavid Ahern dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true); 1000ae90d867SDavid Ahern } 1001ae90d867SDavid Ahern 1002ae90d867SDavid Ahern static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort) 1003ae90d867SDavid Ahern { 1004*6edb3c96SDavid Ahern ip6_rt_init_dst(rt, ort); 1005*6edb3c96SDavid Ahern 1006ae90d867SDavid Ahern rt->rt6i_dst = ort->rt6i_dst; 1007ae90d867SDavid Ahern rt->rt6i_idev = ort->rt6i_idev; 1008ae90d867SDavid Ahern if (rt->rt6i_idev) 1009ae90d867SDavid Ahern in6_dev_hold(rt->rt6i_idev); 10105e670d84SDavid Ahern rt->rt6i_gateway = ort->fib6_nh.nh_gw; 1011ae90d867SDavid Ahern rt->rt6i_flags = ort->rt6i_flags; 1012ae90d867SDavid Ahern rt6_set_from(rt, ort); 1013ae90d867SDavid Ahern rt->rt6i_metric = ort->rt6i_metric; 1014ae90d867SDavid Ahern #ifdef CONFIG_IPV6_SUBTREES 1015ae90d867SDavid Ahern rt->rt6i_src = ort->rt6i_src; 1016ae90d867SDavid Ahern #endif 1017ae90d867SDavid Ahern rt->rt6i_prefsrc = ort->rt6i_prefsrc; 1018ae90d867SDavid Ahern rt->rt6i_table = ort->rt6i_table; 10195e670d84SDavid Ahern rt->dst.lwtstate = lwtstate_get(ort->fib6_nh.nh_lwtstate); 1020ae90d867SDavid Ahern } 1021ae90d867SDavid Ahern 1022a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn, 1023a3c00e46SMartin KaFai Lau struct in6_addr *saddr) 1024a3c00e46SMartin KaFai Lau { 102566f5d6ceSWei Wang struct fib6_node *pn, *sn; 1026a3c00e46SMartin KaFai Lau while (1) { 1027a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_TL_ROOT) 1028a3c00e46SMartin KaFai Lau return NULL; 102966f5d6ceSWei Wang pn = rcu_dereference(fn->parent); 103066f5d6ceSWei Wang sn = FIB6_SUBTREE(pn); 103166f5d6ceSWei Wang if (sn && sn != fn) 103266f5d6ceSWei Wang fn = fib6_lookup(sn, NULL, saddr); 1033a3c00e46SMartin KaFai Lau else 1034a3c00e46SMartin KaFai Lau fn = pn; 1035a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_RTINFO) 1036a3c00e46SMartin KaFai Lau return fn; 1037a3c00e46SMartin KaFai Lau } 1038a3c00e46SMartin KaFai Lau } 1039c71099acSThomas Graf 1040d3843fe5SWei Wang static bool ip6_hold_safe(struct net *net, struct rt6_info **prt, 1041d3843fe5SWei Wang bool null_fallback) 1042d3843fe5SWei Wang { 1043d3843fe5SWei Wang struct rt6_info *rt = *prt; 1044d3843fe5SWei Wang 1045d3843fe5SWei Wang if (dst_hold_safe(&rt->dst)) 1046d3843fe5SWei Wang return true; 1047d3843fe5SWei Wang if (null_fallback) { 1048d3843fe5SWei Wang rt = net->ipv6.ip6_null_entry; 1049d3843fe5SWei Wang dst_hold(&rt->dst); 1050d3843fe5SWei Wang } else { 1051d3843fe5SWei Wang rt = NULL; 1052d3843fe5SWei Wang } 1053d3843fe5SWei Wang *prt = rt; 1054d3843fe5SWei Wang return false; 1055d3843fe5SWei Wang } 1056d3843fe5SWei Wang 10578ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 10588ed67789SDaniel Lezcano struct fib6_table *table, 1059b75cc8f9SDavid Ahern struct flowi6 *fl6, 1060b75cc8f9SDavid Ahern const struct sk_buff *skb, 1061b75cc8f9SDavid Ahern int flags) 10621da177e4SLinus Torvalds { 10632b760fcfSWei Wang struct rt6_info *rt, *rt_cache; 10641da177e4SLinus Torvalds struct fib6_node *fn; 10651da177e4SLinus Torvalds 1066b6cdbc85SDavid Ahern if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) 1067b6cdbc85SDavid Ahern flags &= ~RT6_LOOKUP_F_IFACE; 1068b6cdbc85SDavid Ahern 106966f5d6ceSWei Wang rcu_read_lock(); 10704c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1071c71099acSThomas Graf restart: 107266f5d6ceSWei Wang rt = rcu_dereference(fn->leaf); 107366f5d6ceSWei Wang if (!rt) { 107466f5d6ceSWei Wang rt = net->ipv6.ip6_null_entry; 107566f5d6ceSWei Wang } else { 107666f5d6ceSWei Wang rt = rt6_device_match(net, rt, &fl6->saddr, 107766f5d6ceSWei Wang fl6->flowi6_oif, flags); 107851ebd318SNicolas Dichtel if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) 1079b4bac172SDavid Ahern rt = rt6_multipath_select(net, rt, fl6, fl6->flowi6_oif, 1080b75cc8f9SDavid Ahern skb, flags); 108166f5d6ceSWei Wang } 1082a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1083a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1084a3c00e46SMartin KaFai Lau if (fn) 1085a3c00e46SMartin KaFai Lau goto restart; 1086a3c00e46SMartin KaFai Lau } 10872b760fcfSWei Wang /* Search through exception table */ 10882b760fcfSWei Wang rt_cache = rt6_find_cached_rt(rt, &fl6->daddr, &fl6->saddr); 10892b760fcfSWei Wang if (rt_cache) 10902b760fcfSWei Wang rt = rt_cache; 10912b760fcfSWei Wang 1092d3843fe5SWei Wang if (ip6_hold_safe(net, &rt, true)) 1093d3843fe5SWei Wang dst_use_noref(&rt->dst, jiffies); 1094d3843fe5SWei Wang 109566f5d6ceSWei Wang rcu_read_unlock(); 1096b811580dSDavid Ahern 1097b65f164dSPaolo Abeni trace_fib6_table_lookup(net, rt, table, fl6); 1098b811580dSDavid Ahern 10991da177e4SLinus Torvalds return rt; 1100c71099acSThomas Graf 1101c71099acSThomas Graf } 1102c71099acSThomas Graf 1103ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, 1104b75cc8f9SDavid Ahern const struct sk_buff *skb, int flags) 1105ea6e574eSFlorian Westphal { 1106b75cc8f9SDavid Ahern return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup); 1107ea6e574eSFlorian Westphal } 1108ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 1109ea6e574eSFlorian Westphal 11109acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 1111b75cc8f9SDavid Ahern const struct in6_addr *saddr, int oif, 1112b75cc8f9SDavid Ahern const struct sk_buff *skb, int strict) 1113c71099acSThomas Graf { 11144c9483b2SDavid S. Miller struct flowi6 fl6 = { 11154c9483b2SDavid S. Miller .flowi6_oif = oif, 11164c9483b2SDavid S. Miller .daddr = *daddr, 1117c71099acSThomas Graf }; 1118c71099acSThomas Graf struct dst_entry *dst; 111977d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 1120c71099acSThomas Graf 1121adaa70bbSThomas Graf if (saddr) { 11224c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 1123adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 1124adaa70bbSThomas Graf } 1125adaa70bbSThomas Graf 1126b75cc8f9SDavid Ahern dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup); 1127c71099acSThomas Graf if (dst->error == 0) 1128c71099acSThomas Graf return (struct rt6_info *) dst; 1129c71099acSThomas Graf 1130c71099acSThomas Graf dst_release(dst); 1131c71099acSThomas Graf 11321da177e4SLinus Torvalds return NULL; 11331da177e4SLinus Torvalds } 11347159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 11357159039aSYOSHIFUJI Hideaki 1136c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 11371cfb71eeSWei Wang * It takes new route entry, the addition fails by any reason the 11381cfb71eeSWei Wang * route is released. 11391cfb71eeSWei Wang * Caller must hold dst before calling it. 11401da177e4SLinus Torvalds */ 11411da177e4SLinus Torvalds 1142e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, 1143333c4301SDavid Ahern struct mx6_config *mxc, 1144333c4301SDavid Ahern struct netlink_ext_ack *extack) 11451da177e4SLinus Torvalds { 11461da177e4SLinus Torvalds int err; 1147c71099acSThomas Graf struct fib6_table *table; 11481da177e4SLinus Torvalds 1149c71099acSThomas Graf table = rt->rt6i_table; 115066f5d6ceSWei Wang spin_lock_bh(&table->tb6_lock); 1151333c4301SDavid Ahern err = fib6_add(&table->tb6_root, rt, info, mxc, extack); 115266f5d6ceSWei Wang spin_unlock_bh(&table->tb6_lock); 11531da177e4SLinus Torvalds 11541da177e4SLinus Torvalds return err; 11551da177e4SLinus Torvalds } 11561da177e4SLinus Torvalds 1157afb1d4b5SDavid Ahern int ip6_ins_rt(struct net *net, struct rt6_info *rt) 115840e22e8fSThomas Graf { 1159afb1d4b5SDavid Ahern struct nl_info info = { .nl_net = net, }; 1160e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 1161e715b6d3SFlorian Westphal 11621cfb71eeSWei Wang /* Hold dst to account for the reference from the fib6 tree */ 11631cfb71eeSWei Wang dst_hold(&rt->dst); 1164333c4301SDavid Ahern return __ip6_ins_rt(rt, &info, &mxc, NULL); 116540e22e8fSThomas Graf } 116640e22e8fSThomas Graf 11678b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, 116821efcfa0SEric Dumazet const struct in6_addr *daddr, 1169b71d1d42SEric Dumazet const struct in6_addr *saddr) 11701da177e4SLinus Torvalds { 11714832c30dSDavid Ahern struct net_device *dev; 11721da177e4SLinus Torvalds struct rt6_info *rt; 11731da177e4SLinus Torvalds 11741da177e4SLinus Torvalds /* 11751da177e4SLinus Torvalds * Clone the route. 11761da177e4SLinus Torvalds */ 11771da177e4SLinus Torvalds 1178d52d3997SMartin KaFai Lau if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)) 11793a2232e9SDavid Miller ort = ort->from; 11801da177e4SLinus Torvalds 11814832c30dSDavid Ahern rcu_read_lock(); 11824832c30dSDavid Ahern dev = ip6_rt_get_dev_rcu(ort); 11834832c30dSDavid Ahern rt = __ip6_dst_alloc(dev_net(dev), dev, 0); 11844832c30dSDavid Ahern rcu_read_unlock(); 118583a09abdSMartin KaFai Lau if (!rt) 118683a09abdSMartin KaFai Lau return NULL; 118783a09abdSMartin KaFai Lau 118883a09abdSMartin KaFai Lau ip6_rt_copy_init(rt, ort); 11898b9df265SMartin KaFai Lau rt->rt6i_flags |= RTF_CACHE; 119083a09abdSMartin KaFai Lau rt->rt6i_metric = 0; 119183a09abdSMartin KaFai Lau rt->dst.flags |= DST_HOST; 119283a09abdSMartin KaFai Lau rt->rt6i_dst.addr = *daddr; 119383a09abdSMartin KaFai Lau rt->rt6i_dst.plen = 128; 11948b9df265SMartin KaFai Lau 11958b9df265SMartin KaFai Lau if (!rt6_is_gw_or_nonexthop(ort)) { 1196bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 119721efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 119858c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 11991da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 12001da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 12014e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 12021da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 12031da177e4SLinus Torvalds } 12041da177e4SLinus Torvalds #endif 120595a9a5baSYOSHIFUJI Hideaki } 120695a9a5baSYOSHIFUJI Hideaki 1207299d9939SYOSHIFUJI Hideaki return rt; 1208299d9939SYOSHIFUJI Hideaki } 1209299d9939SYOSHIFUJI Hideaki 1210d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt) 1211d52d3997SMartin KaFai Lau { 12124832c30dSDavid Ahern struct net_device *dev; 1213d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 1214d52d3997SMartin KaFai Lau 12154832c30dSDavid Ahern rcu_read_lock(); 12164832c30dSDavid Ahern dev = ip6_rt_get_dev_rcu(rt); 12174832c30dSDavid Ahern pcpu_rt = __ip6_dst_alloc(dev_net(dev), dev, rt->dst.flags); 12184832c30dSDavid Ahern rcu_read_unlock(); 1219d52d3997SMartin KaFai Lau if (!pcpu_rt) 1220d52d3997SMartin KaFai Lau return NULL; 1221d52d3997SMartin KaFai Lau ip6_rt_copy_init(pcpu_rt, rt); 1222d52d3997SMartin KaFai Lau pcpu_rt->rt6i_protocol = rt->rt6i_protocol; 1223d52d3997SMartin KaFai Lau pcpu_rt->rt6i_flags |= RTF_PCPU; 1224d52d3997SMartin KaFai Lau return pcpu_rt; 1225d52d3997SMartin KaFai Lau } 1226d52d3997SMartin KaFai Lau 122766f5d6ceSWei Wang /* It should be called with rcu_read_lock() acquired */ 1228d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt) 1229d52d3997SMartin KaFai Lau { 1230a73e4195SMartin KaFai Lau struct rt6_info *pcpu_rt, **p; 1231d52d3997SMartin KaFai Lau 1232d52d3997SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 1233d52d3997SMartin KaFai Lau pcpu_rt = *p; 1234d52d3997SMartin KaFai Lau 1235d3843fe5SWei Wang if (pcpu_rt && ip6_hold_safe(NULL, &pcpu_rt, false)) 1236a73e4195SMartin KaFai Lau rt6_dst_from_metrics_check(pcpu_rt); 1237d3843fe5SWei Wang 1238a73e4195SMartin KaFai Lau return pcpu_rt; 1239a73e4195SMartin KaFai Lau } 1240a73e4195SMartin KaFai Lau 1241afb1d4b5SDavid Ahern static struct rt6_info *rt6_make_pcpu_route(struct net *net, 1242afb1d4b5SDavid Ahern struct rt6_info *rt) 1243a73e4195SMartin KaFai Lau { 1244a73e4195SMartin KaFai Lau struct rt6_info *pcpu_rt, *prev, **p; 1245d52d3997SMartin KaFai Lau 1246d52d3997SMartin KaFai Lau pcpu_rt = ip6_rt_pcpu_alloc(rt); 1247d52d3997SMartin KaFai Lau if (!pcpu_rt) { 12489c7370a1SMartin KaFai Lau dst_hold(&net->ipv6.ip6_null_entry->dst); 12499c7370a1SMartin KaFai Lau return net->ipv6.ip6_null_entry; 1250d52d3997SMartin KaFai Lau } 1251d52d3997SMartin KaFai Lau 1252a94b9367SWei Wang dst_hold(&pcpu_rt->dst); 1253a73e4195SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 1254d52d3997SMartin KaFai Lau prev = cmpxchg(p, NULL, pcpu_rt); 1255951f788aSEric Dumazet BUG_ON(prev); 1256a94b9367SWei Wang 1257d52d3997SMartin KaFai Lau rt6_dst_from_metrics_check(pcpu_rt); 1258d52d3997SMartin KaFai Lau return pcpu_rt; 1259d52d3997SMartin KaFai Lau } 1260d52d3997SMartin KaFai Lau 126135732d01SWei Wang /* exception hash table implementation 126235732d01SWei Wang */ 126335732d01SWei Wang static DEFINE_SPINLOCK(rt6_exception_lock); 126435732d01SWei Wang 126535732d01SWei Wang /* Remove rt6_ex from hash table and free the memory 126635732d01SWei Wang * Caller must hold rt6_exception_lock 126735732d01SWei Wang */ 126835732d01SWei Wang static void rt6_remove_exception(struct rt6_exception_bucket *bucket, 126935732d01SWei Wang struct rt6_exception *rt6_ex) 127035732d01SWei Wang { 1271b2427e67SColin Ian King struct net *net; 127281eb8447SWei Wang 127335732d01SWei Wang if (!bucket || !rt6_ex) 127435732d01SWei Wang return; 1275b2427e67SColin Ian King 1276b2427e67SColin Ian King net = dev_net(rt6_ex->rt6i->dst.dev); 127735732d01SWei Wang rt6_ex->rt6i->rt6i_node = NULL; 127835732d01SWei Wang hlist_del_rcu(&rt6_ex->hlist); 127935732d01SWei Wang rt6_release(rt6_ex->rt6i); 128035732d01SWei Wang kfree_rcu(rt6_ex, rcu); 128135732d01SWei Wang WARN_ON_ONCE(!bucket->depth); 128235732d01SWei Wang bucket->depth--; 128381eb8447SWei Wang net->ipv6.rt6_stats->fib_rt_cache--; 128435732d01SWei Wang } 128535732d01SWei Wang 128635732d01SWei Wang /* Remove oldest rt6_ex in bucket and free the memory 128735732d01SWei Wang * Caller must hold rt6_exception_lock 128835732d01SWei Wang */ 128935732d01SWei Wang static void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket) 129035732d01SWei Wang { 129135732d01SWei Wang struct rt6_exception *rt6_ex, *oldest = NULL; 129235732d01SWei Wang 129335732d01SWei Wang if (!bucket) 129435732d01SWei Wang return; 129535732d01SWei Wang 129635732d01SWei Wang hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { 129735732d01SWei Wang if (!oldest || time_before(rt6_ex->stamp, oldest->stamp)) 129835732d01SWei Wang oldest = rt6_ex; 129935732d01SWei Wang } 130035732d01SWei Wang rt6_remove_exception(bucket, oldest); 130135732d01SWei Wang } 130235732d01SWei Wang 130335732d01SWei Wang static u32 rt6_exception_hash(const struct in6_addr *dst, 130435732d01SWei Wang const struct in6_addr *src) 130535732d01SWei Wang { 130635732d01SWei Wang static u32 seed __read_mostly; 130735732d01SWei Wang u32 val; 130835732d01SWei Wang 130935732d01SWei Wang net_get_random_once(&seed, sizeof(seed)); 131035732d01SWei Wang val = jhash(dst, sizeof(*dst), seed); 131135732d01SWei Wang 131235732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 131335732d01SWei Wang if (src) 131435732d01SWei Wang val = jhash(src, sizeof(*src), val); 131535732d01SWei Wang #endif 131635732d01SWei Wang return hash_32(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT); 131735732d01SWei Wang } 131835732d01SWei Wang 131935732d01SWei Wang /* Helper function to find the cached rt in the hash table 132035732d01SWei Wang * and update bucket pointer to point to the bucket for this 132135732d01SWei Wang * (daddr, saddr) pair 132235732d01SWei Wang * Caller must hold rt6_exception_lock 132335732d01SWei Wang */ 132435732d01SWei Wang static struct rt6_exception * 132535732d01SWei Wang __rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket, 132635732d01SWei Wang const struct in6_addr *daddr, 132735732d01SWei Wang const struct in6_addr *saddr) 132835732d01SWei Wang { 132935732d01SWei Wang struct rt6_exception *rt6_ex; 133035732d01SWei Wang u32 hval; 133135732d01SWei Wang 133235732d01SWei Wang if (!(*bucket) || !daddr) 133335732d01SWei Wang return NULL; 133435732d01SWei Wang 133535732d01SWei Wang hval = rt6_exception_hash(daddr, saddr); 133635732d01SWei Wang *bucket += hval; 133735732d01SWei Wang 133835732d01SWei Wang hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) { 133935732d01SWei Wang struct rt6_info *rt6 = rt6_ex->rt6i; 134035732d01SWei Wang bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr); 134135732d01SWei Wang 134235732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 134335732d01SWei Wang if (matched && saddr) 134435732d01SWei Wang matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr); 134535732d01SWei Wang #endif 134635732d01SWei Wang if (matched) 134735732d01SWei Wang return rt6_ex; 134835732d01SWei Wang } 134935732d01SWei Wang return NULL; 135035732d01SWei Wang } 135135732d01SWei Wang 135235732d01SWei Wang /* Helper function to find the cached rt in the hash table 135335732d01SWei Wang * and update bucket pointer to point to the bucket for this 135435732d01SWei Wang * (daddr, saddr) pair 135535732d01SWei Wang * Caller must hold rcu_read_lock() 135635732d01SWei Wang */ 135735732d01SWei Wang static struct rt6_exception * 135835732d01SWei Wang __rt6_find_exception_rcu(struct rt6_exception_bucket **bucket, 135935732d01SWei Wang const struct in6_addr *daddr, 136035732d01SWei Wang const struct in6_addr *saddr) 136135732d01SWei Wang { 136235732d01SWei Wang struct rt6_exception *rt6_ex; 136335732d01SWei Wang u32 hval; 136435732d01SWei Wang 136535732d01SWei Wang WARN_ON_ONCE(!rcu_read_lock_held()); 136635732d01SWei Wang 136735732d01SWei Wang if (!(*bucket) || !daddr) 136835732d01SWei Wang return NULL; 136935732d01SWei Wang 137035732d01SWei Wang hval = rt6_exception_hash(daddr, saddr); 137135732d01SWei Wang *bucket += hval; 137235732d01SWei Wang 137335732d01SWei Wang hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) { 137435732d01SWei Wang struct rt6_info *rt6 = rt6_ex->rt6i; 137535732d01SWei Wang bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr); 137635732d01SWei Wang 137735732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 137835732d01SWei Wang if (matched && saddr) 137935732d01SWei Wang matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr); 138035732d01SWei Wang #endif 138135732d01SWei Wang if (matched) 138235732d01SWei Wang return rt6_ex; 138335732d01SWei Wang } 138435732d01SWei Wang return NULL; 138535732d01SWei Wang } 138635732d01SWei Wang 138735732d01SWei Wang static int rt6_insert_exception(struct rt6_info *nrt, 138835732d01SWei Wang struct rt6_info *ort) 138935732d01SWei Wang { 13905e670d84SDavid Ahern struct net *net = dev_net(nrt->dst.dev); 139135732d01SWei Wang struct rt6_exception_bucket *bucket; 139235732d01SWei Wang struct in6_addr *src_key = NULL; 139335732d01SWei Wang struct rt6_exception *rt6_ex; 139435732d01SWei Wang int err = 0; 139535732d01SWei Wang 139635732d01SWei Wang /* ort can't be a cache or pcpu route */ 139735732d01SWei Wang if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)) 13983a2232e9SDavid Miller ort = ort->from; 139935732d01SWei Wang WARN_ON_ONCE(ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)); 140035732d01SWei Wang 140135732d01SWei Wang spin_lock_bh(&rt6_exception_lock); 140235732d01SWei Wang 140335732d01SWei Wang if (ort->exception_bucket_flushed) { 140435732d01SWei Wang err = -EINVAL; 140535732d01SWei Wang goto out; 140635732d01SWei Wang } 140735732d01SWei Wang 140835732d01SWei Wang bucket = rcu_dereference_protected(ort->rt6i_exception_bucket, 140935732d01SWei Wang lockdep_is_held(&rt6_exception_lock)); 141035732d01SWei Wang if (!bucket) { 141135732d01SWei Wang bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket), 141235732d01SWei Wang GFP_ATOMIC); 141335732d01SWei Wang if (!bucket) { 141435732d01SWei Wang err = -ENOMEM; 141535732d01SWei Wang goto out; 141635732d01SWei Wang } 141735732d01SWei Wang rcu_assign_pointer(ort->rt6i_exception_bucket, bucket); 141835732d01SWei Wang } 141935732d01SWei Wang 142035732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 142135732d01SWei Wang /* rt6i_src.plen != 0 indicates ort is in subtree 142235732d01SWei Wang * and exception table is indexed by a hash of 142335732d01SWei Wang * both rt6i_dst and rt6i_src. 142435732d01SWei Wang * Otherwise, the exception table is indexed by 142535732d01SWei Wang * a hash of only rt6i_dst. 142635732d01SWei Wang */ 142735732d01SWei Wang if (ort->rt6i_src.plen) 142835732d01SWei Wang src_key = &nrt->rt6i_src.addr; 142935732d01SWei Wang #endif 143060006a48SWei Wang 143160006a48SWei Wang /* Update rt6i_prefsrc as it could be changed 143260006a48SWei Wang * in rt6_remove_prefsrc() 143360006a48SWei Wang */ 143460006a48SWei Wang nrt->rt6i_prefsrc = ort->rt6i_prefsrc; 1435f5bbe7eeSWei Wang /* rt6_mtu_change() might lower mtu on ort. 1436f5bbe7eeSWei Wang * Only insert this exception route if its mtu 1437f5bbe7eeSWei Wang * is less than ort's mtu value. 1438f5bbe7eeSWei Wang */ 1439f5bbe7eeSWei Wang if (nrt->rt6i_pmtu >= dst_mtu(&ort->dst)) { 1440f5bbe7eeSWei Wang err = -EINVAL; 1441f5bbe7eeSWei Wang goto out; 1442f5bbe7eeSWei Wang } 144360006a48SWei Wang 144435732d01SWei Wang rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr, 144535732d01SWei Wang src_key); 144635732d01SWei Wang if (rt6_ex) 144735732d01SWei Wang rt6_remove_exception(bucket, rt6_ex); 144835732d01SWei Wang 144935732d01SWei Wang rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC); 145035732d01SWei Wang if (!rt6_ex) { 145135732d01SWei Wang err = -ENOMEM; 145235732d01SWei Wang goto out; 145335732d01SWei Wang } 145435732d01SWei Wang rt6_ex->rt6i = nrt; 145535732d01SWei Wang rt6_ex->stamp = jiffies; 145635732d01SWei Wang atomic_inc(&nrt->rt6i_ref); 145735732d01SWei Wang nrt->rt6i_node = ort->rt6i_node; 145835732d01SWei Wang hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain); 145935732d01SWei Wang bucket->depth++; 146081eb8447SWei Wang net->ipv6.rt6_stats->fib_rt_cache++; 146135732d01SWei Wang 146235732d01SWei Wang if (bucket->depth > FIB6_MAX_DEPTH) 146335732d01SWei Wang rt6_exception_remove_oldest(bucket); 146435732d01SWei Wang 146535732d01SWei Wang out: 146635732d01SWei Wang spin_unlock_bh(&rt6_exception_lock); 146735732d01SWei Wang 146835732d01SWei Wang /* Update fn->fn_sernum to invalidate all cached dst */ 1469b886d5f2SPaolo Abeni if (!err) { 1470922c2ac8SIdo Schimmel spin_lock_bh(&ort->rt6i_table->tb6_lock); 14717aef6859SDavid Ahern fib6_update_sernum(net, ort); 1472922c2ac8SIdo Schimmel spin_unlock_bh(&ort->rt6i_table->tb6_lock); 1473b886d5f2SPaolo Abeni fib6_force_start_gc(net); 1474b886d5f2SPaolo Abeni } 147535732d01SWei Wang 147635732d01SWei Wang return err; 147735732d01SWei Wang } 147835732d01SWei Wang 147935732d01SWei Wang void rt6_flush_exceptions(struct rt6_info *rt) 148035732d01SWei Wang { 148135732d01SWei Wang struct rt6_exception_bucket *bucket; 148235732d01SWei Wang struct rt6_exception *rt6_ex; 148335732d01SWei Wang struct hlist_node *tmp; 148435732d01SWei Wang int i; 148535732d01SWei Wang 148635732d01SWei Wang spin_lock_bh(&rt6_exception_lock); 148735732d01SWei Wang /* Prevent rt6_insert_exception() to recreate the bucket list */ 148835732d01SWei Wang rt->exception_bucket_flushed = 1; 148935732d01SWei Wang 149035732d01SWei Wang bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, 149135732d01SWei Wang lockdep_is_held(&rt6_exception_lock)); 149235732d01SWei Wang if (!bucket) 149335732d01SWei Wang goto out; 149435732d01SWei Wang 149535732d01SWei Wang for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 149635732d01SWei Wang hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist) 149735732d01SWei Wang rt6_remove_exception(bucket, rt6_ex); 149835732d01SWei Wang WARN_ON_ONCE(bucket->depth); 149935732d01SWei Wang bucket++; 150035732d01SWei Wang } 150135732d01SWei Wang 150235732d01SWei Wang out: 150335732d01SWei Wang spin_unlock_bh(&rt6_exception_lock); 150435732d01SWei Wang } 150535732d01SWei Wang 150635732d01SWei Wang /* Find cached rt in the hash table inside passed in rt 150735732d01SWei Wang * Caller has to hold rcu_read_lock() 150835732d01SWei Wang */ 150935732d01SWei Wang static struct rt6_info *rt6_find_cached_rt(struct rt6_info *rt, 151035732d01SWei Wang struct in6_addr *daddr, 151135732d01SWei Wang struct in6_addr *saddr) 151235732d01SWei Wang { 151335732d01SWei Wang struct rt6_exception_bucket *bucket; 151435732d01SWei Wang struct in6_addr *src_key = NULL; 151535732d01SWei Wang struct rt6_exception *rt6_ex; 151635732d01SWei Wang struct rt6_info *res = NULL; 151735732d01SWei Wang 151835732d01SWei Wang bucket = rcu_dereference(rt->rt6i_exception_bucket); 151935732d01SWei Wang 152035732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 152135732d01SWei Wang /* rt6i_src.plen != 0 indicates rt is in subtree 152235732d01SWei Wang * and exception table is indexed by a hash of 152335732d01SWei Wang * both rt6i_dst and rt6i_src. 152435732d01SWei Wang * Otherwise, the exception table is indexed by 152535732d01SWei Wang * a hash of only rt6i_dst. 152635732d01SWei Wang */ 152735732d01SWei Wang if (rt->rt6i_src.plen) 152835732d01SWei Wang src_key = saddr; 152935732d01SWei Wang #endif 153035732d01SWei Wang rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key); 153135732d01SWei Wang 153235732d01SWei Wang if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i)) 153335732d01SWei Wang res = rt6_ex->rt6i; 153435732d01SWei Wang 153535732d01SWei Wang return res; 153635732d01SWei Wang } 153735732d01SWei Wang 153835732d01SWei Wang /* Remove the passed in cached rt from the hash table that contains it */ 153935732d01SWei Wang int rt6_remove_exception_rt(struct rt6_info *rt) 154035732d01SWei Wang { 154135732d01SWei Wang struct rt6_exception_bucket *bucket; 15423a2232e9SDavid Miller struct rt6_info *from = rt->from; 154335732d01SWei Wang struct in6_addr *src_key = NULL; 154435732d01SWei Wang struct rt6_exception *rt6_ex; 154535732d01SWei Wang int err; 154635732d01SWei Wang 154735732d01SWei Wang if (!from || 1548442d713bSColin Ian King !(rt->rt6i_flags & RTF_CACHE)) 154935732d01SWei Wang return -EINVAL; 155035732d01SWei Wang 155135732d01SWei Wang if (!rcu_access_pointer(from->rt6i_exception_bucket)) 155235732d01SWei Wang return -ENOENT; 155335732d01SWei Wang 155435732d01SWei Wang spin_lock_bh(&rt6_exception_lock); 155535732d01SWei Wang bucket = rcu_dereference_protected(from->rt6i_exception_bucket, 155635732d01SWei Wang lockdep_is_held(&rt6_exception_lock)); 155735732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 155835732d01SWei Wang /* rt6i_src.plen != 0 indicates 'from' is in subtree 155935732d01SWei Wang * and exception table is indexed by a hash of 156035732d01SWei Wang * both rt6i_dst and rt6i_src. 156135732d01SWei Wang * Otherwise, the exception table is indexed by 156235732d01SWei Wang * a hash of only rt6i_dst. 156335732d01SWei Wang */ 156435732d01SWei Wang if (from->rt6i_src.plen) 156535732d01SWei Wang src_key = &rt->rt6i_src.addr; 156635732d01SWei Wang #endif 156735732d01SWei Wang rt6_ex = __rt6_find_exception_spinlock(&bucket, 156835732d01SWei Wang &rt->rt6i_dst.addr, 156935732d01SWei Wang src_key); 157035732d01SWei Wang if (rt6_ex) { 157135732d01SWei Wang rt6_remove_exception(bucket, rt6_ex); 157235732d01SWei Wang err = 0; 157335732d01SWei Wang } else { 157435732d01SWei Wang err = -ENOENT; 157535732d01SWei Wang } 157635732d01SWei Wang 157735732d01SWei Wang spin_unlock_bh(&rt6_exception_lock); 157835732d01SWei Wang return err; 157935732d01SWei Wang } 158035732d01SWei Wang 158135732d01SWei Wang /* Find rt6_ex which contains the passed in rt cache and 158235732d01SWei Wang * refresh its stamp 158335732d01SWei Wang */ 158435732d01SWei Wang static void rt6_update_exception_stamp_rt(struct rt6_info *rt) 158535732d01SWei Wang { 158635732d01SWei Wang struct rt6_exception_bucket *bucket; 15873a2232e9SDavid Miller struct rt6_info *from = rt->from; 158835732d01SWei Wang struct in6_addr *src_key = NULL; 158935732d01SWei Wang struct rt6_exception *rt6_ex; 159035732d01SWei Wang 159135732d01SWei Wang if (!from || 1592442d713bSColin Ian King !(rt->rt6i_flags & RTF_CACHE)) 159335732d01SWei Wang return; 159435732d01SWei Wang 159535732d01SWei Wang rcu_read_lock(); 159635732d01SWei Wang bucket = rcu_dereference(from->rt6i_exception_bucket); 159735732d01SWei Wang 159835732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 159935732d01SWei Wang /* rt6i_src.plen != 0 indicates 'from' is in subtree 160035732d01SWei Wang * and exception table is indexed by a hash of 160135732d01SWei Wang * both rt6i_dst and rt6i_src. 160235732d01SWei Wang * Otherwise, the exception table is indexed by 160335732d01SWei Wang * a hash of only rt6i_dst. 160435732d01SWei Wang */ 160535732d01SWei Wang if (from->rt6i_src.plen) 160635732d01SWei Wang src_key = &rt->rt6i_src.addr; 160735732d01SWei Wang #endif 160835732d01SWei Wang rt6_ex = __rt6_find_exception_rcu(&bucket, 160935732d01SWei Wang &rt->rt6i_dst.addr, 161035732d01SWei Wang src_key); 161135732d01SWei Wang if (rt6_ex) 161235732d01SWei Wang rt6_ex->stamp = jiffies; 161335732d01SWei Wang 161435732d01SWei Wang rcu_read_unlock(); 161535732d01SWei Wang } 161635732d01SWei Wang 161760006a48SWei Wang static void rt6_exceptions_remove_prefsrc(struct rt6_info *rt) 161860006a48SWei Wang { 161960006a48SWei Wang struct rt6_exception_bucket *bucket; 162060006a48SWei Wang struct rt6_exception *rt6_ex; 162160006a48SWei Wang int i; 162260006a48SWei Wang 162360006a48SWei Wang bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, 162460006a48SWei Wang lockdep_is_held(&rt6_exception_lock)); 162560006a48SWei Wang 162660006a48SWei Wang if (bucket) { 162760006a48SWei Wang for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 162860006a48SWei Wang hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { 162960006a48SWei Wang rt6_ex->rt6i->rt6i_prefsrc.plen = 0; 163060006a48SWei Wang } 163160006a48SWei Wang bucket++; 163260006a48SWei Wang } 163360006a48SWei Wang } 163460006a48SWei Wang } 163560006a48SWei Wang 1636e9fa1495SStefano Brivio static bool rt6_mtu_change_route_allowed(struct inet6_dev *idev, 1637e9fa1495SStefano Brivio struct rt6_info *rt, int mtu) 1638e9fa1495SStefano Brivio { 1639e9fa1495SStefano Brivio /* If the new MTU is lower than the route PMTU, this new MTU will be the 1640e9fa1495SStefano Brivio * lowest MTU in the path: always allow updating the route PMTU to 1641e9fa1495SStefano Brivio * reflect PMTU decreases. 1642e9fa1495SStefano Brivio * 1643e9fa1495SStefano Brivio * If the new MTU is higher, and the route PMTU is equal to the local 1644e9fa1495SStefano Brivio * MTU, this means the old MTU is the lowest in the path, so allow 1645e9fa1495SStefano Brivio * updating it: if other nodes now have lower MTUs, PMTU discovery will 1646e9fa1495SStefano Brivio * handle this. 1647e9fa1495SStefano Brivio */ 1648e9fa1495SStefano Brivio 1649e9fa1495SStefano Brivio if (dst_mtu(&rt->dst) >= mtu) 1650e9fa1495SStefano Brivio return true; 1651e9fa1495SStefano Brivio 1652e9fa1495SStefano Brivio if (dst_mtu(&rt->dst) == idev->cnf.mtu6) 1653e9fa1495SStefano Brivio return true; 1654e9fa1495SStefano Brivio 1655e9fa1495SStefano Brivio return false; 1656e9fa1495SStefano Brivio } 1657e9fa1495SStefano Brivio 1658e9fa1495SStefano Brivio static void rt6_exceptions_update_pmtu(struct inet6_dev *idev, 1659e9fa1495SStefano Brivio struct rt6_info *rt, int mtu) 1660f5bbe7eeSWei Wang { 1661f5bbe7eeSWei Wang struct rt6_exception_bucket *bucket; 1662f5bbe7eeSWei Wang struct rt6_exception *rt6_ex; 1663f5bbe7eeSWei Wang int i; 1664f5bbe7eeSWei Wang 1665f5bbe7eeSWei Wang bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, 1666f5bbe7eeSWei Wang lockdep_is_held(&rt6_exception_lock)); 1667f5bbe7eeSWei Wang 1668e9fa1495SStefano Brivio if (!bucket) 1669e9fa1495SStefano Brivio return; 1670e9fa1495SStefano Brivio 1671f5bbe7eeSWei Wang for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 1672f5bbe7eeSWei Wang hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { 1673f5bbe7eeSWei Wang struct rt6_info *entry = rt6_ex->rt6i; 1674e9fa1495SStefano Brivio 1675e9fa1495SStefano Brivio /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected 1676e9fa1495SStefano Brivio * route), the metrics of its rt->dst.from have already 1677f5bbe7eeSWei Wang * been updated. 1678f5bbe7eeSWei Wang */ 1679e9fa1495SStefano Brivio if (entry->rt6i_pmtu && 1680e9fa1495SStefano Brivio rt6_mtu_change_route_allowed(idev, entry, mtu)) 1681f5bbe7eeSWei Wang entry->rt6i_pmtu = mtu; 1682f5bbe7eeSWei Wang } 1683f5bbe7eeSWei Wang bucket++; 1684f5bbe7eeSWei Wang } 1685f5bbe7eeSWei Wang } 1686f5bbe7eeSWei Wang 1687b16cb459SWei Wang #define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 1688b16cb459SWei Wang 1689b16cb459SWei Wang static void rt6_exceptions_clean_tohost(struct rt6_info *rt, 1690b16cb459SWei Wang struct in6_addr *gateway) 1691b16cb459SWei Wang { 1692b16cb459SWei Wang struct rt6_exception_bucket *bucket; 1693b16cb459SWei Wang struct rt6_exception *rt6_ex; 1694b16cb459SWei Wang struct hlist_node *tmp; 1695b16cb459SWei Wang int i; 1696b16cb459SWei Wang 1697b16cb459SWei Wang if (!rcu_access_pointer(rt->rt6i_exception_bucket)) 1698b16cb459SWei Wang return; 1699b16cb459SWei Wang 1700b16cb459SWei Wang spin_lock_bh(&rt6_exception_lock); 1701b16cb459SWei Wang bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, 1702b16cb459SWei Wang lockdep_is_held(&rt6_exception_lock)); 1703b16cb459SWei Wang 1704b16cb459SWei Wang if (bucket) { 1705b16cb459SWei Wang for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 1706b16cb459SWei Wang hlist_for_each_entry_safe(rt6_ex, tmp, 1707b16cb459SWei Wang &bucket->chain, hlist) { 1708b16cb459SWei Wang struct rt6_info *entry = rt6_ex->rt6i; 1709b16cb459SWei Wang 1710b16cb459SWei Wang if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) == 1711b16cb459SWei Wang RTF_CACHE_GATEWAY && 1712b16cb459SWei Wang ipv6_addr_equal(gateway, 1713b16cb459SWei Wang &entry->rt6i_gateway)) { 1714b16cb459SWei Wang rt6_remove_exception(bucket, rt6_ex); 1715b16cb459SWei Wang } 1716b16cb459SWei Wang } 1717b16cb459SWei Wang bucket++; 1718b16cb459SWei Wang } 1719b16cb459SWei Wang } 1720b16cb459SWei Wang 1721b16cb459SWei Wang spin_unlock_bh(&rt6_exception_lock); 1722b16cb459SWei Wang } 1723b16cb459SWei Wang 1724c757faa8SWei Wang static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket, 1725c757faa8SWei Wang struct rt6_exception *rt6_ex, 1726c757faa8SWei Wang struct fib6_gc_args *gc_args, 1727c757faa8SWei Wang unsigned long now) 1728c757faa8SWei Wang { 1729c757faa8SWei Wang struct rt6_info *rt = rt6_ex->rt6i; 1730c757faa8SWei Wang 17311859bac0SPaolo Abeni /* we are pruning and obsoleting aged-out and non gateway exceptions 17321859bac0SPaolo Abeni * even if others have still references to them, so that on next 17331859bac0SPaolo Abeni * dst_check() such references can be dropped. 17341859bac0SPaolo Abeni * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when 17351859bac0SPaolo Abeni * expired, independently from their aging, as per RFC 8201 section 4 17361859bac0SPaolo Abeni */ 173731afeb42SWei Wang if (!(rt->rt6i_flags & RTF_EXPIRES)) { 173831afeb42SWei Wang if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) { 1739c757faa8SWei Wang RT6_TRACE("aging clone %p\n", rt); 1740c757faa8SWei Wang rt6_remove_exception(bucket, rt6_ex); 1741c757faa8SWei Wang return; 174231afeb42SWei Wang } 174331afeb42SWei Wang } else if (time_after(jiffies, rt->dst.expires)) { 174431afeb42SWei Wang RT6_TRACE("purging expired route %p\n", rt); 174531afeb42SWei Wang rt6_remove_exception(bucket, rt6_ex); 174631afeb42SWei Wang return; 174731afeb42SWei Wang } 174831afeb42SWei Wang 174931afeb42SWei Wang if (rt->rt6i_flags & RTF_GATEWAY) { 1750c757faa8SWei Wang struct neighbour *neigh; 1751c757faa8SWei Wang __u8 neigh_flags = 0; 1752c757faa8SWei Wang 17531bfa26ffSEric Dumazet neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 17541bfa26ffSEric Dumazet if (neigh) 1755c757faa8SWei Wang neigh_flags = neigh->flags; 17561bfa26ffSEric Dumazet 1757c757faa8SWei Wang if (!(neigh_flags & NTF_ROUTER)) { 1758c757faa8SWei Wang RT6_TRACE("purging route %p via non-router but gateway\n", 1759c757faa8SWei Wang rt); 1760c757faa8SWei Wang rt6_remove_exception(bucket, rt6_ex); 1761c757faa8SWei Wang return; 1762c757faa8SWei Wang } 1763c757faa8SWei Wang } 176431afeb42SWei Wang 1765c757faa8SWei Wang gc_args->more++; 1766c757faa8SWei Wang } 1767c757faa8SWei Wang 1768c757faa8SWei Wang void rt6_age_exceptions(struct rt6_info *rt, 1769c757faa8SWei Wang struct fib6_gc_args *gc_args, 1770c757faa8SWei Wang unsigned long now) 1771c757faa8SWei Wang { 1772c757faa8SWei Wang struct rt6_exception_bucket *bucket; 1773c757faa8SWei Wang struct rt6_exception *rt6_ex; 1774c757faa8SWei Wang struct hlist_node *tmp; 1775c757faa8SWei Wang int i; 1776c757faa8SWei Wang 1777c757faa8SWei Wang if (!rcu_access_pointer(rt->rt6i_exception_bucket)) 1778c757faa8SWei Wang return; 1779c757faa8SWei Wang 17801bfa26ffSEric Dumazet rcu_read_lock_bh(); 17811bfa26ffSEric Dumazet spin_lock(&rt6_exception_lock); 1782c757faa8SWei Wang bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, 1783c757faa8SWei Wang lockdep_is_held(&rt6_exception_lock)); 1784c757faa8SWei Wang 1785c757faa8SWei Wang if (bucket) { 1786c757faa8SWei Wang for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 1787c757faa8SWei Wang hlist_for_each_entry_safe(rt6_ex, tmp, 1788c757faa8SWei Wang &bucket->chain, hlist) { 1789c757faa8SWei Wang rt6_age_examine_exception(bucket, rt6_ex, 1790c757faa8SWei Wang gc_args, now); 1791c757faa8SWei Wang } 1792c757faa8SWei Wang bucket++; 1793c757faa8SWei Wang } 1794c757faa8SWei Wang } 17951bfa26ffSEric Dumazet spin_unlock(&rt6_exception_lock); 17961bfa26ffSEric Dumazet rcu_read_unlock_bh(); 1797c757faa8SWei Wang } 1798c757faa8SWei Wang 17999ff74384SDavid Ahern struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, 1800b75cc8f9SDavid Ahern int oif, struct flowi6 *fl6, 1801b75cc8f9SDavid Ahern const struct sk_buff *skb, int flags) 18021da177e4SLinus Torvalds { 1803367efcb9SMartin KaFai Lau struct fib6_node *fn, *saved_fn; 18042b760fcfSWei Wang struct rt6_info *rt, *rt_cache; 1805c71099acSThomas Graf int strict = 0; 18061da177e4SLinus Torvalds 180777d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 1808d5d32e4bSDavid Ahern strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE; 1809367efcb9SMartin KaFai Lau if (net->ipv6.devconf_all->forwarding == 0) 1810367efcb9SMartin KaFai Lau strict |= RT6_LOOKUP_F_REACHABLE; 18111da177e4SLinus Torvalds 181266f5d6ceSWei Wang rcu_read_lock(); 18131da177e4SLinus Torvalds 18144c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1815367efcb9SMartin KaFai Lau saved_fn = fn; 18161da177e4SLinus Torvalds 1817ca254490SDavid Ahern if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) 1818ca254490SDavid Ahern oif = 0; 1819ca254490SDavid Ahern 1820a3c00e46SMartin KaFai Lau redo_rt6_select: 18218d1040e8SWei Wang rt = rt6_select(net, fn, oif, strict); 182252bd4c0cSNicolas Dichtel if (rt->rt6i_nsiblings) 1823b4bac172SDavid Ahern rt = rt6_multipath_select(net, rt, fl6, oif, skb, strict); 1824a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1825a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1826a3c00e46SMartin KaFai Lau if (fn) 1827a3c00e46SMartin KaFai Lau goto redo_rt6_select; 1828367efcb9SMartin KaFai Lau else if (strict & RT6_LOOKUP_F_REACHABLE) { 1829367efcb9SMartin KaFai Lau /* also consider unreachable route */ 1830367efcb9SMartin KaFai Lau strict &= ~RT6_LOOKUP_F_REACHABLE; 1831367efcb9SMartin KaFai Lau fn = saved_fn; 1832367efcb9SMartin KaFai Lau goto redo_rt6_select; 1833367efcb9SMartin KaFai Lau } 1834a3c00e46SMartin KaFai Lau } 1835a3c00e46SMartin KaFai Lau 18362b760fcfSWei Wang /*Search through exception table */ 18372b760fcfSWei Wang rt_cache = rt6_find_cached_rt(rt, &fl6->daddr, &fl6->saddr); 18382b760fcfSWei Wang if (rt_cache) 18392b760fcfSWei Wang rt = rt_cache; 1840d52d3997SMartin KaFai Lau 1841d3843fe5SWei Wang if (rt == net->ipv6.ip6_null_entry) { 184266f5d6ceSWei Wang rcu_read_unlock(); 1843d3843fe5SWei Wang dst_hold(&rt->dst); 1844b65f164dSPaolo Abeni trace_fib6_table_lookup(net, rt, table, fl6); 1845d3843fe5SWei Wang return rt; 1846d3843fe5SWei Wang } else if (rt->rt6i_flags & RTF_CACHE) { 1847d3843fe5SWei Wang if (ip6_hold_safe(net, &rt, true)) { 1848d3843fe5SWei Wang dst_use_noref(&rt->dst, jiffies); 1849d52d3997SMartin KaFai Lau rt6_dst_from_metrics_check(rt); 1850d3843fe5SWei Wang } 185166f5d6ceSWei Wang rcu_read_unlock(); 1852b65f164dSPaolo Abeni trace_fib6_table_lookup(net, rt, table, fl6); 1853d52d3997SMartin KaFai Lau return rt; 18543da59bd9SMartin KaFai Lau } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && 18553da59bd9SMartin KaFai Lau !(rt->rt6i_flags & RTF_GATEWAY))) { 18563da59bd9SMartin KaFai Lau /* Create a RTF_CACHE clone which will not be 18573da59bd9SMartin KaFai Lau * owned by the fib6 tree. It is for the special case where 18583da59bd9SMartin KaFai Lau * the daddr in the skb during the neighbor look-up is different 18593da59bd9SMartin KaFai Lau * from the fl6->daddr used to look-up route here. 18603da59bd9SMartin KaFai Lau */ 1861c71099acSThomas Graf 18623da59bd9SMartin KaFai Lau struct rt6_info *uncached_rt; 18633da59bd9SMartin KaFai Lau 1864d3843fe5SWei Wang if (ip6_hold_safe(net, &rt, true)) { 1865d3843fe5SWei Wang dst_use_noref(&rt->dst, jiffies); 1866d3843fe5SWei Wang } else { 186766f5d6ceSWei Wang rcu_read_unlock(); 1868d3843fe5SWei Wang uncached_rt = rt; 1869d3843fe5SWei Wang goto uncached_rt_out; 1870d3843fe5SWei Wang } 187166f5d6ceSWei Wang rcu_read_unlock(); 1872d52d3997SMartin KaFai Lau 18733da59bd9SMartin KaFai Lau uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL); 18743da59bd9SMartin KaFai Lau dst_release(&rt->dst); 18753da59bd9SMartin KaFai Lau 18761cfb71eeSWei Wang if (uncached_rt) { 18771cfb71eeSWei Wang /* Uncached_rt's refcnt is taken during ip6_rt_cache_alloc() 18781cfb71eeSWei Wang * No need for another dst_hold() 18791cfb71eeSWei Wang */ 18808d0b94afSMartin KaFai Lau rt6_uncached_list_add(uncached_rt); 188181eb8447SWei Wang atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache); 18821cfb71eeSWei Wang } else { 18833da59bd9SMartin KaFai Lau uncached_rt = net->ipv6.ip6_null_entry; 18843da59bd9SMartin KaFai Lau dst_hold(&uncached_rt->dst); 18851cfb71eeSWei Wang } 1886b811580dSDavid Ahern 1887d3843fe5SWei Wang uncached_rt_out: 1888b65f164dSPaolo Abeni trace_fib6_table_lookup(net, uncached_rt, table, fl6); 18893da59bd9SMartin KaFai Lau return uncached_rt; 18903da59bd9SMartin KaFai Lau 1891d52d3997SMartin KaFai Lau } else { 1892d52d3997SMartin KaFai Lau /* Get a percpu copy */ 1893d52d3997SMartin KaFai Lau 1894d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 1895d52d3997SMartin KaFai Lau 1896d3843fe5SWei Wang dst_use_noref(&rt->dst, jiffies); 1897951f788aSEric Dumazet local_bh_disable(); 1898d52d3997SMartin KaFai Lau pcpu_rt = rt6_get_pcpu_route(rt); 1899d52d3997SMartin KaFai Lau 1900951f788aSEric Dumazet if (!pcpu_rt) { 1901a94b9367SWei Wang /* atomic_inc_not_zero() is needed when using rcu */ 1902a94b9367SWei Wang if (atomic_inc_not_zero(&rt->rt6i_ref)) { 1903951f788aSEric Dumazet /* No dst_hold() on rt is needed because grabbing 1904a94b9367SWei Wang * rt->rt6i_ref makes sure rt can't be released. 19059c7370a1SMartin KaFai Lau */ 1906afb1d4b5SDavid Ahern pcpu_rt = rt6_make_pcpu_route(net, rt); 1907a94b9367SWei Wang rt6_release(rt); 1908a94b9367SWei Wang } else { 1909a94b9367SWei Wang /* rt is already removed from tree */ 1910a94b9367SWei Wang pcpu_rt = net->ipv6.ip6_null_entry; 1911a94b9367SWei Wang dst_hold(&pcpu_rt->dst); 1912a94b9367SWei Wang } 19139c7370a1SMartin KaFai Lau } 1914951f788aSEric Dumazet local_bh_enable(); 1915951f788aSEric Dumazet rcu_read_unlock(); 1916b65f164dSPaolo Abeni trace_fib6_table_lookup(net, pcpu_rt, table, fl6); 1917d52d3997SMartin KaFai Lau return pcpu_rt; 1918d52d3997SMartin KaFai Lau } 1919c71099acSThomas Graf } 19209ff74384SDavid Ahern EXPORT_SYMBOL_GPL(ip6_pol_route); 1921c71099acSThomas Graf 1922b75cc8f9SDavid Ahern static struct rt6_info *ip6_pol_route_input(struct net *net, 1923b75cc8f9SDavid Ahern struct fib6_table *table, 1924b75cc8f9SDavid Ahern struct flowi6 *fl6, 1925b75cc8f9SDavid Ahern const struct sk_buff *skb, 1926b75cc8f9SDavid Ahern int flags) 19274acad72dSPavel Emelyanov { 1928b75cc8f9SDavid Ahern return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags); 19294acad72dSPavel Emelyanov } 19304acad72dSPavel Emelyanov 1931d409b847SMahesh Bandewar struct dst_entry *ip6_route_input_lookup(struct net *net, 193272331bc0SShmulik Ladkani struct net_device *dev, 1933b75cc8f9SDavid Ahern struct flowi6 *fl6, 1934b75cc8f9SDavid Ahern const struct sk_buff *skb, 1935b75cc8f9SDavid Ahern int flags) 193672331bc0SShmulik Ladkani { 193772331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 193872331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 193972331bc0SShmulik Ladkani 1940b75cc8f9SDavid Ahern return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input); 194172331bc0SShmulik Ladkani } 1942d409b847SMahesh Bandewar EXPORT_SYMBOL_GPL(ip6_route_input_lookup); 194372331bc0SShmulik Ladkani 194423aebdacSJakub Sitnicki static void ip6_multipath_l3_keys(const struct sk_buff *skb, 19455e5d6fedSRoopa Prabhu struct flow_keys *keys, 19465e5d6fedSRoopa Prabhu struct flow_keys *flkeys) 194723aebdacSJakub Sitnicki { 194823aebdacSJakub Sitnicki const struct ipv6hdr *outer_iph = ipv6_hdr(skb); 194923aebdacSJakub Sitnicki const struct ipv6hdr *key_iph = outer_iph; 19505e5d6fedSRoopa Prabhu struct flow_keys *_flkeys = flkeys; 195123aebdacSJakub Sitnicki const struct ipv6hdr *inner_iph; 195223aebdacSJakub Sitnicki const struct icmp6hdr *icmph; 195323aebdacSJakub Sitnicki struct ipv6hdr _inner_iph; 195423aebdacSJakub Sitnicki 195523aebdacSJakub Sitnicki if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6)) 195623aebdacSJakub Sitnicki goto out; 195723aebdacSJakub Sitnicki 195823aebdacSJakub Sitnicki icmph = icmp6_hdr(skb); 195923aebdacSJakub Sitnicki if (icmph->icmp6_type != ICMPV6_DEST_UNREACH && 196023aebdacSJakub Sitnicki icmph->icmp6_type != ICMPV6_PKT_TOOBIG && 196123aebdacSJakub Sitnicki icmph->icmp6_type != ICMPV6_TIME_EXCEED && 196223aebdacSJakub Sitnicki icmph->icmp6_type != ICMPV6_PARAMPROB) 196323aebdacSJakub Sitnicki goto out; 196423aebdacSJakub Sitnicki 196523aebdacSJakub Sitnicki inner_iph = skb_header_pointer(skb, 196623aebdacSJakub Sitnicki skb_transport_offset(skb) + sizeof(*icmph), 196723aebdacSJakub Sitnicki sizeof(_inner_iph), &_inner_iph); 196823aebdacSJakub Sitnicki if (!inner_iph) 196923aebdacSJakub Sitnicki goto out; 197023aebdacSJakub Sitnicki 197123aebdacSJakub Sitnicki key_iph = inner_iph; 19725e5d6fedSRoopa Prabhu _flkeys = NULL; 197323aebdacSJakub Sitnicki out: 19745e5d6fedSRoopa Prabhu if (_flkeys) { 19755e5d6fedSRoopa Prabhu keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src; 19765e5d6fedSRoopa Prabhu keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst; 19775e5d6fedSRoopa Prabhu keys->tags.flow_label = _flkeys->tags.flow_label; 19785e5d6fedSRoopa Prabhu keys->basic.ip_proto = _flkeys->basic.ip_proto; 19795e5d6fedSRoopa Prabhu } else { 198023aebdacSJakub Sitnicki keys->addrs.v6addrs.src = key_iph->saddr; 198123aebdacSJakub Sitnicki keys->addrs.v6addrs.dst = key_iph->daddr; 198223aebdacSJakub Sitnicki keys->tags.flow_label = ip6_flowinfo(key_iph); 198323aebdacSJakub Sitnicki keys->basic.ip_proto = key_iph->nexthdr; 198423aebdacSJakub Sitnicki } 19855e5d6fedSRoopa Prabhu } 198623aebdacSJakub Sitnicki 198723aebdacSJakub Sitnicki /* if skb is set it will be used and fl6 can be NULL */ 1988b4bac172SDavid Ahern u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6, 1989b4bac172SDavid Ahern const struct sk_buff *skb, struct flow_keys *flkeys) 199023aebdacSJakub Sitnicki { 199123aebdacSJakub Sitnicki struct flow_keys hash_keys; 19929a2a537aSDavid Ahern u32 mhash; 199323aebdacSJakub Sitnicki 1994bbfa047aSDavid S. Miller switch (ip6_multipath_hash_policy(net)) { 1995b4bac172SDavid Ahern case 0: 19966f74b6c2SDavid Ahern memset(&hash_keys, 0, sizeof(hash_keys)); 19976f74b6c2SDavid Ahern hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 19989a2a537aSDavid Ahern if (skb) { 19995e5d6fedSRoopa Prabhu ip6_multipath_l3_keys(skb, &hash_keys, flkeys); 20009a2a537aSDavid Ahern } else { 20019a2a537aSDavid Ahern hash_keys.addrs.v6addrs.src = fl6->saddr; 20029a2a537aSDavid Ahern hash_keys.addrs.v6addrs.dst = fl6->daddr; 20039a2a537aSDavid Ahern hash_keys.tags.flow_label = (__force u32)fl6->flowlabel; 20049a2a537aSDavid Ahern hash_keys.basic.ip_proto = fl6->flowi6_proto; 200523aebdacSJakub Sitnicki } 2006b4bac172SDavid Ahern break; 2007b4bac172SDavid Ahern case 1: 2008b4bac172SDavid Ahern if (skb) { 2009b4bac172SDavid Ahern unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP; 2010b4bac172SDavid Ahern struct flow_keys keys; 2011b4bac172SDavid Ahern 2012b4bac172SDavid Ahern /* short-circuit if we already have L4 hash present */ 2013b4bac172SDavid Ahern if (skb->l4_hash) 2014b4bac172SDavid Ahern return skb_get_hash_raw(skb) >> 1; 2015b4bac172SDavid Ahern 2016b4bac172SDavid Ahern memset(&hash_keys, 0, sizeof(hash_keys)); 2017b4bac172SDavid Ahern 2018b4bac172SDavid Ahern if (!flkeys) { 2019b4bac172SDavid Ahern skb_flow_dissect_flow_keys(skb, &keys, flag); 2020b4bac172SDavid Ahern flkeys = &keys; 2021b4bac172SDavid Ahern } 2022b4bac172SDavid Ahern hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 2023b4bac172SDavid Ahern hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src; 2024b4bac172SDavid Ahern hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst; 2025b4bac172SDavid Ahern hash_keys.ports.src = flkeys->ports.src; 2026b4bac172SDavid Ahern hash_keys.ports.dst = flkeys->ports.dst; 2027b4bac172SDavid Ahern hash_keys.basic.ip_proto = flkeys->basic.ip_proto; 2028b4bac172SDavid Ahern } else { 2029b4bac172SDavid Ahern memset(&hash_keys, 0, sizeof(hash_keys)); 2030b4bac172SDavid Ahern hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 2031b4bac172SDavid Ahern hash_keys.addrs.v6addrs.src = fl6->saddr; 2032b4bac172SDavid Ahern hash_keys.addrs.v6addrs.dst = fl6->daddr; 2033b4bac172SDavid Ahern hash_keys.ports.src = fl6->fl6_sport; 2034b4bac172SDavid Ahern hash_keys.ports.dst = fl6->fl6_dport; 2035b4bac172SDavid Ahern hash_keys.basic.ip_proto = fl6->flowi6_proto; 2036b4bac172SDavid Ahern } 2037b4bac172SDavid Ahern break; 2038b4bac172SDavid Ahern } 20399a2a537aSDavid Ahern mhash = flow_hash_from_keys(&hash_keys); 204023aebdacSJakub Sitnicki 20419a2a537aSDavid Ahern return mhash >> 1; 204223aebdacSJakub Sitnicki } 204323aebdacSJakub Sitnicki 2044c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 2045c71099acSThomas Graf { 2046b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 2047c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 2048adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 2049904af04dSJiri Benc struct ip_tunnel_info *tun_info; 20504c9483b2SDavid S. Miller struct flowi6 fl6 = { 2051e0d56fddSDavid Ahern .flowi6_iif = skb->dev->ifindex, 20524c9483b2SDavid S. Miller .daddr = iph->daddr, 20534c9483b2SDavid S. Miller .saddr = iph->saddr, 20546502ca52SYOSHIFUJI Hideaki / 吉藤英明 .flowlabel = ip6_flowinfo(iph), 20554c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 20564c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 2057c71099acSThomas Graf }; 20585e5d6fedSRoopa Prabhu struct flow_keys *flkeys = NULL, _flkeys; 2059adaa70bbSThomas Graf 2060904af04dSJiri Benc tun_info = skb_tunnel_info(skb); 206146fa062aSJiri Benc if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX)) 2062904af04dSJiri Benc fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id; 20635e5d6fedSRoopa Prabhu 20645e5d6fedSRoopa Prabhu if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys)) 20655e5d6fedSRoopa Prabhu flkeys = &_flkeys; 20665e5d6fedSRoopa Prabhu 206723aebdacSJakub Sitnicki if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6)) 2068b4bac172SDavid Ahern fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys); 206906e9d040SJiri Benc skb_dst_drop(skb); 2070b75cc8f9SDavid Ahern skb_dst_set(skb, 2071b75cc8f9SDavid Ahern ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags)); 2072c71099acSThomas Graf } 2073c71099acSThomas Graf 2074b75cc8f9SDavid Ahern static struct rt6_info *ip6_pol_route_output(struct net *net, 2075b75cc8f9SDavid Ahern struct fib6_table *table, 2076b75cc8f9SDavid Ahern struct flowi6 *fl6, 2077b75cc8f9SDavid Ahern const struct sk_buff *skb, 2078b75cc8f9SDavid Ahern int flags) 2079c71099acSThomas Graf { 2080b75cc8f9SDavid Ahern return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags); 2081c71099acSThomas Graf } 2082c71099acSThomas Graf 20836f21c96aSPaolo Abeni struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk, 20846f21c96aSPaolo Abeni struct flowi6 *fl6, int flags) 2085c71099acSThomas Graf { 2086d46a9d67SDavid Ahern bool any_src; 2087c71099acSThomas Graf 20884c1feac5SDavid Ahern if (rt6_need_strict(&fl6->daddr)) { 20894c1feac5SDavid Ahern struct dst_entry *dst; 20904c1feac5SDavid Ahern 20914c1feac5SDavid Ahern dst = l3mdev_link_scope_lookup(net, fl6); 2092ca254490SDavid Ahern if (dst) 2093ca254490SDavid Ahern return dst; 20944c1feac5SDavid Ahern } 2095ca254490SDavid Ahern 20961fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 20974dc27d1cSDavid McCullough 2098d46a9d67SDavid Ahern any_src = ipv6_addr_any(&fl6->saddr); 2099741a11d9SDavid Ahern if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) || 2100d46a9d67SDavid Ahern (fl6->flowi6_oif && any_src)) 210177d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 2102c71099acSThomas Graf 2103d46a9d67SDavid Ahern if (!any_src) 2104adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 21050c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 21060c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 2107adaa70bbSThomas Graf 2108b75cc8f9SDavid Ahern return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output); 21091da177e4SLinus Torvalds } 21106f21c96aSPaolo Abeni EXPORT_SYMBOL_GPL(ip6_route_output_flags); 21111da177e4SLinus Torvalds 21122774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 211314e50e57SDavid S. Miller { 21145c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 21151dbe3252SWei Wang struct net_device *loopback_dev = net->loopback_dev; 211614e50e57SDavid S. Miller struct dst_entry *new = NULL; 211714e50e57SDavid S. Miller 21181dbe3252SWei Wang rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1, 211962cf27e5SSteffen Klassert DST_OBSOLETE_DEAD, 0); 212014e50e57SDavid S. Miller if (rt) { 21210a1f5962SMartin KaFai Lau rt6_info_init(rt); 212281eb8447SWei Wang atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc); 21230a1f5962SMartin KaFai Lau 2124d8d1f30bSChangli Gao new = &rt->dst; 212514e50e57SDavid S. Miller new->__use = 1; 2126352e512cSHerbert Xu new->input = dst_discard; 2127ede2059dSEric W. Biederman new->output = dst_discard_out; 212814e50e57SDavid S. Miller 2129defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 213014e50e57SDavid S. Miller 21311dbe3252SWei Wang rt->rt6i_idev = in6_dev_get(loopback_dev); 21324e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 21330a1f5962SMartin KaFai Lau rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU; 213414e50e57SDavid S. Miller rt->rt6i_metric = 0; 213514e50e57SDavid S. Miller 213614e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 213714e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 213814e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 213914e50e57SDavid S. Miller #endif 214014e50e57SDavid S. Miller } 214114e50e57SDavid S. Miller 214269ead7afSDavid S. Miller dst_release(dst_orig); 214369ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 214414e50e57SDavid S. Miller } 214514e50e57SDavid S. Miller 21461da177e4SLinus Torvalds /* 21471da177e4SLinus Torvalds * Destination cache support functions 21481da177e4SLinus Torvalds */ 21491da177e4SLinus Torvalds 21504b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt) 21514b32b5adSMartin KaFai Lau { 21523a2232e9SDavid Miller if (rt->from && 21533a2232e9SDavid Miller dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(&rt->from->dst)) 21543a2232e9SDavid Miller dst_init_metrics(&rt->dst, dst_metrics_ptr(&rt->from->dst), true); 21554b32b5adSMartin KaFai Lau } 21564b32b5adSMartin KaFai Lau 21573da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie) 21583da59bd9SMartin KaFai Lau { 215936143645SSteffen Klassert u32 rt_cookie = 0; 2160c5cff856SWei Wang 2161c5cff856SWei Wang if (!rt6_get_cookie_safe(rt, &rt_cookie) || rt_cookie != cookie) 21623da59bd9SMartin KaFai Lau return NULL; 21633da59bd9SMartin KaFai Lau 21643da59bd9SMartin KaFai Lau if (rt6_check_expired(rt)) 21653da59bd9SMartin KaFai Lau return NULL; 21663da59bd9SMartin KaFai Lau 21673da59bd9SMartin KaFai Lau return &rt->dst; 21683da59bd9SMartin KaFai Lau } 21693da59bd9SMartin KaFai Lau 21703da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie) 21713da59bd9SMartin KaFai Lau { 21725973fb1eSMartin KaFai Lau if (!__rt6_check_expired(rt) && 21735973fb1eSMartin KaFai Lau rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && 21743a2232e9SDavid Miller rt6_check(rt->from, cookie)) 21753da59bd9SMartin KaFai Lau return &rt->dst; 21763da59bd9SMartin KaFai Lau else 21773da59bd9SMartin KaFai Lau return NULL; 21783da59bd9SMartin KaFai Lau } 21793da59bd9SMartin KaFai Lau 21801da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 21811da177e4SLinus Torvalds { 21821da177e4SLinus Torvalds struct rt6_info *rt; 21831da177e4SLinus Torvalds 21841da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 21851da177e4SLinus Torvalds 21866f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 21876f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 21886f3118b5SNicolas Dichtel * into this function always. 21896f3118b5SNicolas Dichtel */ 2190e3bc10bdSHannes Frederic Sowa 21914b32b5adSMartin KaFai Lau rt6_dst_from_metrics_check(rt); 21924b32b5adSMartin KaFai Lau 219302bcf4e0SMartin KaFai Lau if (rt->rt6i_flags & RTF_PCPU || 21943a2232e9SDavid Miller (unlikely(!list_empty(&rt->rt6i_uncached)) && rt->from)) 21953da59bd9SMartin KaFai Lau return rt6_dst_from_check(rt, cookie); 21963da59bd9SMartin KaFai Lau else 21973da59bd9SMartin KaFai Lau return rt6_check(rt, cookie); 21981da177e4SLinus Torvalds } 21991da177e4SLinus Torvalds 22001da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 22011da177e4SLinus Torvalds { 22021da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 22031da177e4SLinus Torvalds 22041da177e4SLinus Torvalds if (rt) { 220554c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 220654c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 2207afb1d4b5SDavid Ahern ip6_del_rt(dev_net(dst->dev), rt); 220854c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 22091da177e4SLinus Torvalds } 221054c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 221154c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 221254c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 221354c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 221454c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 221554c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 22161da177e4SLinus Torvalds } 22171da177e4SLinus Torvalds 22181da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 22191da177e4SLinus Torvalds { 22201da177e4SLinus Torvalds struct rt6_info *rt; 22211da177e4SLinus Torvalds 22223ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 22231da177e4SLinus Torvalds 2224adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 22251da177e4SLinus Torvalds if (rt) { 22261eb4f758SHannes Frederic Sowa if (rt->rt6i_flags & RTF_CACHE) { 2227ad65a2f0SWei Wang if (dst_hold_safe(&rt->dst)) 2228afb1d4b5SDavid Ahern ip6_del_rt(dev_net(rt->dst.dev), rt); 2229c5cff856SWei Wang } else { 2230c5cff856SWei Wang struct fib6_node *fn; 2231c5cff856SWei Wang 2232c5cff856SWei Wang rcu_read_lock(); 2233c5cff856SWei Wang fn = rcu_dereference(rt->rt6i_node); 2234c5cff856SWei Wang if (fn && (rt->rt6i_flags & RTF_DEFAULT)) 2235c5cff856SWei Wang fn->fn_sernum = -1; 2236c5cff856SWei Wang rcu_read_unlock(); 22371da177e4SLinus Torvalds } 22381da177e4SLinus Torvalds } 22391eb4f758SHannes Frederic Sowa } 22401da177e4SLinus Torvalds 224145e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) 224245e4fd26SMartin KaFai Lau { 224345e4fd26SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 224445e4fd26SMartin KaFai Lau 224545e4fd26SMartin KaFai Lau rt->rt6i_flags |= RTF_MODIFIED; 224645e4fd26SMartin KaFai Lau rt->rt6i_pmtu = mtu; 224745e4fd26SMartin KaFai Lau rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); 224845e4fd26SMartin KaFai Lau } 224945e4fd26SMartin KaFai Lau 22500d3f6d29SMartin KaFai Lau static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt) 22510d3f6d29SMartin KaFai Lau { 22520d3f6d29SMartin KaFai Lau return !(rt->rt6i_flags & RTF_CACHE) && 22534e587ea7SWei Wang (rt->rt6i_flags & RTF_PCPU || 22544e587ea7SWei Wang rcu_access_pointer(rt->rt6i_node)); 22550d3f6d29SMartin KaFai Lau } 22560d3f6d29SMartin KaFai Lau 225745e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, 225845e4fd26SMartin KaFai Lau const struct ipv6hdr *iph, u32 mtu) 22591da177e4SLinus Torvalds { 22600dec879fSJulian Anastasov const struct in6_addr *daddr, *saddr; 22611da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info *)dst; 22621da177e4SLinus Torvalds 226345e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_LOCAL) 226445e4fd26SMartin KaFai Lau return; 226545e4fd26SMartin KaFai Lau 226619bda36cSXin Long if (dst_metric_locked(dst, RTAX_MTU)) 226719bda36cSXin Long return; 226819bda36cSXin Long 226945e4fd26SMartin KaFai Lau if (iph) { 227045e4fd26SMartin KaFai Lau daddr = &iph->daddr; 227145e4fd26SMartin KaFai Lau saddr = &iph->saddr; 227245e4fd26SMartin KaFai Lau } else if (sk) { 227345e4fd26SMartin KaFai Lau daddr = &sk->sk_v6_daddr; 227445e4fd26SMartin KaFai Lau saddr = &inet6_sk(sk)->saddr; 227545e4fd26SMartin KaFai Lau } else { 22760dec879fSJulian Anastasov daddr = NULL; 22770dec879fSJulian Anastasov saddr = NULL; 22781da177e4SLinus Torvalds } 22790dec879fSJulian Anastasov dst_confirm_neigh(dst, daddr); 22800dec879fSJulian Anastasov mtu = max_t(u32, mtu, IPV6_MIN_MTU); 22810dec879fSJulian Anastasov if (mtu >= dst_mtu(dst)) 22820dec879fSJulian Anastasov return; 22830dec879fSJulian Anastasov 22840dec879fSJulian Anastasov if (!rt6_cache_allowed_for_pmtu(rt6)) { 22850dec879fSJulian Anastasov rt6_do_update_pmtu(rt6, mtu); 22862b760fcfSWei Wang /* update rt6_ex->stamp for cache */ 22872b760fcfSWei Wang if (rt6->rt6i_flags & RTF_CACHE) 22882b760fcfSWei Wang rt6_update_exception_stamp_rt(rt6); 22890dec879fSJulian Anastasov } else if (daddr) { 22900dec879fSJulian Anastasov struct rt6_info *nrt6; 22910dec879fSJulian Anastasov 229245e4fd26SMartin KaFai Lau nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr); 229345e4fd26SMartin KaFai Lau if (nrt6) { 229445e4fd26SMartin KaFai Lau rt6_do_update_pmtu(nrt6, mtu); 22952b760fcfSWei Wang if (rt6_insert_exception(nrt6, rt6)) 22962b760fcfSWei Wang dst_release_immediate(&nrt6->dst); 229745e4fd26SMartin KaFai Lau } 229845e4fd26SMartin KaFai Lau } 229945e4fd26SMartin KaFai Lau } 230045e4fd26SMartin KaFai Lau 230145e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 230245e4fd26SMartin KaFai Lau struct sk_buff *skb, u32 mtu) 230345e4fd26SMartin KaFai Lau { 230445e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu); 23051da177e4SLinus Torvalds } 23061da177e4SLinus Torvalds 230742ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 2308e2d118a1SLorenzo Colitti int oif, u32 mark, kuid_t uid) 230981aded24SDavid S. Miller { 231081aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 231181aded24SDavid S. Miller struct dst_entry *dst; 231281aded24SDavid S. Miller struct flowi6 fl6; 231381aded24SDavid S. Miller 231481aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 231581aded24SDavid S. Miller fl6.flowi6_oif = oif; 23161b3c61dcSLorenzo Colitti fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark); 231781aded24SDavid S. Miller fl6.daddr = iph->daddr; 231881aded24SDavid S. Miller fl6.saddr = iph->saddr; 23196502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 2320e2d118a1SLorenzo Colitti fl6.flowi6_uid = uid; 232181aded24SDavid S. Miller 232281aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 232381aded24SDavid S. Miller if (!dst->error) 232445e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu)); 232581aded24SDavid S. Miller dst_release(dst); 232681aded24SDavid S. Miller } 232781aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 232881aded24SDavid S. Miller 232981aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 233081aded24SDavid S. Miller { 233133c162a9SMartin KaFai Lau struct dst_entry *dst; 233233c162a9SMartin KaFai Lau 233381aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 2334e2d118a1SLorenzo Colitti sk->sk_bound_dev_if, sk->sk_mark, sk->sk_uid); 233533c162a9SMartin KaFai Lau 233633c162a9SMartin KaFai Lau dst = __sk_dst_get(sk); 233733c162a9SMartin KaFai Lau if (!dst || !dst->obsolete || 233833c162a9SMartin KaFai Lau dst->ops->check(dst, inet6_sk(sk)->dst_cookie)) 233933c162a9SMartin KaFai Lau return; 234033c162a9SMartin KaFai Lau 234133c162a9SMartin KaFai Lau bh_lock_sock(sk); 234233c162a9SMartin KaFai Lau if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 234333c162a9SMartin KaFai Lau ip6_datagram_dst_update(sk, false); 234433c162a9SMartin KaFai Lau bh_unlock_sock(sk); 234581aded24SDavid S. Miller } 234681aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 234781aded24SDavid S. Miller 23487d6850f7SAlexey Kodanev void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst, 23497d6850f7SAlexey Kodanev const struct flowi6 *fl6) 23507d6850f7SAlexey Kodanev { 23517d6850f7SAlexey Kodanev #ifdef CONFIG_IPV6_SUBTREES 23527d6850f7SAlexey Kodanev struct ipv6_pinfo *np = inet6_sk(sk); 23537d6850f7SAlexey Kodanev #endif 23547d6850f7SAlexey Kodanev 23557d6850f7SAlexey Kodanev ip6_dst_store(sk, dst, 23567d6850f7SAlexey Kodanev ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ? 23577d6850f7SAlexey Kodanev &sk->sk_v6_daddr : NULL, 23587d6850f7SAlexey Kodanev #ifdef CONFIG_IPV6_SUBTREES 23597d6850f7SAlexey Kodanev ipv6_addr_equal(&fl6->saddr, &np->saddr) ? 23607d6850f7SAlexey Kodanev &np->saddr : 23617d6850f7SAlexey Kodanev #endif 23627d6850f7SAlexey Kodanev NULL); 23637d6850f7SAlexey Kodanev } 23647d6850f7SAlexey Kodanev 2365b55b76b2SDuan Jiong /* Handle redirects */ 2366b55b76b2SDuan Jiong struct ip6rd_flowi { 2367b55b76b2SDuan Jiong struct flowi6 fl6; 2368b55b76b2SDuan Jiong struct in6_addr gateway; 2369b55b76b2SDuan Jiong }; 2370b55b76b2SDuan Jiong 2371b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net, 2372b55b76b2SDuan Jiong struct fib6_table *table, 2373b55b76b2SDuan Jiong struct flowi6 *fl6, 2374b75cc8f9SDavid Ahern const struct sk_buff *skb, 2375b55b76b2SDuan Jiong int flags) 2376b55b76b2SDuan Jiong { 2377b55b76b2SDuan Jiong struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 23782b760fcfSWei Wang struct rt6_info *rt, *rt_cache; 2379b55b76b2SDuan Jiong struct fib6_node *fn; 2380b55b76b2SDuan Jiong 2381b55b76b2SDuan Jiong /* Get the "current" route for this destination and 238267c408cfSAlexander Alemayhu * check if the redirect has come from appropriate router. 2383b55b76b2SDuan Jiong * 2384b55b76b2SDuan Jiong * RFC 4861 specifies that redirects should only be 2385b55b76b2SDuan Jiong * accepted if they come from the nexthop to the target. 2386b55b76b2SDuan Jiong * Due to the way the routes are chosen, this notion 2387b55b76b2SDuan Jiong * is a bit fuzzy and one might need to check all possible 2388b55b76b2SDuan Jiong * routes. 2389b55b76b2SDuan Jiong */ 2390b55b76b2SDuan Jiong 239166f5d6ceSWei Wang rcu_read_lock(); 2392b55b76b2SDuan Jiong fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 2393b55b76b2SDuan Jiong restart: 239466f5d6ceSWei Wang for_each_fib6_node_rt_rcu(fn) { 23955e670d84SDavid Ahern if (rt->fib6_nh.nh_flags & RTNH_F_DEAD) 23968067bb8cSIdo Schimmel continue; 2397b55b76b2SDuan Jiong if (rt6_check_expired(rt)) 2398b55b76b2SDuan Jiong continue; 2399*6edb3c96SDavid Ahern if (rt->rt6i_flags & RTF_REJECT) 2400b55b76b2SDuan Jiong break; 2401b55b76b2SDuan Jiong if (!(rt->rt6i_flags & RTF_GATEWAY)) 2402b55b76b2SDuan Jiong continue; 24035e670d84SDavid Ahern if (fl6->flowi6_oif != rt->fib6_nh.nh_dev->ifindex) 2404b55b76b2SDuan Jiong continue; 24052b760fcfSWei Wang /* rt_cache's gateway might be different from its 'parent' 24062b760fcfSWei Wang * in the case of an ip redirect. 24072b760fcfSWei Wang * So we keep searching in the exception table if the gateway 24082b760fcfSWei Wang * is different. 24092b760fcfSWei Wang */ 24105e670d84SDavid Ahern if (!ipv6_addr_equal(&rdfl->gateway, &rt->fib6_nh.nh_gw)) { 24112b760fcfSWei Wang rt_cache = rt6_find_cached_rt(rt, 24122b760fcfSWei Wang &fl6->daddr, 24132b760fcfSWei Wang &fl6->saddr); 24142b760fcfSWei Wang if (rt_cache && 24152b760fcfSWei Wang ipv6_addr_equal(&rdfl->gateway, 24162b760fcfSWei Wang &rt_cache->rt6i_gateway)) { 24172b760fcfSWei Wang rt = rt_cache; 24182b760fcfSWei Wang break; 24192b760fcfSWei Wang } 2420b55b76b2SDuan Jiong continue; 24212b760fcfSWei Wang } 2422b55b76b2SDuan Jiong break; 2423b55b76b2SDuan Jiong } 2424b55b76b2SDuan Jiong 2425b55b76b2SDuan Jiong if (!rt) 2426b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 2427*6edb3c96SDavid Ahern else if (rt->rt6i_flags & RTF_REJECT) { 2428b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 2429b0a1ba59SMartin KaFai Lau goto out; 2430b0a1ba59SMartin KaFai Lau } 2431b0a1ba59SMartin KaFai Lau 2432b0a1ba59SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 2433a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 2434a3c00e46SMartin KaFai Lau if (fn) 2435a3c00e46SMartin KaFai Lau goto restart; 2436b55b76b2SDuan Jiong } 2437a3c00e46SMartin KaFai Lau 2438b0a1ba59SMartin KaFai Lau out: 2439d3843fe5SWei Wang ip6_hold_safe(net, &rt, true); 2440b55b76b2SDuan Jiong 244166f5d6ceSWei Wang rcu_read_unlock(); 2442b55b76b2SDuan Jiong 2443b65f164dSPaolo Abeni trace_fib6_table_lookup(net, rt, table, fl6); 2444b55b76b2SDuan Jiong return rt; 2445b55b76b2SDuan Jiong }; 2446b55b76b2SDuan Jiong 2447b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net, 2448b55b76b2SDuan Jiong const struct flowi6 *fl6, 2449b75cc8f9SDavid Ahern const struct sk_buff *skb, 2450b55b76b2SDuan Jiong const struct in6_addr *gateway) 2451b55b76b2SDuan Jiong { 2452b55b76b2SDuan Jiong int flags = RT6_LOOKUP_F_HAS_SADDR; 2453b55b76b2SDuan Jiong struct ip6rd_flowi rdfl; 2454b55b76b2SDuan Jiong 2455b55b76b2SDuan Jiong rdfl.fl6 = *fl6; 2456b55b76b2SDuan Jiong rdfl.gateway = *gateway; 2457b55b76b2SDuan Jiong 2458b75cc8f9SDavid Ahern return fib6_rule_lookup(net, &rdfl.fl6, skb, 2459b55b76b2SDuan Jiong flags, __ip6_route_redirect); 2460b55b76b2SDuan Jiong } 2461b55b76b2SDuan Jiong 2462e2d118a1SLorenzo Colitti void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark, 2463e2d118a1SLorenzo Colitti kuid_t uid) 24643a5ad2eeSDavid S. Miller { 24653a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 24663a5ad2eeSDavid S. Miller struct dst_entry *dst; 24673a5ad2eeSDavid S. Miller struct flowi6 fl6; 24683a5ad2eeSDavid S. Miller 24693a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2470e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 24713a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 24723a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 24733a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 24743a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 24756502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 2476e2d118a1SLorenzo Colitti fl6.flowi6_uid = uid; 24773a5ad2eeSDavid S. Miller 2478b75cc8f9SDavid Ahern dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr); 24796700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 24803a5ad2eeSDavid S. Miller dst_release(dst); 24813a5ad2eeSDavid S. Miller } 24823a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 24833a5ad2eeSDavid S. Miller 2484c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, 2485c92a59ecSDuan Jiong u32 mark) 2486c92a59ecSDuan Jiong { 2487c92a59ecSDuan Jiong const struct ipv6hdr *iph = ipv6_hdr(skb); 2488c92a59ecSDuan Jiong const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 2489c92a59ecSDuan Jiong struct dst_entry *dst; 2490c92a59ecSDuan Jiong struct flowi6 fl6; 2491c92a59ecSDuan Jiong 2492c92a59ecSDuan Jiong memset(&fl6, 0, sizeof(fl6)); 2493e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 2494c92a59ecSDuan Jiong fl6.flowi6_oif = oif; 2495c92a59ecSDuan Jiong fl6.flowi6_mark = mark; 2496c92a59ecSDuan Jiong fl6.daddr = msg->dest; 2497c92a59ecSDuan Jiong fl6.saddr = iph->daddr; 2498e2d118a1SLorenzo Colitti fl6.flowi6_uid = sock_net_uid(net, NULL); 2499c92a59ecSDuan Jiong 2500b75cc8f9SDavid Ahern dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr); 2501c92a59ecSDuan Jiong rt6_do_redirect(dst, NULL, skb); 2502c92a59ecSDuan Jiong dst_release(dst); 2503c92a59ecSDuan Jiong } 2504c92a59ecSDuan Jiong 25053a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 25063a5ad2eeSDavid S. Miller { 2507e2d118a1SLorenzo Colitti ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark, 2508e2d118a1SLorenzo Colitti sk->sk_uid); 25093a5ad2eeSDavid S. Miller } 25103a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 25113a5ad2eeSDavid S. Miller 25120dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 25131da177e4SLinus Torvalds { 25140dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 25150dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 25160dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 25170dbaee3bSDavid S. Miller 25181da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 25191da177e4SLinus Torvalds 25205578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 25215578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 25221da177e4SLinus Torvalds 25231da177e4SLinus Torvalds /* 25241da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 25251da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 25261da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 25271da177e4SLinus Torvalds * rely only on pmtu discovery" 25281da177e4SLinus Torvalds */ 25291da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 25301da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 25311da177e4SLinus Torvalds return mtu; 25321da177e4SLinus Torvalds } 25331da177e4SLinus Torvalds 2534ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 2535d33e4553SDavid S. Miller { 25364b32b5adSMartin KaFai Lau const struct rt6_info *rt = (const struct rt6_info *)dst; 25374b32b5adSMartin KaFai Lau unsigned int mtu = rt->rt6i_pmtu; 2538d33e4553SDavid S. Miller struct inet6_dev *idev; 2539618f9bc7SSteffen Klassert 2540618f9bc7SSteffen Klassert if (mtu) 254130f78d8eSEric Dumazet goto out; 2542618f9bc7SSteffen Klassert 25434b32b5adSMartin KaFai Lau mtu = dst_metric_raw(dst, RTAX_MTU); 25444b32b5adSMartin KaFai Lau if (mtu) 25454b32b5adSMartin KaFai Lau goto out; 25464b32b5adSMartin KaFai Lau 2547618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 2548d33e4553SDavid S. Miller 2549d33e4553SDavid S. Miller rcu_read_lock(); 2550d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 2551d33e4553SDavid S. Miller if (idev) 2552d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 2553d33e4553SDavid S. Miller rcu_read_unlock(); 2554d33e4553SDavid S. Miller 255530f78d8eSEric Dumazet out: 255614972cbdSRoopa Prabhu mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); 255714972cbdSRoopa Prabhu 255814972cbdSRoopa Prabhu return mtu - lwtunnel_headroom(dst->lwtstate, mtu); 2559d33e4553SDavid S. Miller } 2560d33e4553SDavid S. Miller 25613b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 256287a11578SDavid S. Miller struct flowi6 *fl6) 25631da177e4SLinus Torvalds { 256487a11578SDavid S. Miller struct dst_entry *dst; 25651da177e4SLinus Torvalds struct rt6_info *rt; 25661da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 2567c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 25681da177e4SLinus Torvalds 256938308473SDavid S. Miller if (unlikely(!idev)) 2570122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 25711da177e4SLinus Torvalds 2572ad706862SMartin KaFai Lau rt = ip6_dst_alloc(net, dev, 0); 257338308473SDavid S. Miller if (unlikely(!rt)) { 25741da177e4SLinus Torvalds in6_dev_put(idev); 257587a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 25761da177e4SLinus Torvalds goto out; 25771da177e4SLinus Torvalds } 25781da177e4SLinus Torvalds 25798e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 2580588753f1SBrendan McGrath rt->dst.input = ip6_input; 25818e2ec639SYan, Zheng rt->dst.output = ip6_output; 2582550bab42SJulian Anastasov rt->rt6i_gateway = fl6->daddr; 258387a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 25848e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 25858e2ec639SYan, Zheng rt->rt6i_idev = idev; 258614edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 25871da177e4SLinus Torvalds 25884c981e28SIdo Schimmel /* Add this dst into uncached_list so that rt6_disable_ip() can 2589587fea74SWei Wang * do proper release of the net_device 2590587fea74SWei Wang */ 2591587fea74SWei Wang rt6_uncached_list_add(rt); 259281eb8447SWei Wang atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache); 25931da177e4SLinus Torvalds 259487a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 259587a11578SDavid S. Miller 25961da177e4SLinus Torvalds out: 259787a11578SDavid S. Miller return dst; 25981da177e4SLinus Torvalds } 25991da177e4SLinus Torvalds 2600569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 26011da177e4SLinus Torvalds { 260286393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 26037019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 26047019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 26057019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 26067019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 26077019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 2608fc66f95cSEric Dumazet int entries; 26091da177e4SLinus Torvalds 2610fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 261149a18d86SMichal Kubeček if (time_after(rt_last_gc + rt_min_interval, jiffies) && 2612fc66f95cSEric Dumazet entries <= rt_max_size) 26131da177e4SLinus Torvalds goto out; 26141da177e4SLinus Torvalds 26156891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 261614956643SLi RongQing fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true); 2617fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 2618fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 26197019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 26201da177e4SLinus Torvalds out: 26217019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 2622fc66f95cSEric Dumazet return entries > rt_max_size; 26231da177e4SLinus Torvalds } 26241da177e4SLinus Torvalds 2625e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc, 2626e715b6d3SFlorian Westphal const struct fib6_config *cfg) 2627e715b6d3SFlorian Westphal { 26286670e152SStephen Hemminger struct net *net = cfg->fc_nlinfo.nl_net; 2629c3a8d947SDaniel Borkmann bool ecn_ca = false; 2630e715b6d3SFlorian Westphal struct nlattr *nla; 2631e715b6d3SFlorian Westphal int remaining; 2632e715b6d3SFlorian Westphal u32 *mp; 2633e715b6d3SFlorian Westphal 263463159f29SIan Morris if (!cfg->fc_mx) 2635e715b6d3SFlorian Westphal return 0; 2636e715b6d3SFlorian Westphal 2637e715b6d3SFlorian Westphal mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 2638e715b6d3SFlorian Westphal if (unlikely(!mp)) 2639e715b6d3SFlorian Westphal return -ENOMEM; 2640e715b6d3SFlorian Westphal 2641e715b6d3SFlorian Westphal nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 2642e715b6d3SFlorian Westphal int type = nla_type(nla); 2643ea697639SDaniel Borkmann u32 val; 2644ea697639SDaniel Borkmann 26451bb14807SDaniel Borkmann if (!type) 26461bb14807SDaniel Borkmann continue; 2647e715b6d3SFlorian Westphal if (unlikely(type > RTAX_MAX)) 2648e715b6d3SFlorian Westphal goto err; 26491bb14807SDaniel Borkmann 2650ea697639SDaniel Borkmann if (type == RTAX_CC_ALGO) { 2651ea697639SDaniel Borkmann char tmp[TCP_CA_NAME_MAX]; 2652e715b6d3SFlorian Westphal 2653ea697639SDaniel Borkmann nla_strlcpy(tmp, nla, sizeof(tmp)); 26546670e152SStephen Hemminger val = tcp_ca_get_key_by_name(net, tmp, &ecn_ca); 2655ea697639SDaniel Borkmann if (val == TCP_CA_UNSPEC) 2656ea697639SDaniel Borkmann goto err; 2657ea697639SDaniel Borkmann } else { 2658ea697639SDaniel Borkmann val = nla_get_u32(nla); 2659ea697639SDaniel Borkmann } 2660626abd59SPaolo Abeni if (type == RTAX_HOPLIMIT && val > 255) 2661626abd59SPaolo Abeni val = 255; 2662b8d3e416SDaniel Borkmann if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK)) 2663b8d3e416SDaniel Borkmann goto err; 2664ea697639SDaniel Borkmann 2665ea697639SDaniel Borkmann mp[type - 1] = val; 2666e715b6d3SFlorian Westphal __set_bit(type - 1, mxc->mx_valid); 2667e715b6d3SFlorian Westphal } 2668e715b6d3SFlorian Westphal 2669c3a8d947SDaniel Borkmann if (ecn_ca) { 2670c3a8d947SDaniel Borkmann __set_bit(RTAX_FEATURES - 1, mxc->mx_valid); 2671c3a8d947SDaniel Borkmann mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA; 2672c3a8d947SDaniel Borkmann } 2673e715b6d3SFlorian Westphal 2674c3a8d947SDaniel Borkmann mxc->mx = mp; 2675e715b6d3SFlorian Westphal return 0; 2676e715b6d3SFlorian Westphal err: 2677e715b6d3SFlorian Westphal kfree(mp); 2678e715b6d3SFlorian Westphal return -EINVAL; 2679e715b6d3SFlorian Westphal } 26801da177e4SLinus Torvalds 26818c14586fSDavid Ahern static struct rt6_info *ip6_nh_lookup_table(struct net *net, 26828c14586fSDavid Ahern struct fib6_config *cfg, 2683f4797b33SDavid Ahern const struct in6_addr *gw_addr, 2684f4797b33SDavid Ahern u32 tbid, int flags) 26858c14586fSDavid Ahern { 26868c14586fSDavid Ahern struct flowi6 fl6 = { 26878c14586fSDavid Ahern .flowi6_oif = cfg->fc_ifindex, 26888c14586fSDavid Ahern .daddr = *gw_addr, 26898c14586fSDavid Ahern .saddr = cfg->fc_prefsrc, 26908c14586fSDavid Ahern }; 26918c14586fSDavid Ahern struct fib6_table *table; 26928c14586fSDavid Ahern struct rt6_info *rt; 26938c14586fSDavid Ahern 2694f4797b33SDavid Ahern table = fib6_get_table(net, tbid); 26958c14586fSDavid Ahern if (!table) 26968c14586fSDavid Ahern return NULL; 26978c14586fSDavid Ahern 26988c14586fSDavid Ahern if (!ipv6_addr_any(&cfg->fc_prefsrc)) 26998c14586fSDavid Ahern flags |= RT6_LOOKUP_F_HAS_SADDR; 27008c14586fSDavid Ahern 2701f4797b33SDavid Ahern flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE; 2702b75cc8f9SDavid Ahern rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, NULL, flags); 27038c14586fSDavid Ahern 27048c14586fSDavid Ahern /* if table lookup failed, fall back to full lookup */ 27058c14586fSDavid Ahern if (rt == net->ipv6.ip6_null_entry) { 27068c14586fSDavid Ahern ip6_rt_put(rt); 27078c14586fSDavid Ahern rt = NULL; 27088c14586fSDavid Ahern } 27098c14586fSDavid Ahern 27108c14586fSDavid Ahern return rt; 27118c14586fSDavid Ahern } 27128c14586fSDavid Ahern 2713fc1e64e1SDavid Ahern static int ip6_route_check_nh_onlink(struct net *net, 2714fc1e64e1SDavid Ahern struct fib6_config *cfg, 27159fbb704cSDavid Ahern const struct net_device *dev, 2716fc1e64e1SDavid Ahern struct netlink_ext_ack *extack) 2717fc1e64e1SDavid Ahern { 271844750f84SDavid Ahern u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN; 2719fc1e64e1SDavid Ahern const struct in6_addr *gw_addr = &cfg->fc_gateway; 2720fc1e64e1SDavid Ahern u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT; 2721fc1e64e1SDavid Ahern struct rt6_info *grt; 2722fc1e64e1SDavid Ahern int err; 2723fc1e64e1SDavid Ahern 2724fc1e64e1SDavid Ahern err = 0; 2725fc1e64e1SDavid Ahern grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0); 2726fc1e64e1SDavid Ahern if (grt) { 272758e354c0SDavid Ahern if (!grt->dst.error && 272858e354c0SDavid Ahern (grt->rt6i_flags & flags || dev != grt->dst.dev)) { 272944750f84SDavid Ahern NL_SET_ERR_MSG(extack, 273044750f84SDavid Ahern "Nexthop has invalid gateway or device mismatch"); 2731fc1e64e1SDavid Ahern err = -EINVAL; 2732fc1e64e1SDavid Ahern } 2733fc1e64e1SDavid Ahern 2734fc1e64e1SDavid Ahern ip6_rt_put(grt); 2735fc1e64e1SDavid Ahern } 2736fc1e64e1SDavid Ahern 2737fc1e64e1SDavid Ahern return err; 2738fc1e64e1SDavid Ahern } 2739fc1e64e1SDavid Ahern 27401edce99fSDavid Ahern static int ip6_route_check_nh(struct net *net, 27411edce99fSDavid Ahern struct fib6_config *cfg, 27421edce99fSDavid Ahern struct net_device **_dev, 27431edce99fSDavid Ahern struct inet6_dev **idev) 27441edce99fSDavid Ahern { 27451edce99fSDavid Ahern const struct in6_addr *gw_addr = &cfg->fc_gateway; 27461edce99fSDavid Ahern struct net_device *dev = _dev ? *_dev : NULL; 27471edce99fSDavid Ahern struct rt6_info *grt = NULL; 27481edce99fSDavid Ahern int err = -EHOSTUNREACH; 27491edce99fSDavid Ahern 27501edce99fSDavid Ahern if (cfg->fc_table) { 2751f4797b33SDavid Ahern int flags = RT6_LOOKUP_F_IFACE; 2752f4797b33SDavid Ahern 2753f4797b33SDavid Ahern grt = ip6_nh_lookup_table(net, cfg, gw_addr, 2754f4797b33SDavid Ahern cfg->fc_table, flags); 27551edce99fSDavid Ahern if (grt) { 27561edce99fSDavid Ahern if (grt->rt6i_flags & RTF_GATEWAY || 27571edce99fSDavid Ahern (dev && dev != grt->dst.dev)) { 27581edce99fSDavid Ahern ip6_rt_put(grt); 27591edce99fSDavid Ahern grt = NULL; 27601edce99fSDavid Ahern } 27611edce99fSDavid Ahern } 27621edce99fSDavid Ahern } 27631edce99fSDavid Ahern 27641edce99fSDavid Ahern if (!grt) 2765b75cc8f9SDavid Ahern grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, NULL, 1); 27661edce99fSDavid Ahern 27671edce99fSDavid Ahern if (!grt) 27681edce99fSDavid Ahern goto out; 27691edce99fSDavid Ahern 27701edce99fSDavid Ahern if (dev) { 27711edce99fSDavid Ahern if (dev != grt->dst.dev) { 27721edce99fSDavid Ahern ip6_rt_put(grt); 27731edce99fSDavid Ahern goto out; 27741edce99fSDavid Ahern } 27751edce99fSDavid Ahern } else { 27761edce99fSDavid Ahern *_dev = dev = grt->dst.dev; 27771edce99fSDavid Ahern *idev = grt->rt6i_idev; 27781edce99fSDavid Ahern dev_hold(dev); 27791edce99fSDavid Ahern in6_dev_hold(grt->rt6i_idev); 27801edce99fSDavid Ahern } 27811edce99fSDavid Ahern 27821edce99fSDavid Ahern if (!(grt->rt6i_flags & RTF_GATEWAY)) 27831edce99fSDavid Ahern err = 0; 27841edce99fSDavid Ahern 27851edce99fSDavid Ahern ip6_rt_put(grt); 27861edce99fSDavid Ahern 27871edce99fSDavid Ahern out: 27881edce99fSDavid Ahern return err; 27891edce99fSDavid Ahern } 27901edce99fSDavid Ahern 27919fbb704cSDavid Ahern static int ip6_validate_gw(struct net *net, struct fib6_config *cfg, 27929fbb704cSDavid Ahern struct net_device **_dev, struct inet6_dev **idev, 27939fbb704cSDavid Ahern struct netlink_ext_ack *extack) 27949fbb704cSDavid Ahern { 27959fbb704cSDavid Ahern const struct in6_addr *gw_addr = &cfg->fc_gateway; 27969fbb704cSDavid Ahern int gwa_type = ipv6_addr_type(gw_addr); 2797232378e8SDavid Ahern bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true; 27989fbb704cSDavid Ahern const struct net_device *dev = *_dev; 2799232378e8SDavid Ahern bool need_addr_check = !dev; 28009fbb704cSDavid Ahern int err = -EINVAL; 28019fbb704cSDavid Ahern 28029fbb704cSDavid Ahern /* if gw_addr is local we will fail to detect this in case 28039fbb704cSDavid Ahern * address is still TENTATIVE (DAD in progress). rt6_lookup() 28049fbb704cSDavid Ahern * will return already-added prefix route via interface that 28059fbb704cSDavid Ahern * prefix route was assigned to, which might be non-loopback. 28069fbb704cSDavid Ahern */ 2807232378e8SDavid Ahern if (dev && 2808232378e8SDavid Ahern ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) { 2809232378e8SDavid Ahern NL_SET_ERR_MSG(extack, "Gateway can not be a local address"); 28109fbb704cSDavid Ahern goto out; 28119fbb704cSDavid Ahern } 28129fbb704cSDavid Ahern 28139fbb704cSDavid Ahern if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) { 28149fbb704cSDavid Ahern /* IPv6 strictly inhibits using not link-local 28159fbb704cSDavid Ahern * addresses as nexthop address. 28169fbb704cSDavid Ahern * Otherwise, router will not able to send redirects. 28179fbb704cSDavid Ahern * It is very good, but in some (rare!) circumstances 28189fbb704cSDavid Ahern * (SIT, PtP, NBMA NOARP links) it is handy to allow 28199fbb704cSDavid Ahern * some exceptions. --ANK 28209fbb704cSDavid Ahern * We allow IPv4-mapped nexthops to support RFC4798-type 28219fbb704cSDavid Ahern * addressing 28229fbb704cSDavid Ahern */ 28239fbb704cSDavid Ahern if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) { 28249fbb704cSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid gateway address"); 28259fbb704cSDavid Ahern goto out; 28269fbb704cSDavid Ahern } 28279fbb704cSDavid Ahern 28289fbb704cSDavid Ahern if (cfg->fc_flags & RTNH_F_ONLINK) 28299fbb704cSDavid Ahern err = ip6_route_check_nh_onlink(net, cfg, dev, extack); 28309fbb704cSDavid Ahern else 28319fbb704cSDavid Ahern err = ip6_route_check_nh(net, cfg, _dev, idev); 28329fbb704cSDavid Ahern 28339fbb704cSDavid Ahern if (err) 28349fbb704cSDavid Ahern goto out; 28359fbb704cSDavid Ahern } 28369fbb704cSDavid Ahern 28379fbb704cSDavid Ahern /* reload in case device was changed */ 28389fbb704cSDavid Ahern dev = *_dev; 28399fbb704cSDavid Ahern 28409fbb704cSDavid Ahern err = -EINVAL; 28419fbb704cSDavid Ahern if (!dev) { 28429fbb704cSDavid Ahern NL_SET_ERR_MSG(extack, "Egress device not specified"); 28439fbb704cSDavid Ahern goto out; 28449fbb704cSDavid Ahern } else if (dev->flags & IFF_LOOPBACK) { 28459fbb704cSDavid Ahern NL_SET_ERR_MSG(extack, 28469fbb704cSDavid Ahern "Egress device can not be loopback device for this route"); 28479fbb704cSDavid Ahern goto out; 28489fbb704cSDavid Ahern } 2849232378e8SDavid Ahern 2850232378e8SDavid Ahern /* if we did not check gw_addr above, do so now that the 2851232378e8SDavid Ahern * egress device has been resolved. 2852232378e8SDavid Ahern */ 2853232378e8SDavid Ahern if (need_addr_check && 2854232378e8SDavid Ahern ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) { 2855232378e8SDavid Ahern NL_SET_ERR_MSG(extack, "Gateway can not be a local address"); 2856232378e8SDavid Ahern goto out; 2857232378e8SDavid Ahern } 2858232378e8SDavid Ahern 28599fbb704cSDavid Ahern err = 0; 28609fbb704cSDavid Ahern out: 28619fbb704cSDavid Ahern return err; 28629fbb704cSDavid Ahern } 28639fbb704cSDavid Ahern 2864333c4301SDavid Ahern static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, 2865333c4301SDavid Ahern struct netlink_ext_ack *extack) 28661da177e4SLinus Torvalds { 28675578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 28681da177e4SLinus Torvalds struct rt6_info *rt = NULL; 28691da177e4SLinus Torvalds struct net_device *dev = NULL; 28701da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 2871c71099acSThomas Graf struct fib6_table *table; 28721da177e4SLinus Torvalds int addr_type; 28738c5b83f0SRoopa Prabhu int err = -EINVAL; 28741da177e4SLinus Torvalds 2875557c44beSDavid Ahern /* RTF_PCPU is an internal flag; can not be set by userspace */ 2876d5d531cbSDavid Ahern if (cfg->fc_flags & RTF_PCPU) { 2877d5d531cbSDavid Ahern NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU"); 2878557c44beSDavid Ahern goto out; 2879d5d531cbSDavid Ahern } 2880557c44beSDavid Ahern 28812ea2352eSWei Wang /* RTF_CACHE is an internal flag; can not be set by userspace */ 28822ea2352eSWei Wang if (cfg->fc_flags & RTF_CACHE) { 28832ea2352eSWei Wang NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE"); 28842ea2352eSWei Wang goto out; 28852ea2352eSWei Wang } 28862ea2352eSWei Wang 2887e8478e80SDavid Ahern if (cfg->fc_type > RTN_MAX) { 2888e8478e80SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid route type"); 2889e8478e80SDavid Ahern goto out; 2890e8478e80SDavid Ahern } 2891e8478e80SDavid Ahern 2892d5d531cbSDavid Ahern if (cfg->fc_dst_len > 128) { 2893d5d531cbSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid prefix length"); 28948c5b83f0SRoopa Prabhu goto out; 2895d5d531cbSDavid Ahern } 2896d5d531cbSDavid Ahern if (cfg->fc_src_len > 128) { 2897d5d531cbSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid source address length"); 2898d5d531cbSDavid Ahern goto out; 2899d5d531cbSDavid Ahern } 29001da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 2901d5d531cbSDavid Ahern if (cfg->fc_src_len) { 2902d5d531cbSDavid Ahern NL_SET_ERR_MSG(extack, 2903d5d531cbSDavid Ahern "Specifying source address requires IPV6_SUBTREES to be enabled"); 29048c5b83f0SRoopa Prabhu goto out; 2905d5d531cbSDavid Ahern } 29061da177e4SLinus Torvalds #endif 290786872cb5SThomas Graf if (cfg->fc_ifindex) { 29081da177e4SLinus Torvalds err = -ENODEV; 29095578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 29101da177e4SLinus Torvalds if (!dev) 29111da177e4SLinus Torvalds goto out; 29121da177e4SLinus Torvalds idev = in6_dev_get(dev); 29131da177e4SLinus Torvalds if (!idev) 29141da177e4SLinus Torvalds goto out; 29151da177e4SLinus Torvalds } 29161da177e4SLinus Torvalds 291786872cb5SThomas Graf if (cfg->fc_metric == 0) 291886872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 29191da177e4SLinus Torvalds 2920fc1e64e1SDavid Ahern if (cfg->fc_flags & RTNH_F_ONLINK) { 2921fc1e64e1SDavid Ahern if (!dev) { 2922fc1e64e1SDavid Ahern NL_SET_ERR_MSG(extack, 2923fc1e64e1SDavid Ahern "Nexthop device required for onlink"); 2924fc1e64e1SDavid Ahern err = -ENODEV; 2925fc1e64e1SDavid Ahern goto out; 2926fc1e64e1SDavid Ahern } 2927fc1e64e1SDavid Ahern 2928fc1e64e1SDavid Ahern if (!(dev->flags & IFF_UP)) { 2929fc1e64e1SDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop device is not up"); 2930fc1e64e1SDavid Ahern err = -ENETDOWN; 2931fc1e64e1SDavid Ahern goto out; 2932fc1e64e1SDavid Ahern } 2933fc1e64e1SDavid Ahern } 2934fc1e64e1SDavid Ahern 2935c71099acSThomas Graf err = -ENOBUFS; 293638308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 2937d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 2938d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 293938308473SDavid S. Miller if (!table) { 2940f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 2941d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 2942d71314b4SMatti Vaittinen } 2943d71314b4SMatti Vaittinen } else { 2944d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 2945d71314b4SMatti Vaittinen } 294638308473SDavid S. Miller 294738308473SDavid S. Miller if (!table) 2948c71099acSThomas Graf goto out; 2949c71099acSThomas Graf 2950ad706862SMartin KaFai Lau rt = ip6_dst_alloc(net, NULL, 2951ad706862SMartin KaFai Lau (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT); 29521da177e4SLinus Torvalds 295338308473SDavid S. Miller if (!rt) { 29541da177e4SLinus Torvalds err = -ENOMEM; 29551da177e4SLinus Torvalds goto out; 29561da177e4SLinus Torvalds } 29571da177e4SLinus Torvalds 29581716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 29591716a961SGao feng rt6_set_expires(rt, jiffies + 29601716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 29611716a961SGao feng else 29621716a961SGao feng rt6_clean_expires(rt); 29631da177e4SLinus Torvalds 296486872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 296586872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 296686872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 296786872cb5SThomas Graf 296886872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 29691da177e4SLinus Torvalds 297019e42e45SRoopa Prabhu if (cfg->fc_encap) { 297119e42e45SRoopa Prabhu struct lwtunnel_state *lwtstate; 297219e42e45SRoopa Prabhu 297330357d7dSDavid Ahern err = lwtunnel_build_state(cfg->fc_encap_type, 2974127eb7cdSTom Herbert cfg->fc_encap, AF_INET6, cfg, 29759ae28727SDavid Ahern &lwtstate, extack); 297619e42e45SRoopa Prabhu if (err) 297719e42e45SRoopa Prabhu goto out; 29785e670d84SDavid Ahern rt->fib6_nh.nh_lwtstate = lwtstate_get(lwtstate); 297925368623STom Herbert } 298019e42e45SRoopa Prabhu 298186872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 298286872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 2983afc4eef8SMartin KaFai Lau if (rt->rt6i_dst.plen == 128) 298411d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 29851da177e4SLinus Torvalds 29861da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 298786872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 298886872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 29891da177e4SLinus Torvalds #endif 29901da177e4SLinus Torvalds 299186872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 29925e670d84SDavid Ahern rt->fib6_nh.nh_weight = 1; 29931da177e4SLinus Torvalds 2994e8478e80SDavid Ahern rt->fib6_type = cfg->fc_type; 2995e8478e80SDavid Ahern 29961da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 29971da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 29981da177e4SLinus Torvalds */ 299986872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 300038308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 300138308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 300238308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 30031da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 30045578689aSDaniel Lezcano if (dev != net->loopback_dev) { 30051da177e4SLinus Torvalds if (dev) { 30061da177e4SLinus Torvalds dev_put(dev); 30071da177e4SLinus Torvalds in6_dev_put(idev); 30081da177e4SLinus Torvalds } 30095578689aSDaniel Lezcano dev = net->loopback_dev; 30101da177e4SLinus Torvalds dev_hold(dev); 30111da177e4SLinus Torvalds idev = in6_dev_get(dev); 30121da177e4SLinus Torvalds if (!idev) { 30131da177e4SLinus Torvalds err = -ENODEV; 30141da177e4SLinus Torvalds goto out; 30151da177e4SLinus Torvalds } 30161da177e4SLinus Torvalds } 30171da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 30181da177e4SLinus Torvalds goto install_route; 30191da177e4SLinus Torvalds } 30201da177e4SLinus Torvalds 302186872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 30229fbb704cSDavid Ahern err = ip6_validate_gw(net, cfg, &dev, &idev, extack); 30231da177e4SLinus Torvalds if (err) 30241da177e4SLinus Torvalds goto out; 30259fbb704cSDavid Ahern 30265e670d84SDavid Ahern rt->fib6_nh.nh_gw = rt->rt6i_gateway = cfg->fc_gateway; 30271da177e4SLinus Torvalds } 30281da177e4SLinus Torvalds 30291da177e4SLinus Torvalds err = -ENODEV; 303038308473SDavid S. Miller if (!dev) 30311da177e4SLinus Torvalds goto out; 30321da177e4SLinus Torvalds 3033428604fbSLorenzo Bianconi if (idev->cnf.disable_ipv6) { 3034428604fbSLorenzo Bianconi NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device"); 3035428604fbSLorenzo Bianconi err = -EACCES; 3036428604fbSLorenzo Bianconi goto out; 3037428604fbSLorenzo Bianconi } 3038428604fbSLorenzo Bianconi 3039955ec4cbSDavid Ahern if (!(dev->flags & IFF_UP)) { 3040955ec4cbSDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop device is not up"); 3041955ec4cbSDavid Ahern err = -ENETDOWN; 3042955ec4cbSDavid Ahern goto out; 3043955ec4cbSDavid Ahern } 3044955ec4cbSDavid Ahern 3045c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 3046c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 3047d5d531cbSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid source address"); 3048c3968a85SDaniel Walter err = -EINVAL; 3049c3968a85SDaniel Walter goto out; 3050c3968a85SDaniel Walter } 30514e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 3052c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 3053c3968a85SDaniel Walter } else 3054c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 3055c3968a85SDaniel Walter 305686872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 30571da177e4SLinus Torvalds 30581da177e4SLinus Torvalds install_route: 30595609b80aSIdo Schimmel if (!(rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) && 30605609b80aSIdo Schimmel !netif_carrier_ok(dev)) 30615e670d84SDavid Ahern rt->fib6_nh.nh_flags |= RTNH_F_LINKDOWN; 30625e670d84SDavid Ahern rt->fib6_nh.nh_flags |= (cfg->fc_flags & RTNH_F_ONLINK); 30635e670d84SDavid Ahern rt->fib6_nh.nh_dev = rt->dst.dev = dev; 30641da177e4SLinus Torvalds rt->rt6i_idev = idev; 3065c71099acSThomas Graf rt->rt6i_table = table; 306663152fc0SDaniel Lezcano 3067c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 306863152fc0SDaniel Lezcano 30698c5b83f0SRoopa Prabhu return rt; 30701da177e4SLinus Torvalds out: 30711da177e4SLinus Torvalds if (dev) 30721da177e4SLinus Torvalds dev_put(dev); 30731da177e4SLinus Torvalds if (idev) 30741da177e4SLinus Torvalds in6_dev_put(idev); 3075587fea74SWei Wang if (rt) 3076587fea74SWei Wang dst_release_immediate(&rt->dst); 30776b9ea5a6SRoopa Prabhu 30788c5b83f0SRoopa Prabhu return ERR_PTR(err); 30796b9ea5a6SRoopa Prabhu } 30806b9ea5a6SRoopa Prabhu 3081333c4301SDavid Ahern int ip6_route_add(struct fib6_config *cfg, 3082333c4301SDavid Ahern struct netlink_ext_ack *extack) 30836b9ea5a6SRoopa Prabhu { 30846b9ea5a6SRoopa Prabhu struct mx6_config mxc = { .mx = NULL, }; 30858c5b83f0SRoopa Prabhu struct rt6_info *rt; 30866b9ea5a6SRoopa Prabhu int err; 30876b9ea5a6SRoopa Prabhu 3088333c4301SDavid Ahern rt = ip6_route_info_create(cfg, extack); 30898c5b83f0SRoopa Prabhu if (IS_ERR(rt)) { 30908c5b83f0SRoopa Prabhu err = PTR_ERR(rt); 30918c5b83f0SRoopa Prabhu rt = NULL; 30926b9ea5a6SRoopa Prabhu goto out; 30938c5b83f0SRoopa Prabhu } 30946b9ea5a6SRoopa Prabhu 30956b9ea5a6SRoopa Prabhu err = ip6_convert_metrics(&mxc, cfg); 30966b9ea5a6SRoopa Prabhu if (err) 30976b9ea5a6SRoopa Prabhu goto out; 30986b9ea5a6SRoopa Prabhu 3099333c4301SDavid Ahern err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc, extack); 31006b9ea5a6SRoopa Prabhu 31016b9ea5a6SRoopa Prabhu kfree(mxc.mx); 31026b9ea5a6SRoopa Prabhu 31036b9ea5a6SRoopa Prabhu return err; 31046b9ea5a6SRoopa Prabhu out: 3105587fea74SWei Wang if (rt) 3106587fea74SWei Wang dst_release_immediate(&rt->dst); 31076b9ea5a6SRoopa Prabhu 31081da177e4SLinus Torvalds return err; 31091da177e4SLinus Torvalds } 31101da177e4SLinus Torvalds 311186872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 31121da177e4SLinus Torvalds { 3113afb1d4b5SDavid Ahern struct net *net = info->nl_net; 3114c71099acSThomas Graf struct fib6_table *table; 3115afb1d4b5SDavid Ahern int err; 31161da177e4SLinus Torvalds 3117a4c2fd7fSWei Wang if (rt == net->ipv6.ip6_null_entry) { 31186825a26cSGao feng err = -ENOENT; 31196825a26cSGao feng goto out; 31206825a26cSGao feng } 31216c813a72SPatrick McHardy 3122c71099acSThomas Graf table = rt->rt6i_table; 312366f5d6ceSWei Wang spin_lock_bh(&table->tb6_lock); 312486872cb5SThomas Graf err = fib6_del(rt, info); 312566f5d6ceSWei Wang spin_unlock_bh(&table->tb6_lock); 31261da177e4SLinus Torvalds 31276825a26cSGao feng out: 312894e187c0SAmerigo Wang ip6_rt_put(rt); 31291da177e4SLinus Torvalds return err; 31301da177e4SLinus Torvalds } 31311da177e4SLinus Torvalds 3132afb1d4b5SDavid Ahern int ip6_del_rt(struct net *net, struct rt6_info *rt) 3133e0a1ad73SThomas Graf { 3134afb1d4b5SDavid Ahern struct nl_info info = { .nl_net = net }; 3135afb1d4b5SDavid Ahern 3136528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 3137e0a1ad73SThomas Graf } 3138e0a1ad73SThomas Graf 31390ae81335SDavid Ahern static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg) 31400ae81335SDavid Ahern { 31410ae81335SDavid Ahern struct nl_info *info = &cfg->fc_nlinfo; 3142e3330039SWANG Cong struct net *net = info->nl_net; 314316a16cd3SDavid Ahern struct sk_buff *skb = NULL; 31440ae81335SDavid Ahern struct fib6_table *table; 3145e3330039SWANG Cong int err = -ENOENT; 31460ae81335SDavid Ahern 3147e3330039SWANG Cong if (rt == net->ipv6.ip6_null_entry) 3148e3330039SWANG Cong goto out_put; 31490ae81335SDavid Ahern table = rt->rt6i_table; 315066f5d6ceSWei Wang spin_lock_bh(&table->tb6_lock); 31510ae81335SDavid Ahern 31520ae81335SDavid Ahern if (rt->rt6i_nsiblings && cfg->fc_delete_all_nh) { 31530ae81335SDavid Ahern struct rt6_info *sibling, *next_sibling; 31540ae81335SDavid Ahern 315516a16cd3SDavid Ahern /* prefer to send a single notification with all hops */ 315616a16cd3SDavid Ahern skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 315716a16cd3SDavid Ahern if (skb) { 315816a16cd3SDavid Ahern u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; 315916a16cd3SDavid Ahern 3160e3330039SWANG Cong if (rt6_fill_node(net, skb, rt, 316116a16cd3SDavid Ahern NULL, NULL, 0, RTM_DELROUTE, 316216a16cd3SDavid Ahern info->portid, seq, 0) < 0) { 316316a16cd3SDavid Ahern kfree_skb(skb); 316416a16cd3SDavid Ahern skb = NULL; 316516a16cd3SDavid Ahern } else 316616a16cd3SDavid Ahern info->skip_notify = 1; 316716a16cd3SDavid Ahern } 316816a16cd3SDavid Ahern 31690ae81335SDavid Ahern list_for_each_entry_safe(sibling, next_sibling, 31700ae81335SDavid Ahern &rt->rt6i_siblings, 31710ae81335SDavid Ahern rt6i_siblings) { 31720ae81335SDavid Ahern err = fib6_del(sibling, info); 31730ae81335SDavid Ahern if (err) 3174e3330039SWANG Cong goto out_unlock; 31750ae81335SDavid Ahern } 31760ae81335SDavid Ahern } 31770ae81335SDavid Ahern 31780ae81335SDavid Ahern err = fib6_del(rt, info); 3179e3330039SWANG Cong out_unlock: 318066f5d6ceSWei Wang spin_unlock_bh(&table->tb6_lock); 3181e3330039SWANG Cong out_put: 31820ae81335SDavid Ahern ip6_rt_put(rt); 318316a16cd3SDavid Ahern 318416a16cd3SDavid Ahern if (skb) { 3185e3330039SWANG Cong rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 318616a16cd3SDavid Ahern info->nlh, gfp_any()); 318716a16cd3SDavid Ahern } 31880ae81335SDavid Ahern return err; 31890ae81335SDavid Ahern } 31900ae81335SDavid Ahern 3191333c4301SDavid Ahern static int ip6_route_del(struct fib6_config *cfg, 3192333c4301SDavid Ahern struct netlink_ext_ack *extack) 31931da177e4SLinus Torvalds { 31942b760fcfSWei Wang struct rt6_info *rt, *rt_cache; 3195c71099acSThomas Graf struct fib6_table *table; 31961da177e4SLinus Torvalds struct fib6_node *fn; 31971da177e4SLinus Torvalds int err = -ESRCH; 31981da177e4SLinus Torvalds 31995578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 3200d5d531cbSDavid Ahern if (!table) { 3201d5d531cbSDavid Ahern NL_SET_ERR_MSG(extack, "FIB table does not exist"); 3202c71099acSThomas Graf return err; 3203d5d531cbSDavid Ahern } 32041da177e4SLinus Torvalds 320566f5d6ceSWei Wang rcu_read_lock(); 3206c71099acSThomas Graf 3207c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 320886872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 320938fbeeeeSWei Wang &cfg->fc_src, cfg->fc_src_len, 32102b760fcfSWei Wang !(cfg->fc_flags & RTF_CACHE)); 32111da177e4SLinus Torvalds 32121da177e4SLinus Torvalds if (fn) { 321366f5d6ceSWei Wang for_each_fib6_node_rt_rcu(fn) { 32142b760fcfSWei Wang if (cfg->fc_flags & RTF_CACHE) { 32152b760fcfSWei Wang rt_cache = rt6_find_cached_rt(rt, &cfg->fc_dst, 32162b760fcfSWei Wang &cfg->fc_src); 32172b760fcfSWei Wang if (!rt_cache) 32181f56a01fSMartin KaFai Lau continue; 32192b760fcfSWei Wang rt = rt_cache; 32202b760fcfSWei Wang } 322186872cb5SThomas Graf if (cfg->fc_ifindex && 32225e670d84SDavid Ahern (!rt->fib6_nh.nh_dev || 32235e670d84SDavid Ahern rt->fib6_nh.nh_dev->ifindex != cfg->fc_ifindex)) 32241da177e4SLinus Torvalds continue; 322586872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 32265e670d84SDavid Ahern !ipv6_addr_equal(&cfg->fc_gateway, &rt->fib6_nh.nh_gw)) 32271da177e4SLinus Torvalds continue; 322886872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 32291da177e4SLinus Torvalds continue; 3230c2ed1880SMantas M if (cfg->fc_protocol && cfg->fc_protocol != rt->rt6i_protocol) 3231c2ed1880SMantas M continue; 3232d3843fe5SWei Wang if (!dst_hold_safe(&rt->dst)) 3233d3843fe5SWei Wang break; 323466f5d6ceSWei Wang rcu_read_unlock(); 32351da177e4SLinus Torvalds 32360ae81335SDavid Ahern /* if gateway was specified only delete the one hop */ 32370ae81335SDavid Ahern if (cfg->fc_flags & RTF_GATEWAY) 323886872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 32390ae81335SDavid Ahern 32400ae81335SDavid Ahern return __ip6_del_rt_siblings(rt, cfg); 32411da177e4SLinus Torvalds } 32421da177e4SLinus Torvalds } 324366f5d6ceSWei Wang rcu_read_unlock(); 32441da177e4SLinus Torvalds 32451da177e4SLinus Torvalds return err; 32461da177e4SLinus Torvalds } 32471da177e4SLinus Torvalds 32486700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 3249a6279458SYOSHIFUJI Hideaki { 3250a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 3251e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 3252e8599ff4SDavid S. Miller struct ndisc_options ndopts; 3253e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 3254e8599ff4SDavid S. Miller struct neighbour *neigh; 325571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 struct rd_msg *msg; 32566e157b6aSDavid S. Miller int optlen, on_link; 32576e157b6aSDavid S. Miller u8 *lladdr; 3258e8599ff4SDavid S. Miller 325929a3cad5SSimon Horman optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 326071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 optlen -= sizeof(*msg); 3261e8599ff4SDavid S. Miller 3262e8599ff4SDavid S. Miller if (optlen < 0) { 32636e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 3264e8599ff4SDavid S. Miller return; 3265e8599ff4SDavid S. Miller } 3266e8599ff4SDavid S. Miller 326771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 msg = (struct rd_msg *)icmp6_hdr(skb); 3268e8599ff4SDavid S. Miller 326971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_is_multicast(&msg->dest)) { 32706e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 3271e8599ff4SDavid S. Miller return; 3272e8599ff4SDavid S. Miller } 3273e8599ff4SDavid S. Miller 32746e157b6aSDavid S. Miller on_link = 0; 327571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_equal(&msg->dest, &msg->target)) { 3276e8599ff4SDavid S. Miller on_link = 1; 327771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 } else if (ipv6_addr_type(&msg->target) != 3278e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 32796e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 3280e8599ff4SDavid S. Miller return; 3281e8599ff4SDavid S. Miller } 3282e8599ff4SDavid S. Miller 3283e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 3284e8599ff4SDavid S. Miller if (!in6_dev) 3285e8599ff4SDavid S. Miller return; 3286e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 3287e8599ff4SDavid S. Miller return; 3288e8599ff4SDavid S. Miller 3289e8599ff4SDavid S. Miller /* RFC2461 8.1: 3290e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 3291e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 3292e8599ff4SDavid S. Miller */ 3293e8599ff4SDavid S. Miller 3294f997c55cSAlexander Aring if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) { 3295e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 3296e8599ff4SDavid S. Miller return; 3297e8599ff4SDavid S. Miller } 32986e157b6aSDavid S. Miller 32996e157b6aSDavid S. Miller lladdr = NULL; 3300e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 3301e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 3302e8599ff4SDavid S. Miller skb->dev); 3303e8599ff4SDavid S. Miller if (!lladdr) { 3304e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 3305e8599ff4SDavid S. Miller return; 3306e8599ff4SDavid S. Miller } 3307e8599ff4SDavid S. Miller } 3308e8599ff4SDavid S. Miller 33096e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 3310ec13ad1dSMatthias Schiffer if (rt->rt6i_flags & RTF_REJECT) { 33116e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 33126e157b6aSDavid S. Miller return; 33136e157b6aSDavid S. Miller } 33146e157b6aSDavid S. Miller 33156e157b6aSDavid S. Miller /* Redirect received -> path was valid. 33166e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 33176e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 33186e157b6aSDavid S. Miller */ 33190dec879fSJulian Anastasov dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr); 33206e157b6aSDavid S. Miller 332171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 3322e8599ff4SDavid S. Miller if (!neigh) 3323e8599ff4SDavid S. Miller return; 3324e8599ff4SDavid S. Miller 33251da177e4SLinus Torvalds /* 33261da177e4SLinus Torvalds * We have finally decided to accept it. 33271da177e4SLinus Torvalds */ 33281da177e4SLinus Torvalds 3329f997c55cSAlexander Aring ndisc_update(skb->dev, neigh, lladdr, NUD_STALE, 33301da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 33311da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 33321da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 3333f997c55cSAlexander Aring NEIGH_UPDATE_F_ISROUTER)), 3334f997c55cSAlexander Aring NDISC_REDIRECT, &ndopts); 33351da177e4SLinus Torvalds 333683a09abdSMartin KaFai Lau nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL); 333738308473SDavid S. Miller if (!nrt) 33381da177e4SLinus Torvalds goto out; 33391da177e4SLinus Torvalds 33401da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 33411da177e4SLinus Torvalds if (on_link) 33421da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 33431da177e4SLinus Torvalds 3344b91d5329SXin Long nrt->rt6i_protocol = RTPROT_REDIRECT; 33454e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 33461da177e4SLinus Torvalds 33472b760fcfSWei Wang /* No need to remove rt from the exception table if rt is 33482b760fcfSWei Wang * a cached route because rt6_insert_exception() will 33492b760fcfSWei Wang * takes care of it 33502b760fcfSWei Wang */ 33512b760fcfSWei Wang if (rt6_insert_exception(nrt, rt)) { 33522b760fcfSWei Wang dst_release_immediate(&nrt->dst); 33532b760fcfSWei Wang goto out; 33542b760fcfSWei Wang } 33551da177e4SLinus Torvalds 3356d8d1f30bSChangli Gao netevent.old = &rt->dst; 3357d8d1f30bSChangli Gao netevent.new = &nrt->dst; 335871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 netevent.daddr = &msg->dest; 335960592833SYOSHIFUJI Hideaki / 吉藤英明 netevent.neigh = neigh; 33608d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 33618d71740cSTom Tucker 33621da177e4SLinus Torvalds out: 3363e8599ff4SDavid S. Miller neigh_release(neigh); 33646e157b6aSDavid S. Miller } 33656e157b6aSDavid S. Miller 336670ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 3367efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 3368b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 3369830218c1SDavid Ahern const struct in6_addr *gwaddr, 3370830218c1SDavid Ahern struct net_device *dev) 337170ceb4f5SYOSHIFUJI Hideaki { 3372830218c1SDavid Ahern u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO; 3373830218c1SDavid Ahern int ifindex = dev->ifindex; 337470ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 337570ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 3376c71099acSThomas Graf struct fib6_table *table; 337770ceb4f5SYOSHIFUJI Hideaki 3378830218c1SDavid Ahern table = fib6_get_table(net, tb_id); 337938308473SDavid S. Miller if (!table) 3380c71099acSThomas Graf return NULL; 3381c71099acSThomas Graf 338266f5d6ceSWei Wang rcu_read_lock(); 338338fbeeeeSWei Wang fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true); 338470ceb4f5SYOSHIFUJI Hideaki if (!fn) 338570ceb4f5SYOSHIFUJI Hideaki goto out; 338670ceb4f5SYOSHIFUJI Hideaki 338766f5d6ceSWei Wang for_each_fib6_node_rt_rcu(fn) { 33885e670d84SDavid Ahern if (rt->fib6_nh.nh_dev->ifindex != ifindex) 338970ceb4f5SYOSHIFUJI Hideaki continue; 339070ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 339170ceb4f5SYOSHIFUJI Hideaki continue; 33925e670d84SDavid Ahern if (!ipv6_addr_equal(&rt->fib6_nh.nh_gw, gwaddr)) 339370ceb4f5SYOSHIFUJI Hideaki continue; 3394d3843fe5SWei Wang ip6_hold_safe(NULL, &rt, false); 339570ceb4f5SYOSHIFUJI Hideaki break; 339670ceb4f5SYOSHIFUJI Hideaki } 339770ceb4f5SYOSHIFUJI Hideaki out: 339866f5d6ceSWei Wang rcu_read_unlock(); 339970ceb4f5SYOSHIFUJI Hideaki return rt; 340070ceb4f5SYOSHIFUJI Hideaki } 340170ceb4f5SYOSHIFUJI Hideaki 3402efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 3403b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 3404830218c1SDavid Ahern const struct in6_addr *gwaddr, 3405830218c1SDavid Ahern struct net_device *dev, 340695c96174SEric Dumazet unsigned int pref) 340770ceb4f5SYOSHIFUJI Hideaki { 340886872cb5SThomas Graf struct fib6_config cfg = { 3409238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 3410830218c1SDavid Ahern .fc_ifindex = dev->ifindex, 341186872cb5SThomas Graf .fc_dst_len = prefixlen, 341286872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 341386872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 3414b91d5329SXin Long .fc_protocol = RTPROT_RA, 3415e8478e80SDavid Ahern .fc_type = RTN_UNICAST, 341615e47304SEric W. Biederman .fc_nlinfo.portid = 0, 3417efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 3418efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 341986872cb5SThomas Graf }; 342070ceb4f5SYOSHIFUJI Hideaki 3421830218c1SDavid Ahern cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO, 34224e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 34234e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 342486872cb5SThomas Graf 3425e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 3426e317da96SYOSHIFUJI Hideaki if (!prefixlen) 342786872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 342870ceb4f5SYOSHIFUJI Hideaki 3429333c4301SDavid Ahern ip6_route_add(&cfg, NULL); 343070ceb4f5SYOSHIFUJI Hideaki 3431830218c1SDavid Ahern return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev); 343270ceb4f5SYOSHIFUJI Hideaki } 343370ceb4f5SYOSHIFUJI Hideaki #endif 343470ceb4f5SYOSHIFUJI Hideaki 3435afb1d4b5SDavid Ahern struct rt6_info *rt6_get_dflt_router(struct net *net, 3436afb1d4b5SDavid Ahern const struct in6_addr *addr, 3437afb1d4b5SDavid Ahern struct net_device *dev) 34381da177e4SLinus Torvalds { 3439830218c1SDavid Ahern u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT; 34401da177e4SLinus Torvalds struct rt6_info *rt; 3441c71099acSThomas Graf struct fib6_table *table; 34421da177e4SLinus Torvalds 3443afb1d4b5SDavid Ahern table = fib6_get_table(net, tb_id); 344438308473SDavid S. Miller if (!table) 3445c71099acSThomas Graf return NULL; 34461da177e4SLinus Torvalds 344766f5d6ceSWei Wang rcu_read_lock(); 344866f5d6ceSWei Wang for_each_fib6_node_rt_rcu(&table->tb6_root) { 34495e670d84SDavid Ahern if (dev == rt->fib6_nh.nh_dev && 3450045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 34515e670d84SDavid Ahern ipv6_addr_equal(&rt->fib6_nh.nh_gw, addr)) 34521da177e4SLinus Torvalds break; 34531da177e4SLinus Torvalds } 34541da177e4SLinus Torvalds if (rt) 3455d3843fe5SWei Wang ip6_hold_safe(NULL, &rt, false); 345666f5d6ceSWei Wang rcu_read_unlock(); 34571da177e4SLinus Torvalds return rt; 34581da177e4SLinus Torvalds } 34591da177e4SLinus Torvalds 3460afb1d4b5SDavid Ahern struct rt6_info *rt6_add_dflt_router(struct net *net, 3461afb1d4b5SDavid Ahern const struct in6_addr *gwaddr, 3462ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 3463ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 34641da177e4SLinus Torvalds { 346586872cb5SThomas Graf struct fib6_config cfg = { 3466ca254490SDavid Ahern .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT, 3467238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 346886872cb5SThomas Graf .fc_ifindex = dev->ifindex, 346986872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 347086872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 3471b91d5329SXin Long .fc_protocol = RTPROT_RA, 3472e8478e80SDavid Ahern .fc_type = RTN_UNICAST, 347315e47304SEric W. Biederman .fc_nlinfo.portid = 0, 34745578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 3475afb1d4b5SDavid Ahern .fc_nlinfo.nl_net = net, 347686872cb5SThomas Graf }; 34771da177e4SLinus Torvalds 34784e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 34791da177e4SLinus Torvalds 3480333c4301SDavid Ahern if (!ip6_route_add(&cfg, NULL)) { 3481830218c1SDavid Ahern struct fib6_table *table; 3482830218c1SDavid Ahern 3483830218c1SDavid Ahern table = fib6_get_table(dev_net(dev), cfg.fc_table); 3484830218c1SDavid Ahern if (table) 3485830218c1SDavid Ahern table->flags |= RT6_TABLE_HAS_DFLT_ROUTER; 3486830218c1SDavid Ahern } 34871da177e4SLinus Torvalds 3488afb1d4b5SDavid Ahern return rt6_get_dflt_router(net, gwaddr, dev); 34891da177e4SLinus Torvalds } 34901da177e4SLinus Torvalds 3491afb1d4b5SDavid Ahern static void __rt6_purge_dflt_routers(struct net *net, 3492afb1d4b5SDavid Ahern struct fib6_table *table) 34931da177e4SLinus Torvalds { 34941da177e4SLinus Torvalds struct rt6_info *rt; 34951da177e4SLinus Torvalds 34961da177e4SLinus Torvalds restart: 349766f5d6ceSWei Wang rcu_read_lock(); 349866f5d6ceSWei Wang for_each_fib6_node_rt_rcu(&table->tb6_root) { 34993e8b0ac3SLorenzo Colitti if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 35003e8b0ac3SLorenzo Colitti (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { 3501d3843fe5SWei Wang if (dst_hold_safe(&rt->dst)) { 350266f5d6ceSWei Wang rcu_read_unlock(); 3503afb1d4b5SDavid Ahern ip6_del_rt(net, rt); 3504d3843fe5SWei Wang } else { 350566f5d6ceSWei Wang rcu_read_unlock(); 3506d3843fe5SWei Wang } 35071da177e4SLinus Torvalds goto restart; 35081da177e4SLinus Torvalds } 35091da177e4SLinus Torvalds } 351066f5d6ceSWei Wang rcu_read_unlock(); 3511830218c1SDavid Ahern 3512830218c1SDavid Ahern table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER; 3513830218c1SDavid Ahern } 3514830218c1SDavid Ahern 3515830218c1SDavid Ahern void rt6_purge_dflt_routers(struct net *net) 3516830218c1SDavid Ahern { 3517830218c1SDavid Ahern struct fib6_table *table; 3518830218c1SDavid Ahern struct hlist_head *head; 3519830218c1SDavid Ahern unsigned int h; 3520830218c1SDavid Ahern 3521830218c1SDavid Ahern rcu_read_lock(); 3522830218c1SDavid Ahern 3523830218c1SDavid Ahern for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { 3524830218c1SDavid Ahern head = &net->ipv6.fib_table_hash[h]; 3525830218c1SDavid Ahern hlist_for_each_entry_rcu(table, head, tb6_hlist) { 3526830218c1SDavid Ahern if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER) 3527afb1d4b5SDavid Ahern __rt6_purge_dflt_routers(net, table); 3528830218c1SDavid Ahern } 3529830218c1SDavid Ahern } 3530830218c1SDavid Ahern 3531830218c1SDavid Ahern rcu_read_unlock(); 35321da177e4SLinus Torvalds } 35331da177e4SLinus Torvalds 35345578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 35355578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 353686872cb5SThomas Graf struct fib6_config *cfg) 353786872cb5SThomas Graf { 353886872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 353986872cb5SThomas Graf 3540ca254490SDavid Ahern cfg->fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ? 3541ca254490SDavid Ahern : RT6_TABLE_MAIN; 354286872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 354386872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 354486872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 354586872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 354686872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 354786872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 3548e8478e80SDavid Ahern cfg->fc_type = rtmsg->rtmsg_type; 354986872cb5SThomas Graf 35505578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 3551f1243c2dSBenjamin Thery 35524e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 35534e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 35544e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 355586872cb5SThomas Graf } 355686872cb5SThomas Graf 35575578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 35581da177e4SLinus Torvalds { 355986872cb5SThomas Graf struct fib6_config cfg; 35601da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 35611da177e4SLinus Torvalds int err; 35621da177e4SLinus Torvalds 35631da177e4SLinus Torvalds switch (cmd) { 35641da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 35651da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 3566af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 35671da177e4SLinus Torvalds return -EPERM; 35681da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 35691da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 35701da177e4SLinus Torvalds if (err) 35711da177e4SLinus Torvalds return -EFAULT; 35721da177e4SLinus Torvalds 35735578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 357486872cb5SThomas Graf 35751da177e4SLinus Torvalds rtnl_lock(); 35761da177e4SLinus Torvalds switch (cmd) { 35771da177e4SLinus Torvalds case SIOCADDRT: 3578333c4301SDavid Ahern err = ip6_route_add(&cfg, NULL); 35791da177e4SLinus Torvalds break; 35801da177e4SLinus Torvalds case SIOCDELRT: 3581333c4301SDavid Ahern err = ip6_route_del(&cfg, NULL); 35821da177e4SLinus Torvalds break; 35831da177e4SLinus Torvalds default: 35841da177e4SLinus Torvalds err = -EINVAL; 35851da177e4SLinus Torvalds } 35861da177e4SLinus Torvalds rtnl_unlock(); 35871da177e4SLinus Torvalds 35881da177e4SLinus Torvalds return err; 35893ff50b79SStephen Hemminger } 35901da177e4SLinus Torvalds 35911da177e4SLinus Torvalds return -EINVAL; 35921da177e4SLinus Torvalds } 35931da177e4SLinus Torvalds 35941da177e4SLinus Torvalds /* 35951da177e4SLinus Torvalds * Drop the packet on the floor 35961da177e4SLinus Torvalds */ 35971da177e4SLinus Torvalds 3598d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 35991da177e4SLinus Torvalds { 3600612f09e8SYOSHIFUJI Hideaki int type; 3601adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 3602612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 3603612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 36040660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 360545bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 3606bdb7cc64SStephen Suryaputra IP6_INC_STATS(dev_net(dst->dev), 3607bdb7cc64SStephen Suryaputra __in6_dev_get_safely(skb->dev), 36083bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 3609612f09e8SYOSHIFUJI Hideaki break; 3610612f09e8SYOSHIFUJI Hideaki } 3611612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 3612612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 36133bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 36143bd653c8SDenis V. Lunev ipstats_mib_noroutes); 3615612f09e8SYOSHIFUJI Hideaki break; 3616612f09e8SYOSHIFUJI Hideaki } 36173ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 36181da177e4SLinus Torvalds kfree_skb(skb); 36191da177e4SLinus Torvalds return 0; 36201da177e4SLinus Torvalds } 36211da177e4SLinus Torvalds 36229ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 36239ce8ade0SThomas Graf { 3624612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 36259ce8ade0SThomas Graf } 36269ce8ade0SThomas Graf 3627ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb) 36281da177e4SLinus Torvalds { 3629adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 3630612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 36311da177e4SLinus Torvalds } 36321da177e4SLinus Torvalds 36339ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 36349ce8ade0SThomas Graf { 3635612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 36369ce8ade0SThomas Graf } 36379ce8ade0SThomas Graf 3638ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb) 36399ce8ade0SThomas Graf { 3640adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 3641612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 36429ce8ade0SThomas Graf } 36439ce8ade0SThomas Graf 36441da177e4SLinus Torvalds /* 36451da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 36461da177e4SLinus Torvalds */ 36471da177e4SLinus Torvalds 3648afb1d4b5SDavid Ahern struct rt6_info *addrconf_dst_alloc(struct net *net, 3649afb1d4b5SDavid Ahern struct inet6_dev *idev, 36501da177e4SLinus Torvalds const struct in6_addr *addr, 36518f031519SDavid S. Miller bool anycast) 36521da177e4SLinus Torvalds { 3653ca254490SDavid Ahern u32 tb_id; 36544832c30dSDavid Ahern struct net_device *dev = idev->dev; 36555f02ce24SDavid Ahern struct rt6_info *rt; 36565f02ce24SDavid Ahern 36575f02ce24SDavid Ahern rt = ip6_dst_alloc(net, dev, DST_NOCOUNT); 3658a3300ef4SHannes Frederic Sowa if (!rt) 36591da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 36601da177e4SLinus Torvalds 36611da177e4SLinus Torvalds in6_dev_hold(idev); 36621da177e4SLinus Torvalds rt->rt6i_idev = idev; 36631da177e4SLinus Torvalds 3664*6edb3c96SDavid Ahern rt->dst.flags |= DST_HOST; 366594b5e0f9SDavid Ahern rt->rt6i_protocol = RTPROT_KERNEL; 36661da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 3667e8478e80SDavid Ahern if (anycast) { 3668e8478e80SDavid Ahern rt->fib6_type = RTN_ANYCAST; 366958c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 3670e8478e80SDavid Ahern } else { 3671e8478e80SDavid Ahern rt->fib6_type = RTN_LOCAL; 36721da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 3673e8478e80SDavid Ahern } 36741da177e4SLinus Torvalds 36755e670d84SDavid Ahern rt->fib6_nh.nh_gw = *addr; 36765e670d84SDavid Ahern rt->fib6_nh.nh_dev = dev; 3677550bab42SJulian Anastasov rt->rt6i_gateway = *addr; 36784e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 36791da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 3680ca254490SDavid Ahern tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL; 3681ca254490SDavid Ahern rt->rt6i_table = fib6_get_table(net, tb_id); 36821da177e4SLinus Torvalds 36831da177e4SLinus Torvalds return rt; 36841da177e4SLinus Torvalds } 36851da177e4SLinus Torvalds 3686c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 3687c3968a85SDaniel Walter struct arg_dev_net_ip { 3688c3968a85SDaniel Walter struct net_device *dev; 3689c3968a85SDaniel Walter struct net *net; 3690c3968a85SDaniel Walter struct in6_addr *addr; 3691c3968a85SDaniel Walter }; 3692c3968a85SDaniel Walter 3693c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 3694c3968a85SDaniel Walter { 3695c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 3696c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 3697c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 3698c3968a85SDaniel Walter 36995e670d84SDavid Ahern if (((void *)rt->fib6_nh.nh_dev == dev || !dev) && 3700c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 3701c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 370260006a48SWei Wang spin_lock_bh(&rt6_exception_lock); 3703c3968a85SDaniel Walter /* remove prefsrc entry */ 3704c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 370560006a48SWei Wang /* need to update cache as well */ 370660006a48SWei Wang rt6_exceptions_remove_prefsrc(rt); 370760006a48SWei Wang spin_unlock_bh(&rt6_exception_lock); 3708c3968a85SDaniel Walter } 3709c3968a85SDaniel Walter return 0; 3710c3968a85SDaniel Walter } 3711c3968a85SDaniel Walter 3712c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 3713c3968a85SDaniel Walter { 3714c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 3715c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 3716c3968a85SDaniel Walter .dev = ifp->idev->dev, 3717c3968a85SDaniel Walter .net = net, 3718c3968a85SDaniel Walter .addr = &ifp->addr, 3719c3968a85SDaniel Walter }; 37200c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 3721c3968a85SDaniel Walter } 3722c3968a85SDaniel Walter 3723be7a010dSDuan Jiong #define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) 3724be7a010dSDuan Jiong 3725be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */ 3726be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg) 3727be7a010dSDuan Jiong { 3728be7a010dSDuan Jiong struct in6_addr *gateway = (struct in6_addr *)arg; 3729be7a010dSDuan Jiong 37302b760fcfSWei Wang if (((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) && 37315e670d84SDavid Ahern ipv6_addr_equal(gateway, &rt->fib6_nh.nh_gw)) { 3732be7a010dSDuan Jiong return -1; 3733be7a010dSDuan Jiong } 3734b16cb459SWei Wang 3735b16cb459SWei Wang /* Further clean up cached routes in exception table. 3736b16cb459SWei Wang * This is needed because cached route may have a different 3737b16cb459SWei Wang * gateway than its 'parent' in the case of an ip redirect. 3738b16cb459SWei Wang */ 3739b16cb459SWei Wang rt6_exceptions_clean_tohost(rt, gateway); 3740b16cb459SWei Wang 3741be7a010dSDuan Jiong return 0; 3742be7a010dSDuan Jiong } 3743be7a010dSDuan Jiong 3744be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 3745be7a010dSDuan Jiong { 3746be7a010dSDuan Jiong fib6_clean_all(net, fib6_clean_tohost, gateway); 3747be7a010dSDuan Jiong } 3748be7a010dSDuan Jiong 37492127d95aSIdo Schimmel struct arg_netdev_event { 37502127d95aSIdo Schimmel const struct net_device *dev; 37514c981e28SIdo Schimmel union { 37522127d95aSIdo Schimmel unsigned int nh_flags; 37534c981e28SIdo Schimmel unsigned long event; 37544c981e28SIdo Schimmel }; 37552127d95aSIdo Schimmel }; 37562127d95aSIdo Schimmel 3757d7dedee1SIdo Schimmel static struct rt6_info *rt6_multipath_first_sibling(const struct rt6_info *rt) 3758d7dedee1SIdo Schimmel { 3759d7dedee1SIdo Schimmel struct rt6_info *iter; 3760d7dedee1SIdo Schimmel struct fib6_node *fn; 3761d7dedee1SIdo Schimmel 3762d7dedee1SIdo Schimmel fn = rcu_dereference_protected(rt->rt6i_node, 3763d7dedee1SIdo Schimmel lockdep_is_held(&rt->rt6i_table->tb6_lock)); 3764d7dedee1SIdo Schimmel iter = rcu_dereference_protected(fn->leaf, 3765d7dedee1SIdo Schimmel lockdep_is_held(&rt->rt6i_table->tb6_lock)); 3766d7dedee1SIdo Schimmel while (iter) { 3767d7dedee1SIdo Schimmel if (iter->rt6i_metric == rt->rt6i_metric && 3768d7dedee1SIdo Schimmel rt6_qualify_for_ecmp(iter)) 3769d7dedee1SIdo Schimmel return iter; 3770d7dedee1SIdo Schimmel iter = rcu_dereference_protected(iter->rt6_next, 3771d7dedee1SIdo Schimmel lockdep_is_held(&rt->rt6i_table->tb6_lock)); 3772d7dedee1SIdo Schimmel } 3773d7dedee1SIdo Schimmel 3774d7dedee1SIdo Schimmel return NULL; 3775d7dedee1SIdo Schimmel } 3776d7dedee1SIdo Schimmel 3777d7dedee1SIdo Schimmel static bool rt6_is_dead(const struct rt6_info *rt) 3778d7dedee1SIdo Schimmel { 37795e670d84SDavid Ahern if (rt->fib6_nh.nh_flags & RTNH_F_DEAD || 37805e670d84SDavid Ahern (rt->fib6_nh.nh_flags & RTNH_F_LINKDOWN && 3781d7dedee1SIdo Schimmel rt->rt6i_idev->cnf.ignore_routes_with_linkdown)) 3782d7dedee1SIdo Schimmel return true; 3783d7dedee1SIdo Schimmel 3784d7dedee1SIdo Schimmel return false; 3785d7dedee1SIdo Schimmel } 3786d7dedee1SIdo Schimmel 3787d7dedee1SIdo Schimmel static int rt6_multipath_total_weight(const struct rt6_info *rt) 3788d7dedee1SIdo Schimmel { 3789d7dedee1SIdo Schimmel struct rt6_info *iter; 3790d7dedee1SIdo Schimmel int total = 0; 3791d7dedee1SIdo Schimmel 3792d7dedee1SIdo Schimmel if (!rt6_is_dead(rt)) 37935e670d84SDavid Ahern total += rt->fib6_nh.nh_weight; 3794d7dedee1SIdo Schimmel 3795d7dedee1SIdo Schimmel list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings) { 3796d7dedee1SIdo Schimmel if (!rt6_is_dead(iter)) 37975e670d84SDavid Ahern total += iter->fib6_nh.nh_weight; 3798d7dedee1SIdo Schimmel } 3799d7dedee1SIdo Schimmel 3800d7dedee1SIdo Schimmel return total; 3801d7dedee1SIdo Schimmel } 3802d7dedee1SIdo Schimmel 3803d7dedee1SIdo Schimmel static void rt6_upper_bound_set(struct rt6_info *rt, int *weight, int total) 3804d7dedee1SIdo Schimmel { 3805d7dedee1SIdo Schimmel int upper_bound = -1; 3806d7dedee1SIdo Schimmel 3807d7dedee1SIdo Schimmel if (!rt6_is_dead(rt)) { 38085e670d84SDavid Ahern *weight += rt->fib6_nh.nh_weight; 3809d7dedee1SIdo Schimmel upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31, 3810d7dedee1SIdo Schimmel total) - 1; 3811d7dedee1SIdo Schimmel } 38125e670d84SDavid Ahern atomic_set(&rt->fib6_nh.nh_upper_bound, upper_bound); 3813d7dedee1SIdo Schimmel } 3814d7dedee1SIdo Schimmel 3815d7dedee1SIdo Schimmel static void rt6_multipath_upper_bound_set(struct rt6_info *rt, int total) 3816d7dedee1SIdo Schimmel { 3817d7dedee1SIdo Schimmel struct rt6_info *iter; 3818d7dedee1SIdo Schimmel int weight = 0; 3819d7dedee1SIdo Schimmel 3820d7dedee1SIdo Schimmel rt6_upper_bound_set(rt, &weight, total); 3821d7dedee1SIdo Schimmel 3822d7dedee1SIdo Schimmel list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings) 3823d7dedee1SIdo Schimmel rt6_upper_bound_set(iter, &weight, total); 3824d7dedee1SIdo Schimmel } 3825d7dedee1SIdo Schimmel 3826d7dedee1SIdo Schimmel void rt6_multipath_rebalance(struct rt6_info *rt) 3827d7dedee1SIdo Schimmel { 3828d7dedee1SIdo Schimmel struct rt6_info *first; 3829d7dedee1SIdo Schimmel int total; 3830d7dedee1SIdo Schimmel 3831d7dedee1SIdo Schimmel /* In case the entire multipath route was marked for flushing, 3832d7dedee1SIdo Schimmel * then there is no need to rebalance upon the removal of every 3833d7dedee1SIdo Schimmel * sibling route. 3834d7dedee1SIdo Schimmel */ 3835d7dedee1SIdo Schimmel if (!rt->rt6i_nsiblings || rt->should_flush) 3836d7dedee1SIdo Schimmel return; 3837d7dedee1SIdo Schimmel 3838d7dedee1SIdo Schimmel /* During lookup routes are evaluated in order, so we need to 3839d7dedee1SIdo Schimmel * make sure upper bounds are assigned from the first sibling 3840d7dedee1SIdo Schimmel * onwards. 3841d7dedee1SIdo Schimmel */ 3842d7dedee1SIdo Schimmel first = rt6_multipath_first_sibling(rt); 3843d7dedee1SIdo Schimmel if (WARN_ON_ONCE(!first)) 3844d7dedee1SIdo Schimmel return; 3845d7dedee1SIdo Schimmel 3846d7dedee1SIdo Schimmel total = rt6_multipath_total_weight(first); 3847d7dedee1SIdo Schimmel rt6_multipath_upper_bound_set(first, total); 3848d7dedee1SIdo Schimmel } 3849d7dedee1SIdo Schimmel 38502127d95aSIdo Schimmel static int fib6_ifup(struct rt6_info *rt, void *p_arg) 38512127d95aSIdo Schimmel { 38522127d95aSIdo Schimmel const struct arg_netdev_event *arg = p_arg; 38537aef6859SDavid Ahern struct net *net = dev_net(arg->dev); 38542127d95aSIdo Schimmel 38555e670d84SDavid Ahern if (rt != net->ipv6.ip6_null_entry && rt->fib6_nh.nh_dev == arg->dev) { 38565e670d84SDavid Ahern rt->fib6_nh.nh_flags &= ~arg->nh_flags; 38577aef6859SDavid Ahern fib6_update_sernum_upto_root(net, rt); 3858d7dedee1SIdo Schimmel rt6_multipath_rebalance(rt); 38591de178edSIdo Schimmel } 38602127d95aSIdo Schimmel 38612127d95aSIdo Schimmel return 0; 38622127d95aSIdo Schimmel } 38632127d95aSIdo Schimmel 38642127d95aSIdo Schimmel void rt6_sync_up(struct net_device *dev, unsigned int nh_flags) 38652127d95aSIdo Schimmel { 38662127d95aSIdo Schimmel struct arg_netdev_event arg = { 38672127d95aSIdo Schimmel .dev = dev, 38686802f3adSIdo Schimmel { 38692127d95aSIdo Schimmel .nh_flags = nh_flags, 38706802f3adSIdo Schimmel }, 38712127d95aSIdo Schimmel }; 38722127d95aSIdo Schimmel 38732127d95aSIdo Schimmel if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev)) 38742127d95aSIdo Schimmel arg.nh_flags |= RTNH_F_LINKDOWN; 38752127d95aSIdo Schimmel 38762127d95aSIdo Schimmel fib6_clean_all(dev_net(dev), fib6_ifup, &arg); 38772127d95aSIdo Schimmel } 38782127d95aSIdo Schimmel 38791de178edSIdo Schimmel static bool rt6_multipath_uses_dev(const struct rt6_info *rt, 38801de178edSIdo Schimmel const struct net_device *dev) 38811de178edSIdo Schimmel { 38821de178edSIdo Schimmel struct rt6_info *iter; 38831de178edSIdo Schimmel 38845e670d84SDavid Ahern if (rt->fib6_nh.nh_dev == dev) 38851de178edSIdo Schimmel return true; 38861de178edSIdo Schimmel list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings) 38875e670d84SDavid Ahern if (iter->fib6_nh.nh_dev == dev) 38881de178edSIdo Schimmel return true; 38891de178edSIdo Schimmel 38901de178edSIdo Schimmel return false; 38911de178edSIdo Schimmel } 38921de178edSIdo Schimmel 38931de178edSIdo Schimmel static void rt6_multipath_flush(struct rt6_info *rt) 38941de178edSIdo Schimmel { 38951de178edSIdo Schimmel struct rt6_info *iter; 38961de178edSIdo Schimmel 38971de178edSIdo Schimmel rt->should_flush = 1; 38981de178edSIdo Schimmel list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings) 38991de178edSIdo Schimmel iter->should_flush = 1; 39001de178edSIdo Schimmel } 39011de178edSIdo Schimmel 39021de178edSIdo Schimmel static unsigned int rt6_multipath_dead_count(const struct rt6_info *rt, 39031de178edSIdo Schimmel const struct net_device *down_dev) 39041de178edSIdo Schimmel { 39051de178edSIdo Schimmel struct rt6_info *iter; 39061de178edSIdo Schimmel unsigned int dead = 0; 39071de178edSIdo Schimmel 39085e670d84SDavid Ahern if (rt->fib6_nh.nh_dev == down_dev || 39095e670d84SDavid Ahern rt->fib6_nh.nh_flags & RTNH_F_DEAD) 39101de178edSIdo Schimmel dead++; 39111de178edSIdo Schimmel list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings) 39125e670d84SDavid Ahern if (iter->fib6_nh.nh_dev == down_dev || 39135e670d84SDavid Ahern iter->fib6_nh.nh_flags & RTNH_F_DEAD) 39141de178edSIdo Schimmel dead++; 39151de178edSIdo Schimmel 39161de178edSIdo Schimmel return dead; 39171de178edSIdo Schimmel } 39181de178edSIdo Schimmel 39191de178edSIdo Schimmel static void rt6_multipath_nh_flags_set(struct rt6_info *rt, 39201de178edSIdo Schimmel const struct net_device *dev, 39211de178edSIdo Schimmel unsigned int nh_flags) 39221de178edSIdo Schimmel { 39231de178edSIdo Schimmel struct rt6_info *iter; 39241de178edSIdo Schimmel 39255e670d84SDavid Ahern if (rt->fib6_nh.nh_dev == dev) 39265e670d84SDavid Ahern rt->fib6_nh.nh_flags |= nh_flags; 39271de178edSIdo Schimmel list_for_each_entry(iter, &rt->rt6i_siblings, rt6i_siblings) 39285e670d84SDavid Ahern if (iter->fib6_nh.nh_dev == dev) 39295e670d84SDavid Ahern iter->fib6_nh.nh_flags |= nh_flags; 39301de178edSIdo Schimmel } 39311de178edSIdo Schimmel 3932a1a22c12SDavid Ahern /* called with write lock held for table with rt */ 39334c981e28SIdo Schimmel static int fib6_ifdown(struct rt6_info *rt, void *p_arg) 39341da177e4SLinus Torvalds { 39354c981e28SIdo Schimmel const struct arg_netdev_event *arg = p_arg; 39364c981e28SIdo Schimmel const struct net_device *dev = arg->dev; 39377aef6859SDavid Ahern struct net *net = dev_net(dev); 39388ed67789SDaniel Lezcano 39391de178edSIdo Schimmel if (rt == net->ipv6.ip6_null_entry) 394027c6fa73SIdo Schimmel return 0; 394127c6fa73SIdo Schimmel 394227c6fa73SIdo Schimmel switch (arg->event) { 394327c6fa73SIdo Schimmel case NETDEV_UNREGISTER: 39445e670d84SDavid Ahern return rt->fib6_nh.nh_dev == dev ? -1 : 0; 394527c6fa73SIdo Schimmel case NETDEV_DOWN: 39461de178edSIdo Schimmel if (rt->should_flush) 394727c6fa73SIdo Schimmel return -1; 39481de178edSIdo Schimmel if (!rt->rt6i_nsiblings) 39495e670d84SDavid Ahern return rt->fib6_nh.nh_dev == dev ? -1 : 0; 39501de178edSIdo Schimmel if (rt6_multipath_uses_dev(rt, dev)) { 39511de178edSIdo Schimmel unsigned int count; 39521de178edSIdo Schimmel 39531de178edSIdo Schimmel count = rt6_multipath_dead_count(rt, dev); 39541de178edSIdo Schimmel if (rt->rt6i_nsiblings + 1 == count) { 39551de178edSIdo Schimmel rt6_multipath_flush(rt); 39561de178edSIdo Schimmel return -1; 39571de178edSIdo Schimmel } 39581de178edSIdo Schimmel rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD | 39591de178edSIdo Schimmel RTNH_F_LINKDOWN); 39607aef6859SDavid Ahern fib6_update_sernum(net, rt); 3961d7dedee1SIdo Schimmel rt6_multipath_rebalance(rt); 39621de178edSIdo Schimmel } 39631de178edSIdo Schimmel return -2; 396427c6fa73SIdo Schimmel case NETDEV_CHANGE: 39655e670d84SDavid Ahern if (rt->fib6_nh.nh_dev != dev || 39661de178edSIdo Schimmel rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) 396727c6fa73SIdo Schimmel break; 39685e670d84SDavid Ahern rt->fib6_nh.nh_flags |= RTNH_F_LINKDOWN; 3969d7dedee1SIdo Schimmel rt6_multipath_rebalance(rt); 397027c6fa73SIdo Schimmel break; 39712b241361SIdo Schimmel } 3972c159d30cSDavid S. Miller 39731da177e4SLinus Torvalds return 0; 39741da177e4SLinus Torvalds } 39751da177e4SLinus Torvalds 397627c6fa73SIdo Schimmel void rt6_sync_down_dev(struct net_device *dev, unsigned long event) 39771da177e4SLinus Torvalds { 39784c981e28SIdo Schimmel struct arg_netdev_event arg = { 39798ed67789SDaniel Lezcano .dev = dev, 39806802f3adSIdo Schimmel { 39814c981e28SIdo Schimmel .event = event, 39826802f3adSIdo Schimmel }, 39838ed67789SDaniel Lezcano }; 39848ed67789SDaniel Lezcano 39854c981e28SIdo Schimmel fib6_clean_all(dev_net(dev), fib6_ifdown, &arg); 39864c981e28SIdo Schimmel } 39874c981e28SIdo Schimmel 39884c981e28SIdo Schimmel void rt6_disable_ip(struct net_device *dev, unsigned long event) 39894c981e28SIdo Schimmel { 39904c981e28SIdo Schimmel rt6_sync_down_dev(dev, event); 39914c981e28SIdo Schimmel rt6_uncached_list_flush_dev(dev_net(dev), dev); 39924c981e28SIdo Schimmel neigh_ifdown(&nd_tbl, dev); 39931da177e4SLinus Torvalds } 39941da177e4SLinus Torvalds 399595c96174SEric Dumazet struct rt6_mtu_change_arg { 39961da177e4SLinus Torvalds struct net_device *dev; 399795c96174SEric Dumazet unsigned int mtu; 39981da177e4SLinus Torvalds }; 39991da177e4SLinus Torvalds 40001da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 40011da177e4SLinus Torvalds { 40021da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 40031da177e4SLinus Torvalds struct inet6_dev *idev; 40041da177e4SLinus Torvalds 40051da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 40061da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 40071da177e4SLinus Torvalds We still use this lock to block changes 40081da177e4SLinus Torvalds caused by addrconf/ndisc. 40091da177e4SLinus Torvalds */ 40101da177e4SLinus Torvalds 40111da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 401238308473SDavid S. Miller if (!idev) 40131da177e4SLinus Torvalds return 0; 40141da177e4SLinus Torvalds 40151da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 40161da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 40171da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 40181da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 40191da177e4SLinus Torvalds */ 40205e670d84SDavid Ahern if (rt->fib6_nh.nh_dev == arg->dev && 40214b32b5adSMartin KaFai Lau !dst_metric_locked(&rt->dst, RTAX_MTU)) { 4022f5bbe7eeSWei Wang spin_lock_bh(&rt6_exception_lock); 4023e9fa1495SStefano Brivio if (dst_metric_raw(&rt->dst, RTAX_MTU) && 4024e9fa1495SStefano Brivio rt6_mtu_change_route_allowed(idev, rt, arg->mtu)) 4025defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 4026e9fa1495SStefano Brivio rt6_exceptions_update_pmtu(idev, rt, arg->mtu); 4027f5bbe7eeSWei Wang spin_unlock_bh(&rt6_exception_lock); 40284b32b5adSMartin KaFai Lau } 40291da177e4SLinus Torvalds return 0; 40301da177e4SLinus Torvalds } 40311da177e4SLinus Torvalds 403295c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 40331da177e4SLinus Torvalds { 4034c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 4035c71099acSThomas Graf .dev = dev, 4036c71099acSThomas Graf .mtu = mtu, 4037c71099acSThomas Graf }; 40381da177e4SLinus Torvalds 40390c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 40401da177e4SLinus Torvalds } 40411da177e4SLinus Torvalds 4042ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 40435176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 404486872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 4045ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 404686872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 404786872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 404851ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 4049c78ba6d6SLubomir Rintel [RTA_PREF] = { .type = NLA_U8 }, 405019e42e45SRoopa Prabhu [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, 405119e42e45SRoopa Prabhu [RTA_ENCAP] = { .type = NLA_NESTED }, 405232bc201eSXin Long [RTA_EXPIRES] = { .type = NLA_U32 }, 4053622ec2c9SLorenzo Colitti [RTA_UID] = { .type = NLA_U32 }, 40543b45a410SLiping Zhang [RTA_MARK] = { .type = NLA_U32 }, 405586872cb5SThomas Graf }; 405686872cb5SThomas Graf 405786872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 4058333c4301SDavid Ahern struct fib6_config *cfg, 4059333c4301SDavid Ahern struct netlink_ext_ack *extack) 40601da177e4SLinus Torvalds { 406186872cb5SThomas Graf struct rtmsg *rtm; 406286872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 4063c78ba6d6SLubomir Rintel unsigned int pref; 406486872cb5SThomas Graf int err; 40651da177e4SLinus Torvalds 4066fceb6435SJohannes Berg err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy, 4067fceb6435SJohannes Berg NULL); 406886872cb5SThomas Graf if (err < 0) 406986872cb5SThomas Graf goto errout; 40701da177e4SLinus Torvalds 407186872cb5SThomas Graf err = -EINVAL; 407286872cb5SThomas Graf rtm = nlmsg_data(nlh); 407386872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 407486872cb5SThomas Graf 407586872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 407686872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 407786872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 407886872cb5SThomas Graf cfg->fc_flags = RTF_UP; 407986872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 4080ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 408186872cb5SThomas Graf 4082ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 4083ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 4084b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 4085b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 408686872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 408786872cb5SThomas Graf 4088ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 4089ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 4090ab79ad14SMaciej Żenczykowski 40911f56a01fSMartin KaFai Lau if (rtm->rtm_flags & RTM_F_CLONED) 40921f56a01fSMartin KaFai Lau cfg->fc_flags |= RTF_CACHE; 40931f56a01fSMartin KaFai Lau 4094fc1e64e1SDavid Ahern cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK); 4095fc1e64e1SDavid Ahern 409615e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 409786872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 40983b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 409986872cb5SThomas Graf 410086872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 410167b61f6cSJiri Benc cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 410286872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 41031da177e4SLinus Torvalds } 410486872cb5SThomas Graf 410586872cb5SThomas Graf if (tb[RTA_DST]) { 410686872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 410786872cb5SThomas Graf 410886872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 410986872cb5SThomas Graf goto errout; 411086872cb5SThomas Graf 411186872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 41121da177e4SLinus Torvalds } 411386872cb5SThomas Graf 411486872cb5SThomas Graf if (tb[RTA_SRC]) { 411586872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 411686872cb5SThomas Graf 411786872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 411886872cb5SThomas Graf goto errout; 411986872cb5SThomas Graf 412086872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 41211da177e4SLinus Torvalds } 412286872cb5SThomas Graf 4123c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 412467b61f6cSJiri Benc cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 4125c3968a85SDaniel Walter 412686872cb5SThomas Graf if (tb[RTA_OIF]) 412786872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 412886872cb5SThomas Graf 412986872cb5SThomas Graf if (tb[RTA_PRIORITY]) 413086872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 413186872cb5SThomas Graf 413286872cb5SThomas Graf if (tb[RTA_METRICS]) { 413386872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 413486872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 41351da177e4SLinus Torvalds } 413686872cb5SThomas Graf 413786872cb5SThomas Graf if (tb[RTA_TABLE]) 413886872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 413986872cb5SThomas Graf 414051ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 414151ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 414251ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 41439ed59592SDavid Ahern 41449ed59592SDavid Ahern err = lwtunnel_valid_encap_type_attr(cfg->fc_mp, 4145c255bd68SDavid Ahern cfg->fc_mp_len, extack); 41469ed59592SDavid Ahern if (err < 0) 41479ed59592SDavid Ahern goto errout; 414851ebd318SNicolas Dichtel } 414951ebd318SNicolas Dichtel 4150c78ba6d6SLubomir Rintel if (tb[RTA_PREF]) { 4151c78ba6d6SLubomir Rintel pref = nla_get_u8(tb[RTA_PREF]); 4152c78ba6d6SLubomir Rintel if (pref != ICMPV6_ROUTER_PREF_LOW && 4153c78ba6d6SLubomir Rintel pref != ICMPV6_ROUTER_PREF_HIGH) 4154c78ba6d6SLubomir Rintel pref = ICMPV6_ROUTER_PREF_MEDIUM; 4155c78ba6d6SLubomir Rintel cfg->fc_flags |= RTF_PREF(pref); 4156c78ba6d6SLubomir Rintel } 4157c78ba6d6SLubomir Rintel 415819e42e45SRoopa Prabhu if (tb[RTA_ENCAP]) 415919e42e45SRoopa Prabhu cfg->fc_encap = tb[RTA_ENCAP]; 416019e42e45SRoopa Prabhu 41619ed59592SDavid Ahern if (tb[RTA_ENCAP_TYPE]) { 416219e42e45SRoopa Prabhu cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]); 416319e42e45SRoopa Prabhu 4164c255bd68SDavid Ahern err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack); 41659ed59592SDavid Ahern if (err < 0) 41669ed59592SDavid Ahern goto errout; 41679ed59592SDavid Ahern } 41689ed59592SDavid Ahern 416932bc201eSXin Long if (tb[RTA_EXPIRES]) { 417032bc201eSXin Long unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ); 417132bc201eSXin Long 417232bc201eSXin Long if (addrconf_finite_timeout(timeout)) { 417332bc201eSXin Long cfg->fc_expires = jiffies_to_clock_t(timeout * HZ); 417432bc201eSXin Long cfg->fc_flags |= RTF_EXPIRES; 417532bc201eSXin Long } 417632bc201eSXin Long } 417732bc201eSXin Long 417886872cb5SThomas Graf err = 0; 417986872cb5SThomas Graf errout: 418086872cb5SThomas Graf return err; 41811da177e4SLinus Torvalds } 41821da177e4SLinus Torvalds 41836b9ea5a6SRoopa Prabhu struct rt6_nh { 41846b9ea5a6SRoopa Prabhu struct rt6_info *rt6_info; 41856b9ea5a6SRoopa Prabhu struct fib6_config r_cfg; 41866b9ea5a6SRoopa Prabhu struct mx6_config mxc; 41876b9ea5a6SRoopa Prabhu struct list_head next; 41886b9ea5a6SRoopa Prabhu }; 41896b9ea5a6SRoopa Prabhu 41906b9ea5a6SRoopa Prabhu static void ip6_print_replace_route_err(struct list_head *rt6_nh_list) 41916b9ea5a6SRoopa Prabhu { 41926b9ea5a6SRoopa Prabhu struct rt6_nh *nh; 41936b9ea5a6SRoopa Prabhu 41946b9ea5a6SRoopa Prabhu list_for_each_entry(nh, rt6_nh_list, next) { 41957d4d5065SDavid Ahern pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6c nexthop %pI6c ifi %d\n", 41966b9ea5a6SRoopa Prabhu &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway, 41976b9ea5a6SRoopa Prabhu nh->r_cfg.fc_ifindex); 41986b9ea5a6SRoopa Prabhu } 41996b9ea5a6SRoopa Prabhu } 42006b9ea5a6SRoopa Prabhu 42016b9ea5a6SRoopa Prabhu static int ip6_route_info_append(struct list_head *rt6_nh_list, 42026b9ea5a6SRoopa Prabhu struct rt6_info *rt, struct fib6_config *r_cfg) 42036b9ea5a6SRoopa Prabhu { 42046b9ea5a6SRoopa Prabhu struct rt6_nh *nh; 42056b9ea5a6SRoopa Prabhu int err = -EEXIST; 42066b9ea5a6SRoopa Prabhu 42076b9ea5a6SRoopa Prabhu list_for_each_entry(nh, rt6_nh_list, next) { 42086b9ea5a6SRoopa Prabhu /* check if rt6_info already exists */ 4209f06b7549SDavid Ahern if (rt6_duplicate_nexthop(nh->rt6_info, rt)) 42106b9ea5a6SRoopa Prabhu return err; 42116b9ea5a6SRoopa Prabhu } 42126b9ea5a6SRoopa Prabhu 42136b9ea5a6SRoopa Prabhu nh = kzalloc(sizeof(*nh), GFP_KERNEL); 42146b9ea5a6SRoopa Prabhu if (!nh) 42156b9ea5a6SRoopa Prabhu return -ENOMEM; 42166b9ea5a6SRoopa Prabhu nh->rt6_info = rt; 42176b9ea5a6SRoopa Prabhu err = ip6_convert_metrics(&nh->mxc, r_cfg); 42186b9ea5a6SRoopa Prabhu if (err) { 42196b9ea5a6SRoopa Prabhu kfree(nh); 42206b9ea5a6SRoopa Prabhu return err; 42216b9ea5a6SRoopa Prabhu } 42226b9ea5a6SRoopa Prabhu memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg)); 42236b9ea5a6SRoopa Prabhu list_add_tail(&nh->next, rt6_nh_list); 42246b9ea5a6SRoopa Prabhu 42256b9ea5a6SRoopa Prabhu return 0; 42266b9ea5a6SRoopa Prabhu } 42276b9ea5a6SRoopa Prabhu 42283b1137feSDavid Ahern static void ip6_route_mpath_notify(struct rt6_info *rt, 42293b1137feSDavid Ahern struct rt6_info *rt_last, 42303b1137feSDavid Ahern struct nl_info *info, 42313b1137feSDavid Ahern __u16 nlflags) 42323b1137feSDavid Ahern { 42333b1137feSDavid Ahern /* if this is an APPEND route, then rt points to the first route 42343b1137feSDavid Ahern * inserted and rt_last points to last route inserted. Userspace 42353b1137feSDavid Ahern * wants a consistent dump of the route which starts at the first 42363b1137feSDavid Ahern * nexthop. Since sibling routes are always added at the end of 42373b1137feSDavid Ahern * the list, find the first sibling of the last route appended 42383b1137feSDavid Ahern */ 42393b1137feSDavid Ahern if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->rt6i_nsiblings) { 42403b1137feSDavid Ahern rt = list_first_entry(&rt_last->rt6i_siblings, 42413b1137feSDavid Ahern struct rt6_info, 42423b1137feSDavid Ahern rt6i_siblings); 42433b1137feSDavid Ahern } 42443b1137feSDavid Ahern 42453b1137feSDavid Ahern if (rt) 42463b1137feSDavid Ahern inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags); 42473b1137feSDavid Ahern } 42483b1137feSDavid Ahern 4249333c4301SDavid Ahern static int ip6_route_multipath_add(struct fib6_config *cfg, 4250333c4301SDavid Ahern struct netlink_ext_ack *extack) 425151ebd318SNicolas Dichtel { 42523b1137feSDavid Ahern struct rt6_info *rt_notif = NULL, *rt_last = NULL; 42533b1137feSDavid Ahern struct nl_info *info = &cfg->fc_nlinfo; 425451ebd318SNicolas Dichtel struct fib6_config r_cfg; 425551ebd318SNicolas Dichtel struct rtnexthop *rtnh; 42566b9ea5a6SRoopa Prabhu struct rt6_info *rt; 42576b9ea5a6SRoopa Prabhu struct rt6_nh *err_nh; 42586b9ea5a6SRoopa Prabhu struct rt6_nh *nh, *nh_safe; 42593b1137feSDavid Ahern __u16 nlflags; 426051ebd318SNicolas Dichtel int remaining; 426151ebd318SNicolas Dichtel int attrlen; 42626b9ea5a6SRoopa Prabhu int err = 1; 42636b9ea5a6SRoopa Prabhu int nhn = 0; 42646b9ea5a6SRoopa Prabhu int replace = (cfg->fc_nlinfo.nlh && 42656b9ea5a6SRoopa Prabhu (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); 42666b9ea5a6SRoopa Prabhu LIST_HEAD(rt6_nh_list); 426751ebd318SNicolas Dichtel 42683b1137feSDavid Ahern nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE; 42693b1137feSDavid Ahern if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND) 42703b1137feSDavid Ahern nlflags |= NLM_F_APPEND; 42713b1137feSDavid Ahern 427235f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len; 427351ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 427451ebd318SNicolas Dichtel 42756b9ea5a6SRoopa Prabhu /* Parse a Multipath Entry and build a list (rt6_nh_list) of 42766b9ea5a6SRoopa Prabhu * rt6_info structs per nexthop 42776b9ea5a6SRoopa Prabhu */ 427851ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 427951ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 428051ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 428151ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 428251ebd318SNicolas Dichtel 428351ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 428451ebd318SNicolas Dichtel if (attrlen > 0) { 428551ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 428651ebd318SNicolas Dichtel 428751ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 428851ebd318SNicolas Dichtel if (nla) { 428967b61f6cSJiri Benc r_cfg.fc_gateway = nla_get_in6_addr(nla); 429051ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 429151ebd318SNicolas Dichtel } 429219e42e45SRoopa Prabhu r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 429319e42e45SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 429419e42e45SRoopa Prabhu if (nla) 429519e42e45SRoopa Prabhu r_cfg.fc_encap_type = nla_get_u16(nla); 429651ebd318SNicolas Dichtel } 42976b9ea5a6SRoopa Prabhu 429868e2ffdeSDavid Ahern r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK); 4299333c4301SDavid Ahern rt = ip6_route_info_create(&r_cfg, extack); 43008c5b83f0SRoopa Prabhu if (IS_ERR(rt)) { 43018c5b83f0SRoopa Prabhu err = PTR_ERR(rt); 43028c5b83f0SRoopa Prabhu rt = NULL; 43036b9ea5a6SRoopa Prabhu goto cleanup; 43048c5b83f0SRoopa Prabhu } 43056b9ea5a6SRoopa Prabhu 43065e670d84SDavid Ahern rt->fib6_nh.nh_weight = rtnh->rtnh_hops + 1; 4307398958aeSIdo Schimmel 43086b9ea5a6SRoopa Prabhu err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg); 430951ebd318SNicolas Dichtel if (err) { 4310587fea74SWei Wang dst_release_immediate(&rt->dst); 43116b9ea5a6SRoopa Prabhu goto cleanup; 431251ebd318SNicolas Dichtel } 43136b9ea5a6SRoopa Prabhu 43146b9ea5a6SRoopa Prabhu rtnh = rtnh_next(rtnh, &remaining); 431551ebd318SNicolas Dichtel } 43166b9ea5a6SRoopa Prabhu 43173b1137feSDavid Ahern /* for add and replace send one notification with all nexthops. 43183b1137feSDavid Ahern * Skip the notification in fib6_add_rt2node and send one with 43193b1137feSDavid Ahern * the full route when done 43203b1137feSDavid Ahern */ 43213b1137feSDavid Ahern info->skip_notify = 1; 43223b1137feSDavid Ahern 43236b9ea5a6SRoopa Prabhu err_nh = NULL; 43246b9ea5a6SRoopa Prabhu list_for_each_entry(nh, &rt6_nh_list, next) { 43253b1137feSDavid Ahern rt_last = nh->rt6_info; 4326333c4301SDavid Ahern err = __ip6_ins_rt(nh->rt6_info, info, &nh->mxc, extack); 43273b1137feSDavid Ahern /* save reference to first route for notification */ 43283b1137feSDavid Ahern if (!rt_notif && !err) 43293b1137feSDavid Ahern rt_notif = nh->rt6_info; 43303b1137feSDavid Ahern 43316b9ea5a6SRoopa Prabhu /* nh->rt6_info is used or freed at this point, reset to NULL*/ 43326b9ea5a6SRoopa Prabhu nh->rt6_info = NULL; 43336b9ea5a6SRoopa Prabhu if (err) { 43346b9ea5a6SRoopa Prabhu if (replace && nhn) 43356b9ea5a6SRoopa Prabhu ip6_print_replace_route_err(&rt6_nh_list); 43366b9ea5a6SRoopa Prabhu err_nh = nh; 43376b9ea5a6SRoopa Prabhu goto add_errout; 43386b9ea5a6SRoopa Prabhu } 43396b9ea5a6SRoopa Prabhu 43401a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 434127596472SMichal Kubeček * these flags after the first nexthop: if there is a collision, 434227596472SMichal Kubeček * we have already failed to add the first nexthop: 434327596472SMichal Kubeček * fib6_add_rt2node() has rejected it; when replacing, old 434427596472SMichal Kubeček * nexthops have been replaced by first new, the rest should 434527596472SMichal Kubeček * be added to it. 43461a72418bSNicolas Dichtel */ 434727596472SMichal Kubeček cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | 434827596472SMichal Kubeček NLM_F_REPLACE); 43496b9ea5a6SRoopa Prabhu nhn++; 43506b9ea5a6SRoopa Prabhu } 43516b9ea5a6SRoopa Prabhu 43523b1137feSDavid Ahern /* success ... tell user about new route */ 43533b1137feSDavid Ahern ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); 43546b9ea5a6SRoopa Prabhu goto cleanup; 43556b9ea5a6SRoopa Prabhu 43566b9ea5a6SRoopa Prabhu add_errout: 43573b1137feSDavid Ahern /* send notification for routes that were added so that 43583b1137feSDavid Ahern * the delete notifications sent by ip6_route_del are 43593b1137feSDavid Ahern * coherent 43603b1137feSDavid Ahern */ 43613b1137feSDavid Ahern if (rt_notif) 43623b1137feSDavid Ahern ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); 43633b1137feSDavid Ahern 43646b9ea5a6SRoopa Prabhu /* Delete routes that were already added */ 43656b9ea5a6SRoopa Prabhu list_for_each_entry(nh, &rt6_nh_list, next) { 43666b9ea5a6SRoopa Prabhu if (err_nh == nh) 43676b9ea5a6SRoopa Prabhu break; 4368333c4301SDavid Ahern ip6_route_del(&nh->r_cfg, extack); 43696b9ea5a6SRoopa Prabhu } 43706b9ea5a6SRoopa Prabhu 43716b9ea5a6SRoopa Prabhu cleanup: 43726b9ea5a6SRoopa Prabhu list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) { 4373587fea74SWei Wang if (nh->rt6_info) 4374587fea74SWei Wang dst_release_immediate(&nh->rt6_info->dst); 43756b9ea5a6SRoopa Prabhu kfree(nh->mxc.mx); 43766b9ea5a6SRoopa Prabhu list_del(&nh->next); 43776b9ea5a6SRoopa Prabhu kfree(nh); 43786b9ea5a6SRoopa Prabhu } 43796b9ea5a6SRoopa Prabhu 43806b9ea5a6SRoopa Prabhu return err; 43816b9ea5a6SRoopa Prabhu } 43826b9ea5a6SRoopa Prabhu 4383333c4301SDavid Ahern static int ip6_route_multipath_del(struct fib6_config *cfg, 4384333c4301SDavid Ahern struct netlink_ext_ack *extack) 43856b9ea5a6SRoopa Prabhu { 43866b9ea5a6SRoopa Prabhu struct fib6_config r_cfg; 43876b9ea5a6SRoopa Prabhu struct rtnexthop *rtnh; 43886b9ea5a6SRoopa Prabhu int remaining; 43896b9ea5a6SRoopa Prabhu int attrlen; 43906b9ea5a6SRoopa Prabhu int err = 1, last_err = 0; 43916b9ea5a6SRoopa Prabhu 43926b9ea5a6SRoopa Prabhu remaining = cfg->fc_mp_len; 43936b9ea5a6SRoopa Prabhu rtnh = (struct rtnexthop *)cfg->fc_mp; 43946b9ea5a6SRoopa Prabhu 43956b9ea5a6SRoopa Prabhu /* Parse a Multipath Entry */ 43966b9ea5a6SRoopa Prabhu while (rtnh_ok(rtnh, remaining)) { 43976b9ea5a6SRoopa Prabhu memcpy(&r_cfg, cfg, sizeof(*cfg)); 43986b9ea5a6SRoopa Prabhu if (rtnh->rtnh_ifindex) 43996b9ea5a6SRoopa Prabhu r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 44006b9ea5a6SRoopa Prabhu 44016b9ea5a6SRoopa Prabhu attrlen = rtnh_attrlen(rtnh); 44026b9ea5a6SRoopa Prabhu if (attrlen > 0) { 44036b9ea5a6SRoopa Prabhu struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 44046b9ea5a6SRoopa Prabhu 44056b9ea5a6SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_GATEWAY); 44066b9ea5a6SRoopa Prabhu if (nla) { 44076b9ea5a6SRoopa Prabhu nla_memcpy(&r_cfg.fc_gateway, nla, 16); 44086b9ea5a6SRoopa Prabhu r_cfg.fc_flags |= RTF_GATEWAY; 44096b9ea5a6SRoopa Prabhu } 44106b9ea5a6SRoopa Prabhu } 4411333c4301SDavid Ahern err = ip6_route_del(&r_cfg, extack); 44126b9ea5a6SRoopa Prabhu if (err) 44136b9ea5a6SRoopa Prabhu last_err = err; 44146b9ea5a6SRoopa Prabhu 441551ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 441651ebd318SNicolas Dichtel } 441751ebd318SNicolas Dichtel 441851ebd318SNicolas Dichtel return last_err; 441951ebd318SNicolas Dichtel } 442051ebd318SNicolas Dichtel 4421c21ef3e3SDavid Ahern static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, 4422c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 44231da177e4SLinus Torvalds { 442486872cb5SThomas Graf struct fib6_config cfg; 442586872cb5SThomas Graf int err; 44261da177e4SLinus Torvalds 4427333c4301SDavid Ahern err = rtm_to_fib6_config(skb, nlh, &cfg, extack); 442886872cb5SThomas Graf if (err < 0) 442986872cb5SThomas Graf return err; 443086872cb5SThomas Graf 443151ebd318SNicolas Dichtel if (cfg.fc_mp) 4432333c4301SDavid Ahern return ip6_route_multipath_del(&cfg, extack); 44330ae81335SDavid Ahern else { 44340ae81335SDavid Ahern cfg.fc_delete_all_nh = 1; 4435333c4301SDavid Ahern return ip6_route_del(&cfg, extack); 44361da177e4SLinus Torvalds } 44370ae81335SDavid Ahern } 44381da177e4SLinus Torvalds 4439c21ef3e3SDavid Ahern static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, 4440c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 44411da177e4SLinus Torvalds { 444286872cb5SThomas Graf struct fib6_config cfg; 444386872cb5SThomas Graf int err; 44441da177e4SLinus Torvalds 4445333c4301SDavid Ahern err = rtm_to_fib6_config(skb, nlh, &cfg, extack); 444686872cb5SThomas Graf if (err < 0) 444786872cb5SThomas Graf return err; 444886872cb5SThomas Graf 444951ebd318SNicolas Dichtel if (cfg.fc_mp) 4450333c4301SDavid Ahern return ip6_route_multipath_add(&cfg, extack); 445151ebd318SNicolas Dichtel else 4452333c4301SDavid Ahern return ip6_route_add(&cfg, extack); 44531da177e4SLinus Torvalds } 44541da177e4SLinus Torvalds 4455beb1afacSDavid Ahern static size_t rt6_nlmsg_size(struct rt6_info *rt) 4456339bf98fSThomas Graf { 4457beb1afacSDavid Ahern int nexthop_len = 0; 4458beb1afacSDavid Ahern 4459beb1afacSDavid Ahern if (rt->rt6i_nsiblings) { 4460beb1afacSDavid Ahern nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */ 4461beb1afacSDavid Ahern + NLA_ALIGN(sizeof(struct rtnexthop)) 4462beb1afacSDavid Ahern + nla_total_size(16) /* RTA_GATEWAY */ 44635e670d84SDavid Ahern + lwtunnel_get_encap_size(rt->fib6_nh.nh_lwtstate); 4464beb1afacSDavid Ahern 4465beb1afacSDavid Ahern nexthop_len *= rt->rt6i_nsiblings; 4466beb1afacSDavid Ahern } 4467beb1afacSDavid Ahern 4468339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 4469339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 4470339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 4471339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 4472339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 4473339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 4474339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 4475339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 4476339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 44776a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 4478ea697639SDaniel Borkmann + nla_total_size(sizeof(struct rta_cacheinfo)) 4479c78ba6d6SLubomir Rintel + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 448019e42e45SRoopa Prabhu + nla_total_size(1) /* RTA_PREF */ 44815e670d84SDavid Ahern + lwtunnel_get_encap_size(rt->fib6_nh.nh_lwtstate) 4482beb1afacSDavid Ahern + nexthop_len; 4483beb1afacSDavid Ahern } 4484beb1afacSDavid Ahern 4485beb1afacSDavid Ahern static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt, 44865be083ceSDavid Ahern unsigned int *flags, bool skip_oif) 4487beb1afacSDavid Ahern { 44885e670d84SDavid Ahern if (rt->fib6_nh.nh_flags & RTNH_F_DEAD) 4489f9d882eaSIdo Schimmel *flags |= RTNH_F_DEAD; 4490f9d882eaSIdo Schimmel 44915e670d84SDavid Ahern if (rt->fib6_nh.nh_flags & RTNH_F_LINKDOWN) { 4492beb1afacSDavid Ahern *flags |= RTNH_F_LINKDOWN; 4493beb1afacSDavid Ahern if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown) 4494beb1afacSDavid Ahern *flags |= RTNH_F_DEAD; 4495beb1afacSDavid Ahern } 4496beb1afacSDavid Ahern 4497beb1afacSDavid Ahern if (rt->rt6i_flags & RTF_GATEWAY) { 44985e670d84SDavid Ahern if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->fib6_nh.nh_gw) < 0) 4499beb1afacSDavid Ahern goto nla_put_failure; 4500beb1afacSDavid Ahern } 4501beb1afacSDavid Ahern 45025e670d84SDavid Ahern *flags |= (rt->fib6_nh.nh_flags & RTNH_F_ONLINK); 45035e670d84SDavid Ahern if (rt->fib6_nh.nh_flags & RTNH_F_OFFLOAD) 450461e4d01eSIdo Schimmel *flags |= RTNH_F_OFFLOAD; 450561e4d01eSIdo Schimmel 45065be083ceSDavid Ahern /* not needed for multipath encoding b/c it has a rtnexthop struct */ 45075e670d84SDavid Ahern if (!skip_oif && rt->fib6_nh.nh_dev && 45085e670d84SDavid Ahern nla_put_u32(skb, RTA_OIF, rt->fib6_nh.nh_dev->ifindex)) 4509beb1afacSDavid Ahern goto nla_put_failure; 4510beb1afacSDavid Ahern 45115e670d84SDavid Ahern if (rt->fib6_nh.nh_lwtstate && 45125e670d84SDavid Ahern lwtunnel_fill_encap(skb, rt->fib6_nh.nh_lwtstate) < 0) 4513beb1afacSDavid Ahern goto nla_put_failure; 4514beb1afacSDavid Ahern 4515beb1afacSDavid Ahern return 0; 4516beb1afacSDavid Ahern 4517beb1afacSDavid Ahern nla_put_failure: 4518beb1afacSDavid Ahern return -EMSGSIZE; 4519beb1afacSDavid Ahern } 4520beb1afacSDavid Ahern 45215be083ceSDavid Ahern /* add multipath next hop */ 4522beb1afacSDavid Ahern static int rt6_add_nexthop(struct sk_buff *skb, struct rt6_info *rt) 4523beb1afacSDavid Ahern { 45245e670d84SDavid Ahern const struct net_device *dev = rt->fib6_nh.nh_dev; 4525beb1afacSDavid Ahern struct rtnexthop *rtnh; 4526beb1afacSDavid Ahern unsigned int flags = 0; 4527beb1afacSDavid Ahern 4528beb1afacSDavid Ahern rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); 4529beb1afacSDavid Ahern if (!rtnh) 4530beb1afacSDavid Ahern goto nla_put_failure; 4531beb1afacSDavid Ahern 45325e670d84SDavid Ahern rtnh->rtnh_hops = rt->fib6_nh.nh_weight - 1; 45335e670d84SDavid Ahern rtnh->rtnh_ifindex = dev ? dev->ifindex : 0; 4534beb1afacSDavid Ahern 45355be083ceSDavid Ahern if (rt6_nexthop_info(skb, rt, &flags, true) < 0) 4536beb1afacSDavid Ahern goto nla_put_failure; 4537beb1afacSDavid Ahern 4538beb1afacSDavid Ahern rtnh->rtnh_flags = flags; 4539beb1afacSDavid Ahern 4540beb1afacSDavid Ahern /* length of rtnetlink header + attributes */ 4541beb1afacSDavid Ahern rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh; 4542beb1afacSDavid Ahern 4543beb1afacSDavid Ahern return 0; 4544beb1afacSDavid Ahern 4545beb1afacSDavid Ahern nla_put_failure: 4546beb1afacSDavid Ahern return -EMSGSIZE; 4547339bf98fSThomas Graf } 4548339bf98fSThomas Graf 4549191cd582SBrian Haley static int rt6_fill_node(struct net *net, 4550191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 45510d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 455215e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 4553f8cfe2ceSDavid Ahern unsigned int flags) 45541da177e4SLinus Torvalds { 45554b32b5adSMartin KaFai Lau u32 metrics[RTAX_MAX]; 45561da177e4SLinus Torvalds struct rtmsg *rtm; 45571da177e4SLinus Torvalds struct nlmsghdr *nlh; 4558e3703b3dSThomas Graf long expires; 45599e762a4aSPatrick McHardy u32 table; 45601da177e4SLinus Torvalds 456115e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 456238308473SDavid S. Miller if (!nlh) 456326932566SPatrick McHardy return -EMSGSIZE; 45642d7202bfSThomas Graf 45652d7202bfSThomas Graf rtm = nlmsg_data(nlh); 45661da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 45671da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 45681da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 45691da177e4SLinus Torvalds rtm->rtm_tos = 0; 4570c71099acSThomas Graf if (rt->rt6i_table) 45719e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 4572c71099acSThomas Graf else 45739e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 45749e762a4aSPatrick McHardy rtm->rtm_table = table; 4575c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 4576c78679e8SDavid S. Miller goto nla_put_failure; 4577e8478e80SDavid Ahern 4578e8478e80SDavid Ahern rtm->rtm_type = rt->fib6_type; 45791da177e4SLinus Torvalds rtm->rtm_flags = 0; 45801da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 45811da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 45821da177e4SLinus Torvalds 45831da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 45841da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 45851da177e4SLinus Torvalds 45861da177e4SLinus Torvalds if (dst) { 4587930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, dst)) 4588c78679e8SDavid S. Miller goto nla_put_failure; 45891da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 45901da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 4591930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr)) 4592c78679e8SDavid S. Miller goto nla_put_failure; 45931da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 45941da177e4SLinus Torvalds if (src) { 4595930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_SRC, src)) 4596c78679e8SDavid S. Miller goto nla_put_failure; 45971da177e4SLinus Torvalds rtm->rtm_src_len = 128; 4598c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 4599930345eaSJiri Benc nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr)) 4600c78679e8SDavid S. Miller goto nla_put_failure; 46011da177e4SLinus Torvalds #endif 46027bc570c8SYOSHIFUJI Hideaki if (iif) { 46037bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 46047bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 4605fd61c6baSDavid Ahern int err = ip6mr_get_route(net, skb, rtm, portid); 46062cf75070SNikolay Aleksandrov 46077bc570c8SYOSHIFUJI Hideaki if (err == 0) 46087bc570c8SYOSHIFUJI Hideaki return 0; 4609fd61c6baSDavid Ahern if (err < 0) 46107bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 46117bc570c8SYOSHIFUJI Hideaki } else 46127bc570c8SYOSHIFUJI Hideaki #endif 4613c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 4614c78679e8SDavid S. Miller goto nla_put_failure; 46157bc570c8SYOSHIFUJI Hideaki } else if (dst) { 46161da177e4SLinus Torvalds struct in6_addr saddr_buf; 4617c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 4618930345eaSJiri Benc nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 4619c78679e8SDavid S. Miller goto nla_put_failure; 4620c3968a85SDaniel Walter } 4621c3968a85SDaniel Walter 4622c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 4623c3968a85SDaniel Walter struct in6_addr saddr_buf; 46244e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 4625930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 4626c78679e8SDavid S. Miller goto nla_put_failure; 46271da177e4SLinus Torvalds } 46282d7202bfSThomas Graf 46294b32b5adSMartin KaFai Lau memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); 46304b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu) 46314b32b5adSMartin KaFai Lau metrics[RTAX_MTU - 1] = rt->rt6i_pmtu; 46324b32b5adSMartin KaFai Lau if (rtnetlink_put_metrics(skb, metrics) < 0) 46332d7202bfSThomas Graf goto nla_put_failure; 46342d7202bfSThomas Graf 4635beb1afacSDavid Ahern if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 4636beb1afacSDavid Ahern goto nla_put_failure; 4637beb1afacSDavid Ahern 4638beb1afacSDavid Ahern /* For multipath routes, walk the siblings list and add 4639beb1afacSDavid Ahern * each as a nexthop within RTA_MULTIPATH. 4640beb1afacSDavid Ahern */ 4641beb1afacSDavid Ahern if (rt->rt6i_nsiblings) { 4642beb1afacSDavid Ahern struct rt6_info *sibling, *next_sibling; 4643beb1afacSDavid Ahern struct nlattr *mp; 4644beb1afacSDavid Ahern 4645beb1afacSDavid Ahern mp = nla_nest_start(skb, RTA_MULTIPATH); 4646beb1afacSDavid Ahern if (!mp) 4647beb1afacSDavid Ahern goto nla_put_failure; 4648beb1afacSDavid Ahern 4649beb1afacSDavid Ahern if (rt6_add_nexthop(skb, rt) < 0) 4650beb1afacSDavid Ahern goto nla_put_failure; 4651beb1afacSDavid Ahern 4652beb1afacSDavid Ahern list_for_each_entry_safe(sibling, next_sibling, 4653beb1afacSDavid Ahern &rt->rt6i_siblings, rt6i_siblings) { 4654beb1afacSDavid Ahern if (rt6_add_nexthop(skb, sibling) < 0) 465594f826b8SEric Dumazet goto nla_put_failure; 465694f826b8SEric Dumazet } 46572d7202bfSThomas Graf 4658beb1afacSDavid Ahern nla_nest_end(skb, mp); 4659beb1afacSDavid Ahern } else { 46605be083ceSDavid Ahern if (rt6_nexthop_info(skb, rt, &rtm->rtm_flags, false) < 0) 4661c78679e8SDavid S. Miller goto nla_put_failure; 4662beb1afacSDavid Ahern } 46638253947eSLi Wei 46648253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 466569cdf8f9SYOSHIFUJI Hideaki 466687a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 4667e3703b3dSThomas Graf goto nla_put_failure; 46681da177e4SLinus Torvalds 4669c78ba6d6SLubomir Rintel if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) 4670c78ba6d6SLubomir Rintel goto nla_put_failure; 4671c78ba6d6SLubomir Rintel 467219e42e45SRoopa Prabhu 4673053c095aSJohannes Berg nlmsg_end(skb, nlh); 4674053c095aSJohannes Berg return 0; 46752d7202bfSThomas Graf 46762d7202bfSThomas Graf nla_put_failure: 467726932566SPatrick McHardy nlmsg_cancel(skb, nlh); 467826932566SPatrick McHardy return -EMSGSIZE; 46791da177e4SLinus Torvalds } 46801da177e4SLinus Torvalds 46811b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 46821da177e4SLinus Torvalds { 46831da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 46841f17e2f2SDavid Ahern struct net *net = arg->net; 46851f17e2f2SDavid Ahern 46861f17e2f2SDavid Ahern if (rt == net->ipv6.ip6_null_entry) 46871f17e2f2SDavid Ahern return 0; 46881da177e4SLinus Torvalds 46892d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 46902d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 4691f8cfe2ceSDavid Ahern 4692f8cfe2ceSDavid Ahern /* user wants prefix routes only */ 4693f8cfe2ceSDavid Ahern if (rtm->rtm_flags & RTM_F_PREFIX && 4694f8cfe2ceSDavid Ahern !(rt->rt6i_flags & RTF_PREFIX_RT)) { 4695f8cfe2ceSDavid Ahern /* success since this is not a prefix route */ 4696f8cfe2ceSDavid Ahern return 1; 4697f8cfe2ceSDavid Ahern } 4698f8cfe2ceSDavid Ahern } 46991da177e4SLinus Torvalds 47001f17e2f2SDavid Ahern return rt6_fill_node(net, 4701191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 470215e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 4703f8cfe2ceSDavid Ahern NLM_F_MULTI); 47041da177e4SLinus Torvalds } 47051da177e4SLinus Torvalds 4706c21ef3e3SDavid Ahern static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, 4707c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 47081da177e4SLinus Torvalds { 47093b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 4710ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 471118c3a61cSRoopa Prabhu int err, iif = 0, oif = 0; 471218c3a61cSRoopa Prabhu struct dst_entry *dst; 47131da177e4SLinus Torvalds struct rt6_info *rt; 4714ab364a6fSThomas Graf struct sk_buff *skb; 4715ab364a6fSThomas Graf struct rtmsg *rtm; 47164c9483b2SDavid S. Miller struct flowi6 fl6; 471718c3a61cSRoopa Prabhu bool fibmatch; 4718ab364a6fSThomas Graf 4719fceb6435SJohannes Berg err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy, 4720c21ef3e3SDavid Ahern extack); 4721ab364a6fSThomas Graf if (err < 0) 4722ab364a6fSThomas Graf goto errout; 4723ab364a6fSThomas Graf 4724ab364a6fSThomas Graf err = -EINVAL; 47254c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 472638b7097bSHannes Frederic Sowa rtm = nlmsg_data(nlh); 472738b7097bSHannes Frederic Sowa fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0); 472818c3a61cSRoopa Prabhu fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH); 4729ab364a6fSThomas Graf 4730ab364a6fSThomas Graf if (tb[RTA_SRC]) { 4731ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 4732ab364a6fSThomas Graf goto errout; 4733ab364a6fSThomas Graf 47344e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 4735ab364a6fSThomas Graf } 4736ab364a6fSThomas Graf 4737ab364a6fSThomas Graf if (tb[RTA_DST]) { 4738ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 4739ab364a6fSThomas Graf goto errout; 4740ab364a6fSThomas Graf 47414e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 4742ab364a6fSThomas Graf } 4743ab364a6fSThomas Graf 4744ab364a6fSThomas Graf if (tb[RTA_IIF]) 4745ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 4746ab364a6fSThomas Graf 4747ab364a6fSThomas Graf if (tb[RTA_OIF]) 474872331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 4749ab364a6fSThomas Graf 47502e47b291SLorenzo Colitti if (tb[RTA_MARK]) 47512e47b291SLorenzo Colitti fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 47522e47b291SLorenzo Colitti 4753622ec2c9SLorenzo Colitti if (tb[RTA_UID]) 4754622ec2c9SLorenzo Colitti fl6.flowi6_uid = make_kuid(current_user_ns(), 4755622ec2c9SLorenzo Colitti nla_get_u32(tb[RTA_UID])); 4756622ec2c9SLorenzo Colitti else 4757622ec2c9SLorenzo Colitti fl6.flowi6_uid = iif ? INVALID_UID : current_uid(); 4758622ec2c9SLorenzo Colitti 4759ab364a6fSThomas Graf if (iif) { 4760ab364a6fSThomas Graf struct net_device *dev; 476172331bc0SShmulik Ladkani int flags = 0; 476272331bc0SShmulik Ladkani 4763121622dbSFlorian Westphal rcu_read_lock(); 4764121622dbSFlorian Westphal 4765121622dbSFlorian Westphal dev = dev_get_by_index_rcu(net, iif); 4766ab364a6fSThomas Graf if (!dev) { 4767121622dbSFlorian Westphal rcu_read_unlock(); 4768ab364a6fSThomas Graf err = -ENODEV; 4769ab364a6fSThomas Graf goto errout; 4770ab364a6fSThomas Graf } 477172331bc0SShmulik Ladkani 477272331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 477372331bc0SShmulik Ladkani 477472331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 477572331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 477672331bc0SShmulik Ladkani 4777b75cc8f9SDavid Ahern dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags); 4778121622dbSFlorian Westphal 4779121622dbSFlorian Westphal rcu_read_unlock(); 478072331bc0SShmulik Ladkani } else { 478172331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 478272331bc0SShmulik Ladkani 478318c3a61cSRoopa Prabhu dst = ip6_route_output(net, NULL, &fl6); 478418c3a61cSRoopa Prabhu } 478518c3a61cSRoopa Prabhu 478618c3a61cSRoopa Prabhu 478718c3a61cSRoopa Prabhu rt = container_of(dst, struct rt6_info, dst); 478818c3a61cSRoopa Prabhu if (rt->dst.error) { 478918c3a61cSRoopa Prabhu err = rt->dst.error; 479018c3a61cSRoopa Prabhu ip6_rt_put(rt); 479118c3a61cSRoopa Prabhu goto errout; 4792ab364a6fSThomas Graf } 47931da177e4SLinus Torvalds 47949d6acb3bSWANG Cong if (rt == net->ipv6.ip6_null_entry) { 47959d6acb3bSWANG Cong err = rt->dst.error; 47969d6acb3bSWANG Cong ip6_rt_put(rt); 47979d6acb3bSWANG Cong goto errout; 47989d6acb3bSWANG Cong } 47999d6acb3bSWANG Cong 4800fba961abSDavid S. Miller if (fibmatch && rt->from) { 4801fba961abSDavid S. Miller struct rt6_info *ort = rt->from; 480258acfd71SIdo Schimmel 480358acfd71SIdo Schimmel dst_hold(&ort->dst); 480458acfd71SIdo Schimmel ip6_rt_put(rt); 480558acfd71SIdo Schimmel rt = ort; 480658acfd71SIdo Schimmel } 480758acfd71SIdo Schimmel 48081da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 480938308473SDavid S. Miller if (!skb) { 481094e187c0SAmerigo Wang ip6_rt_put(rt); 4811ab364a6fSThomas Graf err = -ENOBUFS; 4812ab364a6fSThomas Graf goto errout; 4813ab364a6fSThomas Graf } 48141da177e4SLinus Torvalds 4815d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 481618c3a61cSRoopa Prabhu if (fibmatch) 481718c3a61cSRoopa Prabhu err = rt6_fill_node(net, skb, rt, NULL, NULL, iif, 481818c3a61cSRoopa Prabhu RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 481918c3a61cSRoopa Prabhu nlh->nlmsg_seq, 0); 482018c3a61cSRoopa Prabhu else 48214c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 482215e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 4823f8cfe2ceSDavid Ahern nlh->nlmsg_seq, 0); 48241da177e4SLinus Torvalds if (err < 0) { 4825ab364a6fSThomas Graf kfree_skb(skb); 4826ab364a6fSThomas Graf goto errout; 48271da177e4SLinus Torvalds } 48281da177e4SLinus Torvalds 482915e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 4830ab364a6fSThomas Graf errout: 48311da177e4SLinus Torvalds return err; 48321da177e4SLinus Torvalds } 48331da177e4SLinus Torvalds 483437a1d361SRoopa Prabhu void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info, 483537a1d361SRoopa Prabhu unsigned int nlm_flags) 48361da177e4SLinus Torvalds { 48371da177e4SLinus Torvalds struct sk_buff *skb; 48385578689aSDaniel Lezcano struct net *net = info->nl_net; 4839528c4cebSDenis V. Lunev u32 seq; 4840528c4cebSDenis V. Lunev int err; 48410d51aa80SJamal Hadi Salim 4842528c4cebSDenis V. Lunev err = -ENOBUFS; 484338308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 484486872cb5SThomas Graf 484519e42e45SRoopa Prabhu skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 484638308473SDavid S. Miller if (!skb) 484721713ebcSThomas Graf goto errout; 48481da177e4SLinus Torvalds 4849191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 4850f8cfe2ceSDavid Ahern event, info->portid, seq, nlm_flags); 485126932566SPatrick McHardy if (err < 0) { 485226932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 485326932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 485426932566SPatrick McHardy kfree_skb(skb); 485526932566SPatrick McHardy goto errout; 485626932566SPatrick McHardy } 485715e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 48585578689aSDaniel Lezcano info->nlh, gfp_any()); 48591ce85fe4SPablo Neira Ayuso return; 486021713ebcSThomas Graf errout: 486121713ebcSThomas Graf if (err < 0) 48625578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 48631da177e4SLinus Torvalds } 48641da177e4SLinus Torvalds 48658ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 4866351638e7SJiri Pirko unsigned long event, void *ptr) 48678ed67789SDaniel Lezcano { 4868351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 4869c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 48708ed67789SDaniel Lezcano 4871242d3a49SWANG Cong if (!(dev->flags & IFF_LOOPBACK)) 4872242d3a49SWANG Cong return NOTIFY_OK; 4873242d3a49SWANG Cong 4874242d3a49SWANG Cong if (event == NETDEV_REGISTER) { 4875d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 48768ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 48778ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 4878d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 48798ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 4880d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 48818ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 48828ed67789SDaniel Lezcano #endif 488376da0704SWANG Cong } else if (event == NETDEV_UNREGISTER && 488476da0704SWANG Cong dev->reg_state != NETREG_UNREGISTERED) { 488576da0704SWANG Cong /* NETDEV_UNREGISTER could be fired for multiple times by 488676da0704SWANG Cong * netdev_wait_allrefs(). Make sure we only call this once. 488776da0704SWANG Cong */ 488812d94a80SEric Dumazet in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev); 4889242d3a49SWANG Cong #ifdef CONFIG_IPV6_MULTIPLE_TABLES 489012d94a80SEric Dumazet in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev); 489112d94a80SEric Dumazet in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev); 4892242d3a49SWANG Cong #endif 48938ed67789SDaniel Lezcano } 48948ed67789SDaniel Lezcano 48958ed67789SDaniel Lezcano return NOTIFY_OK; 48968ed67789SDaniel Lezcano } 48978ed67789SDaniel Lezcano 48981da177e4SLinus Torvalds /* 48991da177e4SLinus Torvalds * /proc 49001da177e4SLinus Torvalds */ 49011da177e4SLinus Torvalds 49021da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 49031da177e4SLinus Torvalds 490433120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 490533120b30SAlexey Dobriyan .open = ipv6_route_open, 490633120b30SAlexey Dobriyan .read = seq_read, 490733120b30SAlexey Dobriyan .llseek = seq_lseek, 49088d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 490933120b30SAlexey Dobriyan }; 491033120b30SAlexey Dobriyan 49111da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 49121da177e4SLinus Torvalds { 491369ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 49141da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 491569ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 491669ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 491781eb8447SWei Wang atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc), 491869ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 491969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 4920fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 492169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 49221da177e4SLinus Torvalds 49231da177e4SLinus Torvalds return 0; 49241da177e4SLinus Torvalds } 49251da177e4SLinus Torvalds 49261da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 49271da177e4SLinus Torvalds { 4928de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 492969ddb805SDaniel Lezcano } 493069ddb805SDaniel Lezcano 49319a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 49321da177e4SLinus Torvalds .open = rt6_stats_seq_open, 49331da177e4SLinus Torvalds .read = seq_read, 49341da177e4SLinus Torvalds .llseek = seq_lseek, 4935b6fcbdb4SPavel Emelyanov .release = single_release_net, 49361da177e4SLinus Torvalds }; 49371da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 49381da177e4SLinus Torvalds 49391da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 49401da177e4SLinus Torvalds 49411da177e4SLinus Torvalds static 4942fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 49431da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 49441da177e4SLinus Torvalds { 4945c486da34SLucian Adrian Grijincu struct net *net; 4946c486da34SLucian Adrian Grijincu int delay; 4947c486da34SLucian Adrian Grijincu if (!write) 4948c486da34SLucian Adrian Grijincu return -EINVAL; 4949c486da34SLucian Adrian Grijincu 4950c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 4951c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 49528d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 49532ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 49541da177e4SLinus Torvalds return 0; 49551da177e4SLinus Torvalds } 49561da177e4SLinus Torvalds 4957fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 49581da177e4SLinus Torvalds { 49591da177e4SLinus Torvalds .procname = "flush", 49604990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 49611da177e4SLinus Torvalds .maxlen = sizeof(int), 496289c8b3a1SDave Jones .mode = 0200, 49636d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 49641da177e4SLinus Torvalds }, 49651da177e4SLinus Torvalds { 49661da177e4SLinus Torvalds .procname = "gc_thresh", 49679a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 49681da177e4SLinus Torvalds .maxlen = sizeof(int), 49691da177e4SLinus Torvalds .mode = 0644, 49706d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 49711da177e4SLinus Torvalds }, 49721da177e4SLinus Torvalds { 49731da177e4SLinus Torvalds .procname = "max_size", 49744990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 49751da177e4SLinus Torvalds .maxlen = sizeof(int), 49761da177e4SLinus Torvalds .mode = 0644, 49776d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 49781da177e4SLinus Torvalds }, 49791da177e4SLinus Torvalds { 49801da177e4SLinus Torvalds .procname = "gc_min_interval", 49814990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 49821da177e4SLinus Torvalds .maxlen = sizeof(int), 49831da177e4SLinus Torvalds .mode = 0644, 49846d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 49851da177e4SLinus Torvalds }, 49861da177e4SLinus Torvalds { 49871da177e4SLinus Torvalds .procname = "gc_timeout", 49884990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 49891da177e4SLinus Torvalds .maxlen = sizeof(int), 49901da177e4SLinus Torvalds .mode = 0644, 49916d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 49921da177e4SLinus Torvalds }, 49931da177e4SLinus Torvalds { 49941da177e4SLinus Torvalds .procname = "gc_interval", 49954990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 49961da177e4SLinus Torvalds .maxlen = sizeof(int), 49971da177e4SLinus Torvalds .mode = 0644, 49986d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 49991da177e4SLinus Torvalds }, 50001da177e4SLinus Torvalds { 50011da177e4SLinus Torvalds .procname = "gc_elasticity", 50024990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 50031da177e4SLinus Torvalds .maxlen = sizeof(int), 50041da177e4SLinus Torvalds .mode = 0644, 5005f3d3f616SMin Zhang .proc_handler = proc_dointvec, 50061da177e4SLinus Torvalds }, 50071da177e4SLinus Torvalds { 50081da177e4SLinus Torvalds .procname = "mtu_expires", 50094990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 50101da177e4SLinus Torvalds .maxlen = sizeof(int), 50111da177e4SLinus Torvalds .mode = 0644, 50126d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 50131da177e4SLinus Torvalds }, 50141da177e4SLinus Torvalds { 50151da177e4SLinus Torvalds .procname = "min_adv_mss", 50164990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 50171da177e4SLinus Torvalds .maxlen = sizeof(int), 50181da177e4SLinus Torvalds .mode = 0644, 5019f3d3f616SMin Zhang .proc_handler = proc_dointvec, 50201da177e4SLinus Torvalds }, 50211da177e4SLinus Torvalds { 50221da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 50234990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 50241da177e4SLinus Torvalds .maxlen = sizeof(int), 50251da177e4SLinus Torvalds .mode = 0644, 50266d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 50271da177e4SLinus Torvalds }, 5028f8572d8fSEric W. Biederman { } 50291da177e4SLinus Torvalds }; 50301da177e4SLinus Torvalds 50312c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 5032760f2d01SDaniel Lezcano { 5033760f2d01SDaniel Lezcano struct ctl_table *table; 5034760f2d01SDaniel Lezcano 5035760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 5036760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 5037760f2d01SDaniel Lezcano GFP_KERNEL); 50385ee09105SYOSHIFUJI Hideaki 50395ee09105SYOSHIFUJI Hideaki if (table) { 50405ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 5041c486da34SLucian Adrian Grijincu table[0].extra1 = net; 504286393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 50435ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 50445ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 50455ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 50465ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 50475ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 50485ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 50495ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 50509c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 5051464dc801SEric W. Biederman 5052464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 5053464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 5054464dc801SEric W. Biederman table[0].procname = NULL; 50555ee09105SYOSHIFUJI Hideaki } 50565ee09105SYOSHIFUJI Hideaki 5057760f2d01SDaniel Lezcano return table; 5058760f2d01SDaniel Lezcano } 50591da177e4SLinus Torvalds #endif 50601da177e4SLinus Torvalds 50612c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 5062cdb18761SDaniel Lezcano { 5063633d424bSPavel Emelyanov int ret = -ENOMEM; 50648ed67789SDaniel Lezcano 506586393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 506686393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 5067f2fc6a54SBenjamin Thery 5068fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 5069fc66f95cSEric Dumazet goto out_ip6_dst_ops; 5070fc66f95cSEric Dumazet 50718ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 50728ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 50738ed67789SDaniel Lezcano GFP_KERNEL); 50748ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 5075fc66f95cSEric Dumazet goto out_ip6_dst_entries; 5076d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 507762fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 507862fa8a84SDavid S. Miller ip6_template_metrics, true); 50798ed67789SDaniel Lezcano 50808ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 5081feca7d8cSVincent Bernat net->ipv6.fib6_has_custom_rules = false; 50828ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 50838ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 50848ed67789SDaniel Lezcano GFP_KERNEL); 508568fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 508668fffc67SPeter Zijlstra goto out_ip6_null_entry; 5087d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 508862fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 508962fa8a84SDavid S. Miller ip6_template_metrics, true); 50908ed67789SDaniel Lezcano 50918ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 50928ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 50938ed67789SDaniel Lezcano GFP_KERNEL); 509468fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 509568fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 5096d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 509762fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 509862fa8a84SDavid S. Miller ip6_template_metrics, true); 50998ed67789SDaniel Lezcano #endif 51008ed67789SDaniel Lezcano 5101b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 5102b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 5103b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 5104b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 5105b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 5106b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 5107b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 5108b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 5109b339a47cSPeter Zijlstra 51106891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 51116891a346SBenjamin Thery 51128ed67789SDaniel Lezcano ret = 0; 51138ed67789SDaniel Lezcano out: 51148ed67789SDaniel Lezcano return ret; 5115f2fc6a54SBenjamin Thery 511668fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 511768fffc67SPeter Zijlstra out_ip6_prohibit_entry: 511868fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 511968fffc67SPeter Zijlstra out_ip6_null_entry: 512068fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 512168fffc67SPeter Zijlstra #endif 5122fc66f95cSEric Dumazet out_ip6_dst_entries: 5123fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 5124f2fc6a54SBenjamin Thery out_ip6_dst_ops: 5125f2fc6a54SBenjamin Thery goto out; 5126cdb18761SDaniel Lezcano } 5127cdb18761SDaniel Lezcano 51282c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 5129cdb18761SDaniel Lezcano { 51308ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 51318ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 51328ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 51338ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 51348ed67789SDaniel Lezcano #endif 513541bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 5136cdb18761SDaniel Lezcano } 5137cdb18761SDaniel Lezcano 5138d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 5139d189634eSThomas Graf { 5140d189634eSThomas Graf #ifdef CONFIG_PROC_FS 5141d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 5142d6444062SJoe Perches proc_create("rt6_stats", 0444, net->proc_net, &rt6_stats_seq_fops); 5143d189634eSThomas Graf #endif 5144d189634eSThomas Graf return 0; 5145d189634eSThomas Graf } 5146d189634eSThomas Graf 5147d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 5148d189634eSThomas Graf { 5149d189634eSThomas Graf #ifdef CONFIG_PROC_FS 5150ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 5151ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 5152d189634eSThomas Graf #endif 5153d189634eSThomas Graf } 5154d189634eSThomas Graf 5155cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 5156cdb18761SDaniel Lezcano .init = ip6_route_net_init, 5157cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 5158cdb18761SDaniel Lezcano }; 5159cdb18761SDaniel Lezcano 5160c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 5161c3426b47SDavid S. Miller { 5162c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 5163c3426b47SDavid S. Miller 5164c3426b47SDavid S. Miller if (!bp) 5165c3426b47SDavid S. Miller return -ENOMEM; 5166c3426b47SDavid S. Miller inet_peer_base_init(bp); 5167c3426b47SDavid S. Miller net->ipv6.peers = bp; 5168c3426b47SDavid S. Miller return 0; 5169c3426b47SDavid S. Miller } 5170c3426b47SDavid S. Miller 5171c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 5172c3426b47SDavid S. Miller { 5173c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 5174c3426b47SDavid S. Miller 5175c3426b47SDavid S. Miller net->ipv6.peers = NULL; 517656a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 5177c3426b47SDavid S. Miller kfree(bp); 5178c3426b47SDavid S. Miller } 5179c3426b47SDavid S. Miller 51802b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 5181c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 5182c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 5183c3426b47SDavid S. Miller }; 5184c3426b47SDavid S. Miller 5185d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 5186d189634eSThomas Graf .init = ip6_route_net_init_late, 5187d189634eSThomas Graf .exit = ip6_route_net_exit_late, 5188d189634eSThomas Graf }; 5189d189634eSThomas Graf 51908ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 51918ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 5192242d3a49SWANG Cong .priority = ADDRCONF_NOTIFY_PRIORITY - 10, 51938ed67789SDaniel Lezcano }; 51948ed67789SDaniel Lezcano 51952f460933SWANG Cong void __init ip6_route_init_special_entries(void) 51962f460933SWANG Cong { 51972f460933SWANG Cong /* Registering of the loopback is done before this portion of code, 51982f460933SWANG Cong * the loopback reference in rt6_info will not be taken, do it 51992f460933SWANG Cong * manually for init_net */ 52002f460933SWANG Cong init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 52012f460933SWANG Cong init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 52022f460933SWANG Cong #ifdef CONFIG_IPV6_MULTIPLE_TABLES 52032f460933SWANG Cong init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 52042f460933SWANG Cong init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 52052f460933SWANG Cong init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 52062f460933SWANG Cong init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 52072f460933SWANG Cong #endif 52082f460933SWANG Cong } 52092f460933SWANG Cong 5210433d49c3SDaniel Lezcano int __init ip6_route_init(void) 52111da177e4SLinus Torvalds { 5212433d49c3SDaniel Lezcano int ret; 52138d0b94afSMartin KaFai Lau int cpu; 5214433d49c3SDaniel Lezcano 52159a7ec3a9SDaniel Lezcano ret = -ENOMEM; 52169a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 52179a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 52189a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 52199a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 5220c19a28e1SFernando Carrijo goto out; 522114e50e57SDavid S. Miller 5222fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 52238ed67789SDaniel Lezcano if (ret) 5224bdb3289fSDaniel Lezcano goto out_kmem_cache; 5225bdb3289fSDaniel Lezcano 5226c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 5227c3426b47SDavid S. Miller if (ret) 5228e8803b6cSDavid S. Miller goto out_dst_entries; 52292a0c451aSThomas Graf 52307e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 52317e52b33bSDavid S. Miller if (ret) 52327e52b33bSDavid S. Miller goto out_register_inetpeer; 5233c3426b47SDavid S. Miller 52345dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 52355dc121e9SArnaud Ebalard 5236e8803b6cSDavid S. Miller ret = fib6_init(); 5237433d49c3SDaniel Lezcano if (ret) 52388ed67789SDaniel Lezcano goto out_register_subsys; 5239433d49c3SDaniel Lezcano 5240433d49c3SDaniel Lezcano ret = xfrm6_init(); 5241433d49c3SDaniel Lezcano if (ret) 5242e8803b6cSDavid S. Miller goto out_fib6_init; 5243c35b7e72SDaniel Lezcano 5244433d49c3SDaniel Lezcano ret = fib6_rules_init(); 5245433d49c3SDaniel Lezcano if (ret) 5246433d49c3SDaniel Lezcano goto xfrm6_init; 52477e5449c2SDaniel Lezcano 5248d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 5249d189634eSThomas Graf if (ret) 5250d189634eSThomas Graf goto fib6_rules_init; 5251d189634eSThomas Graf 525216feebcfSFlorian Westphal ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE, 525316feebcfSFlorian Westphal inet6_rtm_newroute, NULL, 0); 525416feebcfSFlorian Westphal if (ret < 0) 525516feebcfSFlorian Westphal goto out_register_late_subsys; 525616feebcfSFlorian Westphal 525716feebcfSFlorian Westphal ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE, 525816feebcfSFlorian Westphal inet6_rtm_delroute, NULL, 0); 525916feebcfSFlorian Westphal if (ret < 0) 526016feebcfSFlorian Westphal goto out_register_late_subsys; 526116feebcfSFlorian Westphal 526216feebcfSFlorian Westphal ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE, 526316feebcfSFlorian Westphal inet6_rtm_getroute, NULL, 526416feebcfSFlorian Westphal RTNL_FLAG_DOIT_UNLOCKED); 526516feebcfSFlorian Westphal if (ret < 0) 5266d189634eSThomas Graf goto out_register_late_subsys; 5267433d49c3SDaniel Lezcano 52688ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 5269cdb18761SDaniel Lezcano if (ret) 5270d189634eSThomas Graf goto out_register_late_subsys; 52718ed67789SDaniel Lezcano 52728d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 52738d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 52748d0b94afSMartin KaFai Lau 52758d0b94afSMartin KaFai Lau INIT_LIST_HEAD(&ul->head); 52768d0b94afSMartin KaFai Lau spin_lock_init(&ul->lock); 52778d0b94afSMartin KaFai Lau } 52788d0b94afSMartin KaFai Lau 5279433d49c3SDaniel Lezcano out: 5280433d49c3SDaniel Lezcano return ret; 5281433d49c3SDaniel Lezcano 5282d189634eSThomas Graf out_register_late_subsys: 528316feebcfSFlorian Westphal rtnl_unregister_all(PF_INET6); 5284d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 5285433d49c3SDaniel Lezcano fib6_rules_init: 5286433d49c3SDaniel Lezcano fib6_rules_cleanup(); 5287433d49c3SDaniel Lezcano xfrm6_init: 5288433d49c3SDaniel Lezcano xfrm6_fini(); 52892a0c451aSThomas Graf out_fib6_init: 52902a0c451aSThomas Graf fib6_gc_cleanup(); 52918ed67789SDaniel Lezcano out_register_subsys: 52928ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 52937e52b33bSDavid S. Miller out_register_inetpeer: 52947e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 5295fc66f95cSEric Dumazet out_dst_entries: 5296fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 5297433d49c3SDaniel Lezcano out_kmem_cache: 5298f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 5299433d49c3SDaniel Lezcano goto out; 53001da177e4SLinus Torvalds } 53011da177e4SLinus Torvalds 53021da177e4SLinus Torvalds void ip6_route_cleanup(void) 53031da177e4SLinus Torvalds { 53048ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 5305d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 5306101367c2SThomas Graf fib6_rules_cleanup(); 53071da177e4SLinus Torvalds xfrm6_fini(); 53081da177e4SLinus Torvalds fib6_gc_cleanup(); 5309c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 53108ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 531141bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 5312f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 53131da177e4SLinus Torvalds } 5314