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); 998d1c802bSDavid Ahern static int rt6_score_route(struct fib6_info *rt, int oif, int strict); 1008d1c802bSDavid Ahern static size_t rt6_nlmsg_size(struct fib6_info *rt); 101d4ead6b3SDavid Ahern static int rt6_fill_node(struct net *net, struct sk_buff *skb, 1028d1c802bSDavid Ahern struct fib6_info *rt, struct dst_entry *dst, 103d4ead6b3SDavid Ahern struct in6_addr *dest, struct in6_addr *src, 10416a16cd3SDavid Ahern int iif, int type, u32 portid, u32 seq, 10516a16cd3SDavid Ahern unsigned int flags); 1068d1c802bSDavid Ahern static struct rt6_info *rt6_find_cached_rt(struct fib6_info *rt, 10735732d01SWei Wang struct in6_addr *daddr, 10835732d01SWei Wang struct in6_addr *saddr); 1091da177e4SLinus Torvalds 11070ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 1118d1c802bSDavid Ahern static struct fib6_info *rt6_add_route_info(struct net *net, 112b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 113830218c1SDavid Ahern const struct in6_addr *gwaddr, 114830218c1SDavid Ahern struct net_device *dev, 11595c96174SEric Dumazet unsigned int pref); 1168d1c802bSDavid Ahern static struct fib6_info *rt6_get_route_info(struct net *net, 117b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 118830218c1SDavid Ahern const struct in6_addr *gwaddr, 119830218c1SDavid Ahern struct net_device *dev); 12070ceb4f5SYOSHIFUJI Hideaki #endif 12170ceb4f5SYOSHIFUJI Hideaki 1228d0b94afSMartin KaFai Lau struct uncached_list { 1238d0b94afSMartin KaFai Lau spinlock_t lock; 1248d0b94afSMartin KaFai Lau struct list_head head; 1258d0b94afSMartin KaFai Lau }; 1268d0b94afSMartin KaFai Lau 1278d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list); 1288d0b94afSMartin KaFai Lau 129510c321bSXin Long void rt6_uncached_list_add(struct rt6_info *rt) 1308d0b94afSMartin KaFai Lau { 1318d0b94afSMartin KaFai Lau struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list); 1328d0b94afSMartin KaFai Lau 1338d0b94afSMartin KaFai Lau rt->rt6i_uncached_list = ul; 1348d0b94afSMartin KaFai Lau 1358d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1368d0b94afSMartin KaFai Lau list_add_tail(&rt->rt6i_uncached, &ul->head); 1378d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1388d0b94afSMartin KaFai Lau } 1398d0b94afSMartin KaFai Lau 140510c321bSXin Long void rt6_uncached_list_del(struct rt6_info *rt) 1418d0b94afSMartin KaFai Lau { 1428d0b94afSMartin KaFai Lau if (!list_empty(&rt->rt6i_uncached)) { 1438d0b94afSMartin KaFai Lau struct uncached_list *ul = rt->rt6i_uncached_list; 14481eb8447SWei Wang struct net *net = dev_net(rt->dst.dev); 1458d0b94afSMartin KaFai Lau 1468d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1478d0b94afSMartin KaFai Lau list_del(&rt->rt6i_uncached); 14881eb8447SWei Wang atomic_dec(&net->ipv6.rt6_stats->fib_rt_uncache); 1498d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1508d0b94afSMartin KaFai Lau } 1518d0b94afSMartin KaFai Lau } 1528d0b94afSMartin KaFai Lau 1538d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) 1548d0b94afSMartin KaFai Lau { 1558d0b94afSMartin KaFai Lau struct net_device *loopback_dev = net->loopback_dev; 1568d0b94afSMartin KaFai Lau int cpu; 1578d0b94afSMartin KaFai Lau 158e332bc67SEric W. Biederman if (dev == loopback_dev) 159e332bc67SEric W. Biederman return; 160e332bc67SEric W. Biederman 1618d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 1628d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 1638d0b94afSMartin KaFai Lau struct rt6_info *rt; 1648d0b94afSMartin KaFai Lau 1658d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1668d0b94afSMartin KaFai Lau list_for_each_entry(rt, &ul->head, rt6i_uncached) { 1678d0b94afSMartin KaFai Lau struct inet6_dev *rt_idev = rt->rt6i_idev; 1688d0b94afSMartin KaFai Lau struct net_device *rt_dev = rt->dst.dev; 1698d0b94afSMartin KaFai Lau 170e332bc67SEric W. Biederman if (rt_idev->dev == dev) { 1718d0b94afSMartin KaFai Lau rt->rt6i_idev = in6_dev_get(loopback_dev); 1728d0b94afSMartin KaFai Lau in6_dev_put(rt_idev); 1738d0b94afSMartin KaFai Lau } 1748d0b94afSMartin KaFai Lau 175e332bc67SEric W. Biederman if (rt_dev == dev) { 1768d0b94afSMartin KaFai Lau rt->dst.dev = loopback_dev; 1778d0b94afSMartin KaFai Lau dev_hold(rt->dst.dev); 1788d0b94afSMartin KaFai Lau dev_put(rt_dev); 1798d0b94afSMartin KaFai Lau } 1808d0b94afSMartin KaFai Lau } 1818d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1828d0b94afSMartin KaFai Lau } 1838d0b94afSMartin KaFai Lau } 1848d0b94afSMartin KaFai Lau 185f8a1b43bSDavid Ahern static inline const void *choose_neigh_daddr(const struct in6_addr *p, 186f894cbf8SDavid S. Miller struct sk_buff *skb, 187f894cbf8SDavid S. Miller const void *daddr) 18839232973SDavid S. Miller { 189a7563f34SDavid S. Miller if (!ipv6_addr_any(p)) 19039232973SDavid S. Miller return (const void *) p; 191f894cbf8SDavid S. Miller else if (skb) 192f894cbf8SDavid S. Miller return &ipv6_hdr(skb)->daddr; 19339232973SDavid S. Miller return daddr; 19439232973SDavid S. Miller } 19539232973SDavid S. Miller 196f8a1b43bSDavid Ahern struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw, 197f8a1b43bSDavid Ahern struct net_device *dev, 198f894cbf8SDavid S. Miller struct sk_buff *skb, 199f894cbf8SDavid S. Miller const void *daddr) 200d3aaeb38SDavid S. Miller { 20139232973SDavid S. Miller struct neighbour *n; 20239232973SDavid S. Miller 203f8a1b43bSDavid Ahern daddr = choose_neigh_daddr(gw, skb, daddr); 204f8a1b43bSDavid Ahern n = __ipv6_neigh_lookup(dev, daddr); 205f83c7790SDavid S. Miller if (n) 206f83c7790SDavid S. Miller return n; 207f8a1b43bSDavid Ahern return neigh_create(&nd_tbl, daddr, dev); 208f8a1b43bSDavid Ahern } 209f8a1b43bSDavid Ahern 210f8a1b43bSDavid Ahern static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst, 211f8a1b43bSDavid Ahern struct sk_buff *skb, 212f8a1b43bSDavid Ahern const void *daddr) 213f8a1b43bSDavid Ahern { 214f8a1b43bSDavid Ahern const struct rt6_info *rt = container_of(dst, struct rt6_info, dst); 215f8a1b43bSDavid Ahern 216f8a1b43bSDavid Ahern return ip6_neigh_lookup(&rt->rt6i_gateway, dst->dev, skb, daddr); 217f83c7790SDavid S. Miller } 218f83c7790SDavid S. Miller 21963fca65dSJulian Anastasov static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr) 22063fca65dSJulian Anastasov { 22163fca65dSJulian Anastasov struct net_device *dev = dst->dev; 22263fca65dSJulian Anastasov struct rt6_info *rt = (struct rt6_info *)dst; 22363fca65dSJulian Anastasov 224f8a1b43bSDavid Ahern daddr = choose_neigh_daddr(&rt->rt6i_gateway, NULL, daddr); 22563fca65dSJulian Anastasov if (!daddr) 22663fca65dSJulian Anastasov return; 22763fca65dSJulian Anastasov if (dev->flags & (IFF_NOARP | IFF_LOOPBACK)) 22863fca65dSJulian Anastasov return; 22963fca65dSJulian Anastasov if (ipv6_addr_is_multicast((const struct in6_addr *)daddr)) 23063fca65dSJulian Anastasov return; 23163fca65dSJulian Anastasov __ipv6_confirm_neigh(dev, daddr); 23263fca65dSJulian Anastasov } 23363fca65dSJulian Anastasov 2349a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 2351da177e4SLinus Torvalds .family = AF_INET6, 2361da177e4SLinus Torvalds .gc = ip6_dst_gc, 2371da177e4SLinus Torvalds .gc_thresh = 1024, 2381da177e4SLinus Torvalds .check = ip6_dst_check, 2390dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 240ebb762f2SSteffen Klassert .mtu = ip6_mtu, 241d4ead6b3SDavid Ahern .cow_metrics = dst_cow_metrics_generic, 2421da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 2431da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 2441da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 2451da177e4SLinus Torvalds .link_failure = ip6_link_failure, 2461da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 2476e157b6aSDavid S. Miller .redirect = rt6_do_redirect, 2489f8955ccSEric W. Biederman .local_out = __ip6_local_out, 249f8a1b43bSDavid Ahern .neigh_lookup = ip6_dst_neigh_lookup, 25063fca65dSJulian Anastasov .confirm_neigh = ip6_confirm_neigh, 2511da177e4SLinus Torvalds }; 2521da177e4SLinus Torvalds 253ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 254ec831ea7SRoland Dreier { 255618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 256618f9bc7SSteffen Klassert 257618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 258ec831ea7SRoland Dreier } 259ec831ea7SRoland Dreier 2606700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, 2616700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 26214e50e57SDavid S. Miller { 26314e50e57SDavid S. Miller } 26414e50e57SDavid S. Miller 2656700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, 2666700c270SDavid S. Miller struct sk_buff *skb) 267b587ee3bSDavid S. Miller { 268b587ee3bSDavid S. Miller } 269b587ee3bSDavid S. Miller 27014e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 27114e50e57SDavid S. Miller .family = AF_INET6, 27214e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 27314e50e57SDavid S. Miller .check = ip6_dst_check, 274ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 275214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 27614e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 277b587ee3bSDavid S. Miller .redirect = ip6_rt_blackhole_redirect, 2780a1f5962SMartin KaFai Lau .cow_metrics = dst_cow_metrics_generic, 279f8a1b43bSDavid Ahern .neigh_lookup = ip6_dst_neigh_lookup, 28014e50e57SDavid S. Miller }; 28114e50e57SDavid S. Miller 28262fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 28314edd87dSLi RongQing [RTAX_HOPLIMIT - 1] = 0, 28462fa8a84SDavid S. Miller }; 28562fa8a84SDavid S. Miller 2868d1c802bSDavid Ahern static const struct fib6_info fib6_null_entry_template = { 28793c2fb25SDavid Ahern .fib6_flags = (RTF_REJECT | RTF_NONEXTHOP), 28893c2fb25SDavid Ahern .fib6_protocol = RTPROT_KERNEL, 28993c2fb25SDavid Ahern .fib6_metric = ~(u32)0, 29093c2fb25SDavid Ahern .fib6_ref = ATOMIC_INIT(1), 291421842edSDavid Ahern .fib6_type = RTN_UNREACHABLE, 292421842edSDavid Ahern .fib6_metrics = (struct dst_metrics *)&dst_default_metrics, 293421842edSDavid Ahern }; 294421842edSDavid Ahern 295fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = { 2961da177e4SLinus Torvalds .dst = { 2971da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 2981da177e4SLinus Torvalds .__use = 1, 2992c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 3001da177e4SLinus Torvalds .error = -ENETUNREACH, 3011da177e4SLinus Torvalds .input = ip6_pkt_discard, 3021da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 3031da177e4SLinus Torvalds }, 3041da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 3051da177e4SLinus Torvalds }; 3061da177e4SLinus Torvalds 307101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 308101367c2SThomas Graf 309fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = { 310101367c2SThomas Graf .dst = { 311101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 312101367c2SThomas Graf .__use = 1, 3132c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 314101367c2SThomas Graf .error = -EACCES, 3159ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 3169ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 317101367c2SThomas Graf }, 318101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 319101367c2SThomas Graf }; 320101367c2SThomas Graf 321fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = { 322101367c2SThomas Graf .dst = { 323101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 324101367c2SThomas Graf .__use = 1, 3252c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 326101367c2SThomas Graf .error = -EINVAL, 327352e512cSHerbert Xu .input = dst_discard, 328ede2059dSEric W. Biederman .output = dst_discard_out, 329101367c2SThomas Graf }, 330101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 331101367c2SThomas Graf }; 332101367c2SThomas Graf 333101367c2SThomas Graf #endif 334101367c2SThomas Graf 335ebfa45f0SMartin KaFai Lau static void rt6_info_init(struct rt6_info *rt) 336ebfa45f0SMartin KaFai Lau { 337ebfa45f0SMartin KaFai Lau struct dst_entry *dst = &rt->dst; 338ebfa45f0SMartin KaFai Lau 339ebfa45f0SMartin KaFai Lau memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 340ebfa45f0SMartin KaFai Lau INIT_LIST_HEAD(&rt->rt6i_uncached); 341ebfa45f0SMartin KaFai Lau } 342ebfa45f0SMartin KaFai Lau 3431da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 34493531c67SDavid Ahern struct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev, 345ad706862SMartin KaFai Lau int flags) 3461da177e4SLinus Torvalds { 34797bab73fSDavid S. Miller struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 348b2a9c0edSWei Wang 1, DST_OBSOLETE_FORCE_CHK, flags); 349cf911662SDavid S. Miller 35081eb8447SWei Wang if (rt) { 351ebfa45f0SMartin KaFai Lau rt6_info_init(rt); 35281eb8447SWei Wang atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc); 35381eb8447SWei Wang } 3548104891bSSteffen Klassert 355cf911662SDavid S. Miller return rt; 3561da177e4SLinus Torvalds } 3579ab179d8SDavid Ahern EXPORT_SYMBOL(ip6_dst_alloc); 358d52d3997SMartin KaFai Lau 3591da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 3601da177e4SLinus Torvalds { 3611da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3628d1c802bSDavid Ahern struct fib6_info *from = rt->from; 3638d0b94afSMartin KaFai Lau struct inet6_dev *idev; 3641da177e4SLinus Torvalds 3658e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 3668d0b94afSMartin KaFai Lau rt6_uncached_list_del(rt); 3678d0b94afSMartin KaFai Lau 3688d0b94afSMartin KaFai Lau idev = rt->rt6i_idev; 36938308473SDavid S. Miller if (idev) { 3701da177e4SLinus Torvalds rt->rt6i_idev = NULL; 3711da177e4SLinus Torvalds in6_dev_put(idev); 3721da177e4SLinus Torvalds } 373d4ead6b3SDavid Ahern 3743a2232e9SDavid Miller rt->from = NULL; 37593531c67SDavid Ahern fib6_info_release(from); 376b3419363SDavid S. Miller } 377b3419363SDavid S. Miller 3781da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 3791da177e4SLinus Torvalds int how) 3801da177e4SLinus Torvalds { 3811da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3821da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3835a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 384c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 3851da177e4SLinus Torvalds 386e5645f51SWei Wang if (idev && idev->dev != loopback_dev) { 387e5645f51SWei Wang struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev); 38838308473SDavid S. Miller if (loopback_idev) { 3891da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 3901da177e4SLinus Torvalds in6_dev_put(idev); 3911da177e4SLinus Torvalds } 3921da177e4SLinus Torvalds } 39397cac082SDavid S. Miller } 3941da177e4SLinus Torvalds 3955973fb1eSMartin KaFai Lau static bool __rt6_check_expired(const struct rt6_info *rt) 3965973fb1eSMartin KaFai Lau { 3975973fb1eSMartin KaFai Lau if (rt->rt6i_flags & RTF_EXPIRES) 3985973fb1eSMartin KaFai Lau return time_after(jiffies, rt->dst.expires); 3995973fb1eSMartin KaFai Lau else 4005973fb1eSMartin KaFai Lau return false; 4015973fb1eSMartin KaFai Lau } 4025973fb1eSMartin KaFai Lau 403a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt) 4041da177e4SLinus Torvalds { 4051716a961SGao feng if (rt->rt6i_flags & RTF_EXPIRES) { 4061716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 407a50feda5SEric Dumazet return true; 4083a2232e9SDavid Miller } else if (rt->from) { 4091e2ea8adSXin Long return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK || 41014895687SDavid Ahern fib6_check_expired(rt->from); 4111716a961SGao feng } 412a50feda5SEric Dumazet return false; 4131da177e4SLinus Torvalds } 4141da177e4SLinus Torvalds 4158d1c802bSDavid Ahern static struct fib6_info *rt6_multipath_select(const struct net *net, 4168d1c802bSDavid Ahern struct fib6_info *match, 41752bd4c0cSNicolas Dichtel struct flowi6 *fl6, int oif, 418b75cc8f9SDavid Ahern const struct sk_buff *skb, 41952bd4c0cSNicolas Dichtel int strict) 42051ebd318SNicolas Dichtel { 4218d1c802bSDavid Ahern struct fib6_info *sibling, *next_sibling; 42251ebd318SNicolas Dichtel 423b673d6ccSJakub Sitnicki /* We might have already computed the hash for ICMPv6 errors. In such 424b673d6ccSJakub Sitnicki * case it will always be non-zero. Otherwise now is the time to do it. 425b673d6ccSJakub Sitnicki */ 426b673d6ccSJakub Sitnicki if (!fl6->mp_hash) 427b4bac172SDavid Ahern fl6->mp_hash = rt6_multipath_hash(net, fl6, skb, NULL); 428b673d6ccSJakub Sitnicki 4295e670d84SDavid Ahern if (fl6->mp_hash <= atomic_read(&match->fib6_nh.nh_upper_bound)) 4303d709f69SIdo Schimmel return match; 431bbfcd776SIdo Schimmel 43293c2fb25SDavid Ahern list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings, 43393c2fb25SDavid Ahern fib6_siblings) { 4345e670d84SDavid Ahern int nh_upper_bound; 4355e670d84SDavid Ahern 4365e670d84SDavid Ahern nh_upper_bound = atomic_read(&sibling->fib6_nh.nh_upper_bound); 4375e670d84SDavid Ahern if (fl6->mp_hash > nh_upper_bound) 4383d709f69SIdo Schimmel continue; 43952bd4c0cSNicolas Dichtel if (rt6_score_route(sibling, oif, strict) < 0) 44052bd4c0cSNicolas Dichtel break; 44151ebd318SNicolas Dichtel match = sibling; 44251ebd318SNicolas Dichtel break; 44351ebd318SNicolas Dichtel } 4443d709f69SIdo Schimmel 44551ebd318SNicolas Dichtel return match; 44651ebd318SNicolas Dichtel } 44751ebd318SNicolas Dichtel 4481da177e4SLinus Torvalds /* 44966f5d6ceSWei Wang * Route lookup. rcu_read_lock() should be held. 4501da177e4SLinus Torvalds */ 4511da177e4SLinus Torvalds 4528d1c802bSDavid Ahern static inline struct fib6_info *rt6_device_match(struct net *net, 4538d1c802bSDavid Ahern struct fib6_info *rt, 454b71d1d42SEric Dumazet const struct in6_addr *saddr, 4551da177e4SLinus Torvalds int oif, 456d420895eSYOSHIFUJI Hideaki int flags) 4571da177e4SLinus Torvalds { 4588d1c802bSDavid Ahern struct fib6_info *sprt; 4591da177e4SLinus Torvalds 4605e670d84SDavid Ahern if (!oif && ipv6_addr_any(saddr) && 4615e670d84SDavid Ahern !(rt->fib6_nh.nh_flags & RTNH_F_DEAD)) 4628067bb8cSIdo Schimmel return rt; 463dd3abc4eSYOSHIFUJI Hideaki 464071fb37eSDavid Miller for (sprt = rt; sprt; sprt = rcu_dereference(sprt->rt6_next)) { 4655e670d84SDavid Ahern const struct net_device *dev = sprt->fib6_nh.nh_dev; 466dd3abc4eSYOSHIFUJI Hideaki 4675e670d84SDavid Ahern if (sprt->fib6_nh.nh_flags & RTNH_F_DEAD) 4688067bb8cSIdo Schimmel continue; 4698067bb8cSIdo Schimmel 470dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4711da177e4SLinus Torvalds if (dev->ifindex == oif) 4721da177e4SLinus Torvalds return sprt; 473dd3abc4eSYOSHIFUJI Hideaki } else { 474dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 475dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 476dd3abc4eSYOSHIFUJI Hideaki return sprt; 477dd3abc4eSYOSHIFUJI Hideaki } 4781da177e4SLinus Torvalds } 4791da177e4SLinus Torvalds 480eea68cd3SDavid Ahern if (oif && flags & RT6_LOOKUP_F_IFACE) 481421842edSDavid Ahern return net->ipv6.fib6_null_entry; 4828067bb8cSIdo Schimmel 483421842edSDavid Ahern return rt->fib6_nh.nh_flags & RTNH_F_DEAD ? net->ipv6.fib6_null_entry : rt; 4841da177e4SLinus Torvalds } 4851da177e4SLinus Torvalds 48627097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 487c2f17e82SHannes Frederic Sowa struct __rt6_probe_work { 488c2f17e82SHannes Frederic Sowa struct work_struct work; 489c2f17e82SHannes Frederic Sowa struct in6_addr target; 490c2f17e82SHannes Frederic Sowa struct net_device *dev; 491c2f17e82SHannes Frederic Sowa }; 492c2f17e82SHannes Frederic Sowa 493c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w) 494c2f17e82SHannes Frederic Sowa { 495c2f17e82SHannes Frederic Sowa struct in6_addr mcaddr; 496c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work = 497c2f17e82SHannes Frederic Sowa container_of(w, struct __rt6_probe_work, work); 498c2f17e82SHannes Frederic Sowa 499c2f17e82SHannes Frederic Sowa addrconf_addr_solict_mult(&work->target, &mcaddr); 500adc176c5SErik Nordmark ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0); 501c2f17e82SHannes Frederic Sowa dev_put(work->dev); 502662f5533SMichael Büsch kfree(work); 503c2f17e82SHannes Frederic Sowa } 504c2f17e82SHannes Frederic Sowa 5058d1c802bSDavid Ahern static void rt6_probe(struct fib6_info *rt) 50627097255SYOSHIFUJI Hideaki { 507990edb42SMartin KaFai Lau struct __rt6_probe_work *work; 5085e670d84SDavid Ahern const struct in6_addr *nh_gw; 509f2c31e32SEric Dumazet struct neighbour *neigh; 5105e670d84SDavid Ahern struct net_device *dev; 5115e670d84SDavid Ahern 51227097255SYOSHIFUJI Hideaki /* 51327097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 51427097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 51527097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 51627097255SYOSHIFUJI Hideaki * 51727097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 51827097255SYOSHIFUJI Hideaki * to no more than one per minute. 51927097255SYOSHIFUJI Hideaki */ 52093c2fb25SDavid Ahern if (!rt || !(rt->fib6_flags & RTF_GATEWAY)) 521fdd6681dSAmerigo Wang return; 5225e670d84SDavid Ahern 5235e670d84SDavid Ahern nh_gw = &rt->fib6_nh.nh_gw; 5245e670d84SDavid Ahern dev = rt->fib6_nh.nh_dev; 5252152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 5265e670d84SDavid Ahern neigh = __ipv6_neigh_lookup_noref(dev, nh_gw); 5272152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 528dcd1f572SDavid Ahern struct inet6_dev *idev; 529dcd1f572SDavid Ahern 5308d6c31bfSMartin KaFai Lau if (neigh->nud_state & NUD_VALID) 5318d6c31bfSMartin KaFai Lau goto out; 5328d6c31bfSMartin KaFai Lau 533dcd1f572SDavid Ahern idev = __in6_dev_get(dev); 534990edb42SMartin KaFai Lau work = NULL; 5352152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_lock(&neigh->lock); 536990edb42SMartin KaFai Lau if (!(neigh->nud_state & NUD_VALID) && 537990edb42SMartin KaFai Lau time_after(jiffies, 538dcd1f572SDavid Ahern neigh->updated + idev->cnf.rtr_probe_interval)) { 539c2f17e82SHannes Frederic Sowa work = kmalloc(sizeof(*work), GFP_ATOMIC); 540990edb42SMartin KaFai Lau if (work) 5417e980569SJiri Benc __neigh_set_probe_once(neigh); 542990edb42SMartin KaFai Lau } 543c2f17e82SHannes Frederic Sowa write_unlock(&neigh->lock); 544990edb42SMartin KaFai Lau } else { 545990edb42SMartin KaFai Lau work = kmalloc(sizeof(*work), GFP_ATOMIC); 546990edb42SMartin KaFai Lau } 547c2f17e82SHannes Frederic Sowa 548c2f17e82SHannes Frederic Sowa if (work) { 549c2f17e82SHannes Frederic Sowa INIT_WORK(&work->work, rt6_probe_deferred); 5505e670d84SDavid Ahern work->target = *nh_gw; 5515e670d84SDavid Ahern dev_hold(dev); 5525e670d84SDavid Ahern work->dev = dev; 553c2f17e82SHannes Frederic Sowa schedule_work(&work->work); 554c2f17e82SHannes Frederic Sowa } 555990edb42SMartin KaFai Lau 5568d6c31bfSMartin KaFai Lau out: 5572152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 558f2c31e32SEric Dumazet } 55927097255SYOSHIFUJI Hideaki #else 5608d1c802bSDavid Ahern static inline void rt6_probe(struct fib6_info *rt) 56127097255SYOSHIFUJI Hideaki { 56227097255SYOSHIFUJI Hideaki } 56327097255SYOSHIFUJI Hideaki #endif 56427097255SYOSHIFUJI Hideaki 5651da177e4SLinus Torvalds /* 566554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 5671da177e4SLinus Torvalds */ 5688d1c802bSDavid Ahern static inline int rt6_check_dev(struct fib6_info *rt, int oif) 5691da177e4SLinus Torvalds { 5705e670d84SDavid Ahern const struct net_device *dev = rt->fib6_nh.nh_dev; 5715e670d84SDavid Ahern 572161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 573554cfb7eSYOSHIFUJI Hideaki return 2; 574554cfb7eSYOSHIFUJI Hideaki return 0; 5751da177e4SLinus Torvalds } 5761da177e4SLinus Torvalds 5778d1c802bSDavid Ahern static inline enum rt6_nud_state rt6_check_neigh(struct fib6_info *rt) 5781da177e4SLinus Torvalds { 579afc154e9SHannes Frederic Sowa enum rt6_nud_state ret = RT6_NUD_FAIL_HARD; 5805e670d84SDavid Ahern struct neighbour *neigh; 581f2c31e32SEric Dumazet 58293c2fb25SDavid Ahern if (rt->fib6_flags & RTF_NONEXTHOP || 58393c2fb25SDavid Ahern !(rt->fib6_flags & RTF_GATEWAY)) 584afc154e9SHannes Frederic Sowa return RT6_NUD_SUCCEED; 585145a3621SYOSHIFUJI Hideaki / 吉藤英明 586145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 5875e670d84SDavid Ahern neigh = __ipv6_neigh_lookup_noref(rt->fib6_nh.nh_dev, 5885e670d84SDavid Ahern &rt->fib6_nh.nh_gw); 589145a3621SYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 590145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_lock(&neigh->lock); 591554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 592afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 593398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 594a5a81f0bSPaul Marks else if (!(neigh->nud_state & NUD_FAILED)) 595afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 5967e980569SJiri Benc else 5977e980569SJiri Benc ret = RT6_NUD_FAIL_PROBE; 598398bcbebSYOSHIFUJI Hideaki #endif 599145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_unlock(&neigh->lock); 600afc154e9SHannes Frederic Sowa } else { 601afc154e9SHannes Frederic Sowa ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 6027e980569SJiri Benc RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; 603a5a81f0bSPaul Marks } 604145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 605145a3621SYOSHIFUJI Hideaki / 吉藤英明 606a5a81f0bSPaul Marks return ret; 6071da177e4SLinus Torvalds } 6081da177e4SLinus Torvalds 6098d1c802bSDavid Ahern static int rt6_score_route(struct fib6_info *rt, int oif, int strict) 610554cfb7eSYOSHIFUJI Hideaki { 611a5a81f0bSPaul Marks int m; 6124d0c5911SYOSHIFUJI Hideaki 6134d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 61477d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 615afc154e9SHannes Frederic Sowa return RT6_NUD_FAIL_HARD; 616ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 61793c2fb25SDavid Ahern m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->fib6_flags)) << 2; 618ebacaaa0SYOSHIFUJI Hideaki #endif 619afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) { 620afc154e9SHannes Frederic Sowa int n = rt6_check_neigh(rt); 621afc154e9SHannes Frederic Sowa if (n < 0) 622afc154e9SHannes Frederic Sowa return n; 623afc154e9SHannes Frederic Sowa } 624554cfb7eSYOSHIFUJI Hideaki return m; 625554cfb7eSYOSHIFUJI Hideaki } 626554cfb7eSYOSHIFUJI Hideaki 627dcd1f572SDavid Ahern /* called with rc_read_lock held */ 628dcd1f572SDavid Ahern static inline bool fib6_ignore_linkdown(const struct fib6_info *f6i) 629dcd1f572SDavid Ahern { 630dcd1f572SDavid Ahern const struct net_device *dev = fib6_info_nh_dev(f6i); 631dcd1f572SDavid Ahern bool rc = false; 632dcd1f572SDavid Ahern 633dcd1f572SDavid Ahern if (dev) { 634dcd1f572SDavid Ahern const struct inet6_dev *idev = __in6_dev_get(dev); 635dcd1f572SDavid Ahern 636dcd1f572SDavid Ahern rc = !!idev->cnf.ignore_routes_with_linkdown; 637dcd1f572SDavid Ahern } 638dcd1f572SDavid Ahern 639dcd1f572SDavid Ahern return rc; 640dcd1f572SDavid Ahern } 641dcd1f572SDavid Ahern 6428d1c802bSDavid Ahern static struct fib6_info *find_match(struct fib6_info *rt, int oif, int strict, 6438d1c802bSDavid Ahern int *mpri, struct fib6_info *match, 644afc154e9SHannes Frederic Sowa bool *do_rr) 645554cfb7eSYOSHIFUJI Hideaki { 646554cfb7eSYOSHIFUJI Hideaki int m; 647afc154e9SHannes Frederic Sowa bool match_do_rr = false; 64835103d11SAndy Gospodarek 6495e670d84SDavid Ahern if (rt->fib6_nh.nh_flags & RTNH_F_DEAD) 6508067bb8cSIdo Schimmel goto out; 6518067bb8cSIdo Schimmel 652dcd1f572SDavid Ahern if (fib6_ignore_linkdown(rt) && 6535e670d84SDavid Ahern rt->fib6_nh.nh_flags & RTNH_F_LINKDOWN && 654d5d32e4bSDavid Ahern !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE)) 65535103d11SAndy Gospodarek goto out; 656554cfb7eSYOSHIFUJI Hideaki 65714895687SDavid Ahern if (fib6_check_expired(rt)) 658f11e6659SDavid S. Miller goto out; 659554cfb7eSYOSHIFUJI Hideaki 660554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 6617e980569SJiri Benc if (m == RT6_NUD_FAIL_DO_RR) { 662afc154e9SHannes Frederic Sowa match_do_rr = true; 663afc154e9SHannes Frederic Sowa m = 0; /* lowest valid score */ 6647e980569SJiri Benc } else if (m == RT6_NUD_FAIL_HARD) { 665f11e6659SDavid S. Miller goto out; 6661da177e4SLinus Torvalds } 667f11e6659SDavid S. Miller 668afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) 669afc154e9SHannes Frederic Sowa rt6_probe(rt); 670afc154e9SHannes Frederic Sowa 6717e980569SJiri Benc /* note that m can be RT6_NUD_FAIL_PROBE at this point */ 672afc154e9SHannes Frederic Sowa if (m > *mpri) { 673afc154e9SHannes Frederic Sowa *do_rr = match_do_rr; 674afc154e9SHannes Frederic Sowa *mpri = m; 675afc154e9SHannes Frederic Sowa match = rt; 676afc154e9SHannes Frederic Sowa } 677f11e6659SDavid S. Miller out: 678f11e6659SDavid S. Miller return match; 6791da177e4SLinus Torvalds } 6801da177e4SLinus Torvalds 6818d1c802bSDavid Ahern static struct fib6_info *find_rr_leaf(struct fib6_node *fn, 6828d1c802bSDavid Ahern struct fib6_info *leaf, 6838d1c802bSDavid Ahern struct fib6_info *rr_head, 684afc154e9SHannes Frederic Sowa u32 metric, int oif, int strict, 685afc154e9SHannes Frederic Sowa bool *do_rr) 686f11e6659SDavid S. Miller { 6878d1c802bSDavid Ahern struct fib6_info *rt, *match, *cont; 688f11e6659SDavid S. Miller int mpri = -1; 689f11e6659SDavid S. Miller 690f11e6659SDavid S. Miller match = NULL; 6919fbdcfafSSteffen Klassert cont = NULL; 692071fb37eSDavid Miller for (rt = rr_head; rt; rt = rcu_dereference(rt->rt6_next)) { 69393c2fb25SDavid Ahern if (rt->fib6_metric != metric) { 6949fbdcfafSSteffen Klassert cont = rt; 6959fbdcfafSSteffen Klassert break; 6969fbdcfafSSteffen Klassert } 6979fbdcfafSSteffen Klassert 698afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 6999fbdcfafSSteffen Klassert } 7009fbdcfafSSteffen Klassert 70166f5d6ceSWei Wang for (rt = leaf; rt && rt != rr_head; 702071fb37eSDavid Miller rt = rcu_dereference(rt->rt6_next)) { 70393c2fb25SDavid Ahern if (rt->fib6_metric != metric) { 7049fbdcfafSSteffen Klassert cont = rt; 7059fbdcfafSSteffen Klassert break; 7069fbdcfafSSteffen Klassert } 7079fbdcfafSSteffen Klassert 7089fbdcfafSSteffen Klassert match = find_match(rt, oif, strict, &mpri, match, do_rr); 7099fbdcfafSSteffen Klassert } 7109fbdcfafSSteffen Klassert 7119fbdcfafSSteffen Klassert if (match || !cont) 7129fbdcfafSSteffen Klassert return match; 7139fbdcfafSSteffen Klassert 714071fb37eSDavid Miller for (rt = cont; rt; rt = rcu_dereference(rt->rt6_next)) 715afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 716f11e6659SDavid S. Miller 717f11e6659SDavid S. Miller return match; 718f11e6659SDavid S. Miller } 719f11e6659SDavid S. Miller 7208d1c802bSDavid Ahern static struct fib6_info *rt6_select(struct net *net, struct fib6_node *fn, 7218d1040e8SWei Wang int oif, int strict) 722f11e6659SDavid S. Miller { 7238d1c802bSDavid Ahern struct fib6_info *leaf = rcu_dereference(fn->leaf); 7248d1c802bSDavid Ahern struct fib6_info *match, *rt0; 725afc154e9SHannes Frederic Sowa bool do_rr = false; 72617ecf590SWei Wang int key_plen; 727f11e6659SDavid S. Miller 728421842edSDavid Ahern if (!leaf || leaf == net->ipv6.fib6_null_entry) 729421842edSDavid Ahern return net->ipv6.fib6_null_entry; 7308d1040e8SWei Wang 73166f5d6ceSWei Wang rt0 = rcu_dereference(fn->rr_ptr); 732f11e6659SDavid S. Miller if (!rt0) 73366f5d6ceSWei Wang rt0 = leaf; 734f11e6659SDavid S. Miller 73517ecf590SWei Wang /* Double check to make sure fn is not an intermediate node 73617ecf590SWei Wang * and fn->leaf does not points to its child's leaf 73717ecf590SWei Wang * (This might happen if all routes under fn are deleted from 73817ecf590SWei Wang * the tree and fib6_repair_tree() is called on the node.) 73917ecf590SWei Wang */ 74093c2fb25SDavid Ahern key_plen = rt0->fib6_dst.plen; 74117ecf590SWei Wang #ifdef CONFIG_IPV6_SUBTREES 74293c2fb25SDavid Ahern if (rt0->fib6_src.plen) 74393c2fb25SDavid Ahern key_plen = rt0->fib6_src.plen; 74417ecf590SWei Wang #endif 74517ecf590SWei Wang if (fn->fn_bit != key_plen) 746421842edSDavid Ahern return net->ipv6.fib6_null_entry; 74717ecf590SWei Wang 74893c2fb25SDavid Ahern match = find_rr_leaf(fn, leaf, rt0, rt0->fib6_metric, oif, strict, 749afc154e9SHannes Frederic Sowa &do_rr); 750f11e6659SDavid S. Miller 751afc154e9SHannes Frederic Sowa if (do_rr) { 7528d1c802bSDavid Ahern struct fib6_info *next = rcu_dereference(rt0->rt6_next); 753f11e6659SDavid S. Miller 754554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 75593c2fb25SDavid Ahern if (!next || next->fib6_metric != rt0->fib6_metric) 7568d1040e8SWei Wang next = leaf; 757f11e6659SDavid S. Miller 75866f5d6ceSWei Wang if (next != rt0) { 75993c2fb25SDavid Ahern spin_lock_bh(&leaf->fib6_table->tb6_lock); 76066f5d6ceSWei Wang /* make sure next is not being deleted from the tree */ 76193c2fb25SDavid Ahern if (next->fib6_node) 76266f5d6ceSWei Wang rcu_assign_pointer(fn->rr_ptr, next); 76393c2fb25SDavid Ahern spin_unlock_bh(&leaf->fib6_table->tb6_lock); 76466f5d6ceSWei Wang } 765554cfb7eSYOSHIFUJI Hideaki } 766554cfb7eSYOSHIFUJI Hideaki 767421842edSDavid Ahern return match ? match : net->ipv6.fib6_null_entry; 7681da177e4SLinus Torvalds } 7691da177e4SLinus Torvalds 7708d1c802bSDavid Ahern static bool rt6_is_gw_or_nonexthop(const struct fib6_info *rt) 7718b9df265SMartin KaFai Lau { 77293c2fb25SDavid Ahern return (rt->fib6_flags & (RTF_NONEXTHOP | RTF_GATEWAY)); 7738b9df265SMartin KaFai Lau } 7748b9df265SMartin KaFai Lau 77570ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 77670ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 777b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 77870ceb4f5SYOSHIFUJI Hideaki { 779c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 78070ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 78170ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 78270ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 7834bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 7848d1c802bSDavid Ahern struct fib6_info *rt; 78570ceb4f5SYOSHIFUJI Hideaki 78670ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 78770ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 78870ceb4f5SYOSHIFUJI Hideaki } 78970ceb4f5SYOSHIFUJI Hideaki 79070ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 79170ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 79270ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 79370ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 79470ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 79570ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 79670ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 79770ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 79870ceb4f5SYOSHIFUJI Hideaki } 79970ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 80070ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 80170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 80270ceb4f5SYOSHIFUJI Hideaki } 80370ceb4f5SYOSHIFUJI Hideaki } 80470ceb4f5SYOSHIFUJI Hideaki 80570ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 80670ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 8073933fc95SJens Rosenboom return -EINVAL; 80870ceb4f5SYOSHIFUJI Hideaki 8094bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 81070ceb4f5SYOSHIFUJI Hideaki 81170ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 81270ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 81370ceb4f5SYOSHIFUJI Hideaki else { 81470ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 81570ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 81670ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 81770ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 81870ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 81970ceb4f5SYOSHIFUJI Hideaki } 82070ceb4f5SYOSHIFUJI Hideaki 821f104a567SDuan Jiong if (rinfo->prefix_len == 0) 822afb1d4b5SDavid Ahern rt = rt6_get_dflt_router(net, gwaddr, dev); 823f104a567SDuan Jiong else 824f104a567SDuan Jiong rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, 825830218c1SDavid Ahern gwaddr, dev); 82670ceb4f5SYOSHIFUJI Hideaki 82770ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 828afb1d4b5SDavid Ahern ip6_del_rt(net, rt); 82970ceb4f5SYOSHIFUJI Hideaki rt = NULL; 83070ceb4f5SYOSHIFUJI Hideaki } 83170ceb4f5SYOSHIFUJI Hideaki 83270ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 833830218c1SDavid Ahern rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, 834830218c1SDavid Ahern dev, pref); 83570ceb4f5SYOSHIFUJI Hideaki else if (rt) 83693c2fb25SDavid Ahern rt->fib6_flags = RTF_ROUTEINFO | 83793c2fb25SDavid Ahern (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 83870ceb4f5SYOSHIFUJI Hideaki 83970ceb4f5SYOSHIFUJI Hideaki if (rt) { 8401716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 84114895687SDavid Ahern fib6_clean_expires(rt); 8421716a961SGao feng else 84314895687SDavid Ahern fib6_set_expires(rt, jiffies + HZ * lifetime); 8441716a961SGao feng 84593531c67SDavid Ahern fib6_info_release(rt); 84670ceb4f5SYOSHIFUJI Hideaki } 84770ceb4f5SYOSHIFUJI Hideaki return 0; 84870ceb4f5SYOSHIFUJI Hideaki } 84970ceb4f5SYOSHIFUJI Hideaki #endif 85070ceb4f5SYOSHIFUJI Hideaki 851ae90d867SDavid Ahern /* 852ae90d867SDavid Ahern * Misc support functions 853ae90d867SDavid Ahern */ 854ae90d867SDavid Ahern 855ae90d867SDavid Ahern /* called with rcu_lock held */ 8568d1c802bSDavid Ahern static struct net_device *ip6_rt_get_dev_rcu(struct fib6_info *rt) 857ae90d867SDavid Ahern { 8585e670d84SDavid Ahern struct net_device *dev = rt->fib6_nh.nh_dev; 859ae90d867SDavid Ahern 86093c2fb25SDavid Ahern if (rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) { 861ae90d867SDavid Ahern /* for copies of local routes, dst->dev needs to be the 862ae90d867SDavid Ahern * device if it is a master device, the master device if 863ae90d867SDavid Ahern * device is enslaved, and the loopback as the default 864ae90d867SDavid Ahern */ 865ae90d867SDavid Ahern if (netif_is_l3_slave(dev) && 86693c2fb25SDavid Ahern !rt6_need_strict(&rt->fib6_dst.addr)) 867ae90d867SDavid Ahern dev = l3mdev_master_dev_rcu(dev); 868ae90d867SDavid Ahern else if (!netif_is_l3_master(dev)) 869ae90d867SDavid Ahern dev = dev_net(dev)->loopback_dev; 870ae90d867SDavid Ahern /* last case is netif_is_l3_master(dev) is true in which 871ae90d867SDavid Ahern * case we want dev returned to be dev 872ae90d867SDavid Ahern */ 873ae90d867SDavid Ahern } 874ae90d867SDavid Ahern 875ae90d867SDavid Ahern return dev; 876ae90d867SDavid Ahern } 877ae90d867SDavid Ahern 8786edb3c96SDavid Ahern static const int fib6_prop[RTN_MAX + 1] = { 8796edb3c96SDavid Ahern [RTN_UNSPEC] = 0, 8806edb3c96SDavid Ahern [RTN_UNICAST] = 0, 8816edb3c96SDavid Ahern [RTN_LOCAL] = 0, 8826edb3c96SDavid Ahern [RTN_BROADCAST] = 0, 8836edb3c96SDavid Ahern [RTN_ANYCAST] = 0, 8846edb3c96SDavid Ahern [RTN_MULTICAST] = 0, 8856edb3c96SDavid Ahern [RTN_BLACKHOLE] = -EINVAL, 8866edb3c96SDavid Ahern [RTN_UNREACHABLE] = -EHOSTUNREACH, 8876edb3c96SDavid Ahern [RTN_PROHIBIT] = -EACCES, 8886edb3c96SDavid Ahern [RTN_THROW] = -EAGAIN, 8896edb3c96SDavid Ahern [RTN_NAT] = -EINVAL, 8906edb3c96SDavid Ahern [RTN_XRESOLVE] = -EINVAL, 8916edb3c96SDavid Ahern }; 8926edb3c96SDavid Ahern 8936edb3c96SDavid Ahern static int ip6_rt_type_to_error(u8 fib6_type) 8946edb3c96SDavid Ahern { 8956edb3c96SDavid Ahern return fib6_prop[fib6_type]; 8966edb3c96SDavid Ahern } 8976edb3c96SDavid Ahern 8988d1c802bSDavid Ahern static unsigned short fib6_info_dst_flags(struct fib6_info *rt) 8993b6761d1SDavid Ahern { 9003b6761d1SDavid Ahern unsigned short flags = 0; 9013b6761d1SDavid Ahern 9023b6761d1SDavid Ahern if (rt->dst_nocount) 9033b6761d1SDavid Ahern flags |= DST_NOCOUNT; 9043b6761d1SDavid Ahern if (rt->dst_nopolicy) 9053b6761d1SDavid Ahern flags |= DST_NOPOLICY; 9063b6761d1SDavid Ahern if (rt->dst_host) 9073b6761d1SDavid Ahern flags |= DST_HOST; 9083b6761d1SDavid Ahern 9093b6761d1SDavid Ahern return flags; 9103b6761d1SDavid Ahern } 9113b6761d1SDavid Ahern 9128d1c802bSDavid Ahern static void ip6_rt_init_dst_reject(struct rt6_info *rt, struct fib6_info *ort) 9136edb3c96SDavid Ahern { 9146edb3c96SDavid Ahern rt->dst.error = ip6_rt_type_to_error(ort->fib6_type); 9156edb3c96SDavid Ahern 9166edb3c96SDavid Ahern switch (ort->fib6_type) { 9176edb3c96SDavid Ahern case RTN_BLACKHOLE: 9186edb3c96SDavid Ahern rt->dst.output = dst_discard_out; 9196edb3c96SDavid Ahern rt->dst.input = dst_discard; 9206edb3c96SDavid Ahern break; 9216edb3c96SDavid Ahern case RTN_PROHIBIT: 9226edb3c96SDavid Ahern rt->dst.output = ip6_pkt_prohibit_out; 9236edb3c96SDavid Ahern rt->dst.input = ip6_pkt_prohibit; 9246edb3c96SDavid Ahern break; 9256edb3c96SDavid Ahern case RTN_THROW: 9266edb3c96SDavid Ahern case RTN_UNREACHABLE: 9276edb3c96SDavid Ahern default: 9286edb3c96SDavid Ahern rt->dst.output = ip6_pkt_discard_out; 9296edb3c96SDavid Ahern rt->dst.input = ip6_pkt_discard; 9306edb3c96SDavid Ahern break; 9316edb3c96SDavid Ahern } 9326edb3c96SDavid Ahern } 9336edb3c96SDavid Ahern 9348d1c802bSDavid Ahern static void ip6_rt_init_dst(struct rt6_info *rt, struct fib6_info *ort) 9356edb3c96SDavid Ahern { 9363b6761d1SDavid Ahern rt->dst.flags |= fib6_info_dst_flags(ort); 9373b6761d1SDavid Ahern 93893c2fb25SDavid Ahern if (ort->fib6_flags & RTF_REJECT) { 9396edb3c96SDavid Ahern ip6_rt_init_dst_reject(rt, ort); 9406edb3c96SDavid Ahern return; 9416edb3c96SDavid Ahern } 9426edb3c96SDavid Ahern 9436edb3c96SDavid Ahern rt->dst.error = 0; 9446edb3c96SDavid Ahern rt->dst.output = ip6_output; 9456edb3c96SDavid Ahern 9466edb3c96SDavid Ahern if (ort->fib6_type == RTN_LOCAL) { 9476edb3c96SDavid Ahern rt->dst.input = ip6_input; 94893c2fb25SDavid Ahern } else if (ipv6_addr_type(&ort->fib6_dst.addr) & IPV6_ADDR_MULTICAST) { 9496edb3c96SDavid Ahern rt->dst.input = ip6_mc_input; 9506edb3c96SDavid Ahern } else { 9516edb3c96SDavid Ahern rt->dst.input = ip6_forward; 9526edb3c96SDavid Ahern } 9536edb3c96SDavid Ahern 9546edb3c96SDavid Ahern if (ort->fib6_nh.nh_lwtstate) { 9556edb3c96SDavid Ahern rt->dst.lwtstate = lwtstate_get(ort->fib6_nh.nh_lwtstate); 9566edb3c96SDavid Ahern lwtunnel_set_redirect(&rt->dst); 9576edb3c96SDavid Ahern } 9586edb3c96SDavid Ahern 9596edb3c96SDavid Ahern rt->dst.lastuse = jiffies; 9606edb3c96SDavid Ahern } 9616edb3c96SDavid Ahern 9628d1c802bSDavid Ahern static void rt6_set_from(struct rt6_info *rt, struct fib6_info *from) 963ae90d867SDavid Ahern { 964ae90d867SDavid Ahern rt->rt6i_flags &= ~RTF_EXPIRES; 96593531c67SDavid Ahern fib6_info_hold(from); 966ae90d867SDavid Ahern rt->from = from; 967d4ead6b3SDavid Ahern dst_init_metrics(&rt->dst, from->fib6_metrics->metrics, true); 968d4ead6b3SDavid Ahern if (from->fib6_metrics != &dst_default_metrics) { 969d4ead6b3SDavid Ahern rt->dst._metrics |= DST_METRICS_REFCOUNTED; 970d4ead6b3SDavid Ahern refcount_inc(&from->fib6_metrics->refcnt); 971d4ead6b3SDavid Ahern } 972ae90d867SDavid Ahern } 973ae90d867SDavid Ahern 9748d1c802bSDavid Ahern static void ip6_rt_copy_init(struct rt6_info *rt, struct fib6_info *ort) 975ae90d867SDavid Ahern { 976dcd1f572SDavid Ahern struct net_device *dev = fib6_info_nh_dev(ort); 977dcd1f572SDavid Ahern 9786edb3c96SDavid Ahern ip6_rt_init_dst(rt, ort); 9796edb3c96SDavid Ahern 98093c2fb25SDavid Ahern rt->rt6i_dst = ort->fib6_dst; 981dcd1f572SDavid Ahern rt->rt6i_idev = dev ? in6_dev_get(dev) : NULL; 9825e670d84SDavid Ahern rt->rt6i_gateway = ort->fib6_nh.nh_gw; 98393c2fb25SDavid Ahern rt->rt6i_flags = ort->fib6_flags; 984ae90d867SDavid Ahern rt6_set_from(rt, ort); 985ae90d867SDavid Ahern #ifdef CONFIG_IPV6_SUBTREES 98693c2fb25SDavid Ahern rt->rt6i_src = ort->fib6_src; 987ae90d867SDavid Ahern #endif 98893c2fb25SDavid Ahern rt->rt6i_prefsrc = ort->fib6_prefsrc; 9895e670d84SDavid Ahern rt->dst.lwtstate = lwtstate_get(ort->fib6_nh.nh_lwtstate); 990ae90d867SDavid Ahern } 991ae90d867SDavid Ahern 992a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn, 993a3c00e46SMartin KaFai Lau struct in6_addr *saddr) 994a3c00e46SMartin KaFai Lau { 99566f5d6ceSWei Wang struct fib6_node *pn, *sn; 996a3c00e46SMartin KaFai Lau while (1) { 997a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_TL_ROOT) 998a3c00e46SMartin KaFai Lau return NULL; 99966f5d6ceSWei Wang pn = rcu_dereference(fn->parent); 100066f5d6ceSWei Wang sn = FIB6_SUBTREE(pn); 100166f5d6ceSWei Wang if (sn && sn != fn) 100266f5d6ceSWei Wang fn = fib6_lookup(sn, NULL, saddr); 1003a3c00e46SMartin KaFai Lau else 1004a3c00e46SMartin KaFai Lau fn = pn; 1005a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_RTINFO) 1006a3c00e46SMartin KaFai Lau return fn; 1007a3c00e46SMartin KaFai Lau } 1008a3c00e46SMartin KaFai Lau } 1009c71099acSThomas Graf 1010d3843fe5SWei Wang static bool ip6_hold_safe(struct net *net, struct rt6_info **prt, 1011d3843fe5SWei Wang bool null_fallback) 1012d3843fe5SWei Wang { 1013d3843fe5SWei Wang struct rt6_info *rt = *prt; 1014d3843fe5SWei Wang 1015d3843fe5SWei Wang if (dst_hold_safe(&rt->dst)) 1016d3843fe5SWei Wang return true; 1017d3843fe5SWei Wang if (null_fallback) { 1018d3843fe5SWei Wang rt = net->ipv6.ip6_null_entry; 1019d3843fe5SWei Wang dst_hold(&rt->dst); 1020d3843fe5SWei Wang } else { 1021d3843fe5SWei Wang rt = NULL; 1022d3843fe5SWei Wang } 1023d3843fe5SWei Wang *prt = rt; 1024d3843fe5SWei Wang return false; 1025d3843fe5SWei Wang } 1026d3843fe5SWei Wang 1027dec9b0e2SDavid Ahern /* called with rcu_lock held */ 10288d1c802bSDavid Ahern static struct rt6_info *ip6_create_rt_rcu(struct fib6_info *rt) 1029dec9b0e2SDavid Ahern { 10303b6761d1SDavid Ahern unsigned short flags = fib6_info_dst_flags(rt); 1031dec9b0e2SDavid Ahern struct net_device *dev = rt->fib6_nh.nh_dev; 1032dec9b0e2SDavid Ahern struct rt6_info *nrt; 1033dec9b0e2SDavid Ahern 103493531c67SDavid Ahern nrt = ip6_dst_alloc(dev_net(dev), dev, flags); 1035dec9b0e2SDavid Ahern if (nrt) 1036dec9b0e2SDavid Ahern ip6_rt_copy_init(nrt, rt); 1037dec9b0e2SDavid Ahern 1038dec9b0e2SDavid Ahern return nrt; 1039dec9b0e2SDavid Ahern } 1040dec9b0e2SDavid Ahern 10418ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 10428ed67789SDaniel Lezcano struct fib6_table *table, 1043b75cc8f9SDavid Ahern struct flowi6 *fl6, 1044b75cc8f9SDavid Ahern const struct sk_buff *skb, 1045b75cc8f9SDavid Ahern int flags) 10461da177e4SLinus Torvalds { 10478d1c802bSDavid Ahern struct fib6_info *f6i; 10481da177e4SLinus Torvalds struct fib6_node *fn; 104923fb93a4SDavid Ahern struct rt6_info *rt; 10501da177e4SLinus Torvalds 1051b6cdbc85SDavid Ahern if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) 1052b6cdbc85SDavid Ahern flags &= ~RT6_LOOKUP_F_IFACE; 1053b6cdbc85SDavid Ahern 105466f5d6ceSWei Wang rcu_read_lock(); 10554c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1056c71099acSThomas Graf restart: 105723fb93a4SDavid Ahern f6i = rcu_dereference(fn->leaf); 105823fb93a4SDavid Ahern if (!f6i) { 105923fb93a4SDavid Ahern f6i = net->ipv6.fib6_null_entry; 106066f5d6ceSWei Wang } else { 106123fb93a4SDavid Ahern f6i = rt6_device_match(net, f6i, &fl6->saddr, 106266f5d6ceSWei Wang fl6->flowi6_oif, flags); 106393c2fb25SDavid Ahern if (f6i->fib6_nsiblings && fl6->flowi6_oif == 0) 106423fb93a4SDavid Ahern f6i = rt6_multipath_select(net, f6i, fl6, 106523fb93a4SDavid Ahern fl6->flowi6_oif, skb, flags); 106666f5d6ceSWei Wang } 106723fb93a4SDavid Ahern if (f6i == net->ipv6.fib6_null_entry) { 1068a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1069a3c00e46SMartin KaFai Lau if (fn) 1070a3c00e46SMartin KaFai Lau goto restart; 1071a3c00e46SMartin KaFai Lau } 107223fb93a4SDavid Ahern 10732b760fcfSWei Wang /* Search through exception table */ 107423fb93a4SDavid Ahern rt = rt6_find_cached_rt(f6i, &fl6->daddr, &fl6->saddr); 107523fb93a4SDavid Ahern if (rt) { 1076d3843fe5SWei Wang if (ip6_hold_safe(net, &rt, true)) 1077d3843fe5SWei Wang dst_use_noref(&rt->dst, jiffies); 107823fb93a4SDavid Ahern } else if (f6i == net->ipv6.fib6_null_entry) { 1079dec9b0e2SDavid Ahern rt = net->ipv6.ip6_null_entry; 1080dec9b0e2SDavid Ahern dst_hold(&rt->dst); 108123fb93a4SDavid Ahern } else { 108223fb93a4SDavid Ahern rt = ip6_create_rt_rcu(f6i); 108323fb93a4SDavid Ahern if (!rt) { 108423fb93a4SDavid Ahern rt = net->ipv6.ip6_null_entry; 108523fb93a4SDavid Ahern dst_hold(&rt->dst); 108623fb93a4SDavid Ahern } 1087dec9b0e2SDavid Ahern } 1088d3843fe5SWei Wang 108966f5d6ceSWei Wang rcu_read_unlock(); 1090b811580dSDavid Ahern 1091b65f164dSPaolo Abeni trace_fib6_table_lookup(net, rt, table, fl6); 1092b811580dSDavid Ahern 10931da177e4SLinus Torvalds return rt; 1094c71099acSThomas Graf } 1095c71099acSThomas Graf 1096ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, 1097b75cc8f9SDavid Ahern const struct sk_buff *skb, int flags) 1098ea6e574eSFlorian Westphal { 1099b75cc8f9SDavid Ahern return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_lookup); 1100ea6e574eSFlorian Westphal } 1101ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 1102ea6e574eSFlorian Westphal 11039acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 1104b75cc8f9SDavid Ahern const struct in6_addr *saddr, int oif, 1105b75cc8f9SDavid Ahern const struct sk_buff *skb, int strict) 1106c71099acSThomas Graf { 11074c9483b2SDavid S. Miller struct flowi6 fl6 = { 11084c9483b2SDavid S. Miller .flowi6_oif = oif, 11094c9483b2SDavid S. Miller .daddr = *daddr, 1110c71099acSThomas Graf }; 1111c71099acSThomas Graf struct dst_entry *dst; 111277d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 1113c71099acSThomas Graf 1114adaa70bbSThomas Graf if (saddr) { 11154c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 1116adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 1117adaa70bbSThomas Graf } 1118adaa70bbSThomas Graf 1119b75cc8f9SDavid Ahern dst = fib6_rule_lookup(net, &fl6, skb, flags, ip6_pol_route_lookup); 1120c71099acSThomas Graf if (dst->error == 0) 1121c71099acSThomas Graf return (struct rt6_info *) dst; 1122c71099acSThomas Graf 1123c71099acSThomas Graf dst_release(dst); 1124c71099acSThomas Graf 11251da177e4SLinus Torvalds return NULL; 11261da177e4SLinus Torvalds } 11277159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 11287159039aSYOSHIFUJI Hideaki 1129c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 11301cfb71eeSWei Wang * It takes new route entry, the addition fails by any reason the 11311cfb71eeSWei Wang * route is released. 11321cfb71eeSWei Wang * Caller must hold dst before calling it. 11331da177e4SLinus Torvalds */ 11341da177e4SLinus Torvalds 11358d1c802bSDavid Ahern static int __ip6_ins_rt(struct fib6_info *rt, struct nl_info *info, 1136333c4301SDavid Ahern struct netlink_ext_ack *extack) 11371da177e4SLinus Torvalds { 11381da177e4SLinus Torvalds int err; 1139c71099acSThomas Graf struct fib6_table *table; 11401da177e4SLinus Torvalds 114193c2fb25SDavid Ahern table = rt->fib6_table; 114266f5d6ceSWei Wang spin_lock_bh(&table->tb6_lock); 1143d4ead6b3SDavid Ahern err = fib6_add(&table->tb6_root, rt, info, extack); 114466f5d6ceSWei Wang spin_unlock_bh(&table->tb6_lock); 11451da177e4SLinus Torvalds 11461da177e4SLinus Torvalds return err; 11471da177e4SLinus Torvalds } 11481da177e4SLinus Torvalds 11498d1c802bSDavid Ahern int ip6_ins_rt(struct net *net, struct fib6_info *rt) 115040e22e8fSThomas Graf { 1151afb1d4b5SDavid Ahern struct nl_info info = { .nl_net = net, }; 1152e715b6d3SFlorian Westphal 1153d4ead6b3SDavid Ahern return __ip6_ins_rt(rt, &info, NULL); 115440e22e8fSThomas Graf } 115540e22e8fSThomas Graf 11568d1c802bSDavid Ahern static struct rt6_info *ip6_rt_cache_alloc(struct fib6_info *ort, 115721efcfa0SEric Dumazet const struct in6_addr *daddr, 1158b71d1d42SEric Dumazet const struct in6_addr *saddr) 11591da177e4SLinus Torvalds { 11604832c30dSDavid Ahern struct net_device *dev; 11611da177e4SLinus Torvalds struct rt6_info *rt; 11621da177e4SLinus Torvalds 11631da177e4SLinus Torvalds /* 11641da177e4SLinus Torvalds * Clone the route. 11651da177e4SLinus Torvalds */ 11661da177e4SLinus Torvalds 11674832c30dSDavid Ahern dev = ip6_rt_get_dev_rcu(ort); 116893531c67SDavid Ahern rt = ip6_dst_alloc(dev_net(dev), dev, 0); 116983a09abdSMartin KaFai Lau if (!rt) 117083a09abdSMartin KaFai Lau return NULL; 117183a09abdSMartin KaFai Lau 117283a09abdSMartin KaFai Lau ip6_rt_copy_init(rt, ort); 11738b9df265SMartin KaFai Lau rt->rt6i_flags |= RTF_CACHE; 117483a09abdSMartin KaFai Lau rt->dst.flags |= DST_HOST; 117583a09abdSMartin KaFai Lau rt->rt6i_dst.addr = *daddr; 117683a09abdSMartin KaFai Lau rt->rt6i_dst.plen = 128; 11778b9df265SMartin KaFai Lau 11788b9df265SMartin KaFai Lau if (!rt6_is_gw_or_nonexthop(ort)) { 117993c2fb25SDavid Ahern if (ort->fib6_dst.plen != 128 && 118093c2fb25SDavid Ahern ipv6_addr_equal(&ort->fib6_dst.addr, daddr)) 118158c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 11821da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 11831da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 11844e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 11851da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 11861da177e4SLinus Torvalds } 11871da177e4SLinus Torvalds #endif 118895a9a5baSYOSHIFUJI Hideaki } 118995a9a5baSYOSHIFUJI Hideaki 1190299d9939SYOSHIFUJI Hideaki return rt; 1191299d9939SYOSHIFUJI Hideaki } 1192299d9939SYOSHIFUJI Hideaki 11938d1c802bSDavid Ahern static struct rt6_info *ip6_rt_pcpu_alloc(struct fib6_info *rt) 1194d52d3997SMartin KaFai Lau { 11953b6761d1SDavid Ahern unsigned short flags = fib6_info_dst_flags(rt); 11964832c30dSDavid Ahern struct net_device *dev; 1197d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 1198d52d3997SMartin KaFai Lau 11994832c30dSDavid Ahern rcu_read_lock(); 12004832c30dSDavid Ahern dev = ip6_rt_get_dev_rcu(rt); 120193531c67SDavid Ahern pcpu_rt = ip6_dst_alloc(dev_net(dev), dev, flags); 12024832c30dSDavid Ahern rcu_read_unlock(); 1203d52d3997SMartin KaFai Lau if (!pcpu_rt) 1204d52d3997SMartin KaFai Lau return NULL; 1205d52d3997SMartin KaFai Lau ip6_rt_copy_init(pcpu_rt, rt); 1206d52d3997SMartin KaFai Lau pcpu_rt->rt6i_flags |= RTF_PCPU; 1207d52d3997SMartin KaFai Lau return pcpu_rt; 1208d52d3997SMartin KaFai Lau } 1209d52d3997SMartin KaFai Lau 121066f5d6ceSWei Wang /* It should be called with rcu_read_lock() acquired */ 12118d1c802bSDavid Ahern static struct rt6_info *rt6_get_pcpu_route(struct fib6_info *rt) 1212d52d3997SMartin KaFai Lau { 1213a73e4195SMartin KaFai Lau struct rt6_info *pcpu_rt, **p; 1214d52d3997SMartin KaFai Lau 1215d52d3997SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 1216d52d3997SMartin KaFai Lau pcpu_rt = *p; 1217d52d3997SMartin KaFai Lau 1218d4ead6b3SDavid Ahern if (pcpu_rt) 1219d4ead6b3SDavid Ahern ip6_hold_safe(NULL, &pcpu_rt, false); 1220d3843fe5SWei Wang 1221a73e4195SMartin KaFai Lau return pcpu_rt; 1222a73e4195SMartin KaFai Lau } 1223a73e4195SMartin KaFai Lau 1224afb1d4b5SDavid Ahern static struct rt6_info *rt6_make_pcpu_route(struct net *net, 12258d1c802bSDavid Ahern struct fib6_info *rt) 1226a73e4195SMartin KaFai Lau { 1227a73e4195SMartin KaFai Lau struct rt6_info *pcpu_rt, *prev, **p; 1228d52d3997SMartin KaFai Lau 1229d52d3997SMartin KaFai Lau pcpu_rt = ip6_rt_pcpu_alloc(rt); 1230d52d3997SMartin KaFai Lau if (!pcpu_rt) { 12319c7370a1SMartin KaFai Lau dst_hold(&net->ipv6.ip6_null_entry->dst); 12329c7370a1SMartin KaFai Lau return net->ipv6.ip6_null_entry; 1233d52d3997SMartin KaFai Lau } 1234d52d3997SMartin KaFai Lau 1235a94b9367SWei Wang dst_hold(&pcpu_rt->dst); 1236a73e4195SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 1237d52d3997SMartin KaFai Lau prev = cmpxchg(p, NULL, pcpu_rt); 1238951f788aSEric Dumazet BUG_ON(prev); 1239a94b9367SWei Wang 1240d52d3997SMartin KaFai Lau return pcpu_rt; 1241d52d3997SMartin KaFai Lau } 1242d52d3997SMartin KaFai Lau 124335732d01SWei Wang /* exception hash table implementation 124435732d01SWei Wang */ 124535732d01SWei Wang static DEFINE_SPINLOCK(rt6_exception_lock); 124635732d01SWei Wang 124735732d01SWei Wang /* Remove rt6_ex from hash table and free the memory 124835732d01SWei Wang * Caller must hold rt6_exception_lock 124935732d01SWei Wang */ 125035732d01SWei Wang static void rt6_remove_exception(struct rt6_exception_bucket *bucket, 125135732d01SWei Wang struct rt6_exception *rt6_ex) 125235732d01SWei Wang { 1253b2427e67SColin Ian King struct net *net; 125481eb8447SWei Wang 125535732d01SWei Wang if (!bucket || !rt6_ex) 125635732d01SWei Wang return; 1257b2427e67SColin Ian King 1258b2427e67SColin Ian King net = dev_net(rt6_ex->rt6i->dst.dev); 125935732d01SWei Wang hlist_del_rcu(&rt6_ex->hlist); 126077634cc6SDavid Ahern dst_release(&rt6_ex->rt6i->dst); 126135732d01SWei Wang kfree_rcu(rt6_ex, rcu); 126235732d01SWei Wang WARN_ON_ONCE(!bucket->depth); 126335732d01SWei Wang bucket->depth--; 126481eb8447SWei Wang net->ipv6.rt6_stats->fib_rt_cache--; 126535732d01SWei Wang } 126635732d01SWei Wang 126735732d01SWei Wang /* Remove oldest rt6_ex in bucket and free the memory 126835732d01SWei Wang * Caller must hold rt6_exception_lock 126935732d01SWei Wang */ 127035732d01SWei Wang static void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket) 127135732d01SWei Wang { 127235732d01SWei Wang struct rt6_exception *rt6_ex, *oldest = NULL; 127335732d01SWei Wang 127435732d01SWei Wang if (!bucket) 127535732d01SWei Wang return; 127635732d01SWei Wang 127735732d01SWei Wang hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { 127835732d01SWei Wang if (!oldest || time_before(rt6_ex->stamp, oldest->stamp)) 127935732d01SWei Wang oldest = rt6_ex; 128035732d01SWei Wang } 128135732d01SWei Wang rt6_remove_exception(bucket, oldest); 128235732d01SWei Wang } 128335732d01SWei Wang 128435732d01SWei Wang static u32 rt6_exception_hash(const struct in6_addr *dst, 128535732d01SWei Wang const struct in6_addr *src) 128635732d01SWei Wang { 128735732d01SWei Wang static u32 seed __read_mostly; 128835732d01SWei Wang u32 val; 128935732d01SWei Wang 129035732d01SWei Wang net_get_random_once(&seed, sizeof(seed)); 129135732d01SWei Wang val = jhash(dst, sizeof(*dst), seed); 129235732d01SWei Wang 129335732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 129435732d01SWei Wang if (src) 129535732d01SWei Wang val = jhash(src, sizeof(*src), val); 129635732d01SWei Wang #endif 129735732d01SWei Wang return hash_32(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT); 129835732d01SWei Wang } 129935732d01SWei Wang 130035732d01SWei Wang /* Helper function to find the cached rt in the hash table 130135732d01SWei Wang * and update bucket pointer to point to the bucket for this 130235732d01SWei Wang * (daddr, saddr) pair 130335732d01SWei Wang * Caller must hold rt6_exception_lock 130435732d01SWei Wang */ 130535732d01SWei Wang static struct rt6_exception * 130635732d01SWei Wang __rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket, 130735732d01SWei Wang const struct in6_addr *daddr, 130835732d01SWei Wang const struct in6_addr *saddr) 130935732d01SWei Wang { 131035732d01SWei Wang struct rt6_exception *rt6_ex; 131135732d01SWei Wang u32 hval; 131235732d01SWei Wang 131335732d01SWei Wang if (!(*bucket) || !daddr) 131435732d01SWei Wang return NULL; 131535732d01SWei Wang 131635732d01SWei Wang hval = rt6_exception_hash(daddr, saddr); 131735732d01SWei Wang *bucket += hval; 131835732d01SWei Wang 131935732d01SWei Wang hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) { 132035732d01SWei Wang struct rt6_info *rt6 = rt6_ex->rt6i; 132135732d01SWei Wang bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr); 132235732d01SWei Wang 132335732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 132435732d01SWei Wang if (matched && saddr) 132535732d01SWei Wang matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr); 132635732d01SWei Wang #endif 132735732d01SWei Wang if (matched) 132835732d01SWei Wang return rt6_ex; 132935732d01SWei Wang } 133035732d01SWei Wang return NULL; 133135732d01SWei Wang } 133235732d01SWei Wang 133335732d01SWei Wang /* Helper function to find the cached rt in the hash table 133435732d01SWei Wang * and update bucket pointer to point to the bucket for this 133535732d01SWei Wang * (daddr, saddr) pair 133635732d01SWei Wang * Caller must hold rcu_read_lock() 133735732d01SWei Wang */ 133835732d01SWei Wang static struct rt6_exception * 133935732d01SWei Wang __rt6_find_exception_rcu(struct rt6_exception_bucket **bucket, 134035732d01SWei Wang const struct in6_addr *daddr, 134135732d01SWei Wang const struct in6_addr *saddr) 134235732d01SWei Wang { 134335732d01SWei Wang struct rt6_exception *rt6_ex; 134435732d01SWei Wang u32 hval; 134535732d01SWei Wang 134635732d01SWei Wang WARN_ON_ONCE(!rcu_read_lock_held()); 134735732d01SWei Wang 134835732d01SWei Wang if (!(*bucket) || !daddr) 134935732d01SWei Wang return NULL; 135035732d01SWei Wang 135135732d01SWei Wang hval = rt6_exception_hash(daddr, saddr); 135235732d01SWei Wang *bucket += hval; 135335732d01SWei Wang 135435732d01SWei Wang hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) { 135535732d01SWei Wang struct rt6_info *rt6 = rt6_ex->rt6i; 135635732d01SWei Wang bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr); 135735732d01SWei Wang 135835732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 135935732d01SWei Wang if (matched && saddr) 136035732d01SWei Wang matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr); 136135732d01SWei Wang #endif 136235732d01SWei Wang if (matched) 136335732d01SWei Wang return rt6_ex; 136435732d01SWei Wang } 136535732d01SWei Wang return NULL; 136635732d01SWei Wang } 136735732d01SWei Wang 13688d1c802bSDavid Ahern static unsigned int fib6_mtu(const struct fib6_info *rt) 1369d4ead6b3SDavid Ahern { 1370d4ead6b3SDavid Ahern unsigned int mtu; 1371d4ead6b3SDavid Ahern 1372dcd1f572SDavid Ahern if (rt->fib6_pmtu) { 1373dcd1f572SDavid Ahern mtu = rt->fib6_pmtu; 1374dcd1f572SDavid Ahern } else { 1375dcd1f572SDavid Ahern struct net_device *dev = fib6_info_nh_dev(rt); 1376dcd1f572SDavid Ahern struct inet6_dev *idev; 1377dcd1f572SDavid Ahern 1378dcd1f572SDavid Ahern rcu_read_lock(); 1379dcd1f572SDavid Ahern idev = __in6_dev_get(dev); 1380dcd1f572SDavid Ahern mtu = idev->cnf.mtu6; 1381dcd1f572SDavid Ahern rcu_read_unlock(); 1382dcd1f572SDavid Ahern } 1383dcd1f572SDavid Ahern 1384d4ead6b3SDavid Ahern mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); 1385d4ead6b3SDavid Ahern 1386d4ead6b3SDavid Ahern return mtu - lwtunnel_headroom(rt->fib6_nh.nh_lwtstate, mtu); 1387d4ead6b3SDavid Ahern } 1388d4ead6b3SDavid Ahern 138935732d01SWei Wang static int rt6_insert_exception(struct rt6_info *nrt, 13908d1c802bSDavid Ahern struct fib6_info *ort) 139135732d01SWei Wang { 13925e670d84SDavid Ahern struct net *net = dev_net(nrt->dst.dev); 139335732d01SWei Wang struct rt6_exception_bucket *bucket; 139435732d01SWei Wang struct in6_addr *src_key = NULL; 139535732d01SWei Wang struct rt6_exception *rt6_ex; 139635732d01SWei Wang int err = 0; 139735732d01SWei Wang 139835732d01SWei Wang spin_lock_bh(&rt6_exception_lock); 139935732d01SWei Wang 140035732d01SWei Wang if (ort->exception_bucket_flushed) { 140135732d01SWei Wang err = -EINVAL; 140235732d01SWei Wang goto out; 140335732d01SWei Wang } 140435732d01SWei Wang 140535732d01SWei Wang bucket = rcu_dereference_protected(ort->rt6i_exception_bucket, 140635732d01SWei Wang lockdep_is_held(&rt6_exception_lock)); 140735732d01SWei Wang if (!bucket) { 140835732d01SWei Wang bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket), 140935732d01SWei Wang GFP_ATOMIC); 141035732d01SWei Wang if (!bucket) { 141135732d01SWei Wang err = -ENOMEM; 141235732d01SWei Wang goto out; 141335732d01SWei Wang } 141435732d01SWei Wang rcu_assign_pointer(ort->rt6i_exception_bucket, bucket); 141535732d01SWei Wang } 141635732d01SWei Wang 141735732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 141835732d01SWei Wang /* rt6i_src.plen != 0 indicates ort is in subtree 141935732d01SWei Wang * and exception table is indexed by a hash of 142035732d01SWei Wang * both rt6i_dst and rt6i_src. 142135732d01SWei Wang * Otherwise, the exception table is indexed by 142235732d01SWei Wang * a hash of only rt6i_dst. 142335732d01SWei Wang */ 142493c2fb25SDavid Ahern if (ort->fib6_src.plen) 142535732d01SWei Wang src_key = &nrt->rt6i_src.addr; 142635732d01SWei Wang #endif 142760006a48SWei Wang 142860006a48SWei Wang /* Update rt6i_prefsrc as it could be changed 142960006a48SWei Wang * in rt6_remove_prefsrc() 143060006a48SWei Wang */ 143193c2fb25SDavid Ahern nrt->rt6i_prefsrc = ort->fib6_prefsrc; 1432f5bbe7eeSWei Wang /* rt6_mtu_change() might lower mtu on ort. 1433f5bbe7eeSWei Wang * Only insert this exception route if its mtu 1434f5bbe7eeSWei Wang * is less than ort's mtu value. 1435f5bbe7eeSWei Wang */ 1436d4ead6b3SDavid Ahern if (dst_metric_raw(&nrt->dst, RTAX_MTU) >= fib6_mtu(ort)) { 1437f5bbe7eeSWei Wang err = -EINVAL; 1438f5bbe7eeSWei Wang goto out; 1439f5bbe7eeSWei Wang } 144060006a48SWei Wang 144135732d01SWei Wang rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr, 144235732d01SWei Wang src_key); 144335732d01SWei Wang if (rt6_ex) 144435732d01SWei Wang rt6_remove_exception(bucket, rt6_ex); 144535732d01SWei Wang 144635732d01SWei Wang rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC); 144735732d01SWei Wang if (!rt6_ex) { 144835732d01SWei Wang err = -ENOMEM; 144935732d01SWei Wang goto out; 145035732d01SWei Wang } 145135732d01SWei Wang rt6_ex->rt6i = nrt; 145235732d01SWei Wang rt6_ex->stamp = jiffies; 145335732d01SWei Wang hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain); 145435732d01SWei Wang bucket->depth++; 145581eb8447SWei Wang net->ipv6.rt6_stats->fib_rt_cache++; 145635732d01SWei Wang 145735732d01SWei Wang if (bucket->depth > FIB6_MAX_DEPTH) 145835732d01SWei Wang rt6_exception_remove_oldest(bucket); 145935732d01SWei Wang 146035732d01SWei Wang out: 146135732d01SWei Wang spin_unlock_bh(&rt6_exception_lock); 146235732d01SWei Wang 146335732d01SWei Wang /* Update fn->fn_sernum to invalidate all cached dst */ 1464b886d5f2SPaolo Abeni if (!err) { 146593c2fb25SDavid Ahern spin_lock_bh(&ort->fib6_table->tb6_lock); 14667aef6859SDavid Ahern fib6_update_sernum(net, ort); 146793c2fb25SDavid Ahern spin_unlock_bh(&ort->fib6_table->tb6_lock); 1468b886d5f2SPaolo Abeni fib6_force_start_gc(net); 1469b886d5f2SPaolo Abeni } 147035732d01SWei Wang 147135732d01SWei Wang return err; 147235732d01SWei Wang } 147335732d01SWei Wang 14748d1c802bSDavid Ahern void rt6_flush_exceptions(struct fib6_info *rt) 147535732d01SWei Wang { 147635732d01SWei Wang struct rt6_exception_bucket *bucket; 147735732d01SWei Wang struct rt6_exception *rt6_ex; 147835732d01SWei Wang struct hlist_node *tmp; 147935732d01SWei Wang int i; 148035732d01SWei Wang 148135732d01SWei Wang spin_lock_bh(&rt6_exception_lock); 148235732d01SWei Wang /* Prevent rt6_insert_exception() to recreate the bucket list */ 148335732d01SWei Wang rt->exception_bucket_flushed = 1; 148435732d01SWei Wang 148535732d01SWei Wang bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, 148635732d01SWei Wang lockdep_is_held(&rt6_exception_lock)); 148735732d01SWei Wang if (!bucket) 148835732d01SWei Wang goto out; 148935732d01SWei Wang 149035732d01SWei Wang for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 149135732d01SWei Wang hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist) 149235732d01SWei Wang rt6_remove_exception(bucket, rt6_ex); 149335732d01SWei Wang WARN_ON_ONCE(bucket->depth); 149435732d01SWei Wang bucket++; 149535732d01SWei Wang } 149635732d01SWei Wang 149735732d01SWei Wang out: 149835732d01SWei Wang spin_unlock_bh(&rt6_exception_lock); 149935732d01SWei Wang } 150035732d01SWei Wang 150135732d01SWei Wang /* Find cached rt in the hash table inside passed in rt 150235732d01SWei Wang * Caller has to hold rcu_read_lock() 150335732d01SWei Wang */ 15048d1c802bSDavid Ahern static struct rt6_info *rt6_find_cached_rt(struct fib6_info *rt, 150535732d01SWei Wang struct in6_addr *daddr, 150635732d01SWei Wang struct in6_addr *saddr) 150735732d01SWei Wang { 150835732d01SWei Wang struct rt6_exception_bucket *bucket; 150935732d01SWei Wang struct in6_addr *src_key = NULL; 151035732d01SWei Wang struct rt6_exception *rt6_ex; 151135732d01SWei Wang struct rt6_info *res = NULL; 151235732d01SWei Wang 151335732d01SWei Wang bucket = rcu_dereference(rt->rt6i_exception_bucket); 151435732d01SWei Wang 151535732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 151635732d01SWei Wang /* rt6i_src.plen != 0 indicates rt is in subtree 151735732d01SWei Wang * and exception table is indexed by a hash of 151835732d01SWei Wang * both rt6i_dst and rt6i_src. 151935732d01SWei Wang * Otherwise, the exception table is indexed by 152035732d01SWei Wang * a hash of only rt6i_dst. 152135732d01SWei Wang */ 152293c2fb25SDavid Ahern if (rt->fib6_src.plen) 152335732d01SWei Wang src_key = saddr; 152435732d01SWei Wang #endif 152535732d01SWei Wang rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key); 152635732d01SWei Wang 152735732d01SWei Wang if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i)) 152835732d01SWei Wang res = rt6_ex->rt6i; 152935732d01SWei Wang 153035732d01SWei Wang return res; 153135732d01SWei Wang } 153235732d01SWei Wang 153335732d01SWei Wang /* Remove the passed in cached rt from the hash table that contains it */ 153423fb93a4SDavid Ahern static int rt6_remove_exception_rt(struct rt6_info *rt) 153535732d01SWei Wang { 153635732d01SWei Wang struct rt6_exception_bucket *bucket; 15378d1c802bSDavid Ahern struct fib6_info *from = rt->from; 153835732d01SWei Wang struct in6_addr *src_key = NULL; 153935732d01SWei Wang struct rt6_exception *rt6_ex; 154035732d01SWei Wang int err; 154135732d01SWei Wang 154235732d01SWei Wang if (!from || 1543442d713bSColin Ian King !(rt->rt6i_flags & RTF_CACHE)) 154435732d01SWei Wang return -EINVAL; 154535732d01SWei Wang 154635732d01SWei Wang if (!rcu_access_pointer(from->rt6i_exception_bucket)) 154735732d01SWei Wang return -ENOENT; 154835732d01SWei Wang 154935732d01SWei Wang spin_lock_bh(&rt6_exception_lock); 155035732d01SWei Wang bucket = rcu_dereference_protected(from->rt6i_exception_bucket, 155135732d01SWei Wang lockdep_is_held(&rt6_exception_lock)); 155235732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 155335732d01SWei Wang /* rt6i_src.plen != 0 indicates 'from' is in subtree 155435732d01SWei Wang * and exception table is indexed by a hash of 155535732d01SWei Wang * both rt6i_dst and rt6i_src. 155635732d01SWei Wang * Otherwise, the exception table is indexed by 155735732d01SWei Wang * a hash of only rt6i_dst. 155835732d01SWei Wang */ 155993c2fb25SDavid Ahern if (from->fib6_src.plen) 156035732d01SWei Wang src_key = &rt->rt6i_src.addr; 156135732d01SWei Wang #endif 156235732d01SWei Wang rt6_ex = __rt6_find_exception_spinlock(&bucket, 156335732d01SWei Wang &rt->rt6i_dst.addr, 156435732d01SWei Wang src_key); 156535732d01SWei Wang if (rt6_ex) { 156635732d01SWei Wang rt6_remove_exception(bucket, rt6_ex); 156735732d01SWei Wang err = 0; 156835732d01SWei Wang } else { 156935732d01SWei Wang err = -ENOENT; 157035732d01SWei Wang } 157135732d01SWei Wang 157235732d01SWei Wang spin_unlock_bh(&rt6_exception_lock); 157335732d01SWei Wang return err; 157435732d01SWei Wang } 157535732d01SWei Wang 157635732d01SWei Wang /* Find rt6_ex which contains the passed in rt cache and 157735732d01SWei Wang * refresh its stamp 157835732d01SWei Wang */ 157935732d01SWei Wang static void rt6_update_exception_stamp_rt(struct rt6_info *rt) 158035732d01SWei Wang { 158135732d01SWei Wang struct rt6_exception_bucket *bucket; 15828d1c802bSDavid Ahern struct fib6_info *from = rt->from; 158335732d01SWei Wang struct in6_addr *src_key = NULL; 158435732d01SWei Wang struct rt6_exception *rt6_ex; 158535732d01SWei Wang 158635732d01SWei Wang if (!from || 1587442d713bSColin Ian King !(rt->rt6i_flags & RTF_CACHE)) 158835732d01SWei Wang return; 158935732d01SWei Wang 159035732d01SWei Wang rcu_read_lock(); 159135732d01SWei Wang bucket = rcu_dereference(from->rt6i_exception_bucket); 159235732d01SWei Wang 159335732d01SWei Wang #ifdef CONFIG_IPV6_SUBTREES 159435732d01SWei Wang /* rt6i_src.plen != 0 indicates 'from' is in subtree 159535732d01SWei Wang * and exception table is indexed by a hash of 159635732d01SWei Wang * both rt6i_dst and rt6i_src. 159735732d01SWei Wang * Otherwise, the exception table is indexed by 159835732d01SWei Wang * a hash of only rt6i_dst. 159935732d01SWei Wang */ 160093c2fb25SDavid Ahern if (from->fib6_src.plen) 160135732d01SWei Wang src_key = &rt->rt6i_src.addr; 160235732d01SWei Wang #endif 160335732d01SWei Wang rt6_ex = __rt6_find_exception_rcu(&bucket, 160435732d01SWei Wang &rt->rt6i_dst.addr, 160535732d01SWei Wang src_key); 160635732d01SWei Wang if (rt6_ex) 160735732d01SWei Wang rt6_ex->stamp = jiffies; 160835732d01SWei Wang 160935732d01SWei Wang rcu_read_unlock(); 161035732d01SWei Wang } 161135732d01SWei Wang 16128d1c802bSDavid Ahern static void rt6_exceptions_remove_prefsrc(struct fib6_info *rt) 161360006a48SWei Wang { 161460006a48SWei Wang struct rt6_exception_bucket *bucket; 161560006a48SWei Wang struct rt6_exception *rt6_ex; 161660006a48SWei Wang int i; 161760006a48SWei Wang 161860006a48SWei Wang bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, 161960006a48SWei Wang lockdep_is_held(&rt6_exception_lock)); 162060006a48SWei Wang 162160006a48SWei Wang if (bucket) { 162260006a48SWei Wang for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 162360006a48SWei Wang hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { 162460006a48SWei Wang rt6_ex->rt6i->rt6i_prefsrc.plen = 0; 162560006a48SWei Wang } 162660006a48SWei Wang bucket++; 162760006a48SWei Wang } 162860006a48SWei Wang } 162960006a48SWei Wang } 163060006a48SWei Wang 1631e9fa1495SStefano Brivio static bool rt6_mtu_change_route_allowed(struct inet6_dev *idev, 1632e9fa1495SStefano Brivio struct rt6_info *rt, int mtu) 1633e9fa1495SStefano Brivio { 1634e9fa1495SStefano Brivio /* If the new MTU is lower than the route PMTU, this new MTU will be the 1635e9fa1495SStefano Brivio * lowest MTU in the path: always allow updating the route PMTU to 1636e9fa1495SStefano Brivio * reflect PMTU decreases. 1637e9fa1495SStefano Brivio * 1638e9fa1495SStefano Brivio * If the new MTU is higher, and the route PMTU is equal to the local 1639e9fa1495SStefano Brivio * MTU, this means the old MTU is the lowest in the path, so allow 1640e9fa1495SStefano Brivio * updating it: if other nodes now have lower MTUs, PMTU discovery will 1641e9fa1495SStefano Brivio * handle this. 1642e9fa1495SStefano Brivio */ 1643e9fa1495SStefano Brivio 1644e9fa1495SStefano Brivio if (dst_mtu(&rt->dst) >= mtu) 1645e9fa1495SStefano Brivio return true; 1646e9fa1495SStefano Brivio 1647e9fa1495SStefano Brivio if (dst_mtu(&rt->dst) == idev->cnf.mtu6) 1648e9fa1495SStefano Brivio return true; 1649e9fa1495SStefano Brivio 1650e9fa1495SStefano Brivio return false; 1651e9fa1495SStefano Brivio } 1652e9fa1495SStefano Brivio 1653e9fa1495SStefano Brivio static void rt6_exceptions_update_pmtu(struct inet6_dev *idev, 16548d1c802bSDavid Ahern struct fib6_info *rt, int mtu) 1655f5bbe7eeSWei Wang { 1656f5bbe7eeSWei Wang struct rt6_exception_bucket *bucket; 1657f5bbe7eeSWei Wang struct rt6_exception *rt6_ex; 1658f5bbe7eeSWei Wang int i; 1659f5bbe7eeSWei Wang 1660f5bbe7eeSWei Wang bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, 1661f5bbe7eeSWei Wang lockdep_is_held(&rt6_exception_lock)); 1662f5bbe7eeSWei Wang 1663e9fa1495SStefano Brivio if (!bucket) 1664e9fa1495SStefano Brivio return; 1665e9fa1495SStefano Brivio 1666f5bbe7eeSWei Wang for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 1667f5bbe7eeSWei Wang hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { 1668f5bbe7eeSWei Wang struct rt6_info *entry = rt6_ex->rt6i; 1669e9fa1495SStefano Brivio 1670e9fa1495SStefano Brivio /* For RTF_CACHE with rt6i_pmtu == 0 (i.e. a redirected 1671d4ead6b3SDavid Ahern * route), the metrics of its rt->from have already 1672f5bbe7eeSWei Wang * been updated. 1673f5bbe7eeSWei Wang */ 1674d4ead6b3SDavid Ahern if (dst_metric_raw(&entry->dst, RTAX_MTU) && 1675e9fa1495SStefano Brivio rt6_mtu_change_route_allowed(idev, entry, mtu)) 1676d4ead6b3SDavid Ahern dst_metric_set(&entry->dst, RTAX_MTU, mtu); 1677f5bbe7eeSWei Wang } 1678f5bbe7eeSWei Wang bucket++; 1679f5bbe7eeSWei Wang } 1680f5bbe7eeSWei Wang } 1681f5bbe7eeSWei Wang 1682b16cb459SWei Wang #define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 1683b16cb459SWei Wang 16848d1c802bSDavid Ahern static void rt6_exceptions_clean_tohost(struct fib6_info *rt, 1685b16cb459SWei Wang struct in6_addr *gateway) 1686b16cb459SWei Wang { 1687b16cb459SWei Wang struct rt6_exception_bucket *bucket; 1688b16cb459SWei Wang struct rt6_exception *rt6_ex; 1689b16cb459SWei Wang struct hlist_node *tmp; 1690b16cb459SWei Wang int i; 1691b16cb459SWei Wang 1692b16cb459SWei Wang if (!rcu_access_pointer(rt->rt6i_exception_bucket)) 1693b16cb459SWei Wang return; 1694b16cb459SWei Wang 1695b16cb459SWei Wang spin_lock_bh(&rt6_exception_lock); 1696b16cb459SWei Wang bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, 1697b16cb459SWei Wang lockdep_is_held(&rt6_exception_lock)); 1698b16cb459SWei Wang 1699b16cb459SWei Wang if (bucket) { 1700b16cb459SWei Wang for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 1701b16cb459SWei Wang hlist_for_each_entry_safe(rt6_ex, tmp, 1702b16cb459SWei Wang &bucket->chain, hlist) { 1703b16cb459SWei Wang struct rt6_info *entry = rt6_ex->rt6i; 1704b16cb459SWei Wang 1705b16cb459SWei Wang if ((entry->rt6i_flags & RTF_CACHE_GATEWAY) == 1706b16cb459SWei Wang RTF_CACHE_GATEWAY && 1707b16cb459SWei Wang ipv6_addr_equal(gateway, 1708b16cb459SWei Wang &entry->rt6i_gateway)) { 1709b16cb459SWei Wang rt6_remove_exception(bucket, rt6_ex); 1710b16cb459SWei Wang } 1711b16cb459SWei Wang } 1712b16cb459SWei Wang bucket++; 1713b16cb459SWei Wang } 1714b16cb459SWei Wang } 1715b16cb459SWei Wang 1716b16cb459SWei Wang spin_unlock_bh(&rt6_exception_lock); 1717b16cb459SWei Wang } 1718b16cb459SWei Wang 1719c757faa8SWei Wang static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket, 1720c757faa8SWei Wang struct rt6_exception *rt6_ex, 1721c757faa8SWei Wang struct fib6_gc_args *gc_args, 1722c757faa8SWei Wang unsigned long now) 1723c757faa8SWei Wang { 1724c757faa8SWei Wang struct rt6_info *rt = rt6_ex->rt6i; 1725c757faa8SWei Wang 17261859bac0SPaolo Abeni /* we are pruning and obsoleting aged-out and non gateway exceptions 17271859bac0SPaolo Abeni * even if others have still references to them, so that on next 17281859bac0SPaolo Abeni * dst_check() such references can be dropped. 17291859bac0SPaolo Abeni * EXPIRES exceptions - e.g. pmtu-generated ones are pruned when 17301859bac0SPaolo Abeni * expired, independently from their aging, as per RFC 8201 section 4 17311859bac0SPaolo Abeni */ 173231afeb42SWei Wang if (!(rt->rt6i_flags & RTF_EXPIRES)) { 173331afeb42SWei Wang if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) { 1734c757faa8SWei Wang RT6_TRACE("aging clone %p\n", rt); 1735c757faa8SWei Wang rt6_remove_exception(bucket, rt6_ex); 1736c757faa8SWei Wang return; 173731afeb42SWei Wang } 173831afeb42SWei Wang } else if (time_after(jiffies, rt->dst.expires)) { 173931afeb42SWei Wang RT6_TRACE("purging expired route %p\n", rt); 174031afeb42SWei Wang rt6_remove_exception(bucket, rt6_ex); 174131afeb42SWei Wang return; 174231afeb42SWei Wang } 174331afeb42SWei Wang 174431afeb42SWei Wang if (rt->rt6i_flags & RTF_GATEWAY) { 1745c757faa8SWei Wang struct neighbour *neigh; 1746c757faa8SWei Wang __u8 neigh_flags = 0; 1747c757faa8SWei Wang 17481bfa26ffSEric Dumazet neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 17491bfa26ffSEric Dumazet if (neigh) 1750c757faa8SWei Wang neigh_flags = neigh->flags; 17511bfa26ffSEric Dumazet 1752c757faa8SWei Wang if (!(neigh_flags & NTF_ROUTER)) { 1753c757faa8SWei Wang RT6_TRACE("purging route %p via non-router but gateway\n", 1754c757faa8SWei Wang rt); 1755c757faa8SWei Wang rt6_remove_exception(bucket, rt6_ex); 1756c757faa8SWei Wang return; 1757c757faa8SWei Wang } 1758c757faa8SWei Wang } 175931afeb42SWei Wang 1760c757faa8SWei Wang gc_args->more++; 1761c757faa8SWei Wang } 1762c757faa8SWei Wang 17638d1c802bSDavid Ahern void rt6_age_exceptions(struct fib6_info *rt, 1764c757faa8SWei Wang struct fib6_gc_args *gc_args, 1765c757faa8SWei Wang unsigned long now) 1766c757faa8SWei Wang { 1767c757faa8SWei Wang struct rt6_exception_bucket *bucket; 1768c757faa8SWei Wang struct rt6_exception *rt6_ex; 1769c757faa8SWei Wang struct hlist_node *tmp; 1770c757faa8SWei Wang int i; 1771c757faa8SWei Wang 1772c757faa8SWei Wang if (!rcu_access_pointer(rt->rt6i_exception_bucket)) 1773c757faa8SWei Wang return; 1774c757faa8SWei Wang 17751bfa26ffSEric Dumazet rcu_read_lock_bh(); 17761bfa26ffSEric Dumazet spin_lock(&rt6_exception_lock); 1777c757faa8SWei Wang bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, 1778c757faa8SWei Wang lockdep_is_held(&rt6_exception_lock)); 1779c757faa8SWei Wang 1780c757faa8SWei Wang if (bucket) { 1781c757faa8SWei Wang for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { 1782c757faa8SWei Wang hlist_for_each_entry_safe(rt6_ex, tmp, 1783c757faa8SWei Wang &bucket->chain, hlist) { 1784c757faa8SWei Wang rt6_age_examine_exception(bucket, rt6_ex, 1785c757faa8SWei Wang gc_args, now); 1786c757faa8SWei Wang } 1787c757faa8SWei Wang bucket++; 1788c757faa8SWei Wang } 1789c757faa8SWei Wang } 17901bfa26ffSEric Dumazet spin_unlock(&rt6_exception_lock); 17911bfa26ffSEric Dumazet rcu_read_unlock_bh(); 1792c757faa8SWei Wang } 1793c757faa8SWei Wang 17949ff74384SDavid Ahern struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, 1795b75cc8f9SDavid Ahern int oif, struct flowi6 *fl6, 1796b75cc8f9SDavid Ahern const struct sk_buff *skb, int flags) 17971da177e4SLinus Torvalds { 1798367efcb9SMartin KaFai Lau struct fib6_node *fn, *saved_fn; 17998d1c802bSDavid Ahern struct fib6_info *f6i; 180023fb93a4SDavid Ahern struct rt6_info *rt; 1801c71099acSThomas Graf int strict = 0; 18021da177e4SLinus Torvalds 180377d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 1804d5d32e4bSDavid Ahern strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE; 1805367efcb9SMartin KaFai Lau if (net->ipv6.devconf_all->forwarding == 0) 1806367efcb9SMartin KaFai Lau strict |= RT6_LOOKUP_F_REACHABLE; 18071da177e4SLinus Torvalds 180866f5d6ceSWei Wang rcu_read_lock(); 18091da177e4SLinus Torvalds 18104c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1811367efcb9SMartin KaFai Lau saved_fn = fn; 18121da177e4SLinus Torvalds 1813ca254490SDavid Ahern if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) 1814ca254490SDavid Ahern oif = 0; 1815ca254490SDavid Ahern 1816a3c00e46SMartin KaFai Lau redo_rt6_select: 181723fb93a4SDavid Ahern f6i = rt6_select(net, fn, oif, strict); 181893c2fb25SDavid Ahern if (f6i->fib6_nsiblings) 181923fb93a4SDavid Ahern f6i = rt6_multipath_select(net, f6i, fl6, oif, skb, strict); 182023fb93a4SDavid Ahern if (f6i == net->ipv6.fib6_null_entry) { 1821a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1822a3c00e46SMartin KaFai Lau if (fn) 1823a3c00e46SMartin KaFai Lau goto redo_rt6_select; 1824367efcb9SMartin KaFai Lau else if (strict & RT6_LOOKUP_F_REACHABLE) { 1825367efcb9SMartin KaFai Lau /* also consider unreachable route */ 1826367efcb9SMartin KaFai Lau strict &= ~RT6_LOOKUP_F_REACHABLE; 1827367efcb9SMartin KaFai Lau fn = saved_fn; 1828367efcb9SMartin KaFai Lau goto redo_rt6_select; 1829367efcb9SMartin KaFai Lau } 1830a3c00e46SMartin KaFai Lau } 1831a3c00e46SMartin KaFai Lau 183223fb93a4SDavid Ahern if (f6i == net->ipv6.fib6_null_entry) { 1833421842edSDavid Ahern rt = net->ipv6.ip6_null_entry; 183466f5d6ceSWei Wang rcu_read_unlock(); 1835d3843fe5SWei Wang dst_hold(&rt->dst); 1836b65f164dSPaolo Abeni trace_fib6_table_lookup(net, rt, table, fl6); 1837d3843fe5SWei Wang return rt; 183823fb93a4SDavid Ahern } 183923fb93a4SDavid Ahern 184023fb93a4SDavid Ahern /*Search through exception table */ 184123fb93a4SDavid Ahern rt = rt6_find_cached_rt(f6i, &fl6->daddr, &fl6->saddr); 184223fb93a4SDavid Ahern if (rt) { 1843d4ead6b3SDavid Ahern if (ip6_hold_safe(net, &rt, true)) 1844d3843fe5SWei Wang dst_use_noref(&rt->dst, jiffies); 1845d4ead6b3SDavid Ahern 184666f5d6ceSWei Wang rcu_read_unlock(); 1847b65f164dSPaolo Abeni trace_fib6_table_lookup(net, rt, table, fl6); 1848d52d3997SMartin KaFai Lau return rt; 18493da59bd9SMartin KaFai Lau } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && 185093c2fb25SDavid Ahern !(f6i->fib6_flags & RTF_GATEWAY))) { 18513da59bd9SMartin KaFai Lau /* Create a RTF_CACHE clone which will not be 18523da59bd9SMartin KaFai Lau * owned by the fib6 tree. It is for the special case where 18533da59bd9SMartin KaFai Lau * the daddr in the skb during the neighbor look-up is different 18543da59bd9SMartin KaFai Lau * from the fl6->daddr used to look-up route here. 18553da59bd9SMartin KaFai Lau */ 18563da59bd9SMartin KaFai Lau struct rt6_info *uncached_rt; 18573da59bd9SMartin KaFai Lau 185823fb93a4SDavid Ahern uncached_rt = ip6_rt_cache_alloc(f6i, &fl6->daddr, NULL); 1859*4d85cd0cSDavid Ahern 1860*4d85cd0cSDavid Ahern rcu_read_unlock(); 18613da59bd9SMartin KaFai Lau 18621cfb71eeSWei Wang if (uncached_rt) { 18631cfb71eeSWei Wang /* Uncached_rt's refcnt is taken during ip6_rt_cache_alloc() 18641cfb71eeSWei Wang * No need for another dst_hold() 18651cfb71eeSWei Wang */ 18668d0b94afSMartin KaFai Lau rt6_uncached_list_add(uncached_rt); 186781eb8447SWei Wang atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache); 18681cfb71eeSWei Wang } else { 18693da59bd9SMartin KaFai Lau uncached_rt = net->ipv6.ip6_null_entry; 18703da59bd9SMartin KaFai Lau dst_hold(&uncached_rt->dst); 18711cfb71eeSWei Wang } 1872b811580dSDavid Ahern 1873b65f164dSPaolo Abeni trace_fib6_table_lookup(net, uncached_rt, table, fl6); 18743da59bd9SMartin KaFai Lau return uncached_rt; 18753da59bd9SMartin KaFai Lau 1876d52d3997SMartin KaFai Lau } else { 1877d52d3997SMartin KaFai Lau /* Get a percpu copy */ 1878d52d3997SMartin KaFai Lau 1879d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 1880d52d3997SMartin KaFai Lau 1881951f788aSEric Dumazet local_bh_disable(); 188223fb93a4SDavid Ahern pcpu_rt = rt6_get_pcpu_route(f6i); 1883d52d3997SMartin KaFai Lau 188493531c67SDavid Ahern if (!pcpu_rt) 188523fb93a4SDavid Ahern pcpu_rt = rt6_make_pcpu_route(net, f6i); 188693531c67SDavid Ahern 1887951f788aSEric Dumazet local_bh_enable(); 1888951f788aSEric Dumazet rcu_read_unlock(); 1889b65f164dSPaolo Abeni trace_fib6_table_lookup(net, pcpu_rt, table, fl6); 1890d52d3997SMartin KaFai Lau return pcpu_rt; 1891d52d3997SMartin KaFai Lau } 1892c71099acSThomas Graf } 18939ff74384SDavid Ahern EXPORT_SYMBOL_GPL(ip6_pol_route); 1894c71099acSThomas Graf 1895b75cc8f9SDavid Ahern static struct rt6_info *ip6_pol_route_input(struct net *net, 1896b75cc8f9SDavid Ahern struct fib6_table *table, 1897b75cc8f9SDavid Ahern struct flowi6 *fl6, 1898b75cc8f9SDavid Ahern const struct sk_buff *skb, 1899b75cc8f9SDavid Ahern int flags) 19004acad72dSPavel Emelyanov { 1901b75cc8f9SDavid Ahern return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, skb, flags); 19024acad72dSPavel Emelyanov } 19034acad72dSPavel Emelyanov 1904d409b847SMahesh Bandewar struct dst_entry *ip6_route_input_lookup(struct net *net, 190572331bc0SShmulik Ladkani struct net_device *dev, 1906b75cc8f9SDavid Ahern struct flowi6 *fl6, 1907b75cc8f9SDavid Ahern const struct sk_buff *skb, 1908b75cc8f9SDavid Ahern int flags) 190972331bc0SShmulik Ladkani { 191072331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 191172331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 191272331bc0SShmulik Ladkani 1913b75cc8f9SDavid Ahern return fib6_rule_lookup(net, fl6, skb, flags, ip6_pol_route_input); 191472331bc0SShmulik Ladkani } 1915d409b847SMahesh Bandewar EXPORT_SYMBOL_GPL(ip6_route_input_lookup); 191672331bc0SShmulik Ladkani 191723aebdacSJakub Sitnicki static void ip6_multipath_l3_keys(const struct sk_buff *skb, 19185e5d6fedSRoopa Prabhu struct flow_keys *keys, 19195e5d6fedSRoopa Prabhu struct flow_keys *flkeys) 192023aebdacSJakub Sitnicki { 192123aebdacSJakub Sitnicki const struct ipv6hdr *outer_iph = ipv6_hdr(skb); 192223aebdacSJakub Sitnicki const struct ipv6hdr *key_iph = outer_iph; 19235e5d6fedSRoopa Prabhu struct flow_keys *_flkeys = flkeys; 192423aebdacSJakub Sitnicki const struct ipv6hdr *inner_iph; 192523aebdacSJakub Sitnicki const struct icmp6hdr *icmph; 192623aebdacSJakub Sitnicki struct ipv6hdr _inner_iph; 192723aebdacSJakub Sitnicki 192823aebdacSJakub Sitnicki if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6)) 192923aebdacSJakub Sitnicki goto out; 193023aebdacSJakub Sitnicki 193123aebdacSJakub Sitnicki icmph = icmp6_hdr(skb); 193223aebdacSJakub Sitnicki if (icmph->icmp6_type != ICMPV6_DEST_UNREACH && 193323aebdacSJakub Sitnicki icmph->icmp6_type != ICMPV6_PKT_TOOBIG && 193423aebdacSJakub Sitnicki icmph->icmp6_type != ICMPV6_TIME_EXCEED && 193523aebdacSJakub Sitnicki icmph->icmp6_type != ICMPV6_PARAMPROB) 193623aebdacSJakub Sitnicki goto out; 193723aebdacSJakub Sitnicki 193823aebdacSJakub Sitnicki inner_iph = skb_header_pointer(skb, 193923aebdacSJakub Sitnicki skb_transport_offset(skb) + sizeof(*icmph), 194023aebdacSJakub Sitnicki sizeof(_inner_iph), &_inner_iph); 194123aebdacSJakub Sitnicki if (!inner_iph) 194223aebdacSJakub Sitnicki goto out; 194323aebdacSJakub Sitnicki 194423aebdacSJakub Sitnicki key_iph = inner_iph; 19455e5d6fedSRoopa Prabhu _flkeys = NULL; 194623aebdacSJakub Sitnicki out: 19475e5d6fedSRoopa Prabhu if (_flkeys) { 19485e5d6fedSRoopa Prabhu keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src; 19495e5d6fedSRoopa Prabhu keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst; 19505e5d6fedSRoopa Prabhu keys->tags.flow_label = _flkeys->tags.flow_label; 19515e5d6fedSRoopa Prabhu keys->basic.ip_proto = _flkeys->basic.ip_proto; 19525e5d6fedSRoopa Prabhu } else { 195323aebdacSJakub Sitnicki keys->addrs.v6addrs.src = key_iph->saddr; 195423aebdacSJakub Sitnicki keys->addrs.v6addrs.dst = key_iph->daddr; 195523aebdacSJakub Sitnicki keys->tags.flow_label = ip6_flowinfo(key_iph); 195623aebdacSJakub Sitnicki keys->basic.ip_proto = key_iph->nexthdr; 195723aebdacSJakub Sitnicki } 19585e5d6fedSRoopa Prabhu } 195923aebdacSJakub Sitnicki 196023aebdacSJakub Sitnicki /* if skb is set it will be used and fl6 can be NULL */ 1961b4bac172SDavid Ahern u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6, 1962b4bac172SDavid Ahern const struct sk_buff *skb, struct flow_keys *flkeys) 196323aebdacSJakub Sitnicki { 196423aebdacSJakub Sitnicki struct flow_keys hash_keys; 19659a2a537aSDavid Ahern u32 mhash; 196623aebdacSJakub Sitnicki 1967bbfa047aSDavid S. Miller switch (ip6_multipath_hash_policy(net)) { 1968b4bac172SDavid Ahern case 0: 19696f74b6c2SDavid Ahern memset(&hash_keys, 0, sizeof(hash_keys)); 19706f74b6c2SDavid Ahern hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 19719a2a537aSDavid Ahern if (skb) { 19725e5d6fedSRoopa Prabhu ip6_multipath_l3_keys(skb, &hash_keys, flkeys); 19739a2a537aSDavid Ahern } else { 19749a2a537aSDavid Ahern hash_keys.addrs.v6addrs.src = fl6->saddr; 19759a2a537aSDavid Ahern hash_keys.addrs.v6addrs.dst = fl6->daddr; 19769a2a537aSDavid Ahern hash_keys.tags.flow_label = (__force u32)fl6->flowlabel; 19779a2a537aSDavid Ahern hash_keys.basic.ip_proto = fl6->flowi6_proto; 197823aebdacSJakub Sitnicki } 1979b4bac172SDavid Ahern break; 1980b4bac172SDavid Ahern case 1: 1981b4bac172SDavid Ahern if (skb) { 1982b4bac172SDavid Ahern unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP; 1983b4bac172SDavid Ahern struct flow_keys keys; 1984b4bac172SDavid Ahern 1985b4bac172SDavid Ahern /* short-circuit if we already have L4 hash present */ 1986b4bac172SDavid Ahern if (skb->l4_hash) 1987b4bac172SDavid Ahern return skb_get_hash_raw(skb) >> 1; 1988b4bac172SDavid Ahern 1989b4bac172SDavid Ahern memset(&hash_keys, 0, sizeof(hash_keys)); 1990b4bac172SDavid Ahern 1991b4bac172SDavid Ahern if (!flkeys) { 1992b4bac172SDavid Ahern skb_flow_dissect_flow_keys(skb, &keys, flag); 1993b4bac172SDavid Ahern flkeys = &keys; 1994b4bac172SDavid Ahern } 1995b4bac172SDavid Ahern hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 1996b4bac172SDavid Ahern hash_keys.addrs.v6addrs.src = flkeys->addrs.v6addrs.src; 1997b4bac172SDavid Ahern hash_keys.addrs.v6addrs.dst = flkeys->addrs.v6addrs.dst; 1998b4bac172SDavid Ahern hash_keys.ports.src = flkeys->ports.src; 1999b4bac172SDavid Ahern hash_keys.ports.dst = flkeys->ports.dst; 2000b4bac172SDavid Ahern hash_keys.basic.ip_proto = flkeys->basic.ip_proto; 2001b4bac172SDavid Ahern } else { 2002b4bac172SDavid Ahern memset(&hash_keys, 0, sizeof(hash_keys)); 2003b4bac172SDavid Ahern hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 2004b4bac172SDavid Ahern hash_keys.addrs.v6addrs.src = fl6->saddr; 2005b4bac172SDavid Ahern hash_keys.addrs.v6addrs.dst = fl6->daddr; 2006b4bac172SDavid Ahern hash_keys.ports.src = fl6->fl6_sport; 2007b4bac172SDavid Ahern hash_keys.ports.dst = fl6->fl6_dport; 2008b4bac172SDavid Ahern hash_keys.basic.ip_proto = fl6->flowi6_proto; 2009b4bac172SDavid Ahern } 2010b4bac172SDavid Ahern break; 2011b4bac172SDavid Ahern } 20129a2a537aSDavid Ahern mhash = flow_hash_from_keys(&hash_keys); 201323aebdacSJakub Sitnicki 20149a2a537aSDavid Ahern return mhash >> 1; 201523aebdacSJakub Sitnicki } 201623aebdacSJakub Sitnicki 2017c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 2018c71099acSThomas Graf { 2019b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 2020c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 2021adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 2022904af04dSJiri Benc struct ip_tunnel_info *tun_info; 20234c9483b2SDavid S. Miller struct flowi6 fl6 = { 2024e0d56fddSDavid Ahern .flowi6_iif = skb->dev->ifindex, 20254c9483b2SDavid S. Miller .daddr = iph->daddr, 20264c9483b2SDavid S. Miller .saddr = iph->saddr, 20276502ca52SYOSHIFUJI Hideaki / 吉藤英明 .flowlabel = ip6_flowinfo(iph), 20284c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 20294c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 2030c71099acSThomas Graf }; 20315e5d6fedSRoopa Prabhu struct flow_keys *flkeys = NULL, _flkeys; 2032adaa70bbSThomas Graf 2033904af04dSJiri Benc tun_info = skb_tunnel_info(skb); 203446fa062aSJiri Benc if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX)) 2035904af04dSJiri Benc fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id; 20365e5d6fedSRoopa Prabhu 20375e5d6fedSRoopa Prabhu if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys)) 20385e5d6fedSRoopa Prabhu flkeys = &_flkeys; 20395e5d6fedSRoopa Prabhu 204023aebdacSJakub Sitnicki if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6)) 2041b4bac172SDavid Ahern fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys); 204206e9d040SJiri Benc skb_dst_drop(skb); 2043b75cc8f9SDavid Ahern skb_dst_set(skb, 2044b75cc8f9SDavid Ahern ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags)); 2045c71099acSThomas Graf } 2046c71099acSThomas Graf 2047b75cc8f9SDavid Ahern static struct rt6_info *ip6_pol_route_output(struct net *net, 2048b75cc8f9SDavid Ahern struct fib6_table *table, 2049b75cc8f9SDavid Ahern struct flowi6 *fl6, 2050b75cc8f9SDavid Ahern const struct sk_buff *skb, 2051b75cc8f9SDavid Ahern int flags) 2052c71099acSThomas Graf { 2053b75cc8f9SDavid Ahern return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags); 2054c71099acSThomas Graf } 2055c71099acSThomas Graf 20566f21c96aSPaolo Abeni struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk, 20576f21c96aSPaolo Abeni struct flowi6 *fl6, int flags) 2058c71099acSThomas Graf { 2059d46a9d67SDavid Ahern bool any_src; 2060c71099acSThomas Graf 20614c1feac5SDavid Ahern if (rt6_need_strict(&fl6->daddr)) { 20624c1feac5SDavid Ahern struct dst_entry *dst; 20634c1feac5SDavid Ahern 20644c1feac5SDavid Ahern dst = l3mdev_link_scope_lookup(net, fl6); 2065ca254490SDavid Ahern if (dst) 2066ca254490SDavid Ahern return dst; 20674c1feac5SDavid Ahern } 2068ca254490SDavid Ahern 20691fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 20704dc27d1cSDavid McCullough 2071d46a9d67SDavid Ahern any_src = ipv6_addr_any(&fl6->saddr); 2072741a11d9SDavid Ahern if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) || 2073d46a9d67SDavid Ahern (fl6->flowi6_oif && any_src)) 207477d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 2075c71099acSThomas Graf 2076d46a9d67SDavid Ahern if (!any_src) 2077adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 20780c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 20790c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 2080adaa70bbSThomas Graf 2081b75cc8f9SDavid Ahern return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output); 20821da177e4SLinus Torvalds } 20836f21c96aSPaolo Abeni EXPORT_SYMBOL_GPL(ip6_route_output_flags); 20841da177e4SLinus Torvalds 20852774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 208614e50e57SDavid S. Miller { 20875c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 20881dbe3252SWei Wang struct net_device *loopback_dev = net->loopback_dev; 208914e50e57SDavid S. Miller struct dst_entry *new = NULL; 209014e50e57SDavid S. Miller 20911dbe3252SWei Wang rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1, 209262cf27e5SSteffen Klassert DST_OBSOLETE_DEAD, 0); 209314e50e57SDavid S. Miller if (rt) { 20940a1f5962SMartin KaFai Lau rt6_info_init(rt); 209581eb8447SWei Wang atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc); 20960a1f5962SMartin KaFai Lau 2097d8d1f30bSChangli Gao new = &rt->dst; 209814e50e57SDavid S. Miller new->__use = 1; 2099352e512cSHerbert Xu new->input = dst_discard; 2100ede2059dSEric W. Biederman new->output = dst_discard_out; 210114e50e57SDavid S. Miller 2102defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 210314e50e57SDavid S. Miller 21041dbe3252SWei Wang rt->rt6i_idev = in6_dev_get(loopback_dev); 21054e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 21060a1f5962SMartin KaFai Lau rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU; 210714e50e57SDavid S. Miller 210814e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 210914e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 211014e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 211114e50e57SDavid S. Miller #endif 211214e50e57SDavid S. Miller } 211314e50e57SDavid S. Miller 211469ead7afSDavid S. Miller dst_release(dst_orig); 211569ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 211614e50e57SDavid S. Miller } 211714e50e57SDavid S. Miller 21181da177e4SLinus Torvalds /* 21191da177e4SLinus Torvalds * Destination cache support functions 21201da177e4SLinus Torvalds */ 21211da177e4SLinus Torvalds 21228d1c802bSDavid Ahern static bool fib6_check(struct fib6_info *f6i, u32 cookie) 212393531c67SDavid Ahern { 212493531c67SDavid Ahern u32 rt_cookie = 0; 212593531c67SDavid Ahern 2126a269f1a7SDavid Ahern if ((f6i && !fib6_get_cookie_safe(f6i, &rt_cookie)) || 212793531c67SDavid Ahern rt_cookie != cookie) 212893531c67SDavid Ahern return false; 212993531c67SDavid Ahern 213093531c67SDavid Ahern if (fib6_check_expired(f6i)) 213193531c67SDavid Ahern return false; 213293531c67SDavid Ahern 213393531c67SDavid Ahern return true; 213493531c67SDavid Ahern } 213593531c67SDavid Ahern 21363da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie) 21373da59bd9SMartin KaFai Lau { 213836143645SSteffen Klassert u32 rt_cookie = 0; 2139c5cff856SWei Wang 2140a269f1a7SDavid Ahern if ((rt->from && !fib6_get_cookie_safe(rt->from, &rt_cookie)) || 214193531c67SDavid Ahern rt_cookie != cookie) 21423da59bd9SMartin KaFai Lau return NULL; 21433da59bd9SMartin KaFai Lau 21443da59bd9SMartin KaFai Lau if (rt6_check_expired(rt)) 21453da59bd9SMartin KaFai Lau return NULL; 21463da59bd9SMartin KaFai Lau 21473da59bd9SMartin KaFai Lau return &rt->dst; 21483da59bd9SMartin KaFai Lau } 21493da59bd9SMartin KaFai Lau 21503da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie) 21513da59bd9SMartin KaFai Lau { 21525973fb1eSMartin KaFai Lau if (!__rt6_check_expired(rt) && 21535973fb1eSMartin KaFai Lau rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && 215493531c67SDavid Ahern fib6_check(rt->from, cookie)) 21553da59bd9SMartin KaFai Lau return &rt->dst; 21563da59bd9SMartin KaFai Lau else 21573da59bd9SMartin KaFai Lau return NULL; 21583da59bd9SMartin KaFai Lau } 21593da59bd9SMartin KaFai Lau 21601da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 21611da177e4SLinus Torvalds { 21621da177e4SLinus Torvalds struct rt6_info *rt; 21631da177e4SLinus Torvalds 21641da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 21651da177e4SLinus Torvalds 21666f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 21676f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 21686f3118b5SNicolas Dichtel * into this function always. 21696f3118b5SNicolas Dichtel */ 2170e3bc10bdSHannes Frederic Sowa 217102bcf4e0SMartin KaFai Lau if (rt->rt6i_flags & RTF_PCPU || 21723a2232e9SDavid Miller (unlikely(!list_empty(&rt->rt6i_uncached)) && rt->from)) 21733da59bd9SMartin KaFai Lau return rt6_dst_from_check(rt, cookie); 21743da59bd9SMartin KaFai Lau else 21753da59bd9SMartin KaFai Lau return rt6_check(rt, cookie); 21761da177e4SLinus Torvalds } 21771da177e4SLinus Torvalds 21781da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 21791da177e4SLinus Torvalds { 21801da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 21811da177e4SLinus Torvalds 21821da177e4SLinus Torvalds if (rt) { 218354c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 218454c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 218593531c67SDavid Ahern rt6_remove_exception_rt(rt); 218654c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 21871da177e4SLinus Torvalds } 218854c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 218954c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 219054c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 219154c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 219254c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 219354c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 21941da177e4SLinus Torvalds } 21951da177e4SLinus Torvalds 21961da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 21971da177e4SLinus Torvalds { 21981da177e4SLinus Torvalds struct rt6_info *rt; 21991da177e4SLinus Torvalds 22003ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 22011da177e4SLinus Torvalds 2202adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 22031da177e4SLinus Torvalds if (rt) { 22041eb4f758SHannes Frederic Sowa if (rt->rt6i_flags & RTF_CACHE) { 2205ad65a2f0SWei Wang if (dst_hold_safe(&rt->dst)) 220693531c67SDavid Ahern rt6_remove_exception_rt(rt); 220793531c67SDavid Ahern } else if (rt->from) { 2208c5cff856SWei Wang struct fib6_node *fn; 2209c5cff856SWei Wang 2210c5cff856SWei Wang rcu_read_lock(); 221193c2fb25SDavid Ahern fn = rcu_dereference(rt->from->fib6_node); 2212c5cff856SWei Wang if (fn && (rt->rt6i_flags & RTF_DEFAULT)) 2213c5cff856SWei Wang fn->fn_sernum = -1; 2214c5cff856SWei Wang rcu_read_unlock(); 22151da177e4SLinus Torvalds } 22161da177e4SLinus Torvalds } 22171eb4f758SHannes Frederic Sowa } 22181da177e4SLinus Torvalds 22196a3e030fSDavid Ahern static void rt6_update_expires(struct rt6_info *rt0, int timeout) 22206a3e030fSDavid Ahern { 22216a3e030fSDavid Ahern if (!(rt0->rt6i_flags & RTF_EXPIRES) && rt0->from) 22226a3e030fSDavid Ahern rt0->dst.expires = rt0->from->expires; 22236a3e030fSDavid Ahern 22246a3e030fSDavid Ahern dst_set_expires(&rt0->dst, timeout); 22256a3e030fSDavid Ahern rt0->rt6i_flags |= RTF_EXPIRES; 22266a3e030fSDavid Ahern } 22276a3e030fSDavid Ahern 222845e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) 222945e4fd26SMartin KaFai Lau { 223045e4fd26SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 223145e4fd26SMartin KaFai Lau 2232d4ead6b3SDavid Ahern dst_metric_set(&rt->dst, RTAX_MTU, mtu); 223345e4fd26SMartin KaFai Lau rt->rt6i_flags |= RTF_MODIFIED; 223445e4fd26SMartin KaFai Lau rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); 223545e4fd26SMartin KaFai Lau } 223645e4fd26SMartin KaFai Lau 22370d3f6d29SMartin KaFai Lau static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt) 22380d3f6d29SMartin KaFai Lau { 22390d3f6d29SMartin KaFai Lau return !(rt->rt6i_flags & RTF_CACHE) && 224077634cc6SDavid Ahern (rt->rt6i_flags & RTF_PCPU || rt->from); 22410d3f6d29SMartin KaFai Lau } 22420d3f6d29SMartin KaFai Lau 224345e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, 224445e4fd26SMartin KaFai Lau const struct ipv6hdr *iph, u32 mtu) 22451da177e4SLinus Torvalds { 22460dec879fSJulian Anastasov const struct in6_addr *daddr, *saddr; 22471da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info *)dst; 22481da177e4SLinus Torvalds 224945e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_LOCAL) 225045e4fd26SMartin KaFai Lau return; 225145e4fd26SMartin KaFai Lau 225219bda36cSXin Long if (dst_metric_locked(dst, RTAX_MTU)) 225319bda36cSXin Long return; 225419bda36cSXin Long 225545e4fd26SMartin KaFai Lau if (iph) { 225645e4fd26SMartin KaFai Lau daddr = &iph->daddr; 225745e4fd26SMartin KaFai Lau saddr = &iph->saddr; 225845e4fd26SMartin KaFai Lau } else if (sk) { 225945e4fd26SMartin KaFai Lau daddr = &sk->sk_v6_daddr; 226045e4fd26SMartin KaFai Lau saddr = &inet6_sk(sk)->saddr; 226145e4fd26SMartin KaFai Lau } else { 22620dec879fSJulian Anastasov daddr = NULL; 22630dec879fSJulian Anastasov saddr = NULL; 22641da177e4SLinus Torvalds } 22650dec879fSJulian Anastasov dst_confirm_neigh(dst, daddr); 22660dec879fSJulian Anastasov mtu = max_t(u32, mtu, IPV6_MIN_MTU); 22670dec879fSJulian Anastasov if (mtu >= dst_mtu(dst)) 22680dec879fSJulian Anastasov return; 22690dec879fSJulian Anastasov 22700dec879fSJulian Anastasov if (!rt6_cache_allowed_for_pmtu(rt6)) { 22710dec879fSJulian Anastasov rt6_do_update_pmtu(rt6, mtu); 22722b760fcfSWei Wang /* update rt6_ex->stamp for cache */ 22732b760fcfSWei Wang if (rt6->rt6i_flags & RTF_CACHE) 22742b760fcfSWei Wang rt6_update_exception_stamp_rt(rt6); 22750dec879fSJulian Anastasov } else if (daddr) { 22760dec879fSJulian Anastasov struct rt6_info *nrt6; 22770dec879fSJulian Anastasov 2278*4d85cd0cSDavid Ahern rcu_read_lock(); 2279d4ead6b3SDavid Ahern nrt6 = ip6_rt_cache_alloc(rt6->from, daddr, saddr); 2280*4d85cd0cSDavid Ahern rcu_read_unlock(); 228145e4fd26SMartin KaFai Lau if (nrt6) { 228245e4fd26SMartin KaFai Lau rt6_do_update_pmtu(nrt6, mtu); 2283d4ead6b3SDavid Ahern if (rt6_insert_exception(nrt6, rt6->from)) 22842b760fcfSWei Wang dst_release_immediate(&nrt6->dst); 228545e4fd26SMartin KaFai Lau } 228645e4fd26SMartin KaFai Lau } 228745e4fd26SMartin KaFai Lau } 228845e4fd26SMartin KaFai Lau 228945e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 229045e4fd26SMartin KaFai Lau struct sk_buff *skb, u32 mtu) 229145e4fd26SMartin KaFai Lau { 229245e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu); 22931da177e4SLinus Torvalds } 22941da177e4SLinus Torvalds 229542ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 2296e2d118a1SLorenzo Colitti int oif, u32 mark, kuid_t uid) 229781aded24SDavid S. Miller { 229881aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 229981aded24SDavid S. Miller struct dst_entry *dst; 230081aded24SDavid S. Miller struct flowi6 fl6; 230181aded24SDavid S. Miller 230281aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 230381aded24SDavid S. Miller fl6.flowi6_oif = oif; 23041b3c61dcSLorenzo Colitti fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark); 230581aded24SDavid S. Miller fl6.daddr = iph->daddr; 230681aded24SDavid S. Miller fl6.saddr = iph->saddr; 23076502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 2308e2d118a1SLorenzo Colitti fl6.flowi6_uid = uid; 230981aded24SDavid S. Miller 231081aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 231181aded24SDavid S. Miller if (!dst->error) 231245e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu)); 231381aded24SDavid S. Miller dst_release(dst); 231481aded24SDavid S. Miller } 231581aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 231681aded24SDavid S. Miller 231781aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 231881aded24SDavid S. Miller { 231933c162a9SMartin KaFai Lau struct dst_entry *dst; 232033c162a9SMartin KaFai Lau 232181aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 2322e2d118a1SLorenzo Colitti sk->sk_bound_dev_if, sk->sk_mark, sk->sk_uid); 232333c162a9SMartin KaFai Lau 232433c162a9SMartin KaFai Lau dst = __sk_dst_get(sk); 232533c162a9SMartin KaFai Lau if (!dst || !dst->obsolete || 232633c162a9SMartin KaFai Lau dst->ops->check(dst, inet6_sk(sk)->dst_cookie)) 232733c162a9SMartin KaFai Lau return; 232833c162a9SMartin KaFai Lau 232933c162a9SMartin KaFai Lau bh_lock_sock(sk); 233033c162a9SMartin KaFai Lau if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 233133c162a9SMartin KaFai Lau ip6_datagram_dst_update(sk, false); 233233c162a9SMartin KaFai Lau bh_unlock_sock(sk); 233381aded24SDavid S. Miller } 233481aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 233581aded24SDavid S. Miller 23367d6850f7SAlexey Kodanev void ip6_sk_dst_store_flow(struct sock *sk, struct dst_entry *dst, 23377d6850f7SAlexey Kodanev const struct flowi6 *fl6) 23387d6850f7SAlexey Kodanev { 23397d6850f7SAlexey Kodanev #ifdef CONFIG_IPV6_SUBTREES 23407d6850f7SAlexey Kodanev struct ipv6_pinfo *np = inet6_sk(sk); 23417d6850f7SAlexey Kodanev #endif 23427d6850f7SAlexey Kodanev 23437d6850f7SAlexey Kodanev ip6_dst_store(sk, dst, 23447d6850f7SAlexey Kodanev ipv6_addr_equal(&fl6->daddr, &sk->sk_v6_daddr) ? 23457d6850f7SAlexey Kodanev &sk->sk_v6_daddr : NULL, 23467d6850f7SAlexey Kodanev #ifdef CONFIG_IPV6_SUBTREES 23477d6850f7SAlexey Kodanev ipv6_addr_equal(&fl6->saddr, &np->saddr) ? 23487d6850f7SAlexey Kodanev &np->saddr : 23497d6850f7SAlexey Kodanev #endif 23507d6850f7SAlexey Kodanev NULL); 23517d6850f7SAlexey Kodanev } 23527d6850f7SAlexey Kodanev 2353b55b76b2SDuan Jiong /* Handle redirects */ 2354b55b76b2SDuan Jiong struct ip6rd_flowi { 2355b55b76b2SDuan Jiong struct flowi6 fl6; 2356b55b76b2SDuan Jiong struct in6_addr gateway; 2357b55b76b2SDuan Jiong }; 2358b55b76b2SDuan Jiong 2359b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net, 2360b55b76b2SDuan Jiong struct fib6_table *table, 2361b55b76b2SDuan Jiong struct flowi6 *fl6, 2362b75cc8f9SDavid Ahern const struct sk_buff *skb, 2363b55b76b2SDuan Jiong int flags) 2364b55b76b2SDuan Jiong { 2365b55b76b2SDuan Jiong struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 236623fb93a4SDavid Ahern struct rt6_info *ret = NULL, *rt_cache; 23678d1c802bSDavid Ahern struct fib6_info *rt; 2368b55b76b2SDuan Jiong struct fib6_node *fn; 2369b55b76b2SDuan Jiong 2370b55b76b2SDuan Jiong /* Get the "current" route for this destination and 237167c408cfSAlexander Alemayhu * check if the redirect has come from appropriate router. 2372b55b76b2SDuan Jiong * 2373b55b76b2SDuan Jiong * RFC 4861 specifies that redirects should only be 2374b55b76b2SDuan Jiong * accepted if they come from the nexthop to the target. 2375b55b76b2SDuan Jiong * Due to the way the routes are chosen, this notion 2376b55b76b2SDuan Jiong * is a bit fuzzy and one might need to check all possible 2377b55b76b2SDuan Jiong * routes. 2378b55b76b2SDuan Jiong */ 2379b55b76b2SDuan Jiong 238066f5d6ceSWei Wang rcu_read_lock(); 2381b55b76b2SDuan Jiong fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 2382b55b76b2SDuan Jiong restart: 238366f5d6ceSWei Wang for_each_fib6_node_rt_rcu(fn) { 23845e670d84SDavid Ahern if (rt->fib6_nh.nh_flags & RTNH_F_DEAD) 23858067bb8cSIdo Schimmel continue; 238614895687SDavid Ahern if (fib6_check_expired(rt)) 2387b55b76b2SDuan Jiong continue; 238893c2fb25SDavid Ahern if (rt->fib6_flags & RTF_REJECT) 2389b55b76b2SDuan Jiong break; 239093c2fb25SDavid Ahern if (!(rt->fib6_flags & RTF_GATEWAY)) 2391b55b76b2SDuan Jiong continue; 23925e670d84SDavid Ahern if (fl6->flowi6_oif != rt->fib6_nh.nh_dev->ifindex) 2393b55b76b2SDuan Jiong continue; 23942b760fcfSWei Wang /* rt_cache's gateway might be different from its 'parent' 23952b760fcfSWei Wang * in the case of an ip redirect. 23962b760fcfSWei Wang * So we keep searching in the exception table if the gateway 23972b760fcfSWei Wang * is different. 23982b760fcfSWei Wang */ 23995e670d84SDavid Ahern if (!ipv6_addr_equal(&rdfl->gateway, &rt->fib6_nh.nh_gw)) { 24002b760fcfSWei Wang rt_cache = rt6_find_cached_rt(rt, 24012b760fcfSWei Wang &fl6->daddr, 24022b760fcfSWei Wang &fl6->saddr); 24032b760fcfSWei Wang if (rt_cache && 24042b760fcfSWei Wang ipv6_addr_equal(&rdfl->gateway, 24052b760fcfSWei Wang &rt_cache->rt6i_gateway)) { 240623fb93a4SDavid Ahern ret = rt_cache; 24072b760fcfSWei Wang break; 24082b760fcfSWei Wang } 2409b55b76b2SDuan Jiong continue; 24102b760fcfSWei Wang } 2411b55b76b2SDuan Jiong break; 2412b55b76b2SDuan Jiong } 2413b55b76b2SDuan Jiong 2414b55b76b2SDuan Jiong if (!rt) 2415421842edSDavid Ahern rt = net->ipv6.fib6_null_entry; 241693c2fb25SDavid Ahern else if (rt->fib6_flags & RTF_REJECT) { 241723fb93a4SDavid Ahern ret = net->ipv6.ip6_null_entry; 2418b0a1ba59SMartin KaFai Lau goto out; 2419b0a1ba59SMartin KaFai Lau } 2420b0a1ba59SMartin KaFai Lau 2421421842edSDavid Ahern if (rt == net->ipv6.fib6_null_entry) { 2422a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 2423a3c00e46SMartin KaFai Lau if (fn) 2424a3c00e46SMartin KaFai Lau goto restart; 2425b55b76b2SDuan Jiong } 2426a3c00e46SMartin KaFai Lau 2427b0a1ba59SMartin KaFai Lau out: 242823fb93a4SDavid Ahern if (ret) 242923fb93a4SDavid Ahern dst_hold(&ret->dst); 243023fb93a4SDavid Ahern else 243123fb93a4SDavid Ahern ret = ip6_create_rt_rcu(rt); 2432b55b76b2SDuan Jiong 243366f5d6ceSWei Wang rcu_read_unlock(); 2434b55b76b2SDuan Jiong 243523fb93a4SDavid Ahern trace_fib6_table_lookup(net, ret, table, fl6); 243623fb93a4SDavid Ahern return ret; 2437b55b76b2SDuan Jiong }; 2438b55b76b2SDuan Jiong 2439b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net, 2440b55b76b2SDuan Jiong const struct flowi6 *fl6, 2441b75cc8f9SDavid Ahern const struct sk_buff *skb, 2442b55b76b2SDuan Jiong const struct in6_addr *gateway) 2443b55b76b2SDuan Jiong { 2444b55b76b2SDuan Jiong int flags = RT6_LOOKUP_F_HAS_SADDR; 2445b55b76b2SDuan Jiong struct ip6rd_flowi rdfl; 2446b55b76b2SDuan Jiong 2447b55b76b2SDuan Jiong rdfl.fl6 = *fl6; 2448b55b76b2SDuan Jiong rdfl.gateway = *gateway; 2449b55b76b2SDuan Jiong 2450b75cc8f9SDavid Ahern return fib6_rule_lookup(net, &rdfl.fl6, skb, 2451b55b76b2SDuan Jiong flags, __ip6_route_redirect); 2452b55b76b2SDuan Jiong } 2453b55b76b2SDuan Jiong 2454e2d118a1SLorenzo Colitti void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark, 2455e2d118a1SLorenzo Colitti kuid_t uid) 24563a5ad2eeSDavid S. Miller { 24573a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 24583a5ad2eeSDavid S. Miller struct dst_entry *dst; 24593a5ad2eeSDavid S. Miller struct flowi6 fl6; 24603a5ad2eeSDavid S. Miller 24613a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2462e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 24633a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 24643a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 24653a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 24663a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 24676502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 2468e2d118a1SLorenzo Colitti fl6.flowi6_uid = uid; 24693a5ad2eeSDavid S. Miller 2470b75cc8f9SDavid Ahern dst = ip6_route_redirect(net, &fl6, skb, &ipv6_hdr(skb)->saddr); 24716700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 24723a5ad2eeSDavid S. Miller dst_release(dst); 24733a5ad2eeSDavid S. Miller } 24743a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 24753a5ad2eeSDavid S. Miller 2476c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, 2477c92a59ecSDuan Jiong u32 mark) 2478c92a59ecSDuan Jiong { 2479c92a59ecSDuan Jiong const struct ipv6hdr *iph = ipv6_hdr(skb); 2480c92a59ecSDuan Jiong const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 2481c92a59ecSDuan Jiong struct dst_entry *dst; 2482c92a59ecSDuan Jiong struct flowi6 fl6; 2483c92a59ecSDuan Jiong 2484c92a59ecSDuan Jiong memset(&fl6, 0, sizeof(fl6)); 2485e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 2486c92a59ecSDuan Jiong fl6.flowi6_oif = oif; 2487c92a59ecSDuan Jiong fl6.flowi6_mark = mark; 2488c92a59ecSDuan Jiong fl6.daddr = msg->dest; 2489c92a59ecSDuan Jiong fl6.saddr = iph->daddr; 2490e2d118a1SLorenzo Colitti fl6.flowi6_uid = sock_net_uid(net, NULL); 2491c92a59ecSDuan Jiong 2492b75cc8f9SDavid Ahern dst = ip6_route_redirect(net, &fl6, skb, &iph->saddr); 2493c92a59ecSDuan Jiong rt6_do_redirect(dst, NULL, skb); 2494c92a59ecSDuan Jiong dst_release(dst); 2495c92a59ecSDuan Jiong } 2496c92a59ecSDuan Jiong 24973a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 24983a5ad2eeSDavid S. Miller { 2499e2d118a1SLorenzo Colitti ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark, 2500e2d118a1SLorenzo Colitti sk->sk_uid); 25013a5ad2eeSDavid S. Miller } 25023a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 25033a5ad2eeSDavid S. Miller 25040dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 25051da177e4SLinus Torvalds { 25060dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 25070dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 25080dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 25090dbaee3bSDavid S. Miller 25101da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 25111da177e4SLinus Torvalds 25125578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 25135578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 25141da177e4SLinus Torvalds 25151da177e4SLinus Torvalds /* 25161da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 25171da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 25181da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 25191da177e4SLinus Torvalds * rely only on pmtu discovery" 25201da177e4SLinus Torvalds */ 25211da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 25221da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 25231da177e4SLinus Torvalds return mtu; 25241da177e4SLinus Torvalds } 25251da177e4SLinus Torvalds 2526ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 2527d33e4553SDavid S. Miller { 2528d33e4553SDavid S. Miller struct inet6_dev *idev; 2529d4ead6b3SDavid Ahern unsigned int mtu; 2530618f9bc7SSteffen Klassert 25314b32b5adSMartin KaFai Lau mtu = dst_metric_raw(dst, RTAX_MTU); 25324b32b5adSMartin KaFai Lau if (mtu) 25334b32b5adSMartin KaFai Lau goto out; 25344b32b5adSMartin KaFai Lau 2535618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 2536d33e4553SDavid S. Miller 2537d33e4553SDavid S. Miller rcu_read_lock(); 2538d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 2539d33e4553SDavid S. Miller if (idev) 2540d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 2541d33e4553SDavid S. Miller rcu_read_unlock(); 2542d33e4553SDavid S. Miller 254330f78d8eSEric Dumazet out: 254414972cbdSRoopa Prabhu mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); 254514972cbdSRoopa Prabhu 254614972cbdSRoopa Prabhu return mtu - lwtunnel_headroom(dst->lwtstate, mtu); 2547d33e4553SDavid S. Miller } 2548d33e4553SDavid S. Miller 25493b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 255087a11578SDavid S. Miller struct flowi6 *fl6) 25511da177e4SLinus Torvalds { 255287a11578SDavid S. Miller struct dst_entry *dst; 25531da177e4SLinus Torvalds struct rt6_info *rt; 25541da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 2555c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 25561da177e4SLinus Torvalds 255738308473SDavid S. Miller if (unlikely(!idev)) 2558122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 25591da177e4SLinus Torvalds 2560ad706862SMartin KaFai Lau rt = ip6_dst_alloc(net, dev, 0); 256138308473SDavid S. Miller if (unlikely(!rt)) { 25621da177e4SLinus Torvalds in6_dev_put(idev); 256387a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 25641da177e4SLinus Torvalds goto out; 25651da177e4SLinus Torvalds } 25661da177e4SLinus Torvalds 25678e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 2568588753f1SBrendan McGrath rt->dst.input = ip6_input; 25698e2ec639SYan, Zheng rt->dst.output = ip6_output; 2570550bab42SJulian Anastasov rt->rt6i_gateway = fl6->daddr; 257187a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 25728e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 25738e2ec639SYan, Zheng rt->rt6i_idev = idev; 257414edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 25751da177e4SLinus Torvalds 25764c981e28SIdo Schimmel /* Add this dst into uncached_list so that rt6_disable_ip() can 2577587fea74SWei Wang * do proper release of the net_device 2578587fea74SWei Wang */ 2579587fea74SWei Wang rt6_uncached_list_add(rt); 258081eb8447SWei Wang atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache); 25811da177e4SLinus Torvalds 258287a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 258387a11578SDavid S. Miller 25841da177e4SLinus Torvalds out: 258587a11578SDavid S. Miller return dst; 25861da177e4SLinus Torvalds } 25871da177e4SLinus Torvalds 2588569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 25891da177e4SLinus Torvalds { 259086393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 25917019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 25927019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 25937019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 25947019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 25957019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 2596fc66f95cSEric Dumazet int entries; 25971da177e4SLinus Torvalds 2598fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 259949a18d86SMichal Kubeček if (time_after(rt_last_gc + rt_min_interval, jiffies) && 2600fc66f95cSEric Dumazet entries <= rt_max_size) 26011da177e4SLinus Torvalds goto out; 26021da177e4SLinus Torvalds 26036891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 260414956643SLi RongQing fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true); 2605fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 2606fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 26077019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 26081da177e4SLinus Torvalds out: 26097019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 2610fc66f95cSEric Dumazet return entries > rt_max_size; 26111da177e4SLinus Torvalds } 26121da177e4SLinus Torvalds 26138d1c802bSDavid Ahern static int ip6_convert_metrics(struct net *net, struct fib6_info *rt, 2614d4ead6b3SDavid Ahern struct fib6_config *cfg) 2615e715b6d3SFlorian Westphal { 2616263243d6SEric Dumazet struct dst_metrics *p; 2617e715b6d3SFlorian Westphal 2618263243d6SEric Dumazet if (!cfg->fc_mx) 2619263243d6SEric Dumazet return 0; 2620263243d6SEric Dumazet 2621263243d6SEric Dumazet p = kzalloc(sizeof(*rt->fib6_metrics), GFP_KERNEL); 2622263243d6SEric Dumazet if (unlikely(!p)) 2623e715b6d3SFlorian Westphal return -ENOMEM; 2624e715b6d3SFlorian Westphal 2625263243d6SEric Dumazet refcount_set(&p->refcnt, 1); 2626263243d6SEric Dumazet rt->fib6_metrics = p; 2627ea697639SDaniel Borkmann 2628263243d6SEric Dumazet return ip_metrics_convert(net, cfg->fc_mx, cfg->fc_mx_len, p->metrics); 2629e715b6d3SFlorian Westphal } 26301da177e4SLinus Torvalds 26318c14586fSDavid Ahern static struct rt6_info *ip6_nh_lookup_table(struct net *net, 26328c14586fSDavid Ahern struct fib6_config *cfg, 2633f4797b33SDavid Ahern const struct in6_addr *gw_addr, 2634f4797b33SDavid Ahern u32 tbid, int flags) 26358c14586fSDavid Ahern { 26368c14586fSDavid Ahern struct flowi6 fl6 = { 26378c14586fSDavid Ahern .flowi6_oif = cfg->fc_ifindex, 26388c14586fSDavid Ahern .daddr = *gw_addr, 26398c14586fSDavid Ahern .saddr = cfg->fc_prefsrc, 26408c14586fSDavid Ahern }; 26418c14586fSDavid Ahern struct fib6_table *table; 26428c14586fSDavid Ahern struct rt6_info *rt; 26438c14586fSDavid Ahern 2644f4797b33SDavid Ahern table = fib6_get_table(net, tbid); 26458c14586fSDavid Ahern if (!table) 26468c14586fSDavid Ahern return NULL; 26478c14586fSDavid Ahern 26488c14586fSDavid Ahern if (!ipv6_addr_any(&cfg->fc_prefsrc)) 26498c14586fSDavid Ahern flags |= RT6_LOOKUP_F_HAS_SADDR; 26508c14586fSDavid Ahern 2651f4797b33SDavid Ahern flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE; 2652b75cc8f9SDavid Ahern rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, NULL, flags); 26538c14586fSDavid Ahern 26548c14586fSDavid Ahern /* if table lookup failed, fall back to full lookup */ 26558c14586fSDavid Ahern if (rt == net->ipv6.ip6_null_entry) { 26568c14586fSDavid Ahern ip6_rt_put(rt); 26578c14586fSDavid Ahern rt = NULL; 26588c14586fSDavid Ahern } 26598c14586fSDavid Ahern 26608c14586fSDavid Ahern return rt; 26618c14586fSDavid Ahern } 26628c14586fSDavid Ahern 2663fc1e64e1SDavid Ahern static int ip6_route_check_nh_onlink(struct net *net, 2664fc1e64e1SDavid Ahern struct fib6_config *cfg, 26659fbb704cSDavid Ahern const struct net_device *dev, 2666fc1e64e1SDavid Ahern struct netlink_ext_ack *extack) 2667fc1e64e1SDavid Ahern { 266844750f84SDavid Ahern u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN; 2669fc1e64e1SDavid Ahern const struct in6_addr *gw_addr = &cfg->fc_gateway; 2670fc1e64e1SDavid Ahern u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT; 2671fc1e64e1SDavid Ahern struct rt6_info *grt; 2672fc1e64e1SDavid Ahern int err; 2673fc1e64e1SDavid Ahern 2674fc1e64e1SDavid Ahern err = 0; 2675fc1e64e1SDavid Ahern grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0); 2676fc1e64e1SDavid Ahern if (grt) { 267758e354c0SDavid Ahern if (!grt->dst.error && 267858e354c0SDavid Ahern (grt->rt6i_flags & flags || dev != grt->dst.dev)) { 267944750f84SDavid Ahern NL_SET_ERR_MSG(extack, 268044750f84SDavid Ahern "Nexthop has invalid gateway or device mismatch"); 2681fc1e64e1SDavid Ahern err = -EINVAL; 2682fc1e64e1SDavid Ahern } 2683fc1e64e1SDavid Ahern 2684fc1e64e1SDavid Ahern ip6_rt_put(grt); 2685fc1e64e1SDavid Ahern } 2686fc1e64e1SDavid Ahern 2687fc1e64e1SDavid Ahern return err; 2688fc1e64e1SDavid Ahern } 2689fc1e64e1SDavid Ahern 26901edce99fSDavid Ahern static int ip6_route_check_nh(struct net *net, 26911edce99fSDavid Ahern struct fib6_config *cfg, 26921edce99fSDavid Ahern struct net_device **_dev, 26931edce99fSDavid Ahern struct inet6_dev **idev) 26941edce99fSDavid Ahern { 26951edce99fSDavid Ahern const struct in6_addr *gw_addr = &cfg->fc_gateway; 26961edce99fSDavid Ahern struct net_device *dev = _dev ? *_dev : NULL; 26971edce99fSDavid Ahern struct rt6_info *grt = NULL; 26981edce99fSDavid Ahern int err = -EHOSTUNREACH; 26991edce99fSDavid Ahern 27001edce99fSDavid Ahern if (cfg->fc_table) { 2701f4797b33SDavid Ahern int flags = RT6_LOOKUP_F_IFACE; 2702f4797b33SDavid Ahern 2703f4797b33SDavid Ahern grt = ip6_nh_lookup_table(net, cfg, gw_addr, 2704f4797b33SDavid Ahern cfg->fc_table, flags); 27051edce99fSDavid Ahern if (grt) { 27061edce99fSDavid Ahern if (grt->rt6i_flags & RTF_GATEWAY || 27071edce99fSDavid Ahern (dev && dev != grt->dst.dev)) { 27081edce99fSDavid Ahern ip6_rt_put(grt); 27091edce99fSDavid Ahern grt = NULL; 27101edce99fSDavid Ahern } 27111edce99fSDavid Ahern } 27121edce99fSDavid Ahern } 27131edce99fSDavid Ahern 27141edce99fSDavid Ahern if (!grt) 2715b75cc8f9SDavid Ahern grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, NULL, 1); 27161edce99fSDavid Ahern 27171edce99fSDavid Ahern if (!grt) 27181edce99fSDavid Ahern goto out; 27191edce99fSDavid Ahern 27201edce99fSDavid Ahern if (dev) { 27211edce99fSDavid Ahern if (dev != grt->dst.dev) { 27221edce99fSDavid Ahern ip6_rt_put(grt); 27231edce99fSDavid Ahern goto out; 27241edce99fSDavid Ahern } 27251edce99fSDavid Ahern } else { 27261edce99fSDavid Ahern *_dev = dev = grt->dst.dev; 27271edce99fSDavid Ahern *idev = grt->rt6i_idev; 27281edce99fSDavid Ahern dev_hold(dev); 27291edce99fSDavid Ahern in6_dev_hold(grt->rt6i_idev); 27301edce99fSDavid Ahern } 27311edce99fSDavid Ahern 27321edce99fSDavid Ahern if (!(grt->rt6i_flags & RTF_GATEWAY)) 27331edce99fSDavid Ahern err = 0; 27341edce99fSDavid Ahern 27351edce99fSDavid Ahern ip6_rt_put(grt); 27361edce99fSDavid Ahern 27371edce99fSDavid Ahern out: 27381edce99fSDavid Ahern return err; 27391edce99fSDavid Ahern } 27401edce99fSDavid Ahern 27419fbb704cSDavid Ahern static int ip6_validate_gw(struct net *net, struct fib6_config *cfg, 27429fbb704cSDavid Ahern struct net_device **_dev, struct inet6_dev **idev, 27439fbb704cSDavid Ahern struct netlink_ext_ack *extack) 27449fbb704cSDavid Ahern { 27459fbb704cSDavid Ahern const struct in6_addr *gw_addr = &cfg->fc_gateway; 27469fbb704cSDavid Ahern int gwa_type = ipv6_addr_type(gw_addr); 2747232378e8SDavid Ahern bool skip_dev = gwa_type & IPV6_ADDR_LINKLOCAL ? false : true; 27489fbb704cSDavid Ahern const struct net_device *dev = *_dev; 2749232378e8SDavid Ahern bool need_addr_check = !dev; 27509fbb704cSDavid Ahern int err = -EINVAL; 27519fbb704cSDavid Ahern 27529fbb704cSDavid Ahern /* if gw_addr is local we will fail to detect this in case 27539fbb704cSDavid Ahern * address is still TENTATIVE (DAD in progress). rt6_lookup() 27549fbb704cSDavid Ahern * will return already-added prefix route via interface that 27559fbb704cSDavid Ahern * prefix route was assigned to, which might be non-loopback. 27569fbb704cSDavid Ahern */ 2757232378e8SDavid Ahern if (dev && 2758232378e8SDavid Ahern ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) { 2759232378e8SDavid Ahern NL_SET_ERR_MSG(extack, "Gateway can not be a local address"); 27609fbb704cSDavid Ahern goto out; 27619fbb704cSDavid Ahern } 27629fbb704cSDavid Ahern 27639fbb704cSDavid Ahern if (gwa_type != (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST)) { 27649fbb704cSDavid Ahern /* IPv6 strictly inhibits using not link-local 27659fbb704cSDavid Ahern * addresses as nexthop address. 27669fbb704cSDavid Ahern * Otherwise, router will not able to send redirects. 27679fbb704cSDavid Ahern * It is very good, but in some (rare!) circumstances 27689fbb704cSDavid Ahern * (SIT, PtP, NBMA NOARP links) it is handy to allow 27699fbb704cSDavid Ahern * some exceptions. --ANK 27709fbb704cSDavid Ahern * We allow IPv4-mapped nexthops to support RFC4798-type 27719fbb704cSDavid Ahern * addressing 27729fbb704cSDavid Ahern */ 27739fbb704cSDavid Ahern if (!(gwa_type & (IPV6_ADDR_UNICAST | IPV6_ADDR_MAPPED))) { 27749fbb704cSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid gateway address"); 27759fbb704cSDavid Ahern goto out; 27769fbb704cSDavid Ahern } 27779fbb704cSDavid Ahern 27789fbb704cSDavid Ahern if (cfg->fc_flags & RTNH_F_ONLINK) 27799fbb704cSDavid Ahern err = ip6_route_check_nh_onlink(net, cfg, dev, extack); 27809fbb704cSDavid Ahern else 27819fbb704cSDavid Ahern err = ip6_route_check_nh(net, cfg, _dev, idev); 27829fbb704cSDavid Ahern 27839fbb704cSDavid Ahern if (err) 27849fbb704cSDavid Ahern goto out; 27859fbb704cSDavid Ahern } 27869fbb704cSDavid Ahern 27879fbb704cSDavid Ahern /* reload in case device was changed */ 27889fbb704cSDavid Ahern dev = *_dev; 27899fbb704cSDavid Ahern 27909fbb704cSDavid Ahern err = -EINVAL; 27919fbb704cSDavid Ahern if (!dev) { 27929fbb704cSDavid Ahern NL_SET_ERR_MSG(extack, "Egress device not specified"); 27939fbb704cSDavid Ahern goto out; 27949fbb704cSDavid Ahern } else if (dev->flags & IFF_LOOPBACK) { 27959fbb704cSDavid Ahern NL_SET_ERR_MSG(extack, 27969fbb704cSDavid Ahern "Egress device can not be loopback device for this route"); 27979fbb704cSDavid Ahern goto out; 27989fbb704cSDavid Ahern } 2799232378e8SDavid Ahern 2800232378e8SDavid Ahern /* if we did not check gw_addr above, do so now that the 2801232378e8SDavid Ahern * egress device has been resolved. 2802232378e8SDavid Ahern */ 2803232378e8SDavid Ahern if (need_addr_check && 2804232378e8SDavid Ahern ipv6_chk_addr_and_flags(net, gw_addr, dev, skip_dev, 0, 0)) { 2805232378e8SDavid Ahern NL_SET_ERR_MSG(extack, "Gateway can not be a local address"); 2806232378e8SDavid Ahern goto out; 2807232378e8SDavid Ahern } 2808232378e8SDavid Ahern 28099fbb704cSDavid Ahern err = 0; 28109fbb704cSDavid Ahern out: 28119fbb704cSDavid Ahern return err; 28129fbb704cSDavid Ahern } 28139fbb704cSDavid Ahern 28148d1c802bSDavid Ahern static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg, 2815acb54e3cSDavid Ahern gfp_t gfp_flags, 2816333c4301SDavid Ahern struct netlink_ext_ack *extack) 28171da177e4SLinus Torvalds { 28185578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 28198d1c802bSDavid Ahern struct fib6_info *rt = NULL; 28201da177e4SLinus Torvalds struct net_device *dev = NULL; 28211da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 2822c71099acSThomas Graf struct fib6_table *table; 28231da177e4SLinus Torvalds int addr_type; 28248c5b83f0SRoopa Prabhu int err = -EINVAL; 28251da177e4SLinus Torvalds 2826557c44beSDavid Ahern /* RTF_PCPU is an internal flag; can not be set by userspace */ 2827d5d531cbSDavid Ahern if (cfg->fc_flags & RTF_PCPU) { 2828d5d531cbSDavid Ahern NL_SET_ERR_MSG(extack, "Userspace can not set RTF_PCPU"); 2829557c44beSDavid Ahern goto out; 2830d5d531cbSDavid Ahern } 2831557c44beSDavid Ahern 28322ea2352eSWei Wang /* RTF_CACHE is an internal flag; can not be set by userspace */ 28332ea2352eSWei Wang if (cfg->fc_flags & RTF_CACHE) { 28342ea2352eSWei Wang NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE"); 28352ea2352eSWei Wang goto out; 28362ea2352eSWei Wang } 28372ea2352eSWei Wang 2838e8478e80SDavid Ahern if (cfg->fc_type > RTN_MAX) { 2839e8478e80SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid route type"); 2840e8478e80SDavid Ahern goto out; 2841e8478e80SDavid Ahern } 2842e8478e80SDavid Ahern 2843d5d531cbSDavid Ahern if (cfg->fc_dst_len > 128) { 2844d5d531cbSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid prefix length"); 28458c5b83f0SRoopa Prabhu goto out; 2846d5d531cbSDavid Ahern } 2847d5d531cbSDavid Ahern if (cfg->fc_src_len > 128) { 2848d5d531cbSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid source address length"); 2849d5d531cbSDavid Ahern goto out; 2850d5d531cbSDavid Ahern } 28511da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 2852d5d531cbSDavid Ahern if (cfg->fc_src_len) { 2853d5d531cbSDavid Ahern NL_SET_ERR_MSG(extack, 2854d5d531cbSDavid Ahern "Specifying source address requires IPV6_SUBTREES to be enabled"); 28558c5b83f0SRoopa Prabhu goto out; 2856d5d531cbSDavid Ahern } 28571da177e4SLinus Torvalds #endif 285886872cb5SThomas Graf if (cfg->fc_ifindex) { 28591da177e4SLinus Torvalds err = -ENODEV; 28605578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 28611da177e4SLinus Torvalds if (!dev) 28621da177e4SLinus Torvalds goto out; 28631da177e4SLinus Torvalds idev = in6_dev_get(dev); 28641da177e4SLinus Torvalds if (!idev) 28651da177e4SLinus Torvalds goto out; 28661da177e4SLinus Torvalds } 28671da177e4SLinus Torvalds 286886872cb5SThomas Graf if (cfg->fc_metric == 0) 286986872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 28701da177e4SLinus Torvalds 2871fc1e64e1SDavid Ahern if (cfg->fc_flags & RTNH_F_ONLINK) { 2872fc1e64e1SDavid Ahern if (!dev) { 2873fc1e64e1SDavid Ahern NL_SET_ERR_MSG(extack, 2874fc1e64e1SDavid Ahern "Nexthop device required for onlink"); 2875fc1e64e1SDavid Ahern err = -ENODEV; 2876fc1e64e1SDavid Ahern goto out; 2877fc1e64e1SDavid Ahern } 2878fc1e64e1SDavid Ahern 2879fc1e64e1SDavid Ahern if (!(dev->flags & IFF_UP)) { 2880fc1e64e1SDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop device is not up"); 2881fc1e64e1SDavid Ahern err = -ENETDOWN; 2882fc1e64e1SDavid Ahern goto out; 2883fc1e64e1SDavid Ahern } 2884fc1e64e1SDavid Ahern } 2885fc1e64e1SDavid Ahern 2886c71099acSThomas Graf err = -ENOBUFS; 288738308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 2888d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 2889d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 289038308473SDavid S. Miller if (!table) { 2891f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 2892d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 2893d71314b4SMatti Vaittinen } 2894d71314b4SMatti Vaittinen } else { 2895d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 2896d71314b4SMatti Vaittinen } 289738308473SDavid S. Miller 289838308473SDavid S. Miller if (!table) 2899c71099acSThomas Graf goto out; 2900c71099acSThomas Graf 29011da177e4SLinus Torvalds err = -ENOMEM; 290293531c67SDavid Ahern rt = fib6_info_alloc(gfp_flags); 290393531c67SDavid Ahern if (!rt) 29041da177e4SLinus Torvalds goto out; 290593531c67SDavid Ahern 290693531c67SDavid Ahern if (cfg->fc_flags & RTF_ADDRCONF) 290793531c67SDavid Ahern rt->dst_nocount = true; 29081da177e4SLinus Torvalds 2909d4ead6b3SDavid Ahern err = ip6_convert_metrics(net, rt, cfg); 2910d4ead6b3SDavid Ahern if (err < 0) 2911d4ead6b3SDavid Ahern goto out; 2912d4ead6b3SDavid Ahern 29131716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 291414895687SDavid Ahern fib6_set_expires(rt, jiffies + 29151716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 29161716a961SGao feng else 291714895687SDavid Ahern fib6_clean_expires(rt); 29181da177e4SLinus Torvalds 291986872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 292086872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 292193c2fb25SDavid Ahern rt->fib6_protocol = cfg->fc_protocol; 292286872cb5SThomas Graf 292386872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 29241da177e4SLinus Torvalds 292519e42e45SRoopa Prabhu if (cfg->fc_encap) { 292619e42e45SRoopa Prabhu struct lwtunnel_state *lwtstate; 292719e42e45SRoopa Prabhu 292830357d7dSDavid Ahern err = lwtunnel_build_state(cfg->fc_encap_type, 2929127eb7cdSTom Herbert cfg->fc_encap, AF_INET6, cfg, 29309ae28727SDavid Ahern &lwtstate, extack); 293119e42e45SRoopa Prabhu if (err) 293219e42e45SRoopa Prabhu goto out; 29335e670d84SDavid Ahern rt->fib6_nh.nh_lwtstate = lwtstate_get(lwtstate); 293425368623STom Herbert } 293519e42e45SRoopa Prabhu 293693c2fb25SDavid Ahern ipv6_addr_prefix(&rt->fib6_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 293793c2fb25SDavid Ahern rt->fib6_dst.plen = cfg->fc_dst_len; 293893c2fb25SDavid Ahern if (rt->fib6_dst.plen == 128) 29393b6761d1SDavid Ahern rt->dst_host = true; 29401da177e4SLinus Torvalds 29411da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 294293c2fb25SDavid Ahern ipv6_addr_prefix(&rt->fib6_src.addr, &cfg->fc_src, cfg->fc_src_len); 294393c2fb25SDavid Ahern rt->fib6_src.plen = cfg->fc_src_len; 29441da177e4SLinus Torvalds #endif 29451da177e4SLinus Torvalds 294693c2fb25SDavid Ahern rt->fib6_metric = cfg->fc_metric; 29475e670d84SDavid Ahern rt->fib6_nh.nh_weight = 1; 29481da177e4SLinus Torvalds 2949e8478e80SDavid Ahern rt->fib6_type = cfg->fc_type; 2950e8478e80SDavid Ahern 29511da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 29521da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 29531da177e4SLinus Torvalds */ 295486872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 295538308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 295638308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 295738308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 29581da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 29595578689aSDaniel Lezcano if (dev != net->loopback_dev) { 29601da177e4SLinus Torvalds if (dev) { 29611da177e4SLinus Torvalds dev_put(dev); 29621da177e4SLinus Torvalds in6_dev_put(idev); 29631da177e4SLinus Torvalds } 29645578689aSDaniel Lezcano dev = net->loopback_dev; 29651da177e4SLinus Torvalds dev_hold(dev); 29661da177e4SLinus Torvalds idev = in6_dev_get(dev); 29671da177e4SLinus Torvalds if (!idev) { 29681da177e4SLinus Torvalds err = -ENODEV; 29691da177e4SLinus Torvalds goto out; 29701da177e4SLinus Torvalds } 29711da177e4SLinus Torvalds } 297293c2fb25SDavid Ahern rt->fib6_flags = RTF_REJECT|RTF_NONEXTHOP; 29731da177e4SLinus Torvalds goto install_route; 29741da177e4SLinus Torvalds } 29751da177e4SLinus Torvalds 297686872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 29779fbb704cSDavid Ahern err = ip6_validate_gw(net, cfg, &dev, &idev, extack); 29781da177e4SLinus Torvalds if (err) 29791da177e4SLinus Torvalds goto out; 29809fbb704cSDavid Ahern 298193531c67SDavid Ahern rt->fib6_nh.nh_gw = cfg->fc_gateway; 29821da177e4SLinus Torvalds } 29831da177e4SLinus Torvalds 29841da177e4SLinus Torvalds err = -ENODEV; 298538308473SDavid S. Miller if (!dev) 29861da177e4SLinus Torvalds goto out; 29871da177e4SLinus Torvalds 2988428604fbSLorenzo Bianconi if (idev->cnf.disable_ipv6) { 2989428604fbSLorenzo Bianconi NL_SET_ERR_MSG(extack, "IPv6 is disabled on nexthop device"); 2990428604fbSLorenzo Bianconi err = -EACCES; 2991428604fbSLorenzo Bianconi goto out; 2992428604fbSLorenzo Bianconi } 2993428604fbSLorenzo Bianconi 2994955ec4cbSDavid Ahern if (!(dev->flags & IFF_UP)) { 2995955ec4cbSDavid Ahern NL_SET_ERR_MSG(extack, "Nexthop device is not up"); 2996955ec4cbSDavid Ahern err = -ENETDOWN; 2997955ec4cbSDavid Ahern goto out; 2998955ec4cbSDavid Ahern } 2999955ec4cbSDavid Ahern 3000c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 3001c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 3002d5d531cbSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid source address"); 3003c3968a85SDaniel Walter err = -EINVAL; 3004c3968a85SDaniel Walter goto out; 3005c3968a85SDaniel Walter } 300693c2fb25SDavid Ahern rt->fib6_prefsrc.addr = cfg->fc_prefsrc; 300793c2fb25SDavid Ahern rt->fib6_prefsrc.plen = 128; 3008c3968a85SDaniel Walter } else 300993c2fb25SDavid Ahern rt->fib6_prefsrc.plen = 0; 3010c3968a85SDaniel Walter 301193c2fb25SDavid Ahern rt->fib6_flags = cfg->fc_flags; 30121da177e4SLinus Torvalds 30131da177e4SLinus Torvalds install_route: 301493c2fb25SDavid Ahern if (!(rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) && 30155609b80aSIdo Schimmel !netif_carrier_ok(dev)) 30165e670d84SDavid Ahern rt->fib6_nh.nh_flags |= RTNH_F_LINKDOWN; 30175e670d84SDavid Ahern rt->fib6_nh.nh_flags |= (cfg->fc_flags & RTNH_F_ONLINK); 301893531c67SDavid Ahern rt->fib6_nh.nh_dev = dev; 301993c2fb25SDavid Ahern rt->fib6_table = table; 302063152fc0SDaniel Lezcano 3021c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 302263152fc0SDaniel Lezcano 3023dcd1f572SDavid Ahern if (idev) 3024dcd1f572SDavid Ahern in6_dev_put(idev); 3025dcd1f572SDavid Ahern 30268c5b83f0SRoopa Prabhu return rt; 30271da177e4SLinus Torvalds out: 30281da177e4SLinus Torvalds if (dev) 30291da177e4SLinus Torvalds dev_put(dev); 30301da177e4SLinus Torvalds if (idev) 30311da177e4SLinus Torvalds in6_dev_put(idev); 30326b9ea5a6SRoopa Prabhu 303393531c67SDavid Ahern fib6_info_release(rt); 30348c5b83f0SRoopa Prabhu return ERR_PTR(err); 30356b9ea5a6SRoopa Prabhu } 30366b9ea5a6SRoopa Prabhu 3037acb54e3cSDavid Ahern int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags, 3038acb54e3cSDavid Ahern struct netlink_ext_ack *extack) 30396b9ea5a6SRoopa Prabhu { 30408d1c802bSDavid Ahern struct fib6_info *rt; 30416b9ea5a6SRoopa Prabhu int err; 30426b9ea5a6SRoopa Prabhu 3043acb54e3cSDavid Ahern rt = ip6_route_info_create(cfg, gfp_flags, extack); 3044d4ead6b3SDavid Ahern if (IS_ERR(rt)) 3045d4ead6b3SDavid Ahern return PTR_ERR(rt); 30466b9ea5a6SRoopa Prabhu 3047d4ead6b3SDavid Ahern err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack); 304893531c67SDavid Ahern fib6_info_release(rt); 30496b9ea5a6SRoopa Prabhu 30501da177e4SLinus Torvalds return err; 30511da177e4SLinus Torvalds } 30521da177e4SLinus Torvalds 30538d1c802bSDavid Ahern static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info) 30541da177e4SLinus Torvalds { 3055afb1d4b5SDavid Ahern struct net *net = info->nl_net; 3056c71099acSThomas Graf struct fib6_table *table; 3057afb1d4b5SDavid Ahern int err; 30581da177e4SLinus Torvalds 3059421842edSDavid Ahern if (rt == net->ipv6.fib6_null_entry) { 30606825a26cSGao feng err = -ENOENT; 30616825a26cSGao feng goto out; 30626825a26cSGao feng } 30636c813a72SPatrick McHardy 306493c2fb25SDavid Ahern table = rt->fib6_table; 306566f5d6ceSWei Wang spin_lock_bh(&table->tb6_lock); 306686872cb5SThomas Graf err = fib6_del(rt, info); 306766f5d6ceSWei Wang spin_unlock_bh(&table->tb6_lock); 30681da177e4SLinus Torvalds 30696825a26cSGao feng out: 307093531c67SDavid Ahern fib6_info_release(rt); 30711da177e4SLinus Torvalds return err; 30721da177e4SLinus Torvalds } 30731da177e4SLinus Torvalds 30748d1c802bSDavid Ahern int ip6_del_rt(struct net *net, struct fib6_info *rt) 3075e0a1ad73SThomas Graf { 3076afb1d4b5SDavid Ahern struct nl_info info = { .nl_net = net }; 3077afb1d4b5SDavid Ahern 3078528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 3079e0a1ad73SThomas Graf } 3080e0a1ad73SThomas Graf 30818d1c802bSDavid Ahern static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg) 30820ae81335SDavid Ahern { 30830ae81335SDavid Ahern struct nl_info *info = &cfg->fc_nlinfo; 3084e3330039SWANG Cong struct net *net = info->nl_net; 308516a16cd3SDavid Ahern struct sk_buff *skb = NULL; 30860ae81335SDavid Ahern struct fib6_table *table; 3087e3330039SWANG Cong int err = -ENOENT; 30880ae81335SDavid Ahern 3089421842edSDavid Ahern if (rt == net->ipv6.fib6_null_entry) 3090e3330039SWANG Cong goto out_put; 309193c2fb25SDavid Ahern table = rt->fib6_table; 309266f5d6ceSWei Wang spin_lock_bh(&table->tb6_lock); 30930ae81335SDavid Ahern 309493c2fb25SDavid Ahern if (rt->fib6_nsiblings && cfg->fc_delete_all_nh) { 30958d1c802bSDavid Ahern struct fib6_info *sibling, *next_sibling; 30960ae81335SDavid Ahern 309716a16cd3SDavid Ahern /* prefer to send a single notification with all hops */ 309816a16cd3SDavid Ahern skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 309916a16cd3SDavid Ahern if (skb) { 310016a16cd3SDavid Ahern u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; 310116a16cd3SDavid Ahern 3102d4ead6b3SDavid Ahern if (rt6_fill_node(net, skb, rt, NULL, 310316a16cd3SDavid Ahern NULL, NULL, 0, RTM_DELROUTE, 310416a16cd3SDavid Ahern info->portid, seq, 0) < 0) { 310516a16cd3SDavid Ahern kfree_skb(skb); 310616a16cd3SDavid Ahern skb = NULL; 310716a16cd3SDavid Ahern } else 310816a16cd3SDavid Ahern info->skip_notify = 1; 310916a16cd3SDavid Ahern } 311016a16cd3SDavid Ahern 31110ae81335SDavid Ahern list_for_each_entry_safe(sibling, next_sibling, 311293c2fb25SDavid Ahern &rt->fib6_siblings, 311393c2fb25SDavid Ahern fib6_siblings) { 31140ae81335SDavid Ahern err = fib6_del(sibling, info); 31150ae81335SDavid Ahern if (err) 3116e3330039SWANG Cong goto out_unlock; 31170ae81335SDavid Ahern } 31180ae81335SDavid Ahern } 31190ae81335SDavid Ahern 31200ae81335SDavid Ahern err = fib6_del(rt, info); 3121e3330039SWANG Cong out_unlock: 312266f5d6ceSWei Wang spin_unlock_bh(&table->tb6_lock); 3123e3330039SWANG Cong out_put: 312493531c67SDavid Ahern fib6_info_release(rt); 312516a16cd3SDavid Ahern 312616a16cd3SDavid Ahern if (skb) { 3127e3330039SWANG Cong rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 312816a16cd3SDavid Ahern info->nlh, gfp_any()); 312916a16cd3SDavid Ahern } 31300ae81335SDavid Ahern return err; 31310ae81335SDavid Ahern } 31320ae81335SDavid Ahern 313323fb93a4SDavid Ahern static int ip6_del_cached_rt(struct rt6_info *rt, struct fib6_config *cfg) 313423fb93a4SDavid Ahern { 313523fb93a4SDavid Ahern int rc = -ESRCH; 313623fb93a4SDavid Ahern 313723fb93a4SDavid Ahern if (cfg->fc_ifindex && rt->dst.dev->ifindex != cfg->fc_ifindex) 313823fb93a4SDavid Ahern goto out; 313923fb93a4SDavid Ahern 314023fb93a4SDavid Ahern if (cfg->fc_flags & RTF_GATEWAY && 314123fb93a4SDavid Ahern !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 314223fb93a4SDavid Ahern goto out; 314323fb93a4SDavid Ahern if (dst_hold_safe(&rt->dst)) 314423fb93a4SDavid Ahern rc = rt6_remove_exception_rt(rt); 314523fb93a4SDavid Ahern out: 314623fb93a4SDavid Ahern return rc; 314723fb93a4SDavid Ahern } 314823fb93a4SDavid Ahern 3149333c4301SDavid Ahern static int ip6_route_del(struct fib6_config *cfg, 3150333c4301SDavid Ahern struct netlink_ext_ack *extack) 31511da177e4SLinus Torvalds { 31528d1c802bSDavid Ahern struct rt6_info *rt_cache; 3153c71099acSThomas Graf struct fib6_table *table; 31548d1c802bSDavid Ahern struct fib6_info *rt; 31551da177e4SLinus Torvalds struct fib6_node *fn; 31561da177e4SLinus Torvalds int err = -ESRCH; 31571da177e4SLinus Torvalds 31585578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 3159d5d531cbSDavid Ahern if (!table) { 3160d5d531cbSDavid Ahern NL_SET_ERR_MSG(extack, "FIB table does not exist"); 3161c71099acSThomas Graf return err; 3162d5d531cbSDavid Ahern } 31631da177e4SLinus Torvalds 316466f5d6ceSWei Wang rcu_read_lock(); 3165c71099acSThomas Graf 3166c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 316786872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 316838fbeeeeSWei Wang &cfg->fc_src, cfg->fc_src_len, 31692b760fcfSWei Wang !(cfg->fc_flags & RTF_CACHE)); 31701da177e4SLinus Torvalds 31711da177e4SLinus Torvalds if (fn) { 317266f5d6ceSWei Wang for_each_fib6_node_rt_rcu(fn) { 31732b760fcfSWei Wang if (cfg->fc_flags & RTF_CACHE) { 317423fb93a4SDavid Ahern int rc; 317523fb93a4SDavid Ahern 31762b760fcfSWei Wang rt_cache = rt6_find_cached_rt(rt, &cfg->fc_dst, 31772b760fcfSWei Wang &cfg->fc_src); 317823fb93a4SDavid Ahern if (rt_cache) { 317923fb93a4SDavid Ahern rc = ip6_del_cached_rt(rt_cache, cfg); 318023fb93a4SDavid Ahern if (rc != -ESRCH) 318123fb93a4SDavid Ahern return rc; 318223fb93a4SDavid Ahern } 31831f56a01fSMartin KaFai Lau continue; 31842b760fcfSWei Wang } 318586872cb5SThomas Graf if (cfg->fc_ifindex && 31865e670d84SDavid Ahern (!rt->fib6_nh.nh_dev || 31875e670d84SDavid Ahern rt->fib6_nh.nh_dev->ifindex != cfg->fc_ifindex)) 31881da177e4SLinus Torvalds continue; 318986872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 31905e670d84SDavid Ahern !ipv6_addr_equal(&cfg->fc_gateway, &rt->fib6_nh.nh_gw)) 31911da177e4SLinus Torvalds continue; 319293c2fb25SDavid Ahern if (cfg->fc_metric && cfg->fc_metric != rt->fib6_metric) 31931da177e4SLinus Torvalds continue; 319493c2fb25SDavid Ahern if (cfg->fc_protocol && cfg->fc_protocol != rt->fib6_protocol) 3195c2ed1880SMantas M continue; 319693531c67SDavid Ahern fib6_info_hold(rt); 319766f5d6ceSWei Wang rcu_read_unlock(); 31981da177e4SLinus Torvalds 31990ae81335SDavid Ahern /* if gateway was specified only delete the one hop */ 32000ae81335SDavid Ahern if (cfg->fc_flags & RTF_GATEWAY) 320186872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 32020ae81335SDavid Ahern 32030ae81335SDavid Ahern return __ip6_del_rt_siblings(rt, cfg); 32041da177e4SLinus Torvalds } 32051da177e4SLinus Torvalds } 320666f5d6ceSWei Wang rcu_read_unlock(); 32071da177e4SLinus Torvalds 32081da177e4SLinus Torvalds return err; 32091da177e4SLinus Torvalds } 32101da177e4SLinus Torvalds 32116700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 3212a6279458SYOSHIFUJI Hideaki { 3213a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 3214e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 3215e8599ff4SDavid S. Miller struct ndisc_options ndopts; 3216e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 3217e8599ff4SDavid S. Miller struct neighbour *neigh; 321871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 struct rd_msg *msg; 32196e157b6aSDavid S. Miller int optlen, on_link; 32206e157b6aSDavid S. Miller u8 *lladdr; 3221e8599ff4SDavid S. Miller 322229a3cad5SSimon Horman optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 322371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 optlen -= sizeof(*msg); 3224e8599ff4SDavid S. Miller 3225e8599ff4SDavid S. Miller if (optlen < 0) { 32266e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 3227e8599ff4SDavid S. Miller return; 3228e8599ff4SDavid S. Miller } 3229e8599ff4SDavid S. Miller 323071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 msg = (struct rd_msg *)icmp6_hdr(skb); 3231e8599ff4SDavid S. Miller 323271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_is_multicast(&msg->dest)) { 32336e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 3234e8599ff4SDavid S. Miller return; 3235e8599ff4SDavid S. Miller } 3236e8599ff4SDavid S. Miller 32376e157b6aSDavid S. Miller on_link = 0; 323871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_equal(&msg->dest, &msg->target)) { 3239e8599ff4SDavid S. Miller on_link = 1; 324071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 } else if (ipv6_addr_type(&msg->target) != 3241e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 32426e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 3243e8599ff4SDavid S. Miller return; 3244e8599ff4SDavid S. Miller } 3245e8599ff4SDavid S. Miller 3246e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 3247e8599ff4SDavid S. Miller if (!in6_dev) 3248e8599ff4SDavid S. Miller return; 3249e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 3250e8599ff4SDavid S. Miller return; 3251e8599ff4SDavid S. Miller 3252e8599ff4SDavid S. Miller /* RFC2461 8.1: 3253e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 3254e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 3255e8599ff4SDavid S. Miller */ 3256e8599ff4SDavid S. Miller 3257f997c55cSAlexander Aring if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) { 3258e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 3259e8599ff4SDavid S. Miller return; 3260e8599ff4SDavid S. Miller } 32616e157b6aSDavid S. Miller 32626e157b6aSDavid S. Miller lladdr = NULL; 3263e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 3264e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 3265e8599ff4SDavid S. Miller skb->dev); 3266e8599ff4SDavid S. Miller if (!lladdr) { 3267e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 3268e8599ff4SDavid S. Miller return; 3269e8599ff4SDavid S. Miller } 3270e8599ff4SDavid S. Miller } 3271e8599ff4SDavid S. Miller 32726e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 3273ec13ad1dSMatthias Schiffer if (rt->rt6i_flags & RTF_REJECT) { 32746e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 32756e157b6aSDavid S. Miller return; 32766e157b6aSDavid S. Miller } 32776e157b6aSDavid S. Miller 32786e157b6aSDavid S. Miller /* Redirect received -> path was valid. 32796e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 32806e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 32816e157b6aSDavid S. Miller */ 32820dec879fSJulian Anastasov dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr); 32836e157b6aSDavid S. Miller 328471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 3285e8599ff4SDavid S. Miller if (!neigh) 3286e8599ff4SDavid S. Miller return; 3287e8599ff4SDavid S. Miller 32881da177e4SLinus Torvalds /* 32891da177e4SLinus Torvalds * We have finally decided to accept it. 32901da177e4SLinus Torvalds */ 32911da177e4SLinus Torvalds 3292f997c55cSAlexander Aring ndisc_update(skb->dev, neigh, lladdr, NUD_STALE, 32931da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 32941da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 32951da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 3296f997c55cSAlexander Aring NEIGH_UPDATE_F_ISROUTER)), 3297f997c55cSAlexander Aring NDISC_REDIRECT, &ndopts); 32981da177e4SLinus Torvalds 3299*4d85cd0cSDavid Ahern rcu_read_lock(); 330023fb93a4SDavid Ahern nrt = ip6_rt_cache_alloc(rt->from, &msg->dest, NULL); 3301*4d85cd0cSDavid Ahern rcu_read_unlock(); 330238308473SDavid S. Miller if (!nrt) 33031da177e4SLinus Torvalds goto out; 33041da177e4SLinus Torvalds 33051da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 33061da177e4SLinus Torvalds if (on_link) 33071da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 33081da177e4SLinus Torvalds 33094e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 33101da177e4SLinus Torvalds 33112b760fcfSWei Wang /* No need to remove rt from the exception table if rt is 33122b760fcfSWei Wang * a cached route because rt6_insert_exception() will 33132b760fcfSWei Wang * takes care of it 33142b760fcfSWei Wang */ 3315d4ead6b3SDavid Ahern if (rt6_insert_exception(nrt, rt->from)) { 33162b760fcfSWei Wang dst_release_immediate(&nrt->dst); 33172b760fcfSWei Wang goto out; 33182b760fcfSWei Wang } 33191da177e4SLinus Torvalds 3320d8d1f30bSChangli Gao netevent.old = &rt->dst; 3321d8d1f30bSChangli Gao netevent.new = &nrt->dst; 332271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 netevent.daddr = &msg->dest; 332360592833SYOSHIFUJI Hideaki / 吉藤英明 netevent.neigh = neigh; 33248d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 33258d71740cSTom Tucker 33261da177e4SLinus Torvalds out: 3327e8599ff4SDavid S. Miller neigh_release(neigh); 33286e157b6aSDavid S. Miller } 33296e157b6aSDavid S. Miller 333070ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 33318d1c802bSDavid Ahern static struct fib6_info *rt6_get_route_info(struct net *net, 3332b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 3333830218c1SDavid Ahern const struct in6_addr *gwaddr, 3334830218c1SDavid Ahern struct net_device *dev) 333570ceb4f5SYOSHIFUJI Hideaki { 3336830218c1SDavid Ahern u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO; 3337830218c1SDavid Ahern int ifindex = dev->ifindex; 333870ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 33398d1c802bSDavid Ahern struct fib6_info *rt = NULL; 3340c71099acSThomas Graf struct fib6_table *table; 334170ceb4f5SYOSHIFUJI Hideaki 3342830218c1SDavid Ahern table = fib6_get_table(net, tb_id); 334338308473SDavid S. Miller if (!table) 3344c71099acSThomas Graf return NULL; 3345c71099acSThomas Graf 334666f5d6ceSWei Wang rcu_read_lock(); 334738fbeeeeSWei Wang fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true); 334870ceb4f5SYOSHIFUJI Hideaki if (!fn) 334970ceb4f5SYOSHIFUJI Hideaki goto out; 335070ceb4f5SYOSHIFUJI Hideaki 335166f5d6ceSWei Wang for_each_fib6_node_rt_rcu(fn) { 33525e670d84SDavid Ahern if (rt->fib6_nh.nh_dev->ifindex != ifindex) 335370ceb4f5SYOSHIFUJI Hideaki continue; 335493c2fb25SDavid Ahern if ((rt->fib6_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 335570ceb4f5SYOSHIFUJI Hideaki continue; 33565e670d84SDavid Ahern if (!ipv6_addr_equal(&rt->fib6_nh.nh_gw, gwaddr)) 335770ceb4f5SYOSHIFUJI Hideaki continue; 33588d1c802bSDavid Ahern fib6_info_hold(rt); 335970ceb4f5SYOSHIFUJI Hideaki break; 336070ceb4f5SYOSHIFUJI Hideaki } 336170ceb4f5SYOSHIFUJI Hideaki out: 336266f5d6ceSWei Wang rcu_read_unlock(); 336370ceb4f5SYOSHIFUJI Hideaki return rt; 336470ceb4f5SYOSHIFUJI Hideaki } 336570ceb4f5SYOSHIFUJI Hideaki 33668d1c802bSDavid Ahern static struct fib6_info *rt6_add_route_info(struct net *net, 3367b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 3368830218c1SDavid Ahern const struct in6_addr *gwaddr, 3369830218c1SDavid Ahern struct net_device *dev, 337095c96174SEric Dumazet unsigned int pref) 337170ceb4f5SYOSHIFUJI Hideaki { 337286872cb5SThomas Graf struct fib6_config cfg = { 3373238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 3374830218c1SDavid Ahern .fc_ifindex = dev->ifindex, 337586872cb5SThomas Graf .fc_dst_len = prefixlen, 337686872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 337786872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 3378b91d5329SXin Long .fc_protocol = RTPROT_RA, 3379e8478e80SDavid Ahern .fc_type = RTN_UNICAST, 338015e47304SEric W. Biederman .fc_nlinfo.portid = 0, 3381efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 3382efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 338386872cb5SThomas Graf }; 338470ceb4f5SYOSHIFUJI Hideaki 3385830218c1SDavid Ahern cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO, 33864e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 33874e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 338886872cb5SThomas Graf 3389e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 3390e317da96SYOSHIFUJI Hideaki if (!prefixlen) 339186872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 339270ceb4f5SYOSHIFUJI Hideaki 3393acb54e3cSDavid Ahern ip6_route_add(&cfg, GFP_ATOMIC, NULL); 339470ceb4f5SYOSHIFUJI Hideaki 3395830218c1SDavid Ahern return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev); 339670ceb4f5SYOSHIFUJI Hideaki } 339770ceb4f5SYOSHIFUJI Hideaki #endif 339870ceb4f5SYOSHIFUJI Hideaki 33998d1c802bSDavid Ahern struct fib6_info *rt6_get_dflt_router(struct net *net, 3400afb1d4b5SDavid Ahern const struct in6_addr *addr, 3401afb1d4b5SDavid Ahern struct net_device *dev) 34021da177e4SLinus Torvalds { 3403830218c1SDavid Ahern u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT; 34048d1c802bSDavid Ahern struct fib6_info *rt; 3405c71099acSThomas Graf struct fib6_table *table; 34061da177e4SLinus Torvalds 3407afb1d4b5SDavid Ahern table = fib6_get_table(net, tb_id); 340838308473SDavid S. Miller if (!table) 3409c71099acSThomas Graf return NULL; 34101da177e4SLinus Torvalds 341166f5d6ceSWei Wang rcu_read_lock(); 341266f5d6ceSWei Wang for_each_fib6_node_rt_rcu(&table->tb6_root) { 34135e670d84SDavid Ahern if (dev == rt->fib6_nh.nh_dev && 341493c2fb25SDavid Ahern ((rt->fib6_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 34155e670d84SDavid Ahern ipv6_addr_equal(&rt->fib6_nh.nh_gw, addr)) 34161da177e4SLinus Torvalds break; 34171da177e4SLinus Torvalds } 34181da177e4SLinus Torvalds if (rt) 34198d1c802bSDavid Ahern fib6_info_hold(rt); 342066f5d6ceSWei Wang rcu_read_unlock(); 34211da177e4SLinus Torvalds return rt; 34221da177e4SLinus Torvalds } 34231da177e4SLinus Torvalds 34248d1c802bSDavid Ahern struct fib6_info *rt6_add_dflt_router(struct net *net, 3425afb1d4b5SDavid Ahern const struct in6_addr *gwaddr, 3426ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 3427ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 34281da177e4SLinus Torvalds { 342986872cb5SThomas Graf struct fib6_config cfg = { 3430ca254490SDavid Ahern .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT, 3431238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 343286872cb5SThomas Graf .fc_ifindex = dev->ifindex, 343386872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 343486872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 3435b91d5329SXin Long .fc_protocol = RTPROT_RA, 3436e8478e80SDavid Ahern .fc_type = RTN_UNICAST, 343715e47304SEric W. Biederman .fc_nlinfo.portid = 0, 34385578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 3439afb1d4b5SDavid Ahern .fc_nlinfo.nl_net = net, 344086872cb5SThomas Graf }; 34411da177e4SLinus Torvalds 34424e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 34431da177e4SLinus Torvalds 3444acb54e3cSDavid Ahern if (!ip6_route_add(&cfg, GFP_ATOMIC, NULL)) { 3445830218c1SDavid Ahern struct fib6_table *table; 3446830218c1SDavid Ahern 3447830218c1SDavid Ahern table = fib6_get_table(dev_net(dev), cfg.fc_table); 3448830218c1SDavid Ahern if (table) 3449830218c1SDavid Ahern table->flags |= RT6_TABLE_HAS_DFLT_ROUTER; 3450830218c1SDavid Ahern } 34511da177e4SLinus Torvalds 3452afb1d4b5SDavid Ahern return rt6_get_dflt_router(net, gwaddr, dev); 34531da177e4SLinus Torvalds } 34541da177e4SLinus Torvalds 3455afb1d4b5SDavid Ahern static void __rt6_purge_dflt_routers(struct net *net, 3456afb1d4b5SDavid Ahern struct fib6_table *table) 34571da177e4SLinus Torvalds { 34588d1c802bSDavid Ahern struct fib6_info *rt; 34591da177e4SLinus Torvalds 34601da177e4SLinus Torvalds restart: 346166f5d6ceSWei Wang rcu_read_lock(); 346266f5d6ceSWei Wang for_each_fib6_node_rt_rcu(&table->tb6_root) { 3463dcd1f572SDavid Ahern struct net_device *dev = fib6_info_nh_dev(rt); 3464dcd1f572SDavid Ahern struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL; 3465dcd1f572SDavid Ahern 346693c2fb25SDavid Ahern if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 3467dcd1f572SDavid Ahern (!idev || idev->cnf.accept_ra != 2)) { 346893531c67SDavid Ahern fib6_info_hold(rt); 346966f5d6ceSWei Wang rcu_read_unlock(); 3470afb1d4b5SDavid Ahern ip6_del_rt(net, rt); 34711da177e4SLinus Torvalds goto restart; 34721da177e4SLinus Torvalds } 34731da177e4SLinus Torvalds } 347466f5d6ceSWei Wang rcu_read_unlock(); 3475830218c1SDavid Ahern 3476830218c1SDavid Ahern table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER; 3477830218c1SDavid Ahern } 3478830218c1SDavid Ahern 3479830218c1SDavid Ahern void rt6_purge_dflt_routers(struct net *net) 3480830218c1SDavid Ahern { 3481830218c1SDavid Ahern struct fib6_table *table; 3482830218c1SDavid Ahern struct hlist_head *head; 3483830218c1SDavid Ahern unsigned int h; 3484830218c1SDavid Ahern 3485830218c1SDavid Ahern rcu_read_lock(); 3486830218c1SDavid Ahern 3487830218c1SDavid Ahern for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { 3488830218c1SDavid Ahern head = &net->ipv6.fib_table_hash[h]; 3489830218c1SDavid Ahern hlist_for_each_entry_rcu(table, head, tb6_hlist) { 3490830218c1SDavid Ahern if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER) 3491afb1d4b5SDavid Ahern __rt6_purge_dflt_routers(net, table); 3492830218c1SDavid Ahern } 3493830218c1SDavid Ahern } 3494830218c1SDavid Ahern 3495830218c1SDavid Ahern rcu_read_unlock(); 34961da177e4SLinus Torvalds } 34971da177e4SLinus Torvalds 34985578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 34995578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 350086872cb5SThomas Graf struct fib6_config *cfg) 350186872cb5SThomas Graf { 350286872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 350386872cb5SThomas Graf 3504ca254490SDavid Ahern cfg->fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ? 3505ca254490SDavid Ahern : RT6_TABLE_MAIN; 350686872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 350786872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 350886872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 350986872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 351086872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 351186872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 3512e8478e80SDavid Ahern cfg->fc_type = rtmsg->rtmsg_type; 351386872cb5SThomas Graf 35145578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 3515f1243c2dSBenjamin Thery 35164e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 35174e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 35184e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 351986872cb5SThomas Graf } 352086872cb5SThomas Graf 35215578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 35221da177e4SLinus Torvalds { 352386872cb5SThomas Graf struct fib6_config cfg; 35241da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 35251da177e4SLinus Torvalds int err; 35261da177e4SLinus Torvalds 35271da177e4SLinus Torvalds switch (cmd) { 35281da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 35291da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 3530af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 35311da177e4SLinus Torvalds return -EPERM; 35321da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 35331da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 35341da177e4SLinus Torvalds if (err) 35351da177e4SLinus Torvalds return -EFAULT; 35361da177e4SLinus Torvalds 35375578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 353886872cb5SThomas Graf 35391da177e4SLinus Torvalds rtnl_lock(); 35401da177e4SLinus Torvalds switch (cmd) { 35411da177e4SLinus Torvalds case SIOCADDRT: 3542acb54e3cSDavid Ahern err = ip6_route_add(&cfg, GFP_KERNEL, NULL); 35431da177e4SLinus Torvalds break; 35441da177e4SLinus Torvalds case SIOCDELRT: 3545333c4301SDavid Ahern err = ip6_route_del(&cfg, NULL); 35461da177e4SLinus Torvalds break; 35471da177e4SLinus Torvalds default: 35481da177e4SLinus Torvalds err = -EINVAL; 35491da177e4SLinus Torvalds } 35501da177e4SLinus Torvalds rtnl_unlock(); 35511da177e4SLinus Torvalds 35521da177e4SLinus Torvalds return err; 35533ff50b79SStephen Hemminger } 35541da177e4SLinus Torvalds 35551da177e4SLinus Torvalds return -EINVAL; 35561da177e4SLinus Torvalds } 35571da177e4SLinus Torvalds 35581da177e4SLinus Torvalds /* 35591da177e4SLinus Torvalds * Drop the packet on the floor 35601da177e4SLinus Torvalds */ 35611da177e4SLinus Torvalds 3562d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 35631da177e4SLinus Torvalds { 3564612f09e8SYOSHIFUJI Hideaki int type; 3565adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 3566612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 3567612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 35680660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 356945bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 3570bdb7cc64SStephen Suryaputra IP6_INC_STATS(dev_net(dst->dev), 3571bdb7cc64SStephen Suryaputra __in6_dev_get_safely(skb->dev), 35723bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 3573612f09e8SYOSHIFUJI Hideaki break; 3574612f09e8SYOSHIFUJI Hideaki } 3575612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 3576612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 35773bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 35783bd653c8SDenis V. Lunev ipstats_mib_noroutes); 3579612f09e8SYOSHIFUJI Hideaki break; 3580612f09e8SYOSHIFUJI Hideaki } 35813ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 35821da177e4SLinus Torvalds kfree_skb(skb); 35831da177e4SLinus Torvalds return 0; 35841da177e4SLinus Torvalds } 35851da177e4SLinus Torvalds 35869ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 35879ce8ade0SThomas Graf { 3588612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 35899ce8ade0SThomas Graf } 35909ce8ade0SThomas Graf 3591ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb) 35921da177e4SLinus Torvalds { 3593adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 3594612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 35951da177e4SLinus Torvalds } 35961da177e4SLinus Torvalds 35979ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 35989ce8ade0SThomas Graf { 3599612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 36009ce8ade0SThomas Graf } 36019ce8ade0SThomas Graf 3602ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb) 36039ce8ade0SThomas Graf { 3604adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 3605612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 36069ce8ade0SThomas Graf } 36079ce8ade0SThomas Graf 36081da177e4SLinus Torvalds /* 36091da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 36101da177e4SLinus Torvalds */ 36111da177e4SLinus Torvalds 3612360a9887SDavid Ahern struct fib6_info *addrconf_f6i_alloc(struct net *net, 3613afb1d4b5SDavid Ahern struct inet6_dev *idev, 36141da177e4SLinus Torvalds const struct in6_addr *addr, 3615acb54e3cSDavid Ahern bool anycast, gfp_t gfp_flags) 36161da177e4SLinus Torvalds { 3617ca254490SDavid Ahern u32 tb_id; 36184832c30dSDavid Ahern struct net_device *dev = idev->dev; 3619360a9887SDavid Ahern struct fib6_info *f6i; 36205f02ce24SDavid Ahern 3621360a9887SDavid Ahern f6i = fib6_info_alloc(gfp_flags); 3622360a9887SDavid Ahern if (!f6i) 36231da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 36241da177e4SLinus Torvalds 3625360a9887SDavid Ahern f6i->dst_nocount = true; 3626360a9887SDavid Ahern f6i->dst_host = true; 3627360a9887SDavid Ahern f6i->fib6_protocol = RTPROT_KERNEL; 3628360a9887SDavid Ahern f6i->fib6_flags = RTF_UP | RTF_NONEXTHOP; 3629e8478e80SDavid Ahern if (anycast) { 3630360a9887SDavid Ahern f6i->fib6_type = RTN_ANYCAST; 3631360a9887SDavid Ahern f6i->fib6_flags |= RTF_ANYCAST; 3632e8478e80SDavid Ahern } else { 3633360a9887SDavid Ahern f6i->fib6_type = RTN_LOCAL; 3634360a9887SDavid Ahern f6i->fib6_flags |= RTF_LOCAL; 3635e8478e80SDavid Ahern } 36361da177e4SLinus Torvalds 3637360a9887SDavid Ahern f6i->fib6_nh.nh_gw = *addr; 363893531c67SDavid Ahern dev_hold(dev); 3639360a9887SDavid Ahern f6i->fib6_nh.nh_dev = dev; 3640360a9887SDavid Ahern f6i->fib6_dst.addr = *addr; 3641360a9887SDavid Ahern f6i->fib6_dst.plen = 128; 3642ca254490SDavid Ahern tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL; 3643360a9887SDavid Ahern f6i->fib6_table = fib6_get_table(net, tb_id); 36441da177e4SLinus Torvalds 3645360a9887SDavid Ahern return f6i; 36461da177e4SLinus Torvalds } 36471da177e4SLinus Torvalds 3648c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 3649c3968a85SDaniel Walter struct arg_dev_net_ip { 3650c3968a85SDaniel Walter struct net_device *dev; 3651c3968a85SDaniel Walter struct net *net; 3652c3968a85SDaniel Walter struct in6_addr *addr; 3653c3968a85SDaniel Walter }; 3654c3968a85SDaniel Walter 36558d1c802bSDavid Ahern static int fib6_remove_prefsrc(struct fib6_info *rt, void *arg) 3656c3968a85SDaniel Walter { 3657c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 3658c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 3659c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 3660c3968a85SDaniel Walter 36615e670d84SDavid Ahern if (((void *)rt->fib6_nh.nh_dev == dev || !dev) && 3662421842edSDavid Ahern rt != net->ipv6.fib6_null_entry && 366393c2fb25SDavid Ahern ipv6_addr_equal(addr, &rt->fib6_prefsrc.addr)) { 366460006a48SWei Wang spin_lock_bh(&rt6_exception_lock); 3665c3968a85SDaniel Walter /* remove prefsrc entry */ 366693c2fb25SDavid Ahern rt->fib6_prefsrc.plen = 0; 366760006a48SWei Wang /* need to update cache as well */ 366860006a48SWei Wang rt6_exceptions_remove_prefsrc(rt); 366960006a48SWei Wang spin_unlock_bh(&rt6_exception_lock); 3670c3968a85SDaniel Walter } 3671c3968a85SDaniel Walter return 0; 3672c3968a85SDaniel Walter } 3673c3968a85SDaniel Walter 3674c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 3675c3968a85SDaniel Walter { 3676c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 3677c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 3678c3968a85SDaniel Walter .dev = ifp->idev->dev, 3679c3968a85SDaniel Walter .net = net, 3680c3968a85SDaniel Walter .addr = &ifp->addr, 3681c3968a85SDaniel Walter }; 36820c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 3683c3968a85SDaniel Walter } 3684c3968a85SDaniel Walter 3685be7a010dSDuan Jiong #define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) 3686be7a010dSDuan Jiong 3687be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */ 36888d1c802bSDavid Ahern static int fib6_clean_tohost(struct fib6_info *rt, void *arg) 3689be7a010dSDuan Jiong { 3690be7a010dSDuan Jiong struct in6_addr *gateway = (struct in6_addr *)arg; 3691be7a010dSDuan Jiong 369293c2fb25SDavid Ahern if (((rt->fib6_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) && 36935e670d84SDavid Ahern ipv6_addr_equal(gateway, &rt->fib6_nh.nh_gw)) { 3694be7a010dSDuan Jiong return -1; 3695be7a010dSDuan Jiong } 3696b16cb459SWei Wang 3697b16cb459SWei Wang /* Further clean up cached routes in exception table. 3698b16cb459SWei Wang * This is needed because cached route may have a different 3699b16cb459SWei Wang * gateway than its 'parent' in the case of an ip redirect. 3700b16cb459SWei Wang */ 3701b16cb459SWei Wang rt6_exceptions_clean_tohost(rt, gateway); 3702b16cb459SWei Wang 3703be7a010dSDuan Jiong return 0; 3704be7a010dSDuan Jiong } 3705be7a010dSDuan Jiong 3706be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 3707be7a010dSDuan Jiong { 3708be7a010dSDuan Jiong fib6_clean_all(net, fib6_clean_tohost, gateway); 3709be7a010dSDuan Jiong } 3710be7a010dSDuan Jiong 37112127d95aSIdo Schimmel struct arg_netdev_event { 37122127d95aSIdo Schimmel const struct net_device *dev; 37134c981e28SIdo Schimmel union { 37142127d95aSIdo Schimmel unsigned int nh_flags; 37154c981e28SIdo Schimmel unsigned long event; 37164c981e28SIdo Schimmel }; 37172127d95aSIdo Schimmel }; 37182127d95aSIdo Schimmel 37198d1c802bSDavid Ahern static struct fib6_info *rt6_multipath_first_sibling(const struct fib6_info *rt) 3720d7dedee1SIdo Schimmel { 37218d1c802bSDavid Ahern struct fib6_info *iter; 3722d7dedee1SIdo Schimmel struct fib6_node *fn; 3723d7dedee1SIdo Schimmel 372493c2fb25SDavid Ahern fn = rcu_dereference_protected(rt->fib6_node, 372593c2fb25SDavid Ahern lockdep_is_held(&rt->fib6_table->tb6_lock)); 3726d7dedee1SIdo Schimmel iter = rcu_dereference_protected(fn->leaf, 372793c2fb25SDavid Ahern lockdep_is_held(&rt->fib6_table->tb6_lock)); 3728d7dedee1SIdo Schimmel while (iter) { 372993c2fb25SDavid Ahern if (iter->fib6_metric == rt->fib6_metric && 3730d7dedee1SIdo Schimmel rt6_qualify_for_ecmp(iter)) 3731d7dedee1SIdo Schimmel return iter; 3732d7dedee1SIdo Schimmel iter = rcu_dereference_protected(iter->rt6_next, 373393c2fb25SDavid Ahern lockdep_is_held(&rt->fib6_table->tb6_lock)); 3734d7dedee1SIdo Schimmel } 3735d7dedee1SIdo Schimmel 3736d7dedee1SIdo Schimmel return NULL; 3737d7dedee1SIdo Schimmel } 3738d7dedee1SIdo Schimmel 37398d1c802bSDavid Ahern static bool rt6_is_dead(const struct fib6_info *rt) 3740d7dedee1SIdo Schimmel { 37415e670d84SDavid Ahern if (rt->fib6_nh.nh_flags & RTNH_F_DEAD || 37425e670d84SDavid Ahern (rt->fib6_nh.nh_flags & RTNH_F_LINKDOWN && 3743dcd1f572SDavid Ahern fib6_ignore_linkdown(rt))) 3744d7dedee1SIdo Schimmel return true; 3745d7dedee1SIdo Schimmel 3746d7dedee1SIdo Schimmel return false; 3747d7dedee1SIdo Schimmel } 3748d7dedee1SIdo Schimmel 37498d1c802bSDavid Ahern static int rt6_multipath_total_weight(const struct fib6_info *rt) 3750d7dedee1SIdo Schimmel { 37518d1c802bSDavid Ahern struct fib6_info *iter; 3752d7dedee1SIdo Schimmel int total = 0; 3753d7dedee1SIdo Schimmel 3754d7dedee1SIdo Schimmel if (!rt6_is_dead(rt)) 37555e670d84SDavid Ahern total += rt->fib6_nh.nh_weight; 3756d7dedee1SIdo Schimmel 375793c2fb25SDavid Ahern list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) { 3758d7dedee1SIdo Schimmel if (!rt6_is_dead(iter)) 37595e670d84SDavid Ahern total += iter->fib6_nh.nh_weight; 3760d7dedee1SIdo Schimmel } 3761d7dedee1SIdo Schimmel 3762d7dedee1SIdo Schimmel return total; 3763d7dedee1SIdo Schimmel } 3764d7dedee1SIdo Schimmel 37658d1c802bSDavid Ahern static void rt6_upper_bound_set(struct fib6_info *rt, int *weight, int total) 3766d7dedee1SIdo Schimmel { 3767d7dedee1SIdo Schimmel int upper_bound = -1; 3768d7dedee1SIdo Schimmel 3769d7dedee1SIdo Schimmel if (!rt6_is_dead(rt)) { 37705e670d84SDavid Ahern *weight += rt->fib6_nh.nh_weight; 3771d7dedee1SIdo Schimmel upper_bound = DIV_ROUND_CLOSEST_ULL((u64) (*weight) << 31, 3772d7dedee1SIdo Schimmel total) - 1; 3773d7dedee1SIdo Schimmel } 37745e670d84SDavid Ahern atomic_set(&rt->fib6_nh.nh_upper_bound, upper_bound); 3775d7dedee1SIdo Schimmel } 3776d7dedee1SIdo Schimmel 37778d1c802bSDavid Ahern static void rt6_multipath_upper_bound_set(struct fib6_info *rt, int total) 3778d7dedee1SIdo Schimmel { 37798d1c802bSDavid Ahern struct fib6_info *iter; 3780d7dedee1SIdo Schimmel int weight = 0; 3781d7dedee1SIdo Schimmel 3782d7dedee1SIdo Schimmel rt6_upper_bound_set(rt, &weight, total); 3783d7dedee1SIdo Schimmel 378493c2fb25SDavid Ahern list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 3785d7dedee1SIdo Schimmel rt6_upper_bound_set(iter, &weight, total); 3786d7dedee1SIdo Schimmel } 3787d7dedee1SIdo Schimmel 37888d1c802bSDavid Ahern void rt6_multipath_rebalance(struct fib6_info *rt) 3789d7dedee1SIdo Schimmel { 37908d1c802bSDavid Ahern struct fib6_info *first; 3791d7dedee1SIdo Schimmel int total; 3792d7dedee1SIdo Schimmel 3793d7dedee1SIdo Schimmel /* In case the entire multipath route was marked for flushing, 3794d7dedee1SIdo Schimmel * then there is no need to rebalance upon the removal of every 3795d7dedee1SIdo Schimmel * sibling route. 3796d7dedee1SIdo Schimmel */ 379793c2fb25SDavid Ahern if (!rt->fib6_nsiblings || rt->should_flush) 3798d7dedee1SIdo Schimmel return; 3799d7dedee1SIdo Schimmel 3800d7dedee1SIdo Schimmel /* During lookup routes are evaluated in order, so we need to 3801d7dedee1SIdo Schimmel * make sure upper bounds are assigned from the first sibling 3802d7dedee1SIdo Schimmel * onwards. 3803d7dedee1SIdo Schimmel */ 3804d7dedee1SIdo Schimmel first = rt6_multipath_first_sibling(rt); 3805d7dedee1SIdo Schimmel if (WARN_ON_ONCE(!first)) 3806d7dedee1SIdo Schimmel return; 3807d7dedee1SIdo Schimmel 3808d7dedee1SIdo Schimmel total = rt6_multipath_total_weight(first); 3809d7dedee1SIdo Schimmel rt6_multipath_upper_bound_set(first, total); 3810d7dedee1SIdo Schimmel } 3811d7dedee1SIdo Schimmel 38128d1c802bSDavid Ahern static int fib6_ifup(struct fib6_info *rt, void *p_arg) 38132127d95aSIdo Schimmel { 38142127d95aSIdo Schimmel const struct arg_netdev_event *arg = p_arg; 38157aef6859SDavid Ahern struct net *net = dev_net(arg->dev); 38162127d95aSIdo Schimmel 3817421842edSDavid Ahern if (rt != net->ipv6.fib6_null_entry && rt->fib6_nh.nh_dev == arg->dev) { 38185e670d84SDavid Ahern rt->fib6_nh.nh_flags &= ~arg->nh_flags; 38197aef6859SDavid Ahern fib6_update_sernum_upto_root(net, rt); 3820d7dedee1SIdo Schimmel rt6_multipath_rebalance(rt); 38211de178edSIdo Schimmel } 38222127d95aSIdo Schimmel 38232127d95aSIdo Schimmel return 0; 38242127d95aSIdo Schimmel } 38252127d95aSIdo Schimmel 38262127d95aSIdo Schimmel void rt6_sync_up(struct net_device *dev, unsigned int nh_flags) 38272127d95aSIdo Schimmel { 38282127d95aSIdo Schimmel struct arg_netdev_event arg = { 38292127d95aSIdo Schimmel .dev = dev, 38306802f3adSIdo Schimmel { 38312127d95aSIdo Schimmel .nh_flags = nh_flags, 38326802f3adSIdo Schimmel }, 38332127d95aSIdo Schimmel }; 38342127d95aSIdo Schimmel 38352127d95aSIdo Schimmel if (nh_flags & RTNH_F_DEAD && netif_carrier_ok(dev)) 38362127d95aSIdo Schimmel arg.nh_flags |= RTNH_F_LINKDOWN; 38372127d95aSIdo Schimmel 38382127d95aSIdo Schimmel fib6_clean_all(dev_net(dev), fib6_ifup, &arg); 38392127d95aSIdo Schimmel } 38402127d95aSIdo Schimmel 38418d1c802bSDavid Ahern static bool rt6_multipath_uses_dev(const struct fib6_info *rt, 38421de178edSIdo Schimmel const struct net_device *dev) 38431de178edSIdo Schimmel { 38448d1c802bSDavid Ahern struct fib6_info *iter; 38451de178edSIdo Schimmel 38465e670d84SDavid Ahern if (rt->fib6_nh.nh_dev == dev) 38471de178edSIdo Schimmel return true; 384893c2fb25SDavid Ahern list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 38495e670d84SDavid Ahern if (iter->fib6_nh.nh_dev == dev) 38501de178edSIdo Schimmel return true; 38511de178edSIdo Schimmel 38521de178edSIdo Schimmel return false; 38531de178edSIdo Schimmel } 38541de178edSIdo Schimmel 38558d1c802bSDavid Ahern static void rt6_multipath_flush(struct fib6_info *rt) 38561de178edSIdo Schimmel { 38578d1c802bSDavid Ahern struct fib6_info *iter; 38581de178edSIdo Schimmel 38591de178edSIdo Schimmel rt->should_flush = 1; 386093c2fb25SDavid Ahern list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 38611de178edSIdo Schimmel iter->should_flush = 1; 38621de178edSIdo Schimmel } 38631de178edSIdo Schimmel 38648d1c802bSDavid Ahern static unsigned int rt6_multipath_dead_count(const struct fib6_info *rt, 38651de178edSIdo Schimmel const struct net_device *down_dev) 38661de178edSIdo Schimmel { 38678d1c802bSDavid Ahern struct fib6_info *iter; 38681de178edSIdo Schimmel unsigned int dead = 0; 38691de178edSIdo Schimmel 38705e670d84SDavid Ahern if (rt->fib6_nh.nh_dev == down_dev || 38715e670d84SDavid Ahern rt->fib6_nh.nh_flags & RTNH_F_DEAD) 38721de178edSIdo Schimmel dead++; 387393c2fb25SDavid Ahern list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 38745e670d84SDavid Ahern if (iter->fib6_nh.nh_dev == down_dev || 38755e670d84SDavid Ahern iter->fib6_nh.nh_flags & RTNH_F_DEAD) 38761de178edSIdo Schimmel dead++; 38771de178edSIdo Schimmel 38781de178edSIdo Schimmel return dead; 38791de178edSIdo Schimmel } 38801de178edSIdo Schimmel 38818d1c802bSDavid Ahern static void rt6_multipath_nh_flags_set(struct fib6_info *rt, 38821de178edSIdo Schimmel const struct net_device *dev, 38831de178edSIdo Schimmel unsigned int nh_flags) 38841de178edSIdo Schimmel { 38858d1c802bSDavid Ahern struct fib6_info *iter; 38861de178edSIdo Schimmel 38875e670d84SDavid Ahern if (rt->fib6_nh.nh_dev == dev) 38885e670d84SDavid Ahern rt->fib6_nh.nh_flags |= nh_flags; 388993c2fb25SDavid Ahern list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) 38905e670d84SDavid Ahern if (iter->fib6_nh.nh_dev == dev) 38915e670d84SDavid Ahern iter->fib6_nh.nh_flags |= nh_flags; 38921de178edSIdo Schimmel } 38931de178edSIdo Schimmel 3894a1a22c12SDavid Ahern /* called with write lock held for table with rt */ 38958d1c802bSDavid Ahern static int fib6_ifdown(struct fib6_info *rt, void *p_arg) 38961da177e4SLinus Torvalds { 38974c981e28SIdo Schimmel const struct arg_netdev_event *arg = p_arg; 38984c981e28SIdo Schimmel const struct net_device *dev = arg->dev; 38997aef6859SDavid Ahern struct net *net = dev_net(dev); 39008ed67789SDaniel Lezcano 3901421842edSDavid Ahern if (rt == net->ipv6.fib6_null_entry) 390227c6fa73SIdo Schimmel return 0; 390327c6fa73SIdo Schimmel 390427c6fa73SIdo Schimmel switch (arg->event) { 390527c6fa73SIdo Schimmel case NETDEV_UNREGISTER: 39065e670d84SDavid Ahern return rt->fib6_nh.nh_dev == dev ? -1 : 0; 390727c6fa73SIdo Schimmel case NETDEV_DOWN: 39081de178edSIdo Schimmel if (rt->should_flush) 390927c6fa73SIdo Schimmel return -1; 391093c2fb25SDavid Ahern if (!rt->fib6_nsiblings) 39115e670d84SDavid Ahern return rt->fib6_nh.nh_dev == dev ? -1 : 0; 39121de178edSIdo Schimmel if (rt6_multipath_uses_dev(rt, dev)) { 39131de178edSIdo Schimmel unsigned int count; 39141de178edSIdo Schimmel 39151de178edSIdo Schimmel count = rt6_multipath_dead_count(rt, dev); 391693c2fb25SDavid Ahern if (rt->fib6_nsiblings + 1 == count) { 39171de178edSIdo Schimmel rt6_multipath_flush(rt); 39181de178edSIdo Schimmel return -1; 39191de178edSIdo Schimmel } 39201de178edSIdo Schimmel rt6_multipath_nh_flags_set(rt, dev, RTNH_F_DEAD | 39211de178edSIdo Schimmel RTNH_F_LINKDOWN); 39227aef6859SDavid Ahern fib6_update_sernum(net, rt); 3923d7dedee1SIdo Schimmel rt6_multipath_rebalance(rt); 39241de178edSIdo Schimmel } 39251de178edSIdo Schimmel return -2; 392627c6fa73SIdo Schimmel case NETDEV_CHANGE: 39275e670d84SDavid Ahern if (rt->fib6_nh.nh_dev != dev || 392893c2fb25SDavid Ahern rt->fib6_flags & (RTF_LOCAL | RTF_ANYCAST)) 392927c6fa73SIdo Schimmel break; 39305e670d84SDavid Ahern rt->fib6_nh.nh_flags |= RTNH_F_LINKDOWN; 3931d7dedee1SIdo Schimmel rt6_multipath_rebalance(rt); 393227c6fa73SIdo Schimmel break; 39332b241361SIdo Schimmel } 3934c159d30cSDavid S. Miller 39351da177e4SLinus Torvalds return 0; 39361da177e4SLinus Torvalds } 39371da177e4SLinus Torvalds 393827c6fa73SIdo Schimmel void rt6_sync_down_dev(struct net_device *dev, unsigned long event) 39391da177e4SLinus Torvalds { 39404c981e28SIdo Schimmel struct arg_netdev_event arg = { 39418ed67789SDaniel Lezcano .dev = dev, 39426802f3adSIdo Schimmel { 39434c981e28SIdo Schimmel .event = event, 39446802f3adSIdo Schimmel }, 39458ed67789SDaniel Lezcano }; 39468ed67789SDaniel Lezcano 39474c981e28SIdo Schimmel fib6_clean_all(dev_net(dev), fib6_ifdown, &arg); 39484c981e28SIdo Schimmel } 39494c981e28SIdo Schimmel 39504c981e28SIdo Schimmel void rt6_disable_ip(struct net_device *dev, unsigned long event) 39514c981e28SIdo Schimmel { 39524c981e28SIdo Schimmel rt6_sync_down_dev(dev, event); 39534c981e28SIdo Schimmel rt6_uncached_list_flush_dev(dev_net(dev), dev); 39544c981e28SIdo Schimmel neigh_ifdown(&nd_tbl, dev); 39551da177e4SLinus Torvalds } 39561da177e4SLinus Torvalds 395795c96174SEric Dumazet struct rt6_mtu_change_arg { 39581da177e4SLinus Torvalds struct net_device *dev; 395995c96174SEric Dumazet unsigned int mtu; 39601da177e4SLinus Torvalds }; 39611da177e4SLinus Torvalds 39628d1c802bSDavid Ahern static int rt6_mtu_change_route(struct fib6_info *rt, void *p_arg) 39631da177e4SLinus Torvalds { 39641da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 39651da177e4SLinus Torvalds struct inet6_dev *idev; 39661da177e4SLinus Torvalds 39671da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 39681da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 39691da177e4SLinus Torvalds We still use this lock to block changes 39701da177e4SLinus Torvalds caused by addrconf/ndisc. 39711da177e4SLinus Torvalds */ 39721da177e4SLinus Torvalds 39731da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 397438308473SDavid S. Miller if (!idev) 39751da177e4SLinus Torvalds return 0; 39761da177e4SLinus Torvalds 39771da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 39781da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 39791da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 39801da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 39811da177e4SLinus Torvalds */ 39825e670d84SDavid Ahern if (rt->fib6_nh.nh_dev == arg->dev && 3983d4ead6b3SDavid Ahern !fib6_metric_locked(rt, RTAX_MTU)) { 3984d4ead6b3SDavid Ahern u32 mtu = rt->fib6_pmtu; 3985d4ead6b3SDavid Ahern 3986d4ead6b3SDavid Ahern if (mtu >= arg->mtu || 3987d4ead6b3SDavid Ahern (mtu < arg->mtu && mtu == idev->cnf.mtu6)) 3988d4ead6b3SDavid Ahern fib6_metric_set(rt, RTAX_MTU, arg->mtu); 3989d4ead6b3SDavid Ahern 3990f5bbe7eeSWei Wang spin_lock_bh(&rt6_exception_lock); 3991e9fa1495SStefano Brivio rt6_exceptions_update_pmtu(idev, rt, arg->mtu); 3992f5bbe7eeSWei Wang spin_unlock_bh(&rt6_exception_lock); 39934b32b5adSMartin KaFai Lau } 39941da177e4SLinus Torvalds return 0; 39951da177e4SLinus Torvalds } 39961da177e4SLinus Torvalds 399795c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 39981da177e4SLinus Torvalds { 3999c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 4000c71099acSThomas Graf .dev = dev, 4001c71099acSThomas Graf .mtu = mtu, 4002c71099acSThomas Graf }; 40031da177e4SLinus Torvalds 40040c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 40051da177e4SLinus Torvalds } 40061da177e4SLinus Torvalds 4007ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 40085176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 400986872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 4010ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 401186872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 401286872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 401351ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 4014c78ba6d6SLubomir Rintel [RTA_PREF] = { .type = NLA_U8 }, 401519e42e45SRoopa Prabhu [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, 401619e42e45SRoopa Prabhu [RTA_ENCAP] = { .type = NLA_NESTED }, 401732bc201eSXin Long [RTA_EXPIRES] = { .type = NLA_U32 }, 4018622ec2c9SLorenzo Colitti [RTA_UID] = { .type = NLA_U32 }, 40193b45a410SLiping Zhang [RTA_MARK] = { .type = NLA_U32 }, 402086872cb5SThomas Graf }; 402186872cb5SThomas Graf 402286872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 4023333c4301SDavid Ahern struct fib6_config *cfg, 4024333c4301SDavid Ahern struct netlink_ext_ack *extack) 40251da177e4SLinus Torvalds { 402686872cb5SThomas Graf struct rtmsg *rtm; 402786872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 4028c78ba6d6SLubomir Rintel unsigned int pref; 402986872cb5SThomas Graf int err; 40301da177e4SLinus Torvalds 4031fceb6435SJohannes Berg err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy, 4032fceb6435SJohannes Berg NULL); 403386872cb5SThomas Graf if (err < 0) 403486872cb5SThomas Graf goto errout; 40351da177e4SLinus Torvalds 403686872cb5SThomas Graf err = -EINVAL; 403786872cb5SThomas Graf rtm = nlmsg_data(nlh); 403886872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 403986872cb5SThomas Graf 404086872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 404186872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 404286872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 404386872cb5SThomas Graf cfg->fc_flags = RTF_UP; 404486872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 4045ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 404686872cb5SThomas Graf 4047ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 4048ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 4049b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 4050b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 405186872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 405286872cb5SThomas Graf 4053ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 4054ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 4055ab79ad14SMaciej Żenczykowski 40561f56a01fSMartin KaFai Lau if (rtm->rtm_flags & RTM_F_CLONED) 40571f56a01fSMartin KaFai Lau cfg->fc_flags |= RTF_CACHE; 40581f56a01fSMartin KaFai Lau 4059fc1e64e1SDavid Ahern cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK); 4060fc1e64e1SDavid Ahern 406115e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 406286872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 40633b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 406486872cb5SThomas Graf 406586872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 406667b61f6cSJiri Benc cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 406786872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 40681da177e4SLinus Torvalds } 406986872cb5SThomas Graf 407086872cb5SThomas Graf if (tb[RTA_DST]) { 407186872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 407286872cb5SThomas Graf 407386872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 407486872cb5SThomas Graf goto errout; 407586872cb5SThomas Graf 407686872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 40771da177e4SLinus Torvalds } 407886872cb5SThomas Graf 407986872cb5SThomas Graf if (tb[RTA_SRC]) { 408086872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 408186872cb5SThomas Graf 408286872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 408386872cb5SThomas Graf goto errout; 408486872cb5SThomas Graf 408586872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 40861da177e4SLinus Torvalds } 408786872cb5SThomas Graf 4088c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 408967b61f6cSJiri Benc cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 4090c3968a85SDaniel Walter 409186872cb5SThomas Graf if (tb[RTA_OIF]) 409286872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 409386872cb5SThomas Graf 409486872cb5SThomas Graf if (tb[RTA_PRIORITY]) 409586872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 409686872cb5SThomas Graf 409786872cb5SThomas Graf if (tb[RTA_METRICS]) { 409886872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 409986872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 41001da177e4SLinus Torvalds } 410186872cb5SThomas Graf 410286872cb5SThomas Graf if (tb[RTA_TABLE]) 410386872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 410486872cb5SThomas Graf 410551ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 410651ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 410751ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 41089ed59592SDavid Ahern 41099ed59592SDavid Ahern err = lwtunnel_valid_encap_type_attr(cfg->fc_mp, 4110c255bd68SDavid Ahern cfg->fc_mp_len, extack); 41119ed59592SDavid Ahern if (err < 0) 41129ed59592SDavid Ahern goto errout; 411351ebd318SNicolas Dichtel } 411451ebd318SNicolas Dichtel 4115c78ba6d6SLubomir Rintel if (tb[RTA_PREF]) { 4116c78ba6d6SLubomir Rintel pref = nla_get_u8(tb[RTA_PREF]); 4117c78ba6d6SLubomir Rintel if (pref != ICMPV6_ROUTER_PREF_LOW && 4118c78ba6d6SLubomir Rintel pref != ICMPV6_ROUTER_PREF_HIGH) 4119c78ba6d6SLubomir Rintel pref = ICMPV6_ROUTER_PREF_MEDIUM; 4120c78ba6d6SLubomir Rintel cfg->fc_flags |= RTF_PREF(pref); 4121c78ba6d6SLubomir Rintel } 4122c78ba6d6SLubomir Rintel 412319e42e45SRoopa Prabhu if (tb[RTA_ENCAP]) 412419e42e45SRoopa Prabhu cfg->fc_encap = tb[RTA_ENCAP]; 412519e42e45SRoopa Prabhu 41269ed59592SDavid Ahern if (tb[RTA_ENCAP_TYPE]) { 412719e42e45SRoopa Prabhu cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]); 412819e42e45SRoopa Prabhu 4129c255bd68SDavid Ahern err = lwtunnel_valid_encap_type(cfg->fc_encap_type, extack); 41309ed59592SDavid Ahern if (err < 0) 41319ed59592SDavid Ahern goto errout; 41329ed59592SDavid Ahern } 41339ed59592SDavid Ahern 413432bc201eSXin Long if (tb[RTA_EXPIRES]) { 413532bc201eSXin Long unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ); 413632bc201eSXin Long 413732bc201eSXin Long if (addrconf_finite_timeout(timeout)) { 413832bc201eSXin Long cfg->fc_expires = jiffies_to_clock_t(timeout * HZ); 413932bc201eSXin Long cfg->fc_flags |= RTF_EXPIRES; 414032bc201eSXin Long } 414132bc201eSXin Long } 414232bc201eSXin Long 414386872cb5SThomas Graf err = 0; 414486872cb5SThomas Graf errout: 414586872cb5SThomas Graf return err; 41461da177e4SLinus Torvalds } 41471da177e4SLinus Torvalds 41486b9ea5a6SRoopa Prabhu struct rt6_nh { 41498d1c802bSDavid Ahern struct fib6_info *fib6_info; 41506b9ea5a6SRoopa Prabhu struct fib6_config r_cfg; 41516b9ea5a6SRoopa Prabhu struct list_head next; 41526b9ea5a6SRoopa Prabhu }; 41536b9ea5a6SRoopa Prabhu 41546b9ea5a6SRoopa Prabhu static void ip6_print_replace_route_err(struct list_head *rt6_nh_list) 41556b9ea5a6SRoopa Prabhu { 41566b9ea5a6SRoopa Prabhu struct rt6_nh *nh; 41576b9ea5a6SRoopa Prabhu 41586b9ea5a6SRoopa Prabhu list_for_each_entry(nh, rt6_nh_list, next) { 41597d4d5065SDavid Ahern pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6c nexthop %pI6c ifi %d\n", 41606b9ea5a6SRoopa Prabhu &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway, 41616b9ea5a6SRoopa Prabhu nh->r_cfg.fc_ifindex); 41626b9ea5a6SRoopa Prabhu } 41636b9ea5a6SRoopa Prabhu } 41646b9ea5a6SRoopa Prabhu 4165d4ead6b3SDavid Ahern static int ip6_route_info_append(struct net *net, 4166d4ead6b3SDavid Ahern struct list_head *rt6_nh_list, 41678d1c802bSDavid Ahern struct fib6_info *rt, 41688d1c802bSDavid Ahern struct fib6_config *r_cfg) 41696b9ea5a6SRoopa Prabhu { 41706b9ea5a6SRoopa Prabhu struct rt6_nh *nh; 41716b9ea5a6SRoopa Prabhu int err = -EEXIST; 41726b9ea5a6SRoopa Prabhu 41736b9ea5a6SRoopa Prabhu list_for_each_entry(nh, rt6_nh_list, next) { 41748d1c802bSDavid Ahern /* check if fib6_info already exists */ 41758d1c802bSDavid Ahern if (rt6_duplicate_nexthop(nh->fib6_info, rt)) 41766b9ea5a6SRoopa Prabhu return err; 41776b9ea5a6SRoopa Prabhu } 41786b9ea5a6SRoopa Prabhu 41796b9ea5a6SRoopa Prabhu nh = kzalloc(sizeof(*nh), GFP_KERNEL); 41806b9ea5a6SRoopa Prabhu if (!nh) 41816b9ea5a6SRoopa Prabhu return -ENOMEM; 41828d1c802bSDavid Ahern nh->fib6_info = rt; 4183d4ead6b3SDavid Ahern err = ip6_convert_metrics(net, rt, r_cfg); 41846b9ea5a6SRoopa Prabhu if (err) { 41856b9ea5a6SRoopa Prabhu kfree(nh); 41866b9ea5a6SRoopa Prabhu return err; 41876b9ea5a6SRoopa Prabhu } 41886b9ea5a6SRoopa Prabhu memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg)); 41896b9ea5a6SRoopa Prabhu list_add_tail(&nh->next, rt6_nh_list); 41906b9ea5a6SRoopa Prabhu 41916b9ea5a6SRoopa Prabhu return 0; 41926b9ea5a6SRoopa Prabhu } 41936b9ea5a6SRoopa Prabhu 41948d1c802bSDavid Ahern static void ip6_route_mpath_notify(struct fib6_info *rt, 41958d1c802bSDavid Ahern struct fib6_info *rt_last, 41963b1137feSDavid Ahern struct nl_info *info, 41973b1137feSDavid Ahern __u16 nlflags) 41983b1137feSDavid Ahern { 41993b1137feSDavid Ahern /* if this is an APPEND route, then rt points to the first route 42003b1137feSDavid Ahern * inserted and rt_last points to last route inserted. Userspace 42013b1137feSDavid Ahern * wants a consistent dump of the route which starts at the first 42023b1137feSDavid Ahern * nexthop. Since sibling routes are always added at the end of 42033b1137feSDavid Ahern * the list, find the first sibling of the last route appended 42043b1137feSDavid Ahern */ 420593c2fb25SDavid Ahern if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) { 420693c2fb25SDavid Ahern rt = list_first_entry(&rt_last->fib6_siblings, 42078d1c802bSDavid Ahern struct fib6_info, 420893c2fb25SDavid Ahern fib6_siblings); 42093b1137feSDavid Ahern } 42103b1137feSDavid Ahern 42113b1137feSDavid Ahern if (rt) 42123b1137feSDavid Ahern inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags); 42133b1137feSDavid Ahern } 42143b1137feSDavid Ahern 4215333c4301SDavid Ahern static int ip6_route_multipath_add(struct fib6_config *cfg, 4216333c4301SDavid Ahern struct netlink_ext_ack *extack) 421751ebd318SNicolas Dichtel { 42188d1c802bSDavid Ahern struct fib6_info *rt_notif = NULL, *rt_last = NULL; 42193b1137feSDavid Ahern struct nl_info *info = &cfg->fc_nlinfo; 422051ebd318SNicolas Dichtel struct fib6_config r_cfg; 422151ebd318SNicolas Dichtel struct rtnexthop *rtnh; 42228d1c802bSDavid Ahern struct fib6_info *rt; 42236b9ea5a6SRoopa Prabhu struct rt6_nh *err_nh; 42246b9ea5a6SRoopa Prabhu struct rt6_nh *nh, *nh_safe; 42253b1137feSDavid Ahern __u16 nlflags; 422651ebd318SNicolas Dichtel int remaining; 422751ebd318SNicolas Dichtel int attrlen; 42286b9ea5a6SRoopa Prabhu int err = 1; 42296b9ea5a6SRoopa Prabhu int nhn = 0; 42306b9ea5a6SRoopa Prabhu int replace = (cfg->fc_nlinfo.nlh && 42316b9ea5a6SRoopa Prabhu (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); 42326b9ea5a6SRoopa Prabhu LIST_HEAD(rt6_nh_list); 423351ebd318SNicolas Dichtel 42343b1137feSDavid Ahern nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE; 42353b1137feSDavid Ahern if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND) 42363b1137feSDavid Ahern nlflags |= NLM_F_APPEND; 42373b1137feSDavid Ahern 423835f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len; 423951ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 424051ebd318SNicolas Dichtel 42416b9ea5a6SRoopa Prabhu /* Parse a Multipath Entry and build a list (rt6_nh_list) of 42428d1c802bSDavid Ahern * fib6_info structs per nexthop 42436b9ea5a6SRoopa Prabhu */ 424451ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 424551ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 424651ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 424751ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 424851ebd318SNicolas Dichtel 424951ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 425051ebd318SNicolas Dichtel if (attrlen > 0) { 425151ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 425251ebd318SNicolas Dichtel 425351ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 425451ebd318SNicolas Dichtel if (nla) { 425567b61f6cSJiri Benc r_cfg.fc_gateway = nla_get_in6_addr(nla); 425651ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 425751ebd318SNicolas Dichtel } 425819e42e45SRoopa Prabhu r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 425919e42e45SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 426019e42e45SRoopa Prabhu if (nla) 426119e42e45SRoopa Prabhu r_cfg.fc_encap_type = nla_get_u16(nla); 426251ebd318SNicolas Dichtel } 42636b9ea5a6SRoopa Prabhu 426468e2ffdeSDavid Ahern r_cfg.fc_flags |= (rtnh->rtnh_flags & RTNH_F_ONLINK); 4265acb54e3cSDavid Ahern rt = ip6_route_info_create(&r_cfg, GFP_KERNEL, extack); 42668c5b83f0SRoopa Prabhu if (IS_ERR(rt)) { 42678c5b83f0SRoopa Prabhu err = PTR_ERR(rt); 42688c5b83f0SRoopa Prabhu rt = NULL; 42696b9ea5a6SRoopa Prabhu goto cleanup; 42708c5b83f0SRoopa Prabhu } 42716b9ea5a6SRoopa Prabhu 42725e670d84SDavid Ahern rt->fib6_nh.nh_weight = rtnh->rtnh_hops + 1; 4273398958aeSIdo Schimmel 4274d4ead6b3SDavid Ahern err = ip6_route_info_append(info->nl_net, &rt6_nh_list, 4275d4ead6b3SDavid Ahern rt, &r_cfg); 427651ebd318SNicolas Dichtel if (err) { 427793531c67SDavid Ahern fib6_info_release(rt); 42786b9ea5a6SRoopa Prabhu goto cleanup; 427951ebd318SNicolas Dichtel } 42806b9ea5a6SRoopa Prabhu 42816b9ea5a6SRoopa Prabhu rtnh = rtnh_next(rtnh, &remaining); 428251ebd318SNicolas Dichtel } 42836b9ea5a6SRoopa Prabhu 42843b1137feSDavid Ahern /* for add and replace send one notification with all nexthops. 42853b1137feSDavid Ahern * Skip the notification in fib6_add_rt2node and send one with 42863b1137feSDavid Ahern * the full route when done 42873b1137feSDavid Ahern */ 42883b1137feSDavid Ahern info->skip_notify = 1; 42893b1137feSDavid Ahern 42906b9ea5a6SRoopa Prabhu err_nh = NULL; 42916b9ea5a6SRoopa Prabhu list_for_each_entry(nh, &rt6_nh_list, next) { 42928d1c802bSDavid Ahern rt_last = nh->fib6_info; 42938d1c802bSDavid Ahern err = __ip6_ins_rt(nh->fib6_info, info, extack); 42948d1c802bSDavid Ahern fib6_info_release(nh->fib6_info); 429593531c67SDavid Ahern 42963b1137feSDavid Ahern /* save reference to first route for notification */ 42973b1137feSDavid Ahern if (!rt_notif && !err) 42988d1c802bSDavid Ahern rt_notif = nh->fib6_info; 42993b1137feSDavid Ahern 43008d1c802bSDavid Ahern /* nh->fib6_info is used or freed at this point, reset to NULL*/ 43018d1c802bSDavid Ahern nh->fib6_info = NULL; 43026b9ea5a6SRoopa Prabhu if (err) { 43036b9ea5a6SRoopa Prabhu if (replace && nhn) 43046b9ea5a6SRoopa Prabhu ip6_print_replace_route_err(&rt6_nh_list); 43056b9ea5a6SRoopa Prabhu err_nh = nh; 43066b9ea5a6SRoopa Prabhu goto add_errout; 43076b9ea5a6SRoopa Prabhu } 43086b9ea5a6SRoopa Prabhu 43091a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 431027596472SMichal Kubeček * these flags after the first nexthop: if there is a collision, 431127596472SMichal Kubeček * we have already failed to add the first nexthop: 431227596472SMichal Kubeček * fib6_add_rt2node() has rejected it; when replacing, old 431327596472SMichal Kubeček * nexthops have been replaced by first new, the rest should 431427596472SMichal Kubeček * be added to it. 43151a72418bSNicolas Dichtel */ 431627596472SMichal Kubeček cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | 431727596472SMichal Kubeček NLM_F_REPLACE); 43186b9ea5a6SRoopa Prabhu nhn++; 43196b9ea5a6SRoopa Prabhu } 43206b9ea5a6SRoopa Prabhu 43213b1137feSDavid Ahern /* success ... tell user about new route */ 43223b1137feSDavid Ahern ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); 43236b9ea5a6SRoopa Prabhu goto cleanup; 43246b9ea5a6SRoopa Prabhu 43256b9ea5a6SRoopa Prabhu add_errout: 43263b1137feSDavid Ahern /* send notification for routes that were added so that 43273b1137feSDavid Ahern * the delete notifications sent by ip6_route_del are 43283b1137feSDavid Ahern * coherent 43293b1137feSDavid Ahern */ 43303b1137feSDavid Ahern if (rt_notif) 43313b1137feSDavid Ahern ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); 43323b1137feSDavid Ahern 43336b9ea5a6SRoopa Prabhu /* Delete routes that were already added */ 43346b9ea5a6SRoopa Prabhu list_for_each_entry(nh, &rt6_nh_list, next) { 43356b9ea5a6SRoopa Prabhu if (err_nh == nh) 43366b9ea5a6SRoopa Prabhu break; 4337333c4301SDavid Ahern ip6_route_del(&nh->r_cfg, extack); 43386b9ea5a6SRoopa Prabhu } 43396b9ea5a6SRoopa Prabhu 43406b9ea5a6SRoopa Prabhu cleanup: 43416b9ea5a6SRoopa Prabhu list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) { 43428d1c802bSDavid Ahern if (nh->fib6_info) 43438d1c802bSDavid Ahern fib6_info_release(nh->fib6_info); 43446b9ea5a6SRoopa Prabhu list_del(&nh->next); 43456b9ea5a6SRoopa Prabhu kfree(nh); 43466b9ea5a6SRoopa Prabhu } 43476b9ea5a6SRoopa Prabhu 43486b9ea5a6SRoopa Prabhu return err; 43496b9ea5a6SRoopa Prabhu } 43506b9ea5a6SRoopa Prabhu 4351333c4301SDavid Ahern static int ip6_route_multipath_del(struct fib6_config *cfg, 4352333c4301SDavid Ahern struct netlink_ext_ack *extack) 43536b9ea5a6SRoopa Prabhu { 43546b9ea5a6SRoopa Prabhu struct fib6_config r_cfg; 43556b9ea5a6SRoopa Prabhu struct rtnexthop *rtnh; 43566b9ea5a6SRoopa Prabhu int remaining; 43576b9ea5a6SRoopa Prabhu int attrlen; 43586b9ea5a6SRoopa Prabhu int err = 1, last_err = 0; 43596b9ea5a6SRoopa Prabhu 43606b9ea5a6SRoopa Prabhu remaining = cfg->fc_mp_len; 43616b9ea5a6SRoopa Prabhu rtnh = (struct rtnexthop *)cfg->fc_mp; 43626b9ea5a6SRoopa Prabhu 43636b9ea5a6SRoopa Prabhu /* Parse a Multipath Entry */ 43646b9ea5a6SRoopa Prabhu while (rtnh_ok(rtnh, remaining)) { 43656b9ea5a6SRoopa Prabhu memcpy(&r_cfg, cfg, sizeof(*cfg)); 43666b9ea5a6SRoopa Prabhu if (rtnh->rtnh_ifindex) 43676b9ea5a6SRoopa Prabhu r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 43686b9ea5a6SRoopa Prabhu 43696b9ea5a6SRoopa Prabhu attrlen = rtnh_attrlen(rtnh); 43706b9ea5a6SRoopa Prabhu if (attrlen > 0) { 43716b9ea5a6SRoopa Prabhu struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 43726b9ea5a6SRoopa Prabhu 43736b9ea5a6SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_GATEWAY); 43746b9ea5a6SRoopa Prabhu if (nla) { 43756b9ea5a6SRoopa Prabhu nla_memcpy(&r_cfg.fc_gateway, nla, 16); 43766b9ea5a6SRoopa Prabhu r_cfg.fc_flags |= RTF_GATEWAY; 43776b9ea5a6SRoopa Prabhu } 43786b9ea5a6SRoopa Prabhu } 4379333c4301SDavid Ahern err = ip6_route_del(&r_cfg, extack); 43806b9ea5a6SRoopa Prabhu if (err) 43816b9ea5a6SRoopa Prabhu last_err = err; 43826b9ea5a6SRoopa Prabhu 438351ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 438451ebd318SNicolas Dichtel } 438551ebd318SNicolas Dichtel 438651ebd318SNicolas Dichtel return last_err; 438751ebd318SNicolas Dichtel } 438851ebd318SNicolas Dichtel 4389c21ef3e3SDavid Ahern static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, 4390c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 43911da177e4SLinus Torvalds { 439286872cb5SThomas Graf struct fib6_config cfg; 439386872cb5SThomas Graf int err; 43941da177e4SLinus Torvalds 4395333c4301SDavid Ahern err = rtm_to_fib6_config(skb, nlh, &cfg, extack); 439686872cb5SThomas Graf if (err < 0) 439786872cb5SThomas Graf return err; 439886872cb5SThomas Graf 439951ebd318SNicolas Dichtel if (cfg.fc_mp) 4400333c4301SDavid Ahern return ip6_route_multipath_del(&cfg, extack); 44010ae81335SDavid Ahern else { 44020ae81335SDavid Ahern cfg.fc_delete_all_nh = 1; 4403333c4301SDavid Ahern return ip6_route_del(&cfg, extack); 44041da177e4SLinus Torvalds } 44050ae81335SDavid Ahern } 44061da177e4SLinus Torvalds 4407c21ef3e3SDavid Ahern static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, 4408c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 44091da177e4SLinus Torvalds { 441086872cb5SThomas Graf struct fib6_config cfg; 441186872cb5SThomas Graf int err; 44121da177e4SLinus Torvalds 4413333c4301SDavid Ahern err = rtm_to_fib6_config(skb, nlh, &cfg, extack); 441486872cb5SThomas Graf if (err < 0) 441586872cb5SThomas Graf return err; 441686872cb5SThomas Graf 441751ebd318SNicolas Dichtel if (cfg.fc_mp) 4418333c4301SDavid Ahern return ip6_route_multipath_add(&cfg, extack); 441951ebd318SNicolas Dichtel else 4420acb54e3cSDavid Ahern return ip6_route_add(&cfg, GFP_KERNEL, extack); 44211da177e4SLinus Torvalds } 44221da177e4SLinus Torvalds 44238d1c802bSDavid Ahern static size_t rt6_nlmsg_size(struct fib6_info *rt) 4424339bf98fSThomas Graf { 4425beb1afacSDavid Ahern int nexthop_len = 0; 4426beb1afacSDavid Ahern 442793c2fb25SDavid Ahern if (rt->fib6_nsiblings) { 4428beb1afacSDavid Ahern nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */ 4429beb1afacSDavid Ahern + NLA_ALIGN(sizeof(struct rtnexthop)) 4430beb1afacSDavid Ahern + nla_total_size(16) /* RTA_GATEWAY */ 44315e670d84SDavid Ahern + lwtunnel_get_encap_size(rt->fib6_nh.nh_lwtstate); 4432beb1afacSDavid Ahern 443393c2fb25SDavid Ahern nexthop_len *= rt->fib6_nsiblings; 4434beb1afacSDavid Ahern } 4435beb1afacSDavid Ahern 4436339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 4437339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 4438339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 4439339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 4440339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 4441339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 4442339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 4443339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 4444339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 44456a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 4446ea697639SDaniel Borkmann + nla_total_size(sizeof(struct rta_cacheinfo)) 4447c78ba6d6SLubomir Rintel + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 444819e42e45SRoopa Prabhu + nla_total_size(1) /* RTA_PREF */ 44495e670d84SDavid Ahern + lwtunnel_get_encap_size(rt->fib6_nh.nh_lwtstate) 4450beb1afacSDavid Ahern + nexthop_len; 4451beb1afacSDavid Ahern } 4452beb1afacSDavid Ahern 44538d1c802bSDavid Ahern static int rt6_nexthop_info(struct sk_buff *skb, struct fib6_info *rt, 44545be083ceSDavid Ahern unsigned int *flags, bool skip_oif) 4455beb1afacSDavid Ahern { 44565e670d84SDavid Ahern if (rt->fib6_nh.nh_flags & RTNH_F_DEAD) 4457f9d882eaSIdo Schimmel *flags |= RTNH_F_DEAD; 4458f9d882eaSIdo Schimmel 44595e670d84SDavid Ahern if (rt->fib6_nh.nh_flags & RTNH_F_LINKDOWN) { 4460beb1afacSDavid Ahern *flags |= RTNH_F_LINKDOWN; 4461dcd1f572SDavid Ahern 4462dcd1f572SDavid Ahern rcu_read_lock(); 4463dcd1f572SDavid Ahern if (fib6_ignore_linkdown(rt)) 4464beb1afacSDavid Ahern *flags |= RTNH_F_DEAD; 4465dcd1f572SDavid Ahern rcu_read_unlock(); 4466beb1afacSDavid Ahern } 4467beb1afacSDavid Ahern 446893c2fb25SDavid Ahern if (rt->fib6_flags & RTF_GATEWAY) { 44695e670d84SDavid Ahern if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->fib6_nh.nh_gw) < 0) 4470beb1afacSDavid Ahern goto nla_put_failure; 4471beb1afacSDavid Ahern } 4472beb1afacSDavid Ahern 44735e670d84SDavid Ahern *flags |= (rt->fib6_nh.nh_flags & RTNH_F_ONLINK); 44745e670d84SDavid Ahern if (rt->fib6_nh.nh_flags & RTNH_F_OFFLOAD) 447561e4d01eSIdo Schimmel *flags |= RTNH_F_OFFLOAD; 447661e4d01eSIdo Schimmel 44775be083ceSDavid Ahern /* not needed for multipath encoding b/c it has a rtnexthop struct */ 44785e670d84SDavid Ahern if (!skip_oif && rt->fib6_nh.nh_dev && 44795e670d84SDavid Ahern nla_put_u32(skb, RTA_OIF, rt->fib6_nh.nh_dev->ifindex)) 4480beb1afacSDavid Ahern goto nla_put_failure; 4481beb1afacSDavid Ahern 44825e670d84SDavid Ahern if (rt->fib6_nh.nh_lwtstate && 44835e670d84SDavid Ahern lwtunnel_fill_encap(skb, rt->fib6_nh.nh_lwtstate) < 0) 4484beb1afacSDavid Ahern goto nla_put_failure; 4485beb1afacSDavid Ahern 4486beb1afacSDavid Ahern return 0; 4487beb1afacSDavid Ahern 4488beb1afacSDavid Ahern nla_put_failure: 4489beb1afacSDavid Ahern return -EMSGSIZE; 4490beb1afacSDavid Ahern } 4491beb1afacSDavid Ahern 44925be083ceSDavid Ahern /* add multipath next hop */ 44938d1c802bSDavid Ahern static int rt6_add_nexthop(struct sk_buff *skb, struct fib6_info *rt) 4494beb1afacSDavid Ahern { 44955e670d84SDavid Ahern const struct net_device *dev = rt->fib6_nh.nh_dev; 4496beb1afacSDavid Ahern struct rtnexthop *rtnh; 4497beb1afacSDavid Ahern unsigned int flags = 0; 4498beb1afacSDavid Ahern 4499beb1afacSDavid Ahern rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); 4500beb1afacSDavid Ahern if (!rtnh) 4501beb1afacSDavid Ahern goto nla_put_failure; 4502beb1afacSDavid Ahern 45035e670d84SDavid Ahern rtnh->rtnh_hops = rt->fib6_nh.nh_weight - 1; 45045e670d84SDavid Ahern rtnh->rtnh_ifindex = dev ? dev->ifindex : 0; 4505beb1afacSDavid Ahern 45065be083ceSDavid Ahern if (rt6_nexthop_info(skb, rt, &flags, true) < 0) 4507beb1afacSDavid Ahern goto nla_put_failure; 4508beb1afacSDavid Ahern 4509beb1afacSDavid Ahern rtnh->rtnh_flags = flags; 4510beb1afacSDavid Ahern 4511beb1afacSDavid Ahern /* length of rtnetlink header + attributes */ 4512beb1afacSDavid Ahern rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh; 4513beb1afacSDavid Ahern 4514beb1afacSDavid Ahern return 0; 4515beb1afacSDavid Ahern 4516beb1afacSDavid Ahern nla_put_failure: 4517beb1afacSDavid Ahern return -EMSGSIZE; 4518339bf98fSThomas Graf } 4519339bf98fSThomas Graf 4520d4ead6b3SDavid Ahern static int rt6_fill_node(struct net *net, struct sk_buff *skb, 45218d1c802bSDavid Ahern struct fib6_info *rt, struct dst_entry *dst, 4522d4ead6b3SDavid Ahern struct in6_addr *dest, struct in6_addr *src, 452315e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 4524f8cfe2ceSDavid Ahern unsigned int flags) 45251da177e4SLinus Torvalds { 45261da177e4SLinus Torvalds struct rtmsg *rtm; 45271da177e4SLinus Torvalds struct nlmsghdr *nlh; 4528d4ead6b3SDavid Ahern long expires = 0; 4529d4ead6b3SDavid Ahern u32 *pmetrics; 45309e762a4aSPatrick McHardy u32 table; 45311da177e4SLinus Torvalds 453215e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 453338308473SDavid S. Miller if (!nlh) 453426932566SPatrick McHardy return -EMSGSIZE; 45352d7202bfSThomas Graf 45362d7202bfSThomas Graf rtm = nlmsg_data(nlh); 45371da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 453893c2fb25SDavid Ahern rtm->rtm_dst_len = rt->fib6_dst.plen; 453993c2fb25SDavid Ahern rtm->rtm_src_len = rt->fib6_src.plen; 45401da177e4SLinus Torvalds rtm->rtm_tos = 0; 454193c2fb25SDavid Ahern if (rt->fib6_table) 454293c2fb25SDavid Ahern table = rt->fib6_table->tb6_id; 4543c71099acSThomas Graf else 45449e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 45459e762a4aSPatrick McHardy rtm->rtm_table = table; 4546c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 4547c78679e8SDavid S. Miller goto nla_put_failure; 4548e8478e80SDavid Ahern 4549e8478e80SDavid Ahern rtm->rtm_type = rt->fib6_type; 45501da177e4SLinus Torvalds rtm->rtm_flags = 0; 45511da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 455293c2fb25SDavid Ahern rtm->rtm_protocol = rt->fib6_protocol; 45531da177e4SLinus Torvalds 455493c2fb25SDavid Ahern if (rt->fib6_flags & RTF_CACHE) 45551da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 45561da177e4SLinus Torvalds 4557d4ead6b3SDavid Ahern if (dest) { 4558d4ead6b3SDavid Ahern if (nla_put_in6_addr(skb, RTA_DST, dest)) 4559c78679e8SDavid S. Miller goto nla_put_failure; 45601da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 45611da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 456293c2fb25SDavid Ahern if (nla_put_in6_addr(skb, RTA_DST, &rt->fib6_dst.addr)) 4563c78679e8SDavid S. Miller goto nla_put_failure; 45641da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 45651da177e4SLinus Torvalds if (src) { 4566930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_SRC, src)) 4567c78679e8SDavid S. Miller goto nla_put_failure; 45681da177e4SLinus Torvalds rtm->rtm_src_len = 128; 4569c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 457093c2fb25SDavid Ahern nla_put_in6_addr(skb, RTA_SRC, &rt->fib6_src.addr)) 4571c78679e8SDavid S. Miller goto nla_put_failure; 45721da177e4SLinus Torvalds #endif 45737bc570c8SYOSHIFUJI Hideaki if (iif) { 45747bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 457593c2fb25SDavid Ahern if (ipv6_addr_is_multicast(&rt->fib6_dst.addr)) { 4576fd61c6baSDavid Ahern int err = ip6mr_get_route(net, skb, rtm, portid); 45772cf75070SNikolay Aleksandrov 45787bc570c8SYOSHIFUJI Hideaki if (err == 0) 45797bc570c8SYOSHIFUJI Hideaki return 0; 4580fd61c6baSDavid Ahern if (err < 0) 45817bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 45827bc570c8SYOSHIFUJI Hideaki } else 45837bc570c8SYOSHIFUJI Hideaki #endif 4584c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 4585c78679e8SDavid S. Miller goto nla_put_failure; 4586d4ead6b3SDavid Ahern } else if (dest) { 45871da177e4SLinus Torvalds struct in6_addr saddr_buf; 4588d4ead6b3SDavid Ahern if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 && 4589930345eaSJiri Benc nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 4590c78679e8SDavid S. Miller goto nla_put_failure; 4591c3968a85SDaniel Walter } 4592c3968a85SDaniel Walter 459393c2fb25SDavid Ahern if (rt->fib6_prefsrc.plen) { 4594c3968a85SDaniel Walter struct in6_addr saddr_buf; 459593c2fb25SDavid Ahern saddr_buf = rt->fib6_prefsrc.addr; 4596930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 4597c78679e8SDavid S. Miller goto nla_put_failure; 45981da177e4SLinus Torvalds } 45992d7202bfSThomas Graf 4600d4ead6b3SDavid Ahern pmetrics = dst ? dst_metrics_ptr(dst) : rt->fib6_metrics->metrics; 4601d4ead6b3SDavid Ahern if (rtnetlink_put_metrics(skb, pmetrics) < 0) 46022d7202bfSThomas Graf goto nla_put_failure; 46032d7202bfSThomas Graf 460493c2fb25SDavid Ahern if (nla_put_u32(skb, RTA_PRIORITY, rt->fib6_metric)) 4605beb1afacSDavid Ahern goto nla_put_failure; 4606beb1afacSDavid Ahern 4607beb1afacSDavid Ahern /* For multipath routes, walk the siblings list and add 4608beb1afacSDavid Ahern * each as a nexthop within RTA_MULTIPATH. 4609beb1afacSDavid Ahern */ 461093c2fb25SDavid Ahern if (rt->fib6_nsiblings) { 46118d1c802bSDavid Ahern struct fib6_info *sibling, *next_sibling; 4612beb1afacSDavid Ahern struct nlattr *mp; 4613beb1afacSDavid Ahern 4614beb1afacSDavid Ahern mp = nla_nest_start(skb, RTA_MULTIPATH); 4615beb1afacSDavid Ahern if (!mp) 4616beb1afacSDavid Ahern goto nla_put_failure; 4617beb1afacSDavid Ahern 4618beb1afacSDavid Ahern if (rt6_add_nexthop(skb, rt) < 0) 4619beb1afacSDavid Ahern goto nla_put_failure; 4620beb1afacSDavid Ahern 4621beb1afacSDavid Ahern list_for_each_entry_safe(sibling, next_sibling, 462293c2fb25SDavid Ahern &rt->fib6_siblings, fib6_siblings) { 4623beb1afacSDavid Ahern if (rt6_add_nexthop(skb, sibling) < 0) 462494f826b8SEric Dumazet goto nla_put_failure; 462594f826b8SEric Dumazet } 46262d7202bfSThomas Graf 4627beb1afacSDavid Ahern nla_nest_end(skb, mp); 4628beb1afacSDavid Ahern } else { 46295be083ceSDavid Ahern if (rt6_nexthop_info(skb, rt, &rtm->rtm_flags, false) < 0) 4630c78679e8SDavid S. Miller goto nla_put_failure; 4631beb1afacSDavid Ahern } 46328253947eSLi Wei 463393c2fb25SDavid Ahern if (rt->fib6_flags & RTF_EXPIRES) { 463414895687SDavid Ahern expires = dst ? dst->expires : rt->expires; 463514895687SDavid Ahern expires -= jiffies; 463614895687SDavid Ahern } 463769cdf8f9SYOSHIFUJI Hideaki 4638d4ead6b3SDavid Ahern if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0) 4639e3703b3dSThomas Graf goto nla_put_failure; 46401da177e4SLinus Torvalds 464193c2fb25SDavid Ahern if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->fib6_flags))) 4642c78ba6d6SLubomir Rintel goto nla_put_failure; 4643c78ba6d6SLubomir Rintel 464419e42e45SRoopa Prabhu 4645053c095aSJohannes Berg nlmsg_end(skb, nlh); 4646053c095aSJohannes Berg return 0; 46472d7202bfSThomas Graf 46482d7202bfSThomas Graf nla_put_failure: 464926932566SPatrick McHardy nlmsg_cancel(skb, nlh); 465026932566SPatrick McHardy return -EMSGSIZE; 46511da177e4SLinus Torvalds } 46521da177e4SLinus Torvalds 46538d1c802bSDavid Ahern int rt6_dump_route(struct fib6_info *rt, void *p_arg) 46541da177e4SLinus Torvalds { 46551da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 46561f17e2f2SDavid Ahern struct net *net = arg->net; 46571f17e2f2SDavid Ahern 4658421842edSDavid Ahern if (rt == net->ipv6.fib6_null_entry) 46591f17e2f2SDavid Ahern return 0; 46601da177e4SLinus Torvalds 46612d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 46622d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 4663f8cfe2ceSDavid Ahern 4664f8cfe2ceSDavid Ahern /* user wants prefix routes only */ 4665f8cfe2ceSDavid Ahern if (rtm->rtm_flags & RTM_F_PREFIX && 466693c2fb25SDavid Ahern !(rt->fib6_flags & RTF_PREFIX_RT)) { 4667f8cfe2ceSDavid Ahern /* success since this is not a prefix route */ 4668f8cfe2ceSDavid Ahern return 1; 4669f8cfe2ceSDavid Ahern } 4670f8cfe2ceSDavid Ahern } 46711da177e4SLinus Torvalds 4672d4ead6b3SDavid Ahern return rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL, 0, 4673d4ead6b3SDavid Ahern RTM_NEWROUTE, NETLINK_CB(arg->cb->skb).portid, 4674d4ead6b3SDavid Ahern arg->cb->nlh->nlmsg_seq, NLM_F_MULTI); 46751da177e4SLinus Torvalds } 46761da177e4SLinus Torvalds 4677c21ef3e3SDavid Ahern static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, 4678c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 46791da177e4SLinus Torvalds { 46803b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 4681ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 468218c3a61cSRoopa Prabhu int err, iif = 0, oif = 0; 468318c3a61cSRoopa Prabhu struct dst_entry *dst; 46841da177e4SLinus Torvalds struct rt6_info *rt; 4685ab364a6fSThomas Graf struct sk_buff *skb; 4686ab364a6fSThomas Graf struct rtmsg *rtm; 46874c9483b2SDavid S. Miller struct flowi6 fl6; 468818c3a61cSRoopa Prabhu bool fibmatch; 4689ab364a6fSThomas Graf 4690fceb6435SJohannes Berg err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy, 4691c21ef3e3SDavid Ahern extack); 4692ab364a6fSThomas Graf if (err < 0) 4693ab364a6fSThomas Graf goto errout; 4694ab364a6fSThomas Graf 4695ab364a6fSThomas Graf err = -EINVAL; 46964c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 469738b7097bSHannes Frederic Sowa rtm = nlmsg_data(nlh); 469838b7097bSHannes Frederic Sowa fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0); 469918c3a61cSRoopa Prabhu fibmatch = !!(rtm->rtm_flags & RTM_F_FIB_MATCH); 4700ab364a6fSThomas Graf 4701ab364a6fSThomas Graf if (tb[RTA_SRC]) { 4702ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 4703ab364a6fSThomas Graf goto errout; 4704ab364a6fSThomas Graf 47054e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 4706ab364a6fSThomas Graf } 4707ab364a6fSThomas Graf 4708ab364a6fSThomas Graf if (tb[RTA_DST]) { 4709ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 4710ab364a6fSThomas Graf goto errout; 4711ab364a6fSThomas Graf 47124e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 4713ab364a6fSThomas Graf } 4714ab364a6fSThomas Graf 4715ab364a6fSThomas Graf if (tb[RTA_IIF]) 4716ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 4717ab364a6fSThomas Graf 4718ab364a6fSThomas Graf if (tb[RTA_OIF]) 471972331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 4720ab364a6fSThomas Graf 47212e47b291SLorenzo Colitti if (tb[RTA_MARK]) 47222e47b291SLorenzo Colitti fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 47232e47b291SLorenzo Colitti 4724622ec2c9SLorenzo Colitti if (tb[RTA_UID]) 4725622ec2c9SLorenzo Colitti fl6.flowi6_uid = make_kuid(current_user_ns(), 4726622ec2c9SLorenzo Colitti nla_get_u32(tb[RTA_UID])); 4727622ec2c9SLorenzo Colitti else 4728622ec2c9SLorenzo Colitti fl6.flowi6_uid = iif ? INVALID_UID : current_uid(); 4729622ec2c9SLorenzo Colitti 4730ab364a6fSThomas Graf if (iif) { 4731ab364a6fSThomas Graf struct net_device *dev; 473272331bc0SShmulik Ladkani int flags = 0; 473372331bc0SShmulik Ladkani 4734121622dbSFlorian Westphal rcu_read_lock(); 4735121622dbSFlorian Westphal 4736121622dbSFlorian Westphal dev = dev_get_by_index_rcu(net, iif); 4737ab364a6fSThomas Graf if (!dev) { 4738121622dbSFlorian Westphal rcu_read_unlock(); 4739ab364a6fSThomas Graf err = -ENODEV; 4740ab364a6fSThomas Graf goto errout; 4741ab364a6fSThomas Graf } 474272331bc0SShmulik Ladkani 474372331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 474472331bc0SShmulik Ladkani 474572331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 474672331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 474772331bc0SShmulik Ladkani 4748b75cc8f9SDavid Ahern dst = ip6_route_input_lookup(net, dev, &fl6, NULL, flags); 4749121622dbSFlorian Westphal 4750121622dbSFlorian Westphal rcu_read_unlock(); 475172331bc0SShmulik Ladkani } else { 475272331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 475372331bc0SShmulik Ladkani 475418c3a61cSRoopa Prabhu dst = ip6_route_output(net, NULL, &fl6); 475518c3a61cSRoopa Prabhu } 475618c3a61cSRoopa Prabhu 475718c3a61cSRoopa Prabhu 475818c3a61cSRoopa Prabhu rt = container_of(dst, struct rt6_info, dst); 475918c3a61cSRoopa Prabhu if (rt->dst.error) { 476018c3a61cSRoopa Prabhu err = rt->dst.error; 476118c3a61cSRoopa Prabhu ip6_rt_put(rt); 476218c3a61cSRoopa Prabhu goto errout; 4763ab364a6fSThomas Graf } 47641da177e4SLinus Torvalds 47659d6acb3bSWANG Cong if (rt == net->ipv6.ip6_null_entry) { 47669d6acb3bSWANG Cong err = rt->dst.error; 47679d6acb3bSWANG Cong ip6_rt_put(rt); 47689d6acb3bSWANG Cong goto errout; 47699d6acb3bSWANG Cong } 47709d6acb3bSWANG Cong 47711da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 477238308473SDavid S. Miller if (!skb) { 477394e187c0SAmerigo Wang ip6_rt_put(rt); 4774ab364a6fSThomas Graf err = -ENOBUFS; 4775ab364a6fSThomas Graf goto errout; 4776ab364a6fSThomas Graf } 47771da177e4SLinus Torvalds 4778d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 477918c3a61cSRoopa Prabhu if (fibmatch) 478093531c67SDavid Ahern err = rt6_fill_node(net, skb, rt->from, NULL, NULL, NULL, iif, 478118c3a61cSRoopa Prabhu RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 478218c3a61cSRoopa Prabhu nlh->nlmsg_seq, 0); 478318c3a61cSRoopa Prabhu else 478493531c67SDavid Ahern err = rt6_fill_node(net, skb, rt->from, dst, 478593531c67SDavid Ahern &fl6.daddr, &fl6.saddr, iif, RTM_NEWROUTE, 4786d4ead6b3SDavid Ahern NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, 4787d4ead6b3SDavid Ahern 0); 47881da177e4SLinus Torvalds if (err < 0) { 4789ab364a6fSThomas Graf kfree_skb(skb); 4790ab364a6fSThomas Graf goto errout; 47911da177e4SLinus Torvalds } 47921da177e4SLinus Torvalds 479315e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 4794ab364a6fSThomas Graf errout: 47951da177e4SLinus Torvalds return err; 47961da177e4SLinus Torvalds } 47971da177e4SLinus Torvalds 47988d1c802bSDavid Ahern void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info, 479937a1d361SRoopa Prabhu unsigned int nlm_flags) 48001da177e4SLinus Torvalds { 48011da177e4SLinus Torvalds struct sk_buff *skb; 48025578689aSDaniel Lezcano struct net *net = info->nl_net; 4803528c4cebSDenis V. Lunev u32 seq; 4804528c4cebSDenis V. Lunev int err; 48050d51aa80SJamal Hadi Salim 4806528c4cebSDenis V. Lunev err = -ENOBUFS; 480738308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 480886872cb5SThomas Graf 480919e42e45SRoopa Prabhu skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 481038308473SDavid S. Miller if (!skb) 481121713ebcSThomas Graf goto errout; 48121da177e4SLinus Torvalds 4813d4ead6b3SDavid Ahern err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0, 4814f8cfe2ceSDavid Ahern event, info->portid, seq, nlm_flags); 481526932566SPatrick McHardy if (err < 0) { 481626932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 481726932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 481826932566SPatrick McHardy kfree_skb(skb); 481926932566SPatrick McHardy goto errout; 482026932566SPatrick McHardy } 482115e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 48225578689aSDaniel Lezcano info->nlh, gfp_any()); 48231ce85fe4SPablo Neira Ayuso return; 482421713ebcSThomas Graf errout: 482521713ebcSThomas Graf if (err < 0) 48265578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 48271da177e4SLinus Torvalds } 48281da177e4SLinus Torvalds 48298ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 4830351638e7SJiri Pirko unsigned long event, void *ptr) 48318ed67789SDaniel Lezcano { 4832351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 4833c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 48348ed67789SDaniel Lezcano 4835242d3a49SWANG Cong if (!(dev->flags & IFF_LOOPBACK)) 4836242d3a49SWANG Cong return NOTIFY_OK; 4837242d3a49SWANG Cong 4838242d3a49SWANG Cong if (event == NETDEV_REGISTER) { 4839421842edSDavid Ahern net->ipv6.fib6_null_entry->fib6_nh.nh_dev = dev; 4840d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 48418ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 48428ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 4843d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 48448ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 4845d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 48468ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 48478ed67789SDaniel Lezcano #endif 484876da0704SWANG Cong } else if (event == NETDEV_UNREGISTER && 484976da0704SWANG Cong dev->reg_state != NETREG_UNREGISTERED) { 485076da0704SWANG Cong /* NETDEV_UNREGISTER could be fired for multiple times by 485176da0704SWANG Cong * netdev_wait_allrefs(). Make sure we only call this once. 485276da0704SWANG Cong */ 485312d94a80SEric Dumazet in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev); 4854242d3a49SWANG Cong #ifdef CONFIG_IPV6_MULTIPLE_TABLES 485512d94a80SEric Dumazet in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev); 485612d94a80SEric Dumazet in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev); 4857242d3a49SWANG Cong #endif 48588ed67789SDaniel Lezcano } 48598ed67789SDaniel Lezcano 48608ed67789SDaniel Lezcano return NOTIFY_OK; 48618ed67789SDaniel Lezcano } 48628ed67789SDaniel Lezcano 48631da177e4SLinus Torvalds /* 48641da177e4SLinus Torvalds * /proc 48651da177e4SLinus Torvalds */ 48661da177e4SLinus Torvalds 48671da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 48681da177e4SLinus Torvalds 486933120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 487033120b30SAlexey Dobriyan .open = ipv6_route_open, 487133120b30SAlexey Dobriyan .read = seq_read, 487233120b30SAlexey Dobriyan .llseek = seq_lseek, 48738d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 487433120b30SAlexey Dobriyan }; 487533120b30SAlexey Dobriyan 48761da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 48771da177e4SLinus Torvalds { 487869ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 48791da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 488069ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 488169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 488281eb8447SWei Wang atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc), 488369ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 488469ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 4885fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 488669ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 48871da177e4SLinus Torvalds 48881da177e4SLinus Torvalds return 0; 48891da177e4SLinus Torvalds } 48901da177e4SLinus Torvalds 48911da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 48921da177e4SLinus Torvalds { 4893de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 489469ddb805SDaniel Lezcano } 489569ddb805SDaniel Lezcano 48969a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 48971da177e4SLinus Torvalds .open = rt6_stats_seq_open, 48981da177e4SLinus Torvalds .read = seq_read, 48991da177e4SLinus Torvalds .llseek = seq_lseek, 4900b6fcbdb4SPavel Emelyanov .release = single_release_net, 49011da177e4SLinus Torvalds }; 49021da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 49031da177e4SLinus Torvalds 49041da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 49051da177e4SLinus Torvalds 49061da177e4SLinus Torvalds static 4907fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 49081da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 49091da177e4SLinus Torvalds { 4910c486da34SLucian Adrian Grijincu struct net *net; 4911c486da34SLucian Adrian Grijincu int delay; 4912c486da34SLucian Adrian Grijincu if (!write) 4913c486da34SLucian Adrian Grijincu return -EINVAL; 4914c486da34SLucian Adrian Grijincu 4915c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 4916c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 49178d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 49182ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 49191da177e4SLinus Torvalds return 0; 49201da177e4SLinus Torvalds } 49211da177e4SLinus Torvalds 4922fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 49231da177e4SLinus Torvalds { 49241da177e4SLinus Torvalds .procname = "flush", 49254990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 49261da177e4SLinus Torvalds .maxlen = sizeof(int), 492789c8b3a1SDave Jones .mode = 0200, 49286d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 49291da177e4SLinus Torvalds }, 49301da177e4SLinus Torvalds { 49311da177e4SLinus Torvalds .procname = "gc_thresh", 49329a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 49331da177e4SLinus Torvalds .maxlen = sizeof(int), 49341da177e4SLinus Torvalds .mode = 0644, 49356d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 49361da177e4SLinus Torvalds }, 49371da177e4SLinus Torvalds { 49381da177e4SLinus Torvalds .procname = "max_size", 49394990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 49401da177e4SLinus Torvalds .maxlen = sizeof(int), 49411da177e4SLinus Torvalds .mode = 0644, 49426d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 49431da177e4SLinus Torvalds }, 49441da177e4SLinus Torvalds { 49451da177e4SLinus Torvalds .procname = "gc_min_interval", 49464990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 49471da177e4SLinus Torvalds .maxlen = sizeof(int), 49481da177e4SLinus Torvalds .mode = 0644, 49496d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 49501da177e4SLinus Torvalds }, 49511da177e4SLinus Torvalds { 49521da177e4SLinus Torvalds .procname = "gc_timeout", 49534990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 49541da177e4SLinus Torvalds .maxlen = sizeof(int), 49551da177e4SLinus Torvalds .mode = 0644, 49566d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 49571da177e4SLinus Torvalds }, 49581da177e4SLinus Torvalds { 49591da177e4SLinus Torvalds .procname = "gc_interval", 49604990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 49611da177e4SLinus Torvalds .maxlen = sizeof(int), 49621da177e4SLinus Torvalds .mode = 0644, 49636d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 49641da177e4SLinus Torvalds }, 49651da177e4SLinus Torvalds { 49661da177e4SLinus Torvalds .procname = "gc_elasticity", 49674990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 49681da177e4SLinus Torvalds .maxlen = sizeof(int), 49691da177e4SLinus Torvalds .mode = 0644, 4970f3d3f616SMin Zhang .proc_handler = proc_dointvec, 49711da177e4SLinus Torvalds }, 49721da177e4SLinus Torvalds { 49731da177e4SLinus Torvalds .procname = "mtu_expires", 49744990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 49751da177e4SLinus Torvalds .maxlen = sizeof(int), 49761da177e4SLinus Torvalds .mode = 0644, 49776d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 49781da177e4SLinus Torvalds }, 49791da177e4SLinus Torvalds { 49801da177e4SLinus Torvalds .procname = "min_adv_mss", 49814990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 49821da177e4SLinus Torvalds .maxlen = sizeof(int), 49831da177e4SLinus Torvalds .mode = 0644, 4984f3d3f616SMin Zhang .proc_handler = proc_dointvec, 49851da177e4SLinus Torvalds }, 49861da177e4SLinus Torvalds { 49871da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 49884990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 49891da177e4SLinus Torvalds .maxlen = sizeof(int), 49901da177e4SLinus Torvalds .mode = 0644, 49916d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 49921da177e4SLinus Torvalds }, 4993f8572d8fSEric W. Biederman { } 49941da177e4SLinus Torvalds }; 49951da177e4SLinus Torvalds 49962c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 4997760f2d01SDaniel Lezcano { 4998760f2d01SDaniel Lezcano struct ctl_table *table; 4999760f2d01SDaniel Lezcano 5000760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 5001760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 5002760f2d01SDaniel Lezcano GFP_KERNEL); 50035ee09105SYOSHIFUJI Hideaki 50045ee09105SYOSHIFUJI Hideaki if (table) { 50055ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 5006c486da34SLucian Adrian Grijincu table[0].extra1 = net; 500786393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 50085ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 50095ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 50105ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 50115ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 50125ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 50135ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 50145ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 50159c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 5016464dc801SEric W. Biederman 5017464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 5018464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 5019464dc801SEric W. Biederman table[0].procname = NULL; 50205ee09105SYOSHIFUJI Hideaki } 50215ee09105SYOSHIFUJI Hideaki 5022760f2d01SDaniel Lezcano return table; 5023760f2d01SDaniel Lezcano } 50241da177e4SLinus Torvalds #endif 50251da177e4SLinus Torvalds 50262c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 5027cdb18761SDaniel Lezcano { 5028633d424bSPavel Emelyanov int ret = -ENOMEM; 50298ed67789SDaniel Lezcano 503086393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 503186393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 5032f2fc6a54SBenjamin Thery 5033fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 5034fc66f95cSEric Dumazet goto out_ip6_dst_ops; 5035fc66f95cSEric Dumazet 5036421842edSDavid Ahern net->ipv6.fib6_null_entry = kmemdup(&fib6_null_entry_template, 5037421842edSDavid Ahern sizeof(*net->ipv6.fib6_null_entry), 5038421842edSDavid Ahern GFP_KERNEL); 5039421842edSDavid Ahern if (!net->ipv6.fib6_null_entry) 5040421842edSDavid Ahern goto out_ip6_dst_entries; 5041421842edSDavid Ahern 50428ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 50438ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 50448ed67789SDaniel Lezcano GFP_KERNEL); 50458ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 5046421842edSDavid Ahern goto out_fib6_null_entry; 5047d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 504862fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 504962fa8a84SDavid S. Miller ip6_template_metrics, true); 50508ed67789SDaniel Lezcano 50518ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 5052feca7d8cSVincent Bernat net->ipv6.fib6_has_custom_rules = false; 50538ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 50548ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 50558ed67789SDaniel Lezcano GFP_KERNEL); 505668fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 505768fffc67SPeter Zijlstra goto out_ip6_null_entry; 5058d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 505962fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 506062fa8a84SDavid S. Miller ip6_template_metrics, true); 50618ed67789SDaniel Lezcano 50628ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 50638ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 50648ed67789SDaniel Lezcano GFP_KERNEL); 506568fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 506668fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 5067d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 506862fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 506962fa8a84SDavid S. Miller ip6_template_metrics, true); 50708ed67789SDaniel Lezcano #endif 50718ed67789SDaniel Lezcano 5072b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 5073b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 5074b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 5075b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 5076b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 5077b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 5078b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 5079b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 5080b339a47cSPeter Zijlstra 50816891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 50826891a346SBenjamin Thery 50838ed67789SDaniel Lezcano ret = 0; 50848ed67789SDaniel Lezcano out: 50858ed67789SDaniel Lezcano return ret; 5086f2fc6a54SBenjamin Thery 508768fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 508868fffc67SPeter Zijlstra out_ip6_prohibit_entry: 508968fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 509068fffc67SPeter Zijlstra out_ip6_null_entry: 509168fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 509268fffc67SPeter Zijlstra #endif 5093421842edSDavid Ahern out_fib6_null_entry: 5094421842edSDavid Ahern kfree(net->ipv6.fib6_null_entry); 5095fc66f95cSEric Dumazet out_ip6_dst_entries: 5096fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 5097f2fc6a54SBenjamin Thery out_ip6_dst_ops: 5098f2fc6a54SBenjamin Thery goto out; 5099cdb18761SDaniel Lezcano } 5100cdb18761SDaniel Lezcano 51012c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 5102cdb18761SDaniel Lezcano { 5103421842edSDavid Ahern kfree(net->ipv6.fib6_null_entry); 51048ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 51058ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 51068ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 51078ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 51088ed67789SDaniel Lezcano #endif 510941bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 5110cdb18761SDaniel Lezcano } 5111cdb18761SDaniel Lezcano 5112d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 5113d189634eSThomas Graf { 5114d189634eSThomas Graf #ifdef CONFIG_PROC_FS 5115d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 5116d6444062SJoe Perches proc_create("rt6_stats", 0444, net->proc_net, &rt6_stats_seq_fops); 5117d189634eSThomas Graf #endif 5118d189634eSThomas Graf return 0; 5119d189634eSThomas Graf } 5120d189634eSThomas Graf 5121d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 5122d189634eSThomas Graf { 5123d189634eSThomas Graf #ifdef CONFIG_PROC_FS 5124ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 5125ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 5126d189634eSThomas Graf #endif 5127d189634eSThomas Graf } 5128d189634eSThomas Graf 5129cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 5130cdb18761SDaniel Lezcano .init = ip6_route_net_init, 5131cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 5132cdb18761SDaniel Lezcano }; 5133cdb18761SDaniel Lezcano 5134c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 5135c3426b47SDavid S. Miller { 5136c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 5137c3426b47SDavid S. Miller 5138c3426b47SDavid S. Miller if (!bp) 5139c3426b47SDavid S. Miller return -ENOMEM; 5140c3426b47SDavid S. Miller inet_peer_base_init(bp); 5141c3426b47SDavid S. Miller net->ipv6.peers = bp; 5142c3426b47SDavid S. Miller return 0; 5143c3426b47SDavid S. Miller } 5144c3426b47SDavid S. Miller 5145c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 5146c3426b47SDavid S. Miller { 5147c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 5148c3426b47SDavid S. Miller 5149c3426b47SDavid S. Miller net->ipv6.peers = NULL; 515056a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 5151c3426b47SDavid S. Miller kfree(bp); 5152c3426b47SDavid S. Miller } 5153c3426b47SDavid S. Miller 51542b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 5155c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 5156c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 5157c3426b47SDavid S. Miller }; 5158c3426b47SDavid S. Miller 5159d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 5160d189634eSThomas Graf .init = ip6_route_net_init_late, 5161d189634eSThomas Graf .exit = ip6_route_net_exit_late, 5162d189634eSThomas Graf }; 5163d189634eSThomas Graf 51648ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 51658ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 5166242d3a49SWANG Cong .priority = ADDRCONF_NOTIFY_PRIORITY - 10, 51678ed67789SDaniel Lezcano }; 51688ed67789SDaniel Lezcano 51692f460933SWANG Cong void __init ip6_route_init_special_entries(void) 51702f460933SWANG Cong { 51712f460933SWANG Cong /* Registering of the loopback is done before this portion of code, 51722f460933SWANG Cong * the loopback reference in rt6_info will not be taken, do it 51732f460933SWANG Cong * manually for init_net */ 5174421842edSDavid Ahern init_net.ipv6.fib6_null_entry->fib6_nh.nh_dev = init_net.loopback_dev; 51752f460933SWANG Cong init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 51762f460933SWANG Cong init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 51772f460933SWANG Cong #ifdef CONFIG_IPV6_MULTIPLE_TABLES 51782f460933SWANG Cong init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 51792f460933SWANG Cong init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 51802f460933SWANG Cong init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 51812f460933SWANG Cong init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 51822f460933SWANG Cong #endif 51832f460933SWANG Cong } 51842f460933SWANG Cong 5185433d49c3SDaniel Lezcano int __init ip6_route_init(void) 51861da177e4SLinus Torvalds { 5187433d49c3SDaniel Lezcano int ret; 51888d0b94afSMartin KaFai Lau int cpu; 5189433d49c3SDaniel Lezcano 51909a7ec3a9SDaniel Lezcano ret = -ENOMEM; 51919a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 51929a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 51939a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 51949a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 5195c19a28e1SFernando Carrijo goto out; 519614e50e57SDavid S. Miller 5197fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 51988ed67789SDaniel Lezcano if (ret) 5199bdb3289fSDaniel Lezcano goto out_kmem_cache; 5200bdb3289fSDaniel Lezcano 5201c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 5202c3426b47SDavid S. Miller if (ret) 5203e8803b6cSDavid S. Miller goto out_dst_entries; 52042a0c451aSThomas Graf 52057e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 52067e52b33bSDavid S. Miller if (ret) 52077e52b33bSDavid S. Miller goto out_register_inetpeer; 5208c3426b47SDavid S. Miller 52095dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 52105dc121e9SArnaud Ebalard 5211e8803b6cSDavid S. Miller ret = fib6_init(); 5212433d49c3SDaniel Lezcano if (ret) 52138ed67789SDaniel Lezcano goto out_register_subsys; 5214433d49c3SDaniel Lezcano 5215433d49c3SDaniel Lezcano ret = xfrm6_init(); 5216433d49c3SDaniel Lezcano if (ret) 5217e8803b6cSDavid S. Miller goto out_fib6_init; 5218c35b7e72SDaniel Lezcano 5219433d49c3SDaniel Lezcano ret = fib6_rules_init(); 5220433d49c3SDaniel Lezcano if (ret) 5221433d49c3SDaniel Lezcano goto xfrm6_init; 52227e5449c2SDaniel Lezcano 5223d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 5224d189634eSThomas Graf if (ret) 5225d189634eSThomas Graf goto fib6_rules_init; 5226d189634eSThomas Graf 522716feebcfSFlorian Westphal ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWROUTE, 522816feebcfSFlorian Westphal inet6_rtm_newroute, NULL, 0); 522916feebcfSFlorian Westphal if (ret < 0) 523016feebcfSFlorian Westphal goto out_register_late_subsys; 523116feebcfSFlorian Westphal 523216feebcfSFlorian Westphal ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELROUTE, 523316feebcfSFlorian Westphal inet6_rtm_delroute, NULL, 0); 523416feebcfSFlorian Westphal if (ret < 0) 523516feebcfSFlorian Westphal goto out_register_late_subsys; 523616feebcfSFlorian Westphal 523716feebcfSFlorian Westphal ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE, 523816feebcfSFlorian Westphal inet6_rtm_getroute, NULL, 523916feebcfSFlorian Westphal RTNL_FLAG_DOIT_UNLOCKED); 524016feebcfSFlorian Westphal if (ret < 0) 5241d189634eSThomas Graf goto out_register_late_subsys; 5242433d49c3SDaniel Lezcano 52438ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 5244cdb18761SDaniel Lezcano if (ret) 5245d189634eSThomas Graf goto out_register_late_subsys; 52468ed67789SDaniel Lezcano 52478d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 52488d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 52498d0b94afSMartin KaFai Lau 52508d0b94afSMartin KaFai Lau INIT_LIST_HEAD(&ul->head); 52518d0b94afSMartin KaFai Lau spin_lock_init(&ul->lock); 52528d0b94afSMartin KaFai Lau } 52538d0b94afSMartin KaFai Lau 5254433d49c3SDaniel Lezcano out: 5255433d49c3SDaniel Lezcano return ret; 5256433d49c3SDaniel Lezcano 5257d189634eSThomas Graf out_register_late_subsys: 525816feebcfSFlorian Westphal rtnl_unregister_all(PF_INET6); 5259d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 5260433d49c3SDaniel Lezcano fib6_rules_init: 5261433d49c3SDaniel Lezcano fib6_rules_cleanup(); 5262433d49c3SDaniel Lezcano xfrm6_init: 5263433d49c3SDaniel Lezcano xfrm6_fini(); 52642a0c451aSThomas Graf out_fib6_init: 52652a0c451aSThomas Graf fib6_gc_cleanup(); 52668ed67789SDaniel Lezcano out_register_subsys: 52678ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 52687e52b33bSDavid S. Miller out_register_inetpeer: 52697e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 5270fc66f95cSEric Dumazet out_dst_entries: 5271fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 5272433d49c3SDaniel Lezcano out_kmem_cache: 5273f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 5274433d49c3SDaniel Lezcano goto out; 52751da177e4SLinus Torvalds } 52761da177e4SLinus Torvalds 52771da177e4SLinus Torvalds void ip6_route_cleanup(void) 52781da177e4SLinus Torvalds { 52798ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 5280d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 5281101367c2SThomas Graf fib6_rules_cleanup(); 52821da177e4SLinus Torvalds xfrm6_fini(); 52831da177e4SLinus Torvalds fib6_gc_cleanup(); 5284c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 52858ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 528641bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 5287f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 52881da177e4SLinus Torvalds } 5289