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> 47457c4cbcSEric W. Biederman #include <net/net_namespace.h> 481da177e4SLinus Torvalds #include <net/snmp.h> 491da177e4SLinus Torvalds #include <net/ipv6.h> 501da177e4SLinus Torvalds #include <net/ip6_fib.h> 511da177e4SLinus Torvalds #include <net/ip6_route.h> 521da177e4SLinus Torvalds #include <net/ndisc.h> 531da177e4SLinus Torvalds #include <net/addrconf.h> 541da177e4SLinus Torvalds #include <net/tcp.h> 551da177e4SLinus Torvalds #include <linux/rtnetlink.h> 561da177e4SLinus Torvalds #include <net/dst.h> 57904af04dSJiri Benc #include <net/dst_metadata.h> 581da177e4SLinus Torvalds #include <net/xfrm.h> 598d71740cSTom Tucker #include <net/netevent.h> 6021713ebcSThomas Graf #include <net/netlink.h> 6151ebd318SNicolas Dichtel #include <net/nexthop.h> 6219e42e45SRoopa Prabhu #include <net/lwtunnel.h> 63904af04dSJiri Benc #include <net/ip_tunnels.h> 64ca254490SDavid Ahern #include <net/l3mdev.h> 65b811580dSDavid Ahern #include <trace/events/fib6.h> 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds #include <asm/uaccess.h> 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 701da177e4SLinus Torvalds #include <linux/sysctl.h> 711da177e4SLinus Torvalds #endif 721da177e4SLinus Torvalds 73afc154e9SHannes Frederic Sowa enum rt6_nud_state { 747e980569SJiri Benc RT6_NUD_FAIL_HARD = -3, 757e980569SJiri Benc RT6_NUD_FAIL_PROBE = -2, 767e980569SJiri Benc RT6_NUD_FAIL_DO_RR = -1, 77afc154e9SHannes Frederic Sowa RT6_NUD_SUCCEED = 1 78afc154e9SHannes Frederic Sowa }; 79afc154e9SHannes Frederic Sowa 8083a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort); 811da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 820dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst); 83ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst); 841da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *); 851da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *); 861da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *, 871da177e4SLinus Torvalds struct net_device *dev, int how); 88569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops); 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds static int ip6_pkt_discard(struct sk_buff *skb); 91ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); 927150aedeSKamala R static int ip6_pkt_prohibit(struct sk_buff *skb); 93ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb); 941da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb); 956700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 966700c270SDavid S. Miller struct sk_buff *skb, u32 mtu); 976700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, 986700c270SDavid S. Miller struct sk_buff *skb); 994b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt); 10052bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict); 1011da177e4SLinus Torvalds 10270ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 103efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 104b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 105b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 10695c96174SEric Dumazet unsigned int pref); 107efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 108b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 109b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex); 11070ceb4f5SYOSHIFUJI Hideaki #endif 11170ceb4f5SYOSHIFUJI Hideaki 1128d0b94afSMartin KaFai Lau struct uncached_list { 1138d0b94afSMartin KaFai Lau spinlock_t lock; 1148d0b94afSMartin KaFai Lau struct list_head head; 1158d0b94afSMartin KaFai Lau }; 1168d0b94afSMartin KaFai Lau 1178d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list); 1188d0b94afSMartin KaFai Lau 1198d0b94afSMartin KaFai Lau static void rt6_uncached_list_add(struct rt6_info *rt) 1208d0b94afSMartin KaFai Lau { 1218d0b94afSMartin KaFai Lau struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list); 1228d0b94afSMartin KaFai Lau 1238d0b94afSMartin KaFai Lau rt->dst.flags |= DST_NOCACHE; 1248d0b94afSMartin KaFai Lau rt->rt6i_uncached_list = ul; 1258d0b94afSMartin KaFai Lau 1268d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1278d0b94afSMartin KaFai Lau list_add_tail(&rt->rt6i_uncached, &ul->head); 1288d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1298d0b94afSMartin KaFai Lau } 1308d0b94afSMartin KaFai Lau 1318d0b94afSMartin KaFai Lau static void rt6_uncached_list_del(struct rt6_info *rt) 1328d0b94afSMartin KaFai Lau { 1338d0b94afSMartin KaFai Lau if (!list_empty(&rt->rt6i_uncached)) { 1348d0b94afSMartin KaFai Lau struct uncached_list *ul = rt->rt6i_uncached_list; 1358d0b94afSMartin KaFai Lau 1368d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1378d0b94afSMartin KaFai Lau list_del(&rt->rt6i_uncached); 1388d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1398d0b94afSMartin KaFai Lau } 1408d0b94afSMartin KaFai Lau } 1418d0b94afSMartin KaFai Lau 1428d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) 1438d0b94afSMartin KaFai Lau { 1448d0b94afSMartin KaFai Lau struct net_device *loopback_dev = net->loopback_dev; 1458d0b94afSMartin KaFai Lau int cpu; 1468d0b94afSMartin KaFai Lau 147e332bc67SEric W. Biederman if (dev == loopback_dev) 148e332bc67SEric W. Biederman return; 149e332bc67SEric W. Biederman 1508d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 1518d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 1528d0b94afSMartin KaFai Lau struct rt6_info *rt; 1538d0b94afSMartin KaFai Lau 1548d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1558d0b94afSMartin KaFai Lau list_for_each_entry(rt, &ul->head, rt6i_uncached) { 1568d0b94afSMartin KaFai Lau struct inet6_dev *rt_idev = rt->rt6i_idev; 1578d0b94afSMartin KaFai Lau struct net_device *rt_dev = rt->dst.dev; 1588d0b94afSMartin KaFai Lau 159e332bc67SEric W. Biederman if (rt_idev->dev == dev) { 1608d0b94afSMartin KaFai Lau rt->rt6i_idev = in6_dev_get(loopback_dev); 1618d0b94afSMartin KaFai Lau in6_dev_put(rt_idev); 1628d0b94afSMartin KaFai Lau } 1638d0b94afSMartin KaFai Lau 164e332bc67SEric W. Biederman if (rt_dev == dev) { 1658d0b94afSMartin KaFai Lau rt->dst.dev = loopback_dev; 1668d0b94afSMartin KaFai Lau dev_hold(rt->dst.dev); 1678d0b94afSMartin KaFai Lau dev_put(rt_dev); 1688d0b94afSMartin KaFai Lau } 1698d0b94afSMartin KaFai Lau } 1708d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1718d0b94afSMartin KaFai Lau } 1728d0b94afSMartin KaFai Lau } 1738d0b94afSMartin KaFai Lau 174d52d3997SMartin KaFai Lau static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt) 175d52d3997SMartin KaFai Lau { 176d52d3997SMartin KaFai Lau return dst_metrics_write_ptr(rt->dst.from); 177d52d3997SMartin KaFai Lau } 178d52d3997SMartin KaFai Lau 17906582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) 18006582540SDavid S. Miller { 18106582540SDavid S. Miller struct rt6_info *rt = (struct rt6_info *)dst; 18206582540SDavid S. Miller 183d52d3997SMartin KaFai Lau if (rt->rt6i_flags & RTF_PCPU) 184d52d3997SMartin KaFai Lau return rt6_pcpu_cow_metrics(rt); 185d52d3997SMartin KaFai Lau else if (rt->rt6i_flags & RTF_CACHE) 1864b32b5adSMartin KaFai Lau return NULL; 1874b32b5adSMartin KaFai Lau else 1883b471175SMartin KaFai Lau return dst_cow_metrics_generic(dst, old); 18906582540SDavid S. Miller } 19006582540SDavid S. Miller 191f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, 192f894cbf8SDavid S. Miller struct sk_buff *skb, 193f894cbf8SDavid S. Miller const void *daddr) 19439232973SDavid S. Miller { 19539232973SDavid S. Miller struct in6_addr *p = &rt->rt6i_gateway; 19639232973SDavid S. Miller 197a7563f34SDavid S. Miller if (!ipv6_addr_any(p)) 19839232973SDavid S. Miller return (const void *) p; 199f894cbf8SDavid S. Miller else if (skb) 200f894cbf8SDavid S. Miller return &ipv6_hdr(skb)->daddr; 20139232973SDavid S. Miller return daddr; 20239232973SDavid S. Miller } 20339232973SDavid S. Miller 204f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, 205f894cbf8SDavid S. Miller struct sk_buff *skb, 206f894cbf8SDavid S. Miller const void *daddr) 207d3aaeb38SDavid S. Miller { 20839232973SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 20939232973SDavid S. Miller struct neighbour *n; 21039232973SDavid S. Miller 211f894cbf8SDavid S. Miller daddr = choose_neigh_daddr(rt, skb, daddr); 2128e022ee6SYOSHIFUJI Hideaki / 吉藤英明 n = __ipv6_neigh_lookup(dst->dev, daddr); 213f83c7790SDavid S. Miller if (n) 214f83c7790SDavid S. Miller return n; 215f83c7790SDavid S. Miller return neigh_create(&nd_tbl, daddr, dst->dev); 216f83c7790SDavid S. Miller } 217f83c7790SDavid S. Miller 2189a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 2191da177e4SLinus Torvalds .family = AF_INET6, 2201da177e4SLinus Torvalds .gc = ip6_dst_gc, 2211da177e4SLinus Torvalds .gc_thresh = 1024, 2221da177e4SLinus Torvalds .check = ip6_dst_check, 2230dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 224ebb762f2SSteffen Klassert .mtu = ip6_mtu, 22506582540SDavid S. Miller .cow_metrics = ipv6_cow_metrics, 2261da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 2271da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 2281da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 2291da177e4SLinus Torvalds .link_failure = ip6_link_failure, 2301da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 2316e157b6aSDavid S. Miller .redirect = rt6_do_redirect, 2329f8955ccSEric W. Biederman .local_out = __ip6_local_out, 233d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 2341da177e4SLinus Torvalds }; 2351da177e4SLinus Torvalds 236ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 237ec831ea7SRoland Dreier { 238618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 239618f9bc7SSteffen Klassert 240618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 241ec831ea7SRoland Dreier } 242ec831ea7SRoland Dreier 2436700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, 2446700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 24514e50e57SDavid S. Miller { 24614e50e57SDavid S. Miller } 24714e50e57SDavid S. Miller 2486700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, 2496700c270SDavid S. Miller struct sk_buff *skb) 250b587ee3bSDavid S. Miller { 251b587ee3bSDavid S. Miller } 252b587ee3bSDavid S. Miller 25314e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 25414e50e57SDavid S. Miller .family = AF_INET6, 25514e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 25614e50e57SDavid S. Miller .check = ip6_dst_check, 257ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 258214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 25914e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 260b587ee3bSDavid S. Miller .redirect = ip6_rt_blackhole_redirect, 2610a1f5962SMartin KaFai Lau .cow_metrics = dst_cow_metrics_generic, 262d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 26314e50e57SDavid S. Miller }; 26414e50e57SDavid S. Miller 26562fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 26614edd87dSLi RongQing [RTAX_HOPLIMIT - 1] = 0, 26762fa8a84SDavid S. Miller }; 26862fa8a84SDavid S. Miller 269fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = { 2701da177e4SLinus Torvalds .dst = { 2711da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 2721da177e4SLinus Torvalds .__use = 1, 2732c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 2741da177e4SLinus Torvalds .error = -ENETUNREACH, 2751da177e4SLinus Torvalds .input = ip6_pkt_discard, 2761da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 2771da177e4SLinus Torvalds }, 2781da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2794f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 2801da177e4SLinus Torvalds .rt6i_metric = ~(u32) 0, 2811da177e4SLinus Torvalds .rt6i_ref = ATOMIC_INIT(1), 2821da177e4SLinus Torvalds }; 2831da177e4SLinus Torvalds 284101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 285101367c2SThomas Graf 286fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = { 287101367c2SThomas Graf .dst = { 288101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 289101367c2SThomas Graf .__use = 1, 2902c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 291101367c2SThomas Graf .error = -EACCES, 2929ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 2939ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 294101367c2SThomas Graf }, 295101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2964f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 297101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 298101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 299101367c2SThomas Graf }; 300101367c2SThomas Graf 301fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = { 302101367c2SThomas Graf .dst = { 303101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 304101367c2SThomas Graf .__use = 1, 3052c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 306101367c2SThomas Graf .error = -EINVAL, 307352e512cSHerbert Xu .input = dst_discard, 308ede2059dSEric W. Biederman .output = dst_discard_out, 309101367c2SThomas Graf }, 310101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 3114f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 312101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 313101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 314101367c2SThomas Graf }; 315101367c2SThomas Graf 316101367c2SThomas Graf #endif 317101367c2SThomas Graf 318ebfa45f0SMartin KaFai Lau static void rt6_info_init(struct rt6_info *rt) 319ebfa45f0SMartin KaFai Lau { 320ebfa45f0SMartin KaFai Lau struct dst_entry *dst = &rt->dst; 321ebfa45f0SMartin KaFai Lau 322ebfa45f0SMartin KaFai Lau memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 323ebfa45f0SMartin KaFai Lau INIT_LIST_HEAD(&rt->rt6i_siblings); 324ebfa45f0SMartin KaFai Lau INIT_LIST_HEAD(&rt->rt6i_uncached); 325ebfa45f0SMartin KaFai Lau } 326ebfa45f0SMartin KaFai Lau 3271da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 328d52d3997SMartin KaFai Lau static struct rt6_info *__ip6_dst_alloc(struct net *net, 329957c665fSDavid S. Miller struct net_device *dev, 330ad706862SMartin KaFai Lau int flags) 3311da177e4SLinus Torvalds { 33297bab73fSDavid S. Miller struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 3336f3118b5SNicolas Dichtel 0, DST_OBSOLETE_FORCE_CHK, flags); 334cf911662SDavid S. Miller 335ebfa45f0SMartin KaFai Lau if (rt) 336ebfa45f0SMartin KaFai Lau rt6_info_init(rt); 3378104891bSSteffen Klassert 338cf911662SDavid S. Miller return rt; 3391da177e4SLinus Torvalds } 3401da177e4SLinus Torvalds 3419ab179d8SDavid Ahern struct rt6_info *ip6_dst_alloc(struct net *net, 342d52d3997SMartin KaFai Lau struct net_device *dev, 343ad706862SMartin KaFai Lau int flags) 344d52d3997SMartin KaFai Lau { 345ad706862SMartin KaFai Lau struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags); 346d52d3997SMartin KaFai Lau 347d52d3997SMartin KaFai Lau if (rt) { 348d52d3997SMartin KaFai Lau rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC); 349d52d3997SMartin KaFai Lau if (rt->rt6i_pcpu) { 350d52d3997SMartin KaFai Lau int cpu; 351d52d3997SMartin KaFai Lau 352d52d3997SMartin KaFai Lau for_each_possible_cpu(cpu) { 353d52d3997SMartin KaFai Lau struct rt6_info **p; 354d52d3997SMartin KaFai Lau 355d52d3997SMartin KaFai Lau p = per_cpu_ptr(rt->rt6i_pcpu, cpu); 356d52d3997SMartin KaFai Lau /* no one shares rt */ 357d52d3997SMartin KaFai Lau *p = NULL; 358d52d3997SMartin KaFai Lau } 359d52d3997SMartin KaFai Lau } else { 360d52d3997SMartin KaFai Lau dst_destroy((struct dst_entry *)rt); 361d52d3997SMartin KaFai Lau return NULL; 362d52d3997SMartin KaFai Lau } 363d52d3997SMartin KaFai Lau } 364d52d3997SMartin KaFai Lau 365d52d3997SMartin KaFai Lau return rt; 366d52d3997SMartin KaFai Lau } 3679ab179d8SDavid Ahern EXPORT_SYMBOL(ip6_dst_alloc); 368d52d3997SMartin KaFai Lau 3691da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 3701da177e4SLinus Torvalds { 3711da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 372ecd98837SYOSHIFUJI Hideaki / 吉藤英明 struct dst_entry *from = dst->from; 3738d0b94afSMartin KaFai Lau struct inet6_dev *idev; 3741da177e4SLinus Torvalds 3758e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 376d52d3997SMartin KaFai Lau free_percpu(rt->rt6i_pcpu); 3778d0b94afSMartin KaFai Lau rt6_uncached_list_del(rt); 3788d0b94afSMartin KaFai Lau 3798d0b94afSMartin KaFai Lau idev = rt->rt6i_idev; 38038308473SDavid S. Miller if (idev) { 3811da177e4SLinus Torvalds rt->rt6i_idev = NULL; 3821da177e4SLinus Torvalds in6_dev_put(idev); 3831da177e4SLinus Torvalds } 3841716a961SGao feng 385ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst->from = NULL; 386ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst_release(from); 387b3419363SDavid S. Miller } 388b3419363SDavid S. Miller 3891da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 3901da177e4SLinus Torvalds int how) 3911da177e4SLinus Torvalds { 3921da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3931da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3945a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 395c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 3961da177e4SLinus Torvalds 39797cac082SDavid S. Miller if (dev != loopback_dev) { 39897cac082SDavid S. Miller if (idev && idev->dev == dev) { 3995a3e55d6SDenis V. Lunev struct inet6_dev *loopback_idev = 4005a3e55d6SDenis V. Lunev in6_dev_get(loopback_dev); 40138308473SDavid S. Miller if (loopback_idev) { 4021da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 4031da177e4SLinus Torvalds in6_dev_put(idev); 4041da177e4SLinus Torvalds } 4051da177e4SLinus Torvalds } 40697cac082SDavid S. Miller } 4071da177e4SLinus Torvalds } 4081da177e4SLinus Torvalds 4095973fb1eSMartin KaFai Lau static bool __rt6_check_expired(const struct rt6_info *rt) 4105973fb1eSMartin KaFai Lau { 4115973fb1eSMartin KaFai Lau if (rt->rt6i_flags & RTF_EXPIRES) 4125973fb1eSMartin KaFai Lau return time_after(jiffies, rt->dst.expires); 4135973fb1eSMartin KaFai Lau else 4145973fb1eSMartin KaFai Lau return false; 4155973fb1eSMartin KaFai Lau } 4165973fb1eSMartin KaFai Lau 417a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt) 4181da177e4SLinus Torvalds { 4191716a961SGao feng if (rt->rt6i_flags & RTF_EXPIRES) { 4201716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 421a50feda5SEric Dumazet return true; 4221716a961SGao feng } else if (rt->dst.from) { 4233fd91fb3SLi RongQing return rt6_check_expired((struct rt6_info *) rt->dst.from); 4241716a961SGao feng } 425a50feda5SEric Dumazet return false; 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds 42851ebd318SNicolas Dichtel /* Multipath route selection: 42951ebd318SNicolas Dichtel * Hash based function using packet header and flowlabel. 43051ebd318SNicolas Dichtel * Adapted from fib_info_hashfn() 43151ebd318SNicolas Dichtel */ 43251ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count, 43351ebd318SNicolas Dichtel const struct flowi6 *fl6) 43451ebd318SNicolas Dichtel { 435644d0e65STom Herbert return get_hash_from_flowi6(fl6) % candidate_count; 43651ebd318SNicolas Dichtel } 43751ebd318SNicolas Dichtel 43851ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match, 43952bd4c0cSNicolas Dichtel struct flowi6 *fl6, int oif, 44052bd4c0cSNicolas Dichtel int strict) 44151ebd318SNicolas Dichtel { 44251ebd318SNicolas Dichtel struct rt6_info *sibling, *next_sibling; 44351ebd318SNicolas Dichtel int route_choosen; 44451ebd318SNicolas Dichtel 44551ebd318SNicolas Dichtel route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6); 44651ebd318SNicolas Dichtel /* Don't change the route, if route_choosen == 0 44751ebd318SNicolas Dichtel * (siblings does not include ourself) 44851ebd318SNicolas Dichtel */ 44951ebd318SNicolas Dichtel if (route_choosen) 45051ebd318SNicolas Dichtel list_for_each_entry_safe(sibling, next_sibling, 45151ebd318SNicolas Dichtel &match->rt6i_siblings, rt6i_siblings) { 45251ebd318SNicolas Dichtel route_choosen--; 45351ebd318SNicolas Dichtel if (route_choosen == 0) { 45452bd4c0cSNicolas Dichtel if (rt6_score_route(sibling, oif, strict) < 0) 45552bd4c0cSNicolas Dichtel break; 45651ebd318SNicolas Dichtel match = sibling; 45751ebd318SNicolas Dichtel break; 45851ebd318SNicolas Dichtel } 45951ebd318SNicolas Dichtel } 46051ebd318SNicolas Dichtel return match; 46151ebd318SNicolas Dichtel } 46251ebd318SNicolas Dichtel 4631da177e4SLinus Torvalds /* 464c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 4651da177e4SLinus Torvalds */ 4661da177e4SLinus Torvalds 4678ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 4688ed67789SDaniel Lezcano struct rt6_info *rt, 469b71d1d42SEric Dumazet const struct in6_addr *saddr, 4701da177e4SLinus Torvalds int oif, 471d420895eSYOSHIFUJI Hideaki int flags) 4721da177e4SLinus Torvalds { 4731da177e4SLinus Torvalds struct rt6_info *local = NULL; 4741da177e4SLinus Torvalds struct rt6_info *sprt; 4751da177e4SLinus Torvalds 476dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 477dd3abc4eSYOSHIFUJI Hideaki goto out; 478dd3abc4eSYOSHIFUJI Hideaki 479d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 480d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 481dd3abc4eSYOSHIFUJI Hideaki 482dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4831da177e4SLinus Torvalds if (dev->ifindex == oif) 4841da177e4SLinus Torvalds return sprt; 4851da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 48638308473SDavid S. Miller if (!sprt->rt6i_idev || 4871da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 48817fb0b2bSDavid Ahern if (flags & RT6_LOOKUP_F_IFACE) 4891da177e4SLinus Torvalds continue; 49017fb0b2bSDavid Ahern if (local && 49117fb0b2bSDavid Ahern local->rt6i_idev->dev->ifindex == oif) 4921da177e4SLinus Torvalds continue; 4931da177e4SLinus Torvalds } 4941da177e4SLinus Torvalds local = sprt; 4951da177e4SLinus Torvalds } 496dd3abc4eSYOSHIFUJI Hideaki } else { 497dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 498dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 499dd3abc4eSYOSHIFUJI Hideaki return sprt; 500dd3abc4eSYOSHIFUJI Hideaki } 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds 503dd3abc4eSYOSHIFUJI Hideaki if (oif) { 5041da177e4SLinus Torvalds if (local) 5051da177e4SLinus Torvalds return local; 5061da177e4SLinus Torvalds 507d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 5088ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 5091da177e4SLinus Torvalds } 510dd3abc4eSYOSHIFUJI Hideaki out: 5111da177e4SLinus Torvalds return rt; 5121da177e4SLinus Torvalds } 5131da177e4SLinus Torvalds 51427097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 515c2f17e82SHannes Frederic Sowa struct __rt6_probe_work { 516c2f17e82SHannes Frederic Sowa struct work_struct work; 517c2f17e82SHannes Frederic Sowa struct in6_addr target; 518c2f17e82SHannes Frederic Sowa struct net_device *dev; 519c2f17e82SHannes Frederic Sowa }; 520c2f17e82SHannes Frederic Sowa 521c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w) 522c2f17e82SHannes Frederic Sowa { 523c2f17e82SHannes Frederic Sowa struct in6_addr mcaddr; 524c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work = 525c2f17e82SHannes Frederic Sowa container_of(w, struct __rt6_probe_work, work); 526c2f17e82SHannes Frederic Sowa 527c2f17e82SHannes Frederic Sowa addrconf_addr_solict_mult(&work->target, &mcaddr); 528304d888bSNicolas Dichtel ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL); 529c2f17e82SHannes Frederic Sowa dev_put(work->dev); 530662f5533SMichael Büsch kfree(work); 531c2f17e82SHannes Frederic Sowa } 532c2f17e82SHannes Frederic Sowa 53327097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 53427097255SYOSHIFUJI Hideaki { 535990edb42SMartin KaFai Lau struct __rt6_probe_work *work; 536f2c31e32SEric Dumazet struct neighbour *neigh; 53727097255SYOSHIFUJI Hideaki /* 53827097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 53927097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 54027097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 54127097255SYOSHIFUJI Hideaki * 54227097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 54327097255SYOSHIFUJI Hideaki * to no more than one per minute. 54427097255SYOSHIFUJI Hideaki */ 5452152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!rt || !(rt->rt6i_flags & RTF_GATEWAY)) 546fdd6681dSAmerigo Wang return; 5472152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 5482152caeaSYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 5492152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 5508d6c31bfSMartin KaFai Lau if (neigh->nud_state & NUD_VALID) 5518d6c31bfSMartin KaFai Lau goto out; 5528d6c31bfSMartin KaFai Lau 553990edb42SMartin KaFai Lau work = NULL; 5542152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_lock(&neigh->lock); 555990edb42SMartin KaFai Lau if (!(neigh->nud_state & NUD_VALID) && 556990edb42SMartin KaFai Lau time_after(jiffies, 557990edb42SMartin KaFai Lau neigh->updated + 558990edb42SMartin KaFai Lau rt->rt6i_idev->cnf.rtr_probe_interval)) { 559c2f17e82SHannes Frederic Sowa work = kmalloc(sizeof(*work), GFP_ATOMIC); 560990edb42SMartin KaFai Lau if (work) 5617e980569SJiri Benc __neigh_set_probe_once(neigh); 562990edb42SMartin KaFai Lau } 563c2f17e82SHannes Frederic Sowa write_unlock(&neigh->lock); 564990edb42SMartin KaFai Lau } else { 565990edb42SMartin KaFai Lau work = kmalloc(sizeof(*work), GFP_ATOMIC); 566990edb42SMartin KaFai Lau } 567c2f17e82SHannes Frederic Sowa 568c2f17e82SHannes Frederic Sowa if (work) { 569c2f17e82SHannes Frederic Sowa INIT_WORK(&work->work, rt6_probe_deferred); 570c2f17e82SHannes Frederic Sowa work->target = rt->rt6i_gateway; 571c2f17e82SHannes Frederic Sowa dev_hold(rt->dst.dev); 572c2f17e82SHannes Frederic Sowa work->dev = rt->dst.dev; 573c2f17e82SHannes Frederic Sowa schedule_work(&work->work); 574c2f17e82SHannes Frederic Sowa } 575990edb42SMartin KaFai Lau 5768d6c31bfSMartin KaFai Lau out: 5772152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 578f2c31e32SEric Dumazet } 57927097255SYOSHIFUJI Hideaki #else 58027097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 58127097255SYOSHIFUJI Hideaki { 58227097255SYOSHIFUJI Hideaki } 58327097255SYOSHIFUJI Hideaki #endif 58427097255SYOSHIFUJI Hideaki 5851da177e4SLinus Torvalds /* 586554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 5871da177e4SLinus Torvalds */ 588b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 5891da177e4SLinus Torvalds { 590d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 591161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 592554cfb7eSYOSHIFUJI Hideaki return 2; 593161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 594161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 595161980f4SDavid S. Miller return 1; 596554cfb7eSYOSHIFUJI Hideaki return 0; 5971da177e4SLinus Torvalds } 5981da177e4SLinus Torvalds 599afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt) 6001da177e4SLinus Torvalds { 601f2c31e32SEric Dumazet struct neighbour *neigh; 602afc154e9SHannes Frederic Sowa enum rt6_nud_state ret = RT6_NUD_FAIL_HARD; 603f2c31e32SEric Dumazet 6044d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 6054d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 606afc154e9SHannes Frederic Sowa return RT6_NUD_SUCCEED; 607145a3621SYOSHIFUJI Hideaki / 吉藤英明 608145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 609145a3621SYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 610145a3621SYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 611145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_lock(&neigh->lock); 612554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 613afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 614398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 615a5a81f0bSPaul Marks else if (!(neigh->nud_state & NUD_FAILED)) 616afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 6177e980569SJiri Benc else 6187e980569SJiri Benc ret = RT6_NUD_FAIL_PROBE; 619398bcbebSYOSHIFUJI Hideaki #endif 620145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_unlock(&neigh->lock); 621afc154e9SHannes Frederic Sowa } else { 622afc154e9SHannes Frederic Sowa ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 6237e980569SJiri Benc RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; 624a5a81f0bSPaul Marks } 625145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 626145a3621SYOSHIFUJI Hideaki / 吉藤英明 627a5a81f0bSPaul Marks return ret; 6281da177e4SLinus Torvalds } 6291da177e4SLinus Torvalds 630554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 631554cfb7eSYOSHIFUJI Hideaki int strict) 632554cfb7eSYOSHIFUJI Hideaki { 633a5a81f0bSPaul Marks int m; 6344d0c5911SYOSHIFUJI Hideaki 6354d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 63677d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 637afc154e9SHannes Frederic Sowa return RT6_NUD_FAIL_HARD; 638ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 639ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 640ebacaaa0SYOSHIFUJI Hideaki #endif 641afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) { 642afc154e9SHannes Frederic Sowa int n = rt6_check_neigh(rt); 643afc154e9SHannes Frederic Sowa if (n < 0) 644afc154e9SHannes Frederic Sowa return n; 645afc154e9SHannes Frederic Sowa } 646554cfb7eSYOSHIFUJI Hideaki return m; 647554cfb7eSYOSHIFUJI Hideaki } 648554cfb7eSYOSHIFUJI Hideaki 649f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 650afc154e9SHannes Frederic Sowa int *mpri, struct rt6_info *match, 651afc154e9SHannes Frederic Sowa bool *do_rr) 652554cfb7eSYOSHIFUJI Hideaki { 653554cfb7eSYOSHIFUJI Hideaki int m; 654afc154e9SHannes Frederic Sowa bool match_do_rr = false; 65535103d11SAndy Gospodarek struct inet6_dev *idev = rt->rt6i_idev; 65635103d11SAndy Gospodarek struct net_device *dev = rt->dst.dev; 65735103d11SAndy Gospodarek 65835103d11SAndy Gospodarek if (dev && !netif_carrier_ok(dev) && 65935103d11SAndy Gospodarek idev->cnf.ignore_routes_with_linkdown) 66035103d11SAndy Gospodarek goto out; 661554cfb7eSYOSHIFUJI Hideaki 662554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 663f11e6659SDavid S. Miller goto out; 664554cfb7eSYOSHIFUJI Hideaki 665554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 6667e980569SJiri Benc if (m == RT6_NUD_FAIL_DO_RR) { 667afc154e9SHannes Frederic Sowa match_do_rr = true; 668afc154e9SHannes Frederic Sowa m = 0; /* lowest valid score */ 6697e980569SJiri Benc } else if (m == RT6_NUD_FAIL_HARD) { 670f11e6659SDavid S. Miller goto out; 6711da177e4SLinus Torvalds } 672f11e6659SDavid S. Miller 673afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) 674afc154e9SHannes Frederic Sowa rt6_probe(rt); 675afc154e9SHannes Frederic Sowa 6767e980569SJiri Benc /* note that m can be RT6_NUD_FAIL_PROBE at this point */ 677afc154e9SHannes Frederic Sowa if (m > *mpri) { 678afc154e9SHannes Frederic Sowa *do_rr = match_do_rr; 679afc154e9SHannes Frederic Sowa *mpri = m; 680afc154e9SHannes Frederic Sowa match = rt; 681afc154e9SHannes Frederic Sowa } 682f11e6659SDavid S. Miller out: 683f11e6659SDavid S. Miller return match; 6841da177e4SLinus Torvalds } 6851da177e4SLinus Torvalds 686f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 687f11e6659SDavid S. Miller struct rt6_info *rr_head, 688afc154e9SHannes Frederic Sowa u32 metric, int oif, int strict, 689afc154e9SHannes Frederic Sowa bool *do_rr) 690f11e6659SDavid S. Miller { 6919fbdcfafSSteffen Klassert struct rt6_info *rt, *match, *cont; 692f11e6659SDavid S. Miller int mpri = -1; 693f11e6659SDavid S. Miller 694f11e6659SDavid S. Miller match = NULL; 6959fbdcfafSSteffen Klassert cont = NULL; 6969fbdcfafSSteffen Klassert for (rt = rr_head; rt; rt = rt->dst.rt6_next) { 6979fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 6989fbdcfafSSteffen Klassert cont = rt; 6999fbdcfafSSteffen Klassert break; 7009fbdcfafSSteffen Klassert } 7019fbdcfafSSteffen Klassert 702afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 7039fbdcfafSSteffen Klassert } 7049fbdcfafSSteffen Klassert 7059fbdcfafSSteffen Klassert for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) { 7069fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 7079fbdcfafSSteffen Klassert cont = rt; 7089fbdcfafSSteffen Klassert break; 7099fbdcfafSSteffen Klassert } 7109fbdcfafSSteffen Klassert 7119fbdcfafSSteffen Klassert match = find_match(rt, oif, strict, &mpri, match, do_rr); 7129fbdcfafSSteffen Klassert } 7139fbdcfafSSteffen Klassert 7149fbdcfafSSteffen Klassert if (match || !cont) 7159fbdcfafSSteffen Klassert return match; 7169fbdcfafSSteffen Klassert 7179fbdcfafSSteffen Klassert for (rt = cont; rt; rt = rt->dst.rt6_next) 718afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 719f11e6659SDavid S. Miller 720f11e6659SDavid S. Miller return match; 721f11e6659SDavid S. Miller } 722f11e6659SDavid S. Miller 723f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 724f11e6659SDavid S. Miller { 725f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 7268ed67789SDaniel Lezcano struct net *net; 727afc154e9SHannes Frederic Sowa bool do_rr = false; 728f11e6659SDavid S. Miller 729f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 730f11e6659SDavid S. Miller if (!rt0) 731f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 732f11e6659SDavid S. Miller 733afc154e9SHannes Frederic Sowa match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict, 734afc154e9SHannes Frederic Sowa &do_rr); 735f11e6659SDavid S. Miller 736afc154e9SHannes Frederic Sowa if (do_rr) { 737d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 738f11e6659SDavid S. Miller 739554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 740f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 741f11e6659SDavid S. Miller next = fn->leaf; 742f11e6659SDavid S. Miller 743f11e6659SDavid S. Miller if (next != rt0) 744f11e6659SDavid S. Miller fn->rr_ptr = next; 745554cfb7eSYOSHIFUJI Hideaki } 746554cfb7eSYOSHIFUJI Hideaki 747d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 748a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 7491da177e4SLinus Torvalds } 7501da177e4SLinus Torvalds 7518b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt) 7528b9df265SMartin KaFai Lau { 7538b9df265SMartin KaFai Lau return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY)); 7548b9df265SMartin KaFai Lau } 7558b9df265SMartin KaFai Lau 75670ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 75770ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 758b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 75970ceb4f5SYOSHIFUJI Hideaki { 760c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 76170ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 76270ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 76370ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 7644bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 76570ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 76670ceb4f5SYOSHIFUJI Hideaki 76770ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 76870ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 76970ceb4f5SYOSHIFUJI Hideaki } 77070ceb4f5SYOSHIFUJI Hideaki 77170ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 77270ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 77370ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 77470ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 77570ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 77670ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 77770ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 77870ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 77970ceb4f5SYOSHIFUJI Hideaki } 78070ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 78170ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 78270ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 78370ceb4f5SYOSHIFUJI Hideaki } 78470ceb4f5SYOSHIFUJI Hideaki } 78570ceb4f5SYOSHIFUJI Hideaki 78670ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 78770ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 7883933fc95SJens Rosenboom return -EINVAL; 78970ceb4f5SYOSHIFUJI Hideaki 7904bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 79170ceb4f5SYOSHIFUJI Hideaki 79270ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 79370ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 79470ceb4f5SYOSHIFUJI Hideaki else { 79570ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 79670ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 79770ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 79870ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 79970ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 80070ceb4f5SYOSHIFUJI Hideaki } 80170ceb4f5SYOSHIFUJI Hideaki 802f104a567SDuan Jiong if (rinfo->prefix_len == 0) 803f104a567SDuan Jiong rt = rt6_get_dflt_router(gwaddr, dev); 804f104a567SDuan Jiong else 805f104a567SDuan Jiong rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, 806f104a567SDuan Jiong gwaddr, dev->ifindex); 80770ceb4f5SYOSHIFUJI Hideaki 80870ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 809e0a1ad73SThomas Graf ip6_del_rt(rt); 81070ceb4f5SYOSHIFUJI Hideaki rt = NULL; 81170ceb4f5SYOSHIFUJI Hideaki } 81270ceb4f5SYOSHIFUJI Hideaki 81370ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 814efa2cea0SDaniel Lezcano rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 81570ceb4f5SYOSHIFUJI Hideaki pref); 81670ceb4f5SYOSHIFUJI Hideaki else if (rt) 81770ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 81870ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 81970ceb4f5SYOSHIFUJI Hideaki 82070ceb4f5SYOSHIFUJI Hideaki if (rt) { 8211716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 8221716a961SGao feng rt6_clean_expires(rt); 8231716a961SGao feng else 8241716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 8251716a961SGao feng 82694e187c0SAmerigo Wang ip6_rt_put(rt); 82770ceb4f5SYOSHIFUJI Hideaki } 82870ceb4f5SYOSHIFUJI Hideaki return 0; 82970ceb4f5SYOSHIFUJI Hideaki } 83070ceb4f5SYOSHIFUJI Hideaki #endif 83170ceb4f5SYOSHIFUJI Hideaki 832a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn, 833a3c00e46SMartin KaFai Lau struct in6_addr *saddr) 834a3c00e46SMartin KaFai Lau { 835a3c00e46SMartin KaFai Lau struct fib6_node *pn; 836a3c00e46SMartin KaFai Lau while (1) { 837a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_TL_ROOT) 838a3c00e46SMartin KaFai Lau return NULL; 839a3c00e46SMartin KaFai Lau pn = fn->parent; 840a3c00e46SMartin KaFai Lau if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) 841a3c00e46SMartin KaFai Lau fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); 842a3c00e46SMartin KaFai Lau else 843a3c00e46SMartin KaFai Lau fn = pn; 844a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_RTINFO) 845a3c00e46SMartin KaFai Lau return fn; 846a3c00e46SMartin KaFai Lau } 847a3c00e46SMartin KaFai Lau } 848c71099acSThomas Graf 8498ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 8508ed67789SDaniel Lezcano struct fib6_table *table, 8514c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 8521da177e4SLinus Torvalds { 8531da177e4SLinus Torvalds struct fib6_node *fn; 8541da177e4SLinus Torvalds struct rt6_info *rt; 8551da177e4SLinus Torvalds 856c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 8574c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 858c71099acSThomas Graf restart: 859c71099acSThomas Graf rt = fn->leaf; 8604c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 86151ebd318SNicolas Dichtel if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) 86252bd4c0cSNicolas Dichtel rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags); 863a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 864a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 865a3c00e46SMartin KaFai Lau if (fn) 866a3c00e46SMartin KaFai Lau goto restart; 867a3c00e46SMartin KaFai Lau } 868d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 869c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 870b811580dSDavid Ahern 871b811580dSDavid Ahern trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); 872b811580dSDavid Ahern 8731da177e4SLinus Torvalds return rt; 874c71099acSThomas Graf 875c71099acSThomas Graf } 876c71099acSThomas Graf 877ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, 878ea6e574eSFlorian Westphal int flags) 879ea6e574eSFlorian Westphal { 880ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 881ea6e574eSFlorian Westphal } 882ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 883ea6e574eSFlorian Westphal 8849acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 8859acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 886c71099acSThomas Graf { 8874c9483b2SDavid S. Miller struct flowi6 fl6 = { 8884c9483b2SDavid S. Miller .flowi6_oif = oif, 8894c9483b2SDavid S. Miller .daddr = *daddr, 890c71099acSThomas Graf }; 891c71099acSThomas Graf struct dst_entry *dst; 89277d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 893c71099acSThomas Graf 894adaa70bbSThomas Graf if (saddr) { 8954c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 896adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 897adaa70bbSThomas Graf } 898adaa70bbSThomas Graf 8994c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 900c71099acSThomas Graf if (dst->error == 0) 901c71099acSThomas Graf return (struct rt6_info *) dst; 902c71099acSThomas Graf 903c71099acSThomas Graf dst_release(dst); 904c71099acSThomas Graf 9051da177e4SLinus Torvalds return NULL; 9061da177e4SLinus Torvalds } 9077159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 9087159039aSYOSHIFUJI Hideaki 909c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 9101da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 9111da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 9121da177e4SLinus Torvalds be destroyed. 9131da177e4SLinus Torvalds */ 9141da177e4SLinus Torvalds 915e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, 916e715b6d3SFlorian Westphal struct mx6_config *mxc) 9171da177e4SLinus Torvalds { 9181da177e4SLinus Torvalds int err; 919c71099acSThomas Graf struct fib6_table *table; 9201da177e4SLinus Torvalds 921c71099acSThomas Graf table = rt->rt6i_table; 922c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 923e715b6d3SFlorian Westphal err = fib6_add(&table->tb6_root, rt, info, mxc); 924c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 9251da177e4SLinus Torvalds 9261da177e4SLinus Torvalds return err; 9271da177e4SLinus Torvalds } 9281da177e4SLinus Torvalds 92940e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 93040e22e8fSThomas Graf { 931e715b6d3SFlorian Westphal struct nl_info info = { .nl_net = dev_net(rt->dst.dev), }; 932e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 933e715b6d3SFlorian Westphal 934e715b6d3SFlorian Westphal return __ip6_ins_rt(rt, &info, &mxc); 93540e22e8fSThomas Graf } 93640e22e8fSThomas Graf 9378b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, 93821efcfa0SEric Dumazet const struct in6_addr *daddr, 939b71d1d42SEric Dumazet const struct in6_addr *saddr) 9401da177e4SLinus Torvalds { 9411da177e4SLinus Torvalds struct rt6_info *rt; 9421da177e4SLinus Torvalds 9431da177e4SLinus Torvalds /* 9441da177e4SLinus Torvalds * Clone the route. 9451da177e4SLinus Torvalds */ 9461da177e4SLinus Torvalds 947d52d3997SMartin KaFai Lau if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)) 94883a09abdSMartin KaFai Lau ort = (struct rt6_info *)ort->dst.from; 9491da177e4SLinus Torvalds 950ad706862SMartin KaFai Lau rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0); 95183a09abdSMartin KaFai Lau 95283a09abdSMartin KaFai Lau if (!rt) 95383a09abdSMartin KaFai Lau return NULL; 95483a09abdSMartin KaFai Lau 95583a09abdSMartin KaFai Lau ip6_rt_copy_init(rt, ort); 9568b9df265SMartin KaFai Lau rt->rt6i_flags |= RTF_CACHE; 95783a09abdSMartin KaFai Lau rt->rt6i_metric = 0; 95883a09abdSMartin KaFai Lau rt->dst.flags |= DST_HOST; 95983a09abdSMartin KaFai Lau rt->rt6i_dst.addr = *daddr; 96083a09abdSMartin KaFai Lau rt->rt6i_dst.plen = 128; 9618b9df265SMartin KaFai Lau 9628b9df265SMartin KaFai Lau if (!rt6_is_gw_or_nonexthop(ort)) { 963bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 96421efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 96558c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 9661da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 9671da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 9684e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 9691da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 9701da177e4SLinus Torvalds } 9711da177e4SLinus Torvalds #endif 97295a9a5baSYOSHIFUJI Hideaki } 97395a9a5baSYOSHIFUJI Hideaki 974299d9939SYOSHIFUJI Hideaki return rt; 975299d9939SYOSHIFUJI Hideaki } 976299d9939SYOSHIFUJI Hideaki 977d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt) 978d52d3997SMartin KaFai Lau { 979d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 980d52d3997SMartin KaFai Lau 981d52d3997SMartin KaFai Lau pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev), 982ad706862SMartin KaFai Lau rt->dst.dev, rt->dst.flags); 983d52d3997SMartin KaFai Lau 984d52d3997SMartin KaFai Lau if (!pcpu_rt) 985d52d3997SMartin KaFai Lau return NULL; 986d52d3997SMartin KaFai Lau ip6_rt_copy_init(pcpu_rt, rt); 987d52d3997SMartin KaFai Lau pcpu_rt->rt6i_protocol = rt->rt6i_protocol; 988d52d3997SMartin KaFai Lau pcpu_rt->rt6i_flags |= RTF_PCPU; 989d52d3997SMartin KaFai Lau return pcpu_rt; 990d52d3997SMartin KaFai Lau } 991d52d3997SMartin KaFai Lau 992d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */ 993d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt) 994d52d3997SMartin KaFai Lau { 995a73e4195SMartin KaFai Lau struct rt6_info *pcpu_rt, **p; 996d52d3997SMartin KaFai Lau 997d52d3997SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 998d52d3997SMartin KaFai Lau pcpu_rt = *p; 999d52d3997SMartin KaFai Lau 1000a73e4195SMartin KaFai Lau if (pcpu_rt) { 1001a73e4195SMartin KaFai Lau dst_hold(&pcpu_rt->dst); 1002a73e4195SMartin KaFai Lau rt6_dst_from_metrics_check(pcpu_rt); 1003a73e4195SMartin KaFai Lau } 1004a73e4195SMartin KaFai Lau return pcpu_rt; 1005a73e4195SMartin KaFai Lau } 1006a73e4195SMartin KaFai Lau 1007a73e4195SMartin KaFai Lau static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt) 1008a73e4195SMartin KaFai Lau { 10099c7370a1SMartin KaFai Lau struct fib6_table *table = rt->rt6i_table; 1010a73e4195SMartin KaFai Lau struct rt6_info *pcpu_rt, *prev, **p; 1011d52d3997SMartin KaFai Lau 1012d52d3997SMartin KaFai Lau pcpu_rt = ip6_rt_pcpu_alloc(rt); 1013d52d3997SMartin KaFai Lau if (!pcpu_rt) { 1014d52d3997SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 1015d52d3997SMartin KaFai Lau 10169c7370a1SMartin KaFai Lau dst_hold(&net->ipv6.ip6_null_entry->dst); 10179c7370a1SMartin KaFai Lau return net->ipv6.ip6_null_entry; 1018d52d3997SMartin KaFai Lau } 1019d52d3997SMartin KaFai Lau 10209c7370a1SMartin KaFai Lau read_lock_bh(&table->tb6_lock); 10219c7370a1SMartin KaFai Lau if (rt->rt6i_pcpu) { 1022a73e4195SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 1023d52d3997SMartin KaFai Lau prev = cmpxchg(p, NULL, pcpu_rt); 1024d52d3997SMartin KaFai Lau if (prev) { 1025d52d3997SMartin KaFai Lau /* If someone did it before us, return prev instead */ 1026d52d3997SMartin KaFai Lau dst_destroy(&pcpu_rt->dst); 1027d52d3997SMartin KaFai Lau pcpu_rt = prev; 1028d52d3997SMartin KaFai Lau } 10299c7370a1SMartin KaFai Lau } else { 10309c7370a1SMartin KaFai Lau /* rt has been removed from the fib6 tree 10319c7370a1SMartin KaFai Lau * before we have a chance to acquire the read_lock. 10329c7370a1SMartin KaFai Lau * In this case, don't brother to create a pcpu rt 10339c7370a1SMartin KaFai Lau * since rt is going away anyway. The next 10349c7370a1SMartin KaFai Lau * dst_check() will trigger a re-lookup. 10359c7370a1SMartin KaFai Lau */ 10369c7370a1SMartin KaFai Lau dst_destroy(&pcpu_rt->dst); 10379c7370a1SMartin KaFai Lau pcpu_rt = rt; 10389c7370a1SMartin KaFai Lau } 1039d52d3997SMartin KaFai Lau dst_hold(&pcpu_rt->dst); 1040d52d3997SMartin KaFai Lau rt6_dst_from_metrics_check(pcpu_rt); 10419c7370a1SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 1042d52d3997SMartin KaFai Lau return pcpu_rt; 1043d52d3997SMartin KaFai Lau } 1044d52d3997SMartin KaFai Lau 1045*9ff74384SDavid Ahern struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, 1046*9ff74384SDavid Ahern int oif, struct flowi6 *fl6, int flags) 10471da177e4SLinus Torvalds { 1048367efcb9SMartin KaFai Lau struct fib6_node *fn, *saved_fn; 104945e4fd26SMartin KaFai Lau struct rt6_info *rt; 1050c71099acSThomas Graf int strict = 0; 10511da177e4SLinus Torvalds 105277d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 1053367efcb9SMartin KaFai Lau if (net->ipv6.devconf_all->forwarding == 0) 1054367efcb9SMartin KaFai Lau strict |= RT6_LOOKUP_F_REACHABLE; 10551da177e4SLinus Torvalds 1056c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 10571da177e4SLinus Torvalds 10584c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1059367efcb9SMartin KaFai Lau saved_fn = fn; 10601da177e4SLinus Torvalds 1061ca254490SDavid Ahern if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) 1062ca254490SDavid Ahern oif = 0; 1063ca254490SDavid Ahern 1064a3c00e46SMartin KaFai Lau redo_rt6_select: 1065367efcb9SMartin KaFai Lau rt = rt6_select(fn, oif, strict); 106652bd4c0cSNicolas Dichtel if (rt->rt6i_nsiblings) 1067367efcb9SMartin KaFai Lau rt = rt6_multipath_select(rt, fl6, oif, strict); 1068a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1069a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1070a3c00e46SMartin KaFai Lau if (fn) 1071a3c00e46SMartin KaFai Lau goto redo_rt6_select; 1072367efcb9SMartin KaFai Lau else if (strict & RT6_LOOKUP_F_REACHABLE) { 1073367efcb9SMartin KaFai Lau /* also consider unreachable route */ 1074367efcb9SMartin KaFai Lau strict &= ~RT6_LOOKUP_F_REACHABLE; 1075367efcb9SMartin KaFai Lau fn = saved_fn; 1076367efcb9SMartin KaFai Lau goto redo_rt6_select; 1077367efcb9SMartin KaFai Lau } 1078a3c00e46SMartin KaFai Lau } 1079a3c00e46SMartin KaFai Lau 1080d52d3997SMartin KaFai Lau 1081d52d3997SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) { 10823da59bd9SMartin KaFai Lau dst_use(&rt->dst, jiffies); 1083c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 10841da177e4SLinus Torvalds 1085d52d3997SMartin KaFai Lau rt6_dst_from_metrics_check(rt); 1086b811580dSDavid Ahern 1087b811580dSDavid Ahern trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); 1088d52d3997SMartin KaFai Lau return rt; 10893da59bd9SMartin KaFai Lau } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && 10903da59bd9SMartin KaFai Lau !(rt->rt6i_flags & RTF_GATEWAY))) { 10913da59bd9SMartin KaFai Lau /* Create a RTF_CACHE clone which will not be 10923da59bd9SMartin KaFai Lau * owned by the fib6 tree. It is for the special case where 10933da59bd9SMartin KaFai Lau * the daddr in the skb during the neighbor look-up is different 10943da59bd9SMartin KaFai Lau * from the fl6->daddr used to look-up route here. 10953da59bd9SMartin KaFai Lau */ 1096c71099acSThomas Graf 10973da59bd9SMartin KaFai Lau struct rt6_info *uncached_rt; 10983da59bd9SMartin KaFai Lau 1099d52d3997SMartin KaFai Lau dst_use(&rt->dst, jiffies); 1100d52d3997SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 1101d52d3997SMartin KaFai Lau 11023da59bd9SMartin KaFai Lau uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL); 11033da59bd9SMartin KaFai Lau dst_release(&rt->dst); 11043da59bd9SMartin KaFai Lau 11053da59bd9SMartin KaFai Lau if (uncached_rt) 11068d0b94afSMartin KaFai Lau rt6_uncached_list_add(uncached_rt); 11073da59bd9SMartin KaFai Lau else 11083da59bd9SMartin KaFai Lau uncached_rt = net->ipv6.ip6_null_entry; 1109d52d3997SMartin KaFai Lau 11103da59bd9SMartin KaFai Lau dst_hold(&uncached_rt->dst); 1111b811580dSDavid Ahern 1112b811580dSDavid Ahern trace_fib6_table_lookup(net, uncached_rt, table->tb6_id, fl6); 11133da59bd9SMartin KaFai Lau return uncached_rt; 11143da59bd9SMartin KaFai Lau 1115d52d3997SMartin KaFai Lau } else { 1116d52d3997SMartin KaFai Lau /* Get a percpu copy */ 1117d52d3997SMartin KaFai Lau 1118d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 1119d52d3997SMartin KaFai Lau 1120d52d3997SMartin KaFai Lau rt->dst.lastuse = jiffies; 1121d52d3997SMartin KaFai Lau rt->dst.__use++; 1122d52d3997SMartin KaFai Lau pcpu_rt = rt6_get_pcpu_route(rt); 1123d52d3997SMartin KaFai Lau 11249c7370a1SMartin KaFai Lau if (pcpu_rt) { 1125d52d3997SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 11269c7370a1SMartin KaFai Lau } else { 11279c7370a1SMartin KaFai Lau /* We have to do the read_unlock first 11289c7370a1SMartin KaFai Lau * because rt6_make_pcpu_route() may trigger 11299c7370a1SMartin KaFai Lau * ip6_dst_gc() which will take the write_lock. 11309c7370a1SMartin KaFai Lau */ 11319c7370a1SMartin KaFai Lau dst_hold(&rt->dst); 11329c7370a1SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 11339c7370a1SMartin KaFai Lau pcpu_rt = rt6_make_pcpu_route(rt); 11349c7370a1SMartin KaFai Lau dst_release(&rt->dst); 11359c7370a1SMartin KaFai Lau } 1136d52d3997SMartin KaFai Lau 1137b811580dSDavid Ahern trace_fib6_table_lookup(net, pcpu_rt, table->tb6_id, fl6); 1138d52d3997SMartin KaFai Lau return pcpu_rt; 11399c7370a1SMartin KaFai Lau 1140d52d3997SMartin KaFai Lau } 1141c71099acSThomas Graf } 1142*9ff74384SDavid Ahern EXPORT_SYMBOL_GPL(ip6_pol_route); 1143c71099acSThomas Graf 11448ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 11454c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 11464acad72dSPavel Emelyanov { 11474c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 11484acad72dSPavel Emelyanov } 11494acad72dSPavel Emelyanov 115072331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 115172331bc0SShmulik Ladkani struct net_device *dev, 115272331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 115372331bc0SShmulik Ladkani { 115472331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 115572331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 115672331bc0SShmulik Ladkani 115772331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 115872331bc0SShmulik Ladkani } 115972331bc0SShmulik Ladkani 1160c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 1161c71099acSThomas Graf { 1162b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 1163c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 1164adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 1165904af04dSJiri Benc struct ip_tunnel_info *tun_info; 11664c9483b2SDavid S. Miller struct flowi6 fl6 = { 1167ca254490SDavid Ahern .flowi6_iif = l3mdev_fib_oif(skb->dev), 11684c9483b2SDavid S. Miller .daddr = iph->daddr, 11694c9483b2SDavid S. Miller .saddr = iph->saddr, 11706502ca52SYOSHIFUJI Hideaki / 吉藤英明 .flowlabel = ip6_flowinfo(iph), 11714c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 11724c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 1173c71099acSThomas Graf }; 1174adaa70bbSThomas Graf 1175904af04dSJiri Benc tun_info = skb_tunnel_info(skb); 117646fa062aSJiri Benc if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX)) 1177904af04dSJiri Benc fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id; 117806e9d040SJiri Benc skb_dst_drop(skb); 117972331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 1180c71099acSThomas Graf } 1181c71099acSThomas Graf 11828ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 11834c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 1184c71099acSThomas Graf { 11854c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 1186c71099acSThomas Graf } 1187c71099acSThomas Graf 11886f21c96aSPaolo Abeni struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk, 11896f21c96aSPaolo Abeni struct flowi6 *fl6, int flags) 1190c71099acSThomas Graf { 1191ca254490SDavid Ahern struct dst_entry *dst; 1192d46a9d67SDavid Ahern bool any_src; 1193c71099acSThomas Graf 11944a65896fSDavid Ahern dst = l3mdev_get_rt6_dst(net, fl6); 1195ca254490SDavid Ahern if (dst) 1196ca254490SDavid Ahern return dst; 1197ca254490SDavid Ahern 11981fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 11994dc27d1cSDavid McCullough 1200d46a9d67SDavid Ahern any_src = ipv6_addr_any(&fl6->saddr); 1201741a11d9SDavid Ahern if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) || 1202d46a9d67SDavid Ahern (fl6->flowi6_oif && any_src)) 120377d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 1204c71099acSThomas Graf 1205d46a9d67SDavid Ahern if (!any_src) 1206adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 12070c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 12080c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 1209adaa70bbSThomas Graf 12104c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 12111da177e4SLinus Torvalds } 12126f21c96aSPaolo Abeni EXPORT_SYMBOL_GPL(ip6_route_output_flags); 12131da177e4SLinus Torvalds 12142774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 121514e50e57SDavid S. Miller { 12165c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 121714e50e57SDavid S. Miller struct dst_entry *new = NULL; 121814e50e57SDavid S. Miller 1219f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 122014e50e57SDavid S. Miller if (rt) { 12210a1f5962SMartin KaFai Lau rt6_info_init(rt); 12220a1f5962SMartin KaFai Lau 1223d8d1f30bSChangli Gao new = &rt->dst; 122414e50e57SDavid S. Miller new->__use = 1; 1225352e512cSHerbert Xu new->input = dst_discard; 1226ede2059dSEric W. Biederman new->output = dst_discard_out; 122714e50e57SDavid S. Miller 1228defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 122914e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 123014e50e57SDavid S. Miller if (rt->rt6i_idev) 123114e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 123214e50e57SDavid S. Miller 12334e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 12340a1f5962SMartin KaFai Lau rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU; 123514e50e57SDavid S. Miller rt->rt6i_metric = 0; 123614e50e57SDavid S. Miller 123714e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 123814e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 123914e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 124014e50e57SDavid S. Miller #endif 124114e50e57SDavid S. Miller 124214e50e57SDavid S. Miller dst_free(new); 124314e50e57SDavid S. Miller } 124414e50e57SDavid S. Miller 124569ead7afSDavid S. Miller dst_release(dst_orig); 124669ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 124714e50e57SDavid S. Miller } 124814e50e57SDavid S. Miller 12491da177e4SLinus Torvalds /* 12501da177e4SLinus Torvalds * Destination cache support functions 12511da177e4SLinus Torvalds */ 12521da177e4SLinus Torvalds 12534b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt) 12544b32b5adSMartin KaFai Lau { 12554b32b5adSMartin KaFai Lau if (rt->dst.from && 12564b32b5adSMartin KaFai Lau dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from)) 12574b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true); 12584b32b5adSMartin KaFai Lau } 12594b32b5adSMartin KaFai Lau 12603da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie) 12613da59bd9SMartin KaFai Lau { 12623da59bd9SMartin KaFai Lau if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) 12633da59bd9SMartin KaFai Lau return NULL; 12643da59bd9SMartin KaFai Lau 12653da59bd9SMartin KaFai Lau if (rt6_check_expired(rt)) 12663da59bd9SMartin KaFai Lau return NULL; 12673da59bd9SMartin KaFai Lau 12683da59bd9SMartin KaFai Lau return &rt->dst; 12693da59bd9SMartin KaFai Lau } 12703da59bd9SMartin KaFai Lau 12713da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie) 12723da59bd9SMartin KaFai Lau { 12735973fb1eSMartin KaFai Lau if (!__rt6_check_expired(rt) && 12745973fb1eSMartin KaFai Lau rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && 12753da59bd9SMartin KaFai Lau rt6_check((struct rt6_info *)(rt->dst.from), cookie)) 12763da59bd9SMartin KaFai Lau return &rt->dst; 12773da59bd9SMartin KaFai Lau else 12783da59bd9SMartin KaFai Lau return NULL; 12793da59bd9SMartin KaFai Lau } 12803da59bd9SMartin KaFai Lau 12811da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 12821da177e4SLinus Torvalds { 12831da177e4SLinus Torvalds struct rt6_info *rt; 12841da177e4SLinus Torvalds 12851da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 12861da177e4SLinus Torvalds 12876f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 12886f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 12896f3118b5SNicolas Dichtel * into this function always. 12906f3118b5SNicolas Dichtel */ 1291e3bc10bdSHannes Frederic Sowa 12924b32b5adSMartin KaFai Lau rt6_dst_from_metrics_check(rt); 12934b32b5adSMartin KaFai Lau 129402bcf4e0SMartin KaFai Lau if (rt->rt6i_flags & RTF_PCPU || 129502bcf4e0SMartin KaFai Lau (unlikely(dst->flags & DST_NOCACHE) && rt->dst.from)) 12963da59bd9SMartin KaFai Lau return rt6_dst_from_check(rt, cookie); 12973da59bd9SMartin KaFai Lau else 12983da59bd9SMartin KaFai Lau return rt6_check(rt, cookie); 12991da177e4SLinus Torvalds } 13001da177e4SLinus Torvalds 13011da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 13021da177e4SLinus Torvalds { 13031da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 13041da177e4SLinus Torvalds 13051da177e4SLinus Torvalds if (rt) { 130654c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 130754c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1308e0a1ad73SThomas Graf ip6_del_rt(rt); 130954c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 13101da177e4SLinus Torvalds } 131154c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 131254c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 131354c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 131454c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 131554c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 131654c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 13171da177e4SLinus Torvalds } 13181da177e4SLinus Torvalds 13191da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 13201da177e4SLinus Torvalds { 13211da177e4SLinus Torvalds struct rt6_info *rt; 13221da177e4SLinus Torvalds 13233ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 13241da177e4SLinus Torvalds 1325adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 13261da177e4SLinus Torvalds if (rt) { 13271eb4f758SHannes Frederic Sowa if (rt->rt6i_flags & RTF_CACHE) { 13281eb4f758SHannes Frederic Sowa dst_hold(&rt->dst); 13298e3d5be7SMartin KaFai Lau ip6_del_rt(rt); 13301eb4f758SHannes Frederic Sowa } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) { 13311da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 13321da177e4SLinus Torvalds } 13331da177e4SLinus Torvalds } 13341eb4f758SHannes Frederic Sowa } 13351da177e4SLinus Torvalds 133645e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) 133745e4fd26SMartin KaFai Lau { 133845e4fd26SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 133945e4fd26SMartin KaFai Lau 134045e4fd26SMartin KaFai Lau rt->rt6i_flags |= RTF_MODIFIED; 134145e4fd26SMartin KaFai Lau rt->rt6i_pmtu = mtu; 134245e4fd26SMartin KaFai Lau rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); 134345e4fd26SMartin KaFai Lau } 134445e4fd26SMartin KaFai Lau 13450d3f6d29SMartin KaFai Lau static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt) 13460d3f6d29SMartin KaFai Lau { 13470d3f6d29SMartin KaFai Lau return !(rt->rt6i_flags & RTF_CACHE) && 13480d3f6d29SMartin KaFai Lau (rt->rt6i_flags & RTF_PCPU || rt->rt6i_node); 13490d3f6d29SMartin KaFai Lau } 13500d3f6d29SMartin KaFai Lau 135145e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, 135245e4fd26SMartin KaFai Lau const struct ipv6hdr *iph, u32 mtu) 13531da177e4SLinus Torvalds { 13541da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info *)dst; 13551da177e4SLinus Torvalds 135645e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_LOCAL) 135745e4fd26SMartin KaFai Lau return; 135845e4fd26SMartin KaFai Lau 135981aded24SDavid S. Miller dst_confirm(dst); 136045e4fd26SMartin KaFai Lau mtu = max_t(u32, mtu, IPV6_MIN_MTU); 136145e4fd26SMartin KaFai Lau if (mtu >= dst_mtu(dst)) 136245e4fd26SMartin KaFai Lau return; 136381aded24SDavid S. Miller 13640d3f6d29SMartin KaFai Lau if (!rt6_cache_allowed_for_pmtu(rt6)) { 136545e4fd26SMartin KaFai Lau rt6_do_update_pmtu(rt6, mtu); 136645e4fd26SMartin KaFai Lau } else { 136745e4fd26SMartin KaFai Lau const struct in6_addr *daddr, *saddr; 136845e4fd26SMartin KaFai Lau struct rt6_info *nrt6; 13699d289715SHagen Paul Pfeifer 137045e4fd26SMartin KaFai Lau if (iph) { 137145e4fd26SMartin KaFai Lau daddr = &iph->daddr; 137245e4fd26SMartin KaFai Lau saddr = &iph->saddr; 137345e4fd26SMartin KaFai Lau } else if (sk) { 137445e4fd26SMartin KaFai Lau daddr = &sk->sk_v6_daddr; 137545e4fd26SMartin KaFai Lau saddr = &inet6_sk(sk)->saddr; 137645e4fd26SMartin KaFai Lau } else { 137745e4fd26SMartin KaFai Lau return; 13781da177e4SLinus Torvalds } 137945e4fd26SMartin KaFai Lau nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr); 138045e4fd26SMartin KaFai Lau if (nrt6) { 138145e4fd26SMartin KaFai Lau rt6_do_update_pmtu(nrt6, mtu); 138245e4fd26SMartin KaFai Lau 138345e4fd26SMartin KaFai Lau /* ip6_ins_rt(nrt6) will bump the 138445e4fd26SMartin KaFai Lau * rt6->rt6i_node->fn_sernum 138545e4fd26SMartin KaFai Lau * which will fail the next rt6_check() and 138645e4fd26SMartin KaFai Lau * invalidate the sk->sk_dst_cache. 138745e4fd26SMartin KaFai Lau */ 138845e4fd26SMartin KaFai Lau ip6_ins_rt(nrt6); 138945e4fd26SMartin KaFai Lau } 139045e4fd26SMartin KaFai Lau } 139145e4fd26SMartin KaFai Lau } 139245e4fd26SMartin KaFai Lau 139345e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 139445e4fd26SMartin KaFai Lau struct sk_buff *skb, u32 mtu) 139545e4fd26SMartin KaFai Lau { 139645e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu); 13971da177e4SLinus Torvalds } 13981da177e4SLinus Torvalds 139942ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 140042ae66c8SDavid S. Miller int oif, u32 mark) 140181aded24SDavid S. Miller { 140281aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 140381aded24SDavid S. Miller struct dst_entry *dst; 140481aded24SDavid S. Miller struct flowi6 fl6; 140581aded24SDavid S. Miller 140681aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 140781aded24SDavid S. Miller fl6.flowi6_oif = oif; 14081b3c61dcSLorenzo Colitti fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark); 140981aded24SDavid S. Miller fl6.daddr = iph->daddr; 141081aded24SDavid S. Miller fl6.saddr = iph->saddr; 14116502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 141281aded24SDavid S. Miller 141381aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 141481aded24SDavid S. Miller if (!dst->error) 141545e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu)); 141681aded24SDavid S. Miller dst_release(dst); 141781aded24SDavid S. Miller } 141881aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 141981aded24SDavid S. Miller 142081aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 142181aded24SDavid S. Miller { 142233c162a9SMartin KaFai Lau struct dst_entry *dst; 142333c162a9SMartin KaFai Lau 142481aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 142581aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 142633c162a9SMartin KaFai Lau 142733c162a9SMartin KaFai Lau dst = __sk_dst_get(sk); 142833c162a9SMartin KaFai Lau if (!dst || !dst->obsolete || 142933c162a9SMartin KaFai Lau dst->ops->check(dst, inet6_sk(sk)->dst_cookie)) 143033c162a9SMartin KaFai Lau return; 143133c162a9SMartin KaFai Lau 143233c162a9SMartin KaFai Lau bh_lock_sock(sk); 143333c162a9SMartin KaFai Lau if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 143433c162a9SMartin KaFai Lau ip6_datagram_dst_update(sk, false); 143533c162a9SMartin KaFai Lau bh_unlock_sock(sk); 143681aded24SDavid S. Miller } 143781aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 143881aded24SDavid S. Miller 1439b55b76b2SDuan Jiong /* Handle redirects */ 1440b55b76b2SDuan Jiong struct ip6rd_flowi { 1441b55b76b2SDuan Jiong struct flowi6 fl6; 1442b55b76b2SDuan Jiong struct in6_addr gateway; 1443b55b76b2SDuan Jiong }; 1444b55b76b2SDuan Jiong 1445b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net, 1446b55b76b2SDuan Jiong struct fib6_table *table, 1447b55b76b2SDuan Jiong struct flowi6 *fl6, 1448b55b76b2SDuan Jiong int flags) 1449b55b76b2SDuan Jiong { 1450b55b76b2SDuan Jiong struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 1451b55b76b2SDuan Jiong struct rt6_info *rt; 1452b55b76b2SDuan Jiong struct fib6_node *fn; 1453b55b76b2SDuan Jiong 1454b55b76b2SDuan Jiong /* Get the "current" route for this destination and 1455b55b76b2SDuan Jiong * check if the redirect has come from approriate router. 1456b55b76b2SDuan Jiong * 1457b55b76b2SDuan Jiong * RFC 4861 specifies that redirects should only be 1458b55b76b2SDuan Jiong * accepted if they come from the nexthop to the target. 1459b55b76b2SDuan Jiong * Due to the way the routes are chosen, this notion 1460b55b76b2SDuan Jiong * is a bit fuzzy and one might need to check all possible 1461b55b76b2SDuan Jiong * routes. 1462b55b76b2SDuan Jiong */ 1463b55b76b2SDuan Jiong 1464b55b76b2SDuan Jiong read_lock_bh(&table->tb6_lock); 1465b55b76b2SDuan Jiong fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1466b55b76b2SDuan Jiong restart: 1467b55b76b2SDuan Jiong for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1468b55b76b2SDuan Jiong if (rt6_check_expired(rt)) 1469b55b76b2SDuan Jiong continue; 1470b55b76b2SDuan Jiong if (rt->dst.error) 1471b55b76b2SDuan Jiong break; 1472b55b76b2SDuan Jiong if (!(rt->rt6i_flags & RTF_GATEWAY)) 1473b55b76b2SDuan Jiong continue; 1474b55b76b2SDuan Jiong if (fl6->flowi6_oif != rt->dst.dev->ifindex) 1475b55b76b2SDuan Jiong continue; 1476b55b76b2SDuan Jiong if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) 1477b55b76b2SDuan Jiong continue; 1478b55b76b2SDuan Jiong break; 1479b55b76b2SDuan Jiong } 1480b55b76b2SDuan Jiong 1481b55b76b2SDuan Jiong if (!rt) 1482b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1483b55b76b2SDuan Jiong else if (rt->dst.error) { 1484b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1485b0a1ba59SMartin KaFai Lau goto out; 1486b0a1ba59SMartin KaFai Lau } 1487b0a1ba59SMartin KaFai Lau 1488b0a1ba59SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1489a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1490a3c00e46SMartin KaFai Lau if (fn) 1491a3c00e46SMartin KaFai Lau goto restart; 1492b55b76b2SDuan Jiong } 1493a3c00e46SMartin KaFai Lau 1494b0a1ba59SMartin KaFai Lau out: 1495b55b76b2SDuan Jiong dst_hold(&rt->dst); 1496b55b76b2SDuan Jiong 1497b55b76b2SDuan Jiong read_unlock_bh(&table->tb6_lock); 1498b55b76b2SDuan Jiong 1499b811580dSDavid Ahern trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); 1500b55b76b2SDuan Jiong return rt; 1501b55b76b2SDuan Jiong }; 1502b55b76b2SDuan Jiong 1503b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net, 1504b55b76b2SDuan Jiong const struct flowi6 *fl6, 1505b55b76b2SDuan Jiong const struct in6_addr *gateway) 1506b55b76b2SDuan Jiong { 1507b55b76b2SDuan Jiong int flags = RT6_LOOKUP_F_HAS_SADDR; 1508b55b76b2SDuan Jiong struct ip6rd_flowi rdfl; 1509b55b76b2SDuan Jiong 1510b55b76b2SDuan Jiong rdfl.fl6 = *fl6; 1511b55b76b2SDuan Jiong rdfl.gateway = *gateway; 1512b55b76b2SDuan Jiong 1513b55b76b2SDuan Jiong return fib6_rule_lookup(net, &rdfl.fl6, 1514b55b76b2SDuan Jiong flags, __ip6_route_redirect); 1515b55b76b2SDuan Jiong } 1516b55b76b2SDuan Jiong 15173a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 15183a5ad2eeSDavid S. Miller { 15193a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 15203a5ad2eeSDavid S. Miller struct dst_entry *dst; 15213a5ad2eeSDavid S. Miller struct flowi6 fl6; 15223a5ad2eeSDavid S. Miller 15233a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 1524e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 15253a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 15263a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 15273a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 15283a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 15296502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 15303a5ad2eeSDavid S. Miller 1531b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); 15326700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 15333a5ad2eeSDavid S. Miller dst_release(dst); 15343a5ad2eeSDavid S. Miller } 15353a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 15363a5ad2eeSDavid S. Miller 1537c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, 1538c92a59ecSDuan Jiong u32 mark) 1539c92a59ecSDuan Jiong { 1540c92a59ecSDuan Jiong const struct ipv6hdr *iph = ipv6_hdr(skb); 1541c92a59ecSDuan Jiong const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 1542c92a59ecSDuan Jiong struct dst_entry *dst; 1543c92a59ecSDuan Jiong struct flowi6 fl6; 1544c92a59ecSDuan Jiong 1545c92a59ecSDuan Jiong memset(&fl6, 0, sizeof(fl6)); 1546e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 1547c92a59ecSDuan Jiong fl6.flowi6_oif = oif; 1548c92a59ecSDuan Jiong fl6.flowi6_mark = mark; 1549c92a59ecSDuan Jiong fl6.daddr = msg->dest; 1550c92a59ecSDuan Jiong fl6.saddr = iph->daddr; 1551c92a59ecSDuan Jiong 1552b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &iph->saddr); 1553c92a59ecSDuan Jiong rt6_do_redirect(dst, NULL, skb); 1554c92a59ecSDuan Jiong dst_release(dst); 1555c92a59ecSDuan Jiong } 1556c92a59ecSDuan Jiong 15573a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 15583a5ad2eeSDavid S. Miller { 15593a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 15603a5ad2eeSDavid S. Miller } 15613a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 15623a5ad2eeSDavid S. Miller 15630dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 15641da177e4SLinus Torvalds { 15650dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 15660dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 15670dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 15680dbaee3bSDavid S. Miller 15691da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 15701da177e4SLinus Torvalds 15715578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 15725578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 15731da177e4SLinus Torvalds 15741da177e4SLinus Torvalds /* 15751da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 15761da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 15771da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 15781da177e4SLinus Torvalds * rely only on pmtu discovery" 15791da177e4SLinus Torvalds */ 15801da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 15811da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 15821da177e4SLinus Torvalds return mtu; 15831da177e4SLinus Torvalds } 15841da177e4SLinus Torvalds 1585ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1586d33e4553SDavid S. Miller { 15874b32b5adSMartin KaFai Lau const struct rt6_info *rt = (const struct rt6_info *)dst; 15884b32b5adSMartin KaFai Lau unsigned int mtu = rt->rt6i_pmtu; 1589d33e4553SDavid S. Miller struct inet6_dev *idev; 1590618f9bc7SSteffen Klassert 1591618f9bc7SSteffen Klassert if (mtu) 159230f78d8eSEric Dumazet goto out; 1593618f9bc7SSteffen Klassert 15944b32b5adSMartin KaFai Lau mtu = dst_metric_raw(dst, RTAX_MTU); 15954b32b5adSMartin KaFai Lau if (mtu) 15964b32b5adSMartin KaFai Lau goto out; 15974b32b5adSMartin KaFai Lau 1598618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1599d33e4553SDavid S. Miller 1600d33e4553SDavid S. Miller rcu_read_lock(); 1601d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1602d33e4553SDavid S. Miller if (idev) 1603d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1604d33e4553SDavid S. Miller rcu_read_unlock(); 1605d33e4553SDavid S. Miller 160630f78d8eSEric Dumazet out: 160730f78d8eSEric Dumazet return min_t(unsigned int, mtu, IP6_MAX_MTU); 1608d33e4553SDavid S. Miller } 1609d33e4553SDavid S. Miller 16103b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 16113b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 16125d0bbeebSThomas Graf 16133b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 161487a11578SDavid S. Miller struct flowi6 *fl6) 16151da177e4SLinus Torvalds { 161687a11578SDavid S. Miller struct dst_entry *dst; 16171da177e4SLinus Torvalds struct rt6_info *rt; 16181da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1619c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 16201da177e4SLinus Torvalds 162138308473SDavid S. Miller if (unlikely(!idev)) 1622122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 16231da177e4SLinus Torvalds 1624ad706862SMartin KaFai Lau rt = ip6_dst_alloc(net, dev, 0); 162538308473SDavid S. Miller if (unlikely(!rt)) { 16261da177e4SLinus Torvalds in6_dev_put(idev); 162787a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 16281da177e4SLinus Torvalds goto out; 16291da177e4SLinus Torvalds } 16301da177e4SLinus Torvalds 16318e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 16328e2ec639SYan, Zheng rt->dst.output = ip6_output; 1633d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 1634550bab42SJulian Anastasov rt->rt6i_gateway = fl6->daddr; 163587a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 16368e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 16378e2ec639SYan, Zheng rt->rt6i_idev = idev; 163814edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 16391da177e4SLinus Torvalds 16403b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1641d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1642d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 16433b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 16441da177e4SLinus Torvalds 16455578689aSDaniel Lezcano fib6_force_start_gc(net); 16461da177e4SLinus Torvalds 164787a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 164887a11578SDavid S. Miller 16491da177e4SLinus Torvalds out: 165087a11578SDavid S. Miller return dst; 16511da177e4SLinus Torvalds } 16521da177e4SLinus Torvalds 16533d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 16541da177e4SLinus Torvalds { 1655e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 16563d0f24a7SStephen Hemminger int more = 0; 16571da177e4SLinus Torvalds 16583b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 16593b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 16605d0bbeebSThomas Graf 16611da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 16621da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 16631da177e4SLinus Torvalds *pprev = dst->next; 16641da177e4SLinus Torvalds dst_free(dst); 16651da177e4SLinus Torvalds } else { 16661da177e4SLinus Torvalds pprev = &dst->next; 16673d0f24a7SStephen Hemminger ++more; 16681da177e4SLinus Torvalds } 16691da177e4SLinus Torvalds } 16701da177e4SLinus Torvalds 16713b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 16725d0bbeebSThomas Graf 16733d0f24a7SStephen Hemminger return more; 16741da177e4SLinus Torvalds } 16751da177e4SLinus Torvalds 16761e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 16771e493d19SDavid S. Miller void *arg) 16781e493d19SDavid S. Miller { 16791e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 16801e493d19SDavid S. Miller 16811e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 16821e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 16831e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 16841e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 16851e493d19SDavid S. Miller if (func(rt, arg)) { 16861e493d19SDavid S. Miller *pprev = dst->next; 16871e493d19SDavid S. Miller dst_free(dst); 16881e493d19SDavid S. Miller } else { 16891e493d19SDavid S. Miller pprev = &dst->next; 16901e493d19SDavid S. Miller } 16911e493d19SDavid S. Miller } 16921e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 16931e493d19SDavid S. Miller } 16941e493d19SDavid S. Miller 1695569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 16961da177e4SLinus Torvalds { 169786393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 16987019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 16997019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 17007019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 17017019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 17027019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1703fc66f95cSEric Dumazet int entries; 17041da177e4SLinus Torvalds 1705fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 170649a18d86SMichal Kubeček if (time_after(rt_last_gc + rt_min_interval, jiffies) && 1707fc66f95cSEric Dumazet entries <= rt_max_size) 17081da177e4SLinus Torvalds goto out; 17091da177e4SLinus Torvalds 17106891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 171114956643SLi RongQing fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true); 1712fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1713fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 17147019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 17151da177e4SLinus Torvalds out: 17167019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1717fc66f95cSEric Dumazet return entries > rt_max_size; 17181da177e4SLinus Torvalds } 17191da177e4SLinus Torvalds 1720e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc, 1721e715b6d3SFlorian Westphal const struct fib6_config *cfg) 1722e715b6d3SFlorian Westphal { 1723c3a8d947SDaniel Borkmann bool ecn_ca = false; 1724e715b6d3SFlorian Westphal struct nlattr *nla; 1725e715b6d3SFlorian Westphal int remaining; 1726e715b6d3SFlorian Westphal u32 *mp; 1727e715b6d3SFlorian Westphal 172863159f29SIan Morris if (!cfg->fc_mx) 1729e715b6d3SFlorian Westphal return 0; 1730e715b6d3SFlorian Westphal 1731e715b6d3SFlorian Westphal mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 1732e715b6d3SFlorian Westphal if (unlikely(!mp)) 1733e715b6d3SFlorian Westphal return -ENOMEM; 1734e715b6d3SFlorian Westphal 1735e715b6d3SFlorian Westphal nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 1736e715b6d3SFlorian Westphal int type = nla_type(nla); 1737ea697639SDaniel Borkmann u32 val; 1738ea697639SDaniel Borkmann 17391bb14807SDaniel Borkmann if (!type) 17401bb14807SDaniel Borkmann continue; 1741e715b6d3SFlorian Westphal if (unlikely(type > RTAX_MAX)) 1742e715b6d3SFlorian Westphal goto err; 17431bb14807SDaniel Borkmann 1744ea697639SDaniel Borkmann if (type == RTAX_CC_ALGO) { 1745ea697639SDaniel Borkmann char tmp[TCP_CA_NAME_MAX]; 1746e715b6d3SFlorian Westphal 1747ea697639SDaniel Borkmann nla_strlcpy(tmp, nla, sizeof(tmp)); 1748c3a8d947SDaniel Borkmann val = tcp_ca_get_key_by_name(tmp, &ecn_ca); 1749ea697639SDaniel Borkmann if (val == TCP_CA_UNSPEC) 1750ea697639SDaniel Borkmann goto err; 1751ea697639SDaniel Borkmann } else { 1752ea697639SDaniel Borkmann val = nla_get_u32(nla); 1753ea697639SDaniel Borkmann } 1754626abd59SPaolo Abeni if (type == RTAX_HOPLIMIT && val > 255) 1755626abd59SPaolo Abeni val = 255; 1756b8d3e416SDaniel Borkmann if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK)) 1757b8d3e416SDaniel Borkmann goto err; 1758ea697639SDaniel Borkmann 1759ea697639SDaniel Borkmann mp[type - 1] = val; 1760e715b6d3SFlorian Westphal __set_bit(type - 1, mxc->mx_valid); 1761e715b6d3SFlorian Westphal } 1762e715b6d3SFlorian Westphal 1763c3a8d947SDaniel Borkmann if (ecn_ca) { 1764c3a8d947SDaniel Borkmann __set_bit(RTAX_FEATURES - 1, mxc->mx_valid); 1765c3a8d947SDaniel Borkmann mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA; 1766c3a8d947SDaniel Borkmann } 1767e715b6d3SFlorian Westphal 1768c3a8d947SDaniel Borkmann mxc->mx = mp; 1769e715b6d3SFlorian Westphal return 0; 1770e715b6d3SFlorian Westphal err: 1771e715b6d3SFlorian Westphal kfree(mp); 1772e715b6d3SFlorian Westphal return -EINVAL; 1773e715b6d3SFlorian Westphal } 17741da177e4SLinus Torvalds 17758c14586fSDavid Ahern static struct rt6_info *ip6_nh_lookup_table(struct net *net, 17768c14586fSDavid Ahern struct fib6_config *cfg, 17778c14586fSDavid Ahern const struct in6_addr *gw_addr) 17788c14586fSDavid Ahern { 17798c14586fSDavid Ahern struct flowi6 fl6 = { 17808c14586fSDavid Ahern .flowi6_oif = cfg->fc_ifindex, 17818c14586fSDavid Ahern .daddr = *gw_addr, 17828c14586fSDavid Ahern .saddr = cfg->fc_prefsrc, 17838c14586fSDavid Ahern }; 17848c14586fSDavid Ahern struct fib6_table *table; 17858c14586fSDavid Ahern struct rt6_info *rt; 17868c14586fSDavid Ahern int flags = 0; 17878c14586fSDavid Ahern 17888c14586fSDavid Ahern table = fib6_get_table(net, cfg->fc_table); 17898c14586fSDavid Ahern if (!table) 17908c14586fSDavid Ahern return NULL; 17918c14586fSDavid Ahern 17928c14586fSDavid Ahern if (!ipv6_addr_any(&cfg->fc_prefsrc)) 17938c14586fSDavid Ahern flags |= RT6_LOOKUP_F_HAS_SADDR; 17948c14586fSDavid Ahern 17958c14586fSDavid Ahern rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, flags); 17968c14586fSDavid Ahern 17978c14586fSDavid Ahern /* if table lookup failed, fall back to full lookup */ 17988c14586fSDavid Ahern if (rt == net->ipv6.ip6_null_entry) { 17998c14586fSDavid Ahern ip6_rt_put(rt); 18008c14586fSDavid Ahern rt = NULL; 18018c14586fSDavid Ahern } 18028c14586fSDavid Ahern 18038c14586fSDavid Ahern return rt; 18048c14586fSDavid Ahern } 18058c14586fSDavid Ahern 18068c5b83f0SRoopa Prabhu static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg) 18071da177e4SLinus Torvalds { 18085578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 18091da177e4SLinus Torvalds struct rt6_info *rt = NULL; 18101da177e4SLinus Torvalds struct net_device *dev = NULL; 18111da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1812c71099acSThomas Graf struct fib6_table *table; 18131da177e4SLinus Torvalds int addr_type; 18148c5b83f0SRoopa Prabhu int err = -EINVAL; 18151da177e4SLinus Torvalds 181686872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 18178c5b83f0SRoopa Prabhu goto out; 18181da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 181986872cb5SThomas Graf if (cfg->fc_src_len) 18208c5b83f0SRoopa Prabhu goto out; 18211da177e4SLinus Torvalds #endif 182286872cb5SThomas Graf if (cfg->fc_ifindex) { 18231da177e4SLinus Torvalds err = -ENODEV; 18245578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 18251da177e4SLinus Torvalds if (!dev) 18261da177e4SLinus Torvalds goto out; 18271da177e4SLinus Torvalds idev = in6_dev_get(dev); 18281da177e4SLinus Torvalds if (!idev) 18291da177e4SLinus Torvalds goto out; 18301da177e4SLinus Torvalds } 18311da177e4SLinus Torvalds 183286872cb5SThomas Graf if (cfg->fc_metric == 0) 183386872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 18341da177e4SLinus Torvalds 1835c71099acSThomas Graf err = -ENOBUFS; 183638308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1837d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1838d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 183938308473SDavid S. Miller if (!table) { 1840f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1841d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1842d71314b4SMatti Vaittinen } 1843d71314b4SMatti Vaittinen } else { 1844d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1845d71314b4SMatti Vaittinen } 184638308473SDavid S. Miller 184738308473SDavid S. Miller if (!table) 1848c71099acSThomas Graf goto out; 1849c71099acSThomas Graf 1850ad706862SMartin KaFai Lau rt = ip6_dst_alloc(net, NULL, 1851ad706862SMartin KaFai Lau (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT); 18521da177e4SLinus Torvalds 185338308473SDavid S. Miller if (!rt) { 18541da177e4SLinus Torvalds err = -ENOMEM; 18551da177e4SLinus Torvalds goto out; 18561da177e4SLinus Torvalds } 18571da177e4SLinus Torvalds 18581716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 18591716a961SGao feng rt6_set_expires(rt, jiffies + 18601716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 18611716a961SGao feng else 18621716a961SGao feng rt6_clean_expires(rt); 18631da177e4SLinus Torvalds 186486872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 186586872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 186686872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 186786872cb5SThomas Graf 186886872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 18691da177e4SLinus Torvalds 18701da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1871d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1872ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1873ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 18741da177e4SLinus Torvalds else 1875d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 18761da177e4SLinus Torvalds 1877d8d1f30bSChangli Gao rt->dst.output = ip6_output; 18781da177e4SLinus Torvalds 187919e42e45SRoopa Prabhu if (cfg->fc_encap) { 188019e42e45SRoopa Prabhu struct lwtunnel_state *lwtstate; 188119e42e45SRoopa Prabhu 188219e42e45SRoopa Prabhu err = lwtunnel_build_state(dev, cfg->fc_encap_type, 1883127eb7cdSTom Herbert cfg->fc_encap, AF_INET6, cfg, 1884127eb7cdSTom Herbert &lwtstate); 188519e42e45SRoopa Prabhu if (err) 188619e42e45SRoopa Prabhu goto out; 188761adedf3SJiri Benc rt->dst.lwtstate = lwtstate_get(lwtstate); 188861adedf3SJiri Benc if (lwtunnel_output_redirect(rt->dst.lwtstate)) { 188961adedf3SJiri Benc rt->dst.lwtstate->orig_output = rt->dst.output; 189061adedf3SJiri Benc rt->dst.output = lwtunnel_output; 189119e42e45SRoopa Prabhu } 189261adedf3SJiri Benc if (lwtunnel_input_redirect(rt->dst.lwtstate)) { 189361adedf3SJiri Benc rt->dst.lwtstate->orig_input = rt->dst.input; 189461adedf3SJiri Benc rt->dst.input = lwtunnel_input; 189525368623STom Herbert } 189625368623STom Herbert } 189719e42e45SRoopa Prabhu 189886872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 189986872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 1900afc4eef8SMartin KaFai Lau if (rt->rt6i_dst.plen == 128) 190111d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 19021da177e4SLinus Torvalds 19031da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 190486872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 190586872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 19061da177e4SLinus Torvalds #endif 19071da177e4SLinus Torvalds 190886872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 19091da177e4SLinus Torvalds 19101da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 19111da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 19121da177e4SLinus Torvalds */ 191386872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 191438308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 191538308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 191638308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 19171da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 19185578689aSDaniel Lezcano if (dev != net->loopback_dev) { 19191da177e4SLinus Torvalds if (dev) { 19201da177e4SLinus Torvalds dev_put(dev); 19211da177e4SLinus Torvalds in6_dev_put(idev); 19221da177e4SLinus Torvalds } 19235578689aSDaniel Lezcano dev = net->loopback_dev; 19241da177e4SLinus Torvalds dev_hold(dev); 19251da177e4SLinus Torvalds idev = in6_dev_get(dev); 19261da177e4SLinus Torvalds if (!idev) { 19271da177e4SLinus Torvalds err = -ENODEV; 19281da177e4SLinus Torvalds goto out; 19291da177e4SLinus Torvalds } 19301da177e4SLinus Torvalds } 19311da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1932ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1933ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1934ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 1935ede2059dSEric W. Biederman rt->dst.output = dst_discard_out; 19367150aedeSKamala R rt->dst.input = dst_discard; 1937ef2c7d7bSNicolas Dichtel break; 1938ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1939ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 19407150aedeSKamala R rt->dst.output = ip6_pkt_prohibit_out; 19417150aedeSKamala R rt->dst.input = ip6_pkt_prohibit; 1942ef2c7d7bSNicolas Dichtel break; 1943b4949ab2SNicolas Dichtel case RTN_THROW: 19440315e382SNikola Forró case RTN_UNREACHABLE: 1945ef2c7d7bSNicolas Dichtel default: 19467150aedeSKamala R rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN 19470315e382SNikola Forró : (cfg->fc_type == RTN_UNREACHABLE) 19480315e382SNikola Forró ? -EHOSTUNREACH : -ENETUNREACH; 19497150aedeSKamala R rt->dst.output = ip6_pkt_discard_out; 19507150aedeSKamala R rt->dst.input = ip6_pkt_discard; 1951ef2c7d7bSNicolas Dichtel break; 1952ef2c7d7bSNicolas Dichtel } 19531da177e4SLinus Torvalds goto install_route; 19541da177e4SLinus Torvalds } 19551da177e4SLinus Torvalds 195686872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1957b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 19581da177e4SLinus Torvalds int gwa_type; 19591da177e4SLinus Torvalds 196086872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 1961330567b7SFlorian Westphal gwa_type = ipv6_addr_type(gw_addr); 196248ed7b26SFlorian Westphal 196348ed7b26SFlorian Westphal /* if gw_addr is local we will fail to detect this in case 196448ed7b26SFlorian Westphal * address is still TENTATIVE (DAD in progress). rt6_lookup() 196548ed7b26SFlorian Westphal * will return already-added prefix route via interface that 196648ed7b26SFlorian Westphal * prefix route was assigned to, which might be non-loopback. 196748ed7b26SFlorian Westphal */ 196848ed7b26SFlorian Westphal err = -EINVAL; 1969330567b7SFlorian Westphal if (ipv6_chk_addr_and_flags(net, gw_addr, 1970330567b7SFlorian Westphal gwa_type & IPV6_ADDR_LINKLOCAL ? 1971330567b7SFlorian Westphal dev : NULL, 0, 0)) 197248ed7b26SFlorian Westphal goto out; 197348ed7b26SFlorian Westphal 19744e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 19751da177e4SLinus Torvalds 19761da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 19778c14586fSDavid Ahern struct rt6_info *grt = NULL; 19781da177e4SLinus Torvalds 19791da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 19801da177e4SLinus Torvalds addresses as nexthop address. 19811da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 19821da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 19831da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 19841da177e4SLinus Torvalds some exceptions. --ANK 19851da177e4SLinus Torvalds */ 19861da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 19871da177e4SLinus Torvalds goto out; 19881da177e4SLinus Torvalds 19898c14586fSDavid Ahern if (cfg->fc_table) 19908c14586fSDavid Ahern grt = ip6_nh_lookup_table(net, cfg, gw_addr); 19918c14586fSDavid Ahern 19928c14586fSDavid Ahern if (!grt) 19938c14586fSDavid Ahern grt = rt6_lookup(net, gw_addr, NULL, 19948c14586fSDavid Ahern cfg->fc_ifindex, 1); 19951da177e4SLinus Torvalds 19961da177e4SLinus Torvalds err = -EHOSTUNREACH; 199738308473SDavid S. Miller if (!grt) 19981da177e4SLinus Torvalds goto out; 19991da177e4SLinus Torvalds if (dev) { 2000d1918542SDavid S. Miller if (dev != grt->dst.dev) { 200194e187c0SAmerigo Wang ip6_rt_put(grt); 20021da177e4SLinus Torvalds goto out; 20031da177e4SLinus Torvalds } 20041da177e4SLinus Torvalds } else { 2005d1918542SDavid S. Miller dev = grt->dst.dev; 20061da177e4SLinus Torvalds idev = grt->rt6i_idev; 20071da177e4SLinus Torvalds dev_hold(dev); 20081da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 20091da177e4SLinus Torvalds } 20101da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 20111da177e4SLinus Torvalds err = 0; 201294e187c0SAmerigo Wang ip6_rt_put(grt); 20131da177e4SLinus Torvalds 20141da177e4SLinus Torvalds if (err) 20151da177e4SLinus Torvalds goto out; 20161da177e4SLinus Torvalds } 20171da177e4SLinus Torvalds err = -EINVAL; 201838308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 20191da177e4SLinus Torvalds goto out; 20201da177e4SLinus Torvalds } 20211da177e4SLinus Torvalds 20221da177e4SLinus Torvalds err = -ENODEV; 202338308473SDavid S. Miller if (!dev) 20241da177e4SLinus Torvalds goto out; 20251da177e4SLinus Torvalds 2026c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 2027c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 2028c3968a85SDaniel Walter err = -EINVAL; 2029c3968a85SDaniel Walter goto out; 2030c3968a85SDaniel Walter } 20314e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 2032c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 2033c3968a85SDaniel Walter } else 2034c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2035c3968a85SDaniel Walter 203686872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 20371da177e4SLinus Torvalds 20381da177e4SLinus Torvalds install_route: 2039d8d1f30bSChangli Gao rt->dst.dev = dev; 20401da177e4SLinus Torvalds rt->rt6i_idev = idev; 2041c71099acSThomas Graf rt->rt6i_table = table; 204263152fc0SDaniel Lezcano 2043c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 204463152fc0SDaniel Lezcano 20458c5b83f0SRoopa Prabhu return rt; 20461da177e4SLinus Torvalds out: 20471da177e4SLinus Torvalds if (dev) 20481da177e4SLinus Torvalds dev_put(dev); 20491da177e4SLinus Torvalds if (idev) 20501da177e4SLinus Torvalds in6_dev_put(idev); 20511da177e4SLinus Torvalds if (rt) 2052d8d1f30bSChangli Gao dst_free(&rt->dst); 20536b9ea5a6SRoopa Prabhu 20548c5b83f0SRoopa Prabhu return ERR_PTR(err); 20556b9ea5a6SRoopa Prabhu } 20566b9ea5a6SRoopa Prabhu 20576b9ea5a6SRoopa Prabhu int ip6_route_add(struct fib6_config *cfg) 20586b9ea5a6SRoopa Prabhu { 20596b9ea5a6SRoopa Prabhu struct mx6_config mxc = { .mx = NULL, }; 20608c5b83f0SRoopa Prabhu struct rt6_info *rt; 20616b9ea5a6SRoopa Prabhu int err; 20626b9ea5a6SRoopa Prabhu 20638c5b83f0SRoopa Prabhu rt = ip6_route_info_create(cfg); 20648c5b83f0SRoopa Prabhu if (IS_ERR(rt)) { 20658c5b83f0SRoopa Prabhu err = PTR_ERR(rt); 20668c5b83f0SRoopa Prabhu rt = NULL; 20676b9ea5a6SRoopa Prabhu goto out; 20688c5b83f0SRoopa Prabhu } 20696b9ea5a6SRoopa Prabhu 20706b9ea5a6SRoopa Prabhu err = ip6_convert_metrics(&mxc, cfg); 20716b9ea5a6SRoopa Prabhu if (err) 20726b9ea5a6SRoopa Prabhu goto out; 20736b9ea5a6SRoopa Prabhu 20746b9ea5a6SRoopa Prabhu err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); 20756b9ea5a6SRoopa Prabhu 20766b9ea5a6SRoopa Prabhu kfree(mxc.mx); 20776b9ea5a6SRoopa Prabhu 20786b9ea5a6SRoopa Prabhu return err; 20796b9ea5a6SRoopa Prabhu out: 20806b9ea5a6SRoopa Prabhu if (rt) 20816b9ea5a6SRoopa Prabhu dst_free(&rt->dst); 20826b9ea5a6SRoopa Prabhu 20831da177e4SLinus Torvalds return err; 20841da177e4SLinus Torvalds } 20851da177e4SLinus Torvalds 208686872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 20871da177e4SLinus Torvalds { 20881da177e4SLinus Torvalds int err; 2089c71099acSThomas Graf struct fib6_table *table; 2090d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 20911da177e4SLinus Torvalds 20928e3d5be7SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry || 20938e3d5be7SMartin KaFai Lau rt->dst.flags & DST_NOCACHE) { 20946825a26cSGao feng err = -ENOENT; 20956825a26cSGao feng goto out; 20966825a26cSGao feng } 20976c813a72SPatrick McHardy 2098c71099acSThomas Graf table = rt->rt6i_table; 2099c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 210086872cb5SThomas Graf err = fib6_del(rt, info); 2101c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 21021da177e4SLinus Torvalds 21036825a26cSGao feng out: 210494e187c0SAmerigo Wang ip6_rt_put(rt); 21051da177e4SLinus Torvalds return err; 21061da177e4SLinus Torvalds } 21071da177e4SLinus Torvalds 2108e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 2109e0a1ad73SThomas Graf { 21104d1169c1SDenis V. Lunev struct nl_info info = { 2111d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 21124d1169c1SDenis V. Lunev }; 2113528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 2114e0a1ad73SThomas Graf } 2115e0a1ad73SThomas Graf 211686872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 21171da177e4SLinus Torvalds { 2118c71099acSThomas Graf struct fib6_table *table; 21191da177e4SLinus Torvalds struct fib6_node *fn; 21201da177e4SLinus Torvalds struct rt6_info *rt; 21211da177e4SLinus Torvalds int err = -ESRCH; 21221da177e4SLinus Torvalds 21235578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 212438308473SDavid S. Miller if (!table) 2125c71099acSThomas Graf return err; 21261da177e4SLinus Torvalds 2127c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2128c71099acSThomas Graf 2129c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 213086872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 213186872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 21321da177e4SLinus Torvalds 21331da177e4SLinus Torvalds if (fn) { 2134d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 21351f56a01fSMartin KaFai Lau if ((rt->rt6i_flags & RTF_CACHE) && 21361f56a01fSMartin KaFai Lau !(cfg->fc_flags & RTF_CACHE)) 21371f56a01fSMartin KaFai Lau continue; 213886872cb5SThomas Graf if (cfg->fc_ifindex && 2139d1918542SDavid S. Miller (!rt->dst.dev || 2140d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 21411da177e4SLinus Torvalds continue; 214286872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 214386872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 21441da177e4SLinus Torvalds continue; 214586872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 21461da177e4SLinus Torvalds continue; 2147d8d1f30bSChangli Gao dst_hold(&rt->dst); 2148c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 21491da177e4SLinus Torvalds 215086872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 21511da177e4SLinus Torvalds } 21521da177e4SLinus Torvalds } 2153c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 21541da177e4SLinus Torvalds 21551da177e4SLinus Torvalds return err; 21561da177e4SLinus Torvalds } 21571da177e4SLinus Torvalds 21586700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 2159a6279458SYOSHIFUJI Hideaki { 2160a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 2161e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 2162e8599ff4SDavid S. Miller struct ndisc_options ndopts; 2163e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 2164e8599ff4SDavid S. Miller struct neighbour *neigh; 216571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 struct rd_msg *msg; 21666e157b6aSDavid S. Miller int optlen, on_link; 21676e157b6aSDavid S. Miller u8 *lladdr; 2168e8599ff4SDavid S. Miller 216929a3cad5SSimon Horman optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 217071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 optlen -= sizeof(*msg); 2171e8599ff4SDavid S. Miller 2172e8599ff4SDavid S. Miller if (optlen < 0) { 21736e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 2174e8599ff4SDavid S. Miller return; 2175e8599ff4SDavid S. Miller } 2176e8599ff4SDavid S. Miller 217771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 msg = (struct rd_msg *)icmp6_hdr(skb); 2178e8599ff4SDavid S. Miller 217971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_is_multicast(&msg->dest)) { 21806e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 2181e8599ff4SDavid S. Miller return; 2182e8599ff4SDavid S. Miller } 2183e8599ff4SDavid S. Miller 21846e157b6aSDavid S. Miller on_link = 0; 218571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_equal(&msg->dest, &msg->target)) { 2186e8599ff4SDavid S. Miller on_link = 1; 218771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 } else if (ipv6_addr_type(&msg->target) != 2188e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 21896e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 2190e8599ff4SDavid S. Miller return; 2191e8599ff4SDavid S. Miller } 2192e8599ff4SDavid S. Miller 2193e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 2194e8599ff4SDavid S. Miller if (!in6_dev) 2195e8599ff4SDavid S. Miller return; 2196e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 2197e8599ff4SDavid S. Miller return; 2198e8599ff4SDavid S. Miller 2199e8599ff4SDavid S. Miller /* RFC2461 8.1: 2200e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 2201e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 2202e8599ff4SDavid S. Miller */ 2203e8599ff4SDavid S. Miller 220471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) { 2205e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 2206e8599ff4SDavid S. Miller return; 2207e8599ff4SDavid S. Miller } 22086e157b6aSDavid S. Miller 22096e157b6aSDavid S. Miller lladdr = NULL; 2210e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 2211e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 2212e8599ff4SDavid S. Miller skb->dev); 2213e8599ff4SDavid S. Miller if (!lladdr) { 2214e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 2215e8599ff4SDavid S. Miller return; 2216e8599ff4SDavid S. Miller } 2217e8599ff4SDavid S. Miller } 2218e8599ff4SDavid S. Miller 22196e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 2220ec13ad1dSMatthias Schiffer if (rt->rt6i_flags & RTF_REJECT) { 22216e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 22226e157b6aSDavid S. Miller return; 22236e157b6aSDavid S. Miller } 22246e157b6aSDavid S. Miller 22256e157b6aSDavid S. Miller /* Redirect received -> path was valid. 22266e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 22276e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 22286e157b6aSDavid S. Miller */ 22296e157b6aSDavid S. Miller dst_confirm(&rt->dst); 22306e157b6aSDavid S. Miller 223171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 2232e8599ff4SDavid S. Miller if (!neigh) 2233e8599ff4SDavid S. Miller return; 2234e8599ff4SDavid S. Miller 22351da177e4SLinus Torvalds /* 22361da177e4SLinus Torvalds * We have finally decided to accept it. 22371da177e4SLinus Torvalds */ 22381da177e4SLinus Torvalds 22391da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 22401da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 22411da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 22421da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 22431da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 22441da177e4SLinus Torvalds ); 22451da177e4SLinus Torvalds 224683a09abdSMartin KaFai Lau nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL); 224738308473SDavid S. Miller if (!nrt) 22481da177e4SLinus Torvalds goto out; 22491da177e4SLinus Torvalds 22501da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 22511da177e4SLinus Torvalds if (on_link) 22521da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 22531da177e4SLinus Torvalds 22544e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 22551da177e4SLinus Torvalds 225640e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 22571da177e4SLinus Torvalds goto out; 22581da177e4SLinus Torvalds 2259d8d1f30bSChangli Gao netevent.old = &rt->dst; 2260d8d1f30bSChangli Gao netevent.new = &nrt->dst; 226171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 netevent.daddr = &msg->dest; 226260592833SYOSHIFUJI Hideaki / 吉藤英明 netevent.neigh = neigh; 22638d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 22648d71740cSTom Tucker 22651da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 22666e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 2267e0a1ad73SThomas Graf ip6_del_rt(rt); 22681da177e4SLinus Torvalds } 22691da177e4SLinus Torvalds 22701da177e4SLinus Torvalds out: 2271e8599ff4SDavid S. Miller neigh_release(neigh); 22726e157b6aSDavid S. Miller } 22736e157b6aSDavid S. Miller 22741da177e4SLinus Torvalds /* 22751da177e4SLinus Torvalds * Misc support functions 22761da177e4SLinus Torvalds */ 22771da177e4SLinus Torvalds 22784b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) 22794b32b5adSMartin KaFai Lau { 22804b32b5adSMartin KaFai Lau BUG_ON(from->dst.from); 22814b32b5adSMartin KaFai Lau 22824b32b5adSMartin KaFai Lau rt->rt6i_flags &= ~RTF_EXPIRES; 22834b32b5adSMartin KaFai Lau dst_hold(&from->dst); 22844b32b5adSMartin KaFai Lau rt->dst.from = &from->dst; 22854b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true); 22864b32b5adSMartin KaFai Lau } 22874b32b5adSMartin KaFai Lau 228883a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort) 22891da177e4SLinus Torvalds { 2290d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 2291d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 229283a09abdSMartin KaFai Lau rt->rt6i_dst = ort->rt6i_dst; 2293d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 22941da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 22951da177e4SLinus Torvalds if (rt->rt6i_idev) 22961da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 2297d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 22984e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 22991716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 23001716a961SGao feng rt6_set_from(rt, ort); 230183a09abdSMartin KaFai Lau rt->rt6i_metric = ort->rt6i_metric; 23021da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 230383a09abdSMartin KaFai Lau rt->rt6i_src = ort->rt6i_src; 23041da177e4SLinus Torvalds #endif 230583a09abdSMartin KaFai Lau rt->rt6i_prefsrc = ort->rt6i_prefsrc; 2306c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 230761adedf3SJiri Benc rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate); 23081da177e4SLinus Torvalds } 23091da177e4SLinus Torvalds 231070ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 2311efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 2312b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2313b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 231470ceb4f5SYOSHIFUJI Hideaki { 231570ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 231670ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 2317c71099acSThomas Graf struct fib6_table *table; 231870ceb4f5SYOSHIFUJI Hideaki 2319efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 232038308473SDavid S. Miller if (!table) 2321c71099acSThomas Graf return NULL; 2322c71099acSThomas Graf 23235744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2324c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0); 232570ceb4f5SYOSHIFUJI Hideaki if (!fn) 232670ceb4f5SYOSHIFUJI Hideaki goto out; 232770ceb4f5SYOSHIFUJI Hideaki 2328d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 2329d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 233070ceb4f5SYOSHIFUJI Hideaki continue; 233170ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 233270ceb4f5SYOSHIFUJI Hideaki continue; 233370ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 233470ceb4f5SYOSHIFUJI Hideaki continue; 2335d8d1f30bSChangli Gao dst_hold(&rt->dst); 233670ceb4f5SYOSHIFUJI Hideaki break; 233770ceb4f5SYOSHIFUJI Hideaki } 233870ceb4f5SYOSHIFUJI Hideaki out: 23395744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 234070ceb4f5SYOSHIFUJI Hideaki return rt; 234170ceb4f5SYOSHIFUJI Hideaki } 234270ceb4f5SYOSHIFUJI Hideaki 2343efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 2344b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2345b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 234695c96174SEric Dumazet unsigned int pref) 234770ceb4f5SYOSHIFUJI Hideaki { 234886872cb5SThomas Graf struct fib6_config cfg = { 2349238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 235086872cb5SThomas Graf .fc_ifindex = ifindex, 235186872cb5SThomas Graf .fc_dst_len = prefixlen, 235286872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 235386872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 235415e47304SEric W. Biederman .fc_nlinfo.portid = 0, 2355efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 2356efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 235786872cb5SThomas Graf }; 235870ceb4f5SYOSHIFUJI Hideaki 2359ca254490SDavid Ahern cfg.fc_table = l3mdev_fib_table_by_index(net, ifindex) ? : RT6_TABLE_INFO; 23604e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 23614e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 236286872cb5SThomas Graf 2363e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 2364e317da96SYOSHIFUJI Hideaki if (!prefixlen) 236586872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 236670ceb4f5SYOSHIFUJI Hideaki 236786872cb5SThomas Graf ip6_route_add(&cfg); 236870ceb4f5SYOSHIFUJI Hideaki 2369efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 237070ceb4f5SYOSHIFUJI Hideaki } 237170ceb4f5SYOSHIFUJI Hideaki #endif 237270ceb4f5SYOSHIFUJI Hideaki 2373b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 23741da177e4SLinus Torvalds { 23751da177e4SLinus Torvalds struct rt6_info *rt; 2376c71099acSThomas Graf struct fib6_table *table; 23771da177e4SLinus Torvalds 2378c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 237938308473SDavid S. Miller if (!table) 2380c71099acSThomas Graf return NULL; 23811da177e4SLinus Torvalds 23825744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2383d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 2384d1918542SDavid S. Miller if (dev == rt->dst.dev && 2385045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 23861da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 23871da177e4SLinus Torvalds break; 23881da177e4SLinus Torvalds } 23891da177e4SLinus Torvalds if (rt) 2390d8d1f30bSChangli Gao dst_hold(&rt->dst); 23915744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 23921da177e4SLinus Torvalds return rt; 23931da177e4SLinus Torvalds } 23941da177e4SLinus Torvalds 2395b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 2396ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 2397ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 23981da177e4SLinus Torvalds { 239986872cb5SThomas Graf struct fib6_config cfg = { 2400ca254490SDavid Ahern .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT, 2401238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 240286872cb5SThomas Graf .fc_ifindex = dev->ifindex, 240386872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 240486872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 240515e47304SEric W. Biederman .fc_nlinfo.portid = 0, 24065578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 2407c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 240886872cb5SThomas Graf }; 24091da177e4SLinus Torvalds 24104e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 24111da177e4SLinus Torvalds 241286872cb5SThomas Graf ip6_route_add(&cfg); 24131da177e4SLinus Torvalds 24141da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 24151da177e4SLinus Torvalds } 24161da177e4SLinus Torvalds 24177b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 24181da177e4SLinus Torvalds { 24191da177e4SLinus Torvalds struct rt6_info *rt; 2420c71099acSThomas Graf struct fib6_table *table; 2421c71099acSThomas Graf 2422c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 24237b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 242438308473SDavid S. Miller if (!table) 2425c71099acSThomas Graf return; 24261da177e4SLinus Torvalds 24271da177e4SLinus Torvalds restart: 2428c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2429d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 24303e8b0ac3SLorenzo Colitti if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 24313e8b0ac3SLorenzo Colitti (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { 2432d8d1f30bSChangli Gao dst_hold(&rt->dst); 2433c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2434e0a1ad73SThomas Graf ip6_del_rt(rt); 24351da177e4SLinus Torvalds goto restart; 24361da177e4SLinus Torvalds } 24371da177e4SLinus Torvalds } 2438c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 24391da177e4SLinus Torvalds } 24401da177e4SLinus Torvalds 24415578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 24425578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 244386872cb5SThomas Graf struct fib6_config *cfg) 244486872cb5SThomas Graf { 244586872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 244686872cb5SThomas Graf 2447ca254490SDavid Ahern cfg->fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ? 2448ca254490SDavid Ahern : RT6_TABLE_MAIN; 244986872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 245086872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 245186872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 245286872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 245386872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 245486872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 245586872cb5SThomas Graf 24565578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 2457f1243c2dSBenjamin Thery 24584e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 24594e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 24604e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 246186872cb5SThomas Graf } 246286872cb5SThomas Graf 24635578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 24641da177e4SLinus Torvalds { 246586872cb5SThomas Graf struct fib6_config cfg; 24661da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 24671da177e4SLinus Torvalds int err; 24681da177e4SLinus Torvalds 24691da177e4SLinus Torvalds switch (cmd) { 24701da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 24711da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 2472af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 24731da177e4SLinus Torvalds return -EPERM; 24741da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 24751da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 24761da177e4SLinus Torvalds if (err) 24771da177e4SLinus Torvalds return -EFAULT; 24781da177e4SLinus Torvalds 24795578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 248086872cb5SThomas Graf 24811da177e4SLinus Torvalds rtnl_lock(); 24821da177e4SLinus Torvalds switch (cmd) { 24831da177e4SLinus Torvalds case SIOCADDRT: 248486872cb5SThomas Graf err = ip6_route_add(&cfg); 24851da177e4SLinus Torvalds break; 24861da177e4SLinus Torvalds case SIOCDELRT: 248786872cb5SThomas Graf err = ip6_route_del(&cfg); 24881da177e4SLinus Torvalds break; 24891da177e4SLinus Torvalds default: 24901da177e4SLinus Torvalds err = -EINVAL; 24911da177e4SLinus Torvalds } 24921da177e4SLinus Torvalds rtnl_unlock(); 24931da177e4SLinus Torvalds 24941da177e4SLinus Torvalds return err; 24953ff50b79SStephen Hemminger } 24961da177e4SLinus Torvalds 24971da177e4SLinus Torvalds return -EINVAL; 24981da177e4SLinus Torvalds } 24991da177e4SLinus Torvalds 25001da177e4SLinus Torvalds /* 25011da177e4SLinus Torvalds * Drop the packet on the floor 25021da177e4SLinus Torvalds */ 25031da177e4SLinus Torvalds 2504d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 25051da177e4SLinus Torvalds { 2506612f09e8SYOSHIFUJI Hideaki int type; 2507adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2508612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2509612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 25100660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 251145bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 25123bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 25133bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2514612f09e8SYOSHIFUJI Hideaki break; 2515612f09e8SYOSHIFUJI Hideaki } 2516612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2517612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 25183bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 25193bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2520612f09e8SYOSHIFUJI Hideaki break; 2521612f09e8SYOSHIFUJI Hideaki } 25223ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 25231da177e4SLinus Torvalds kfree_skb(skb); 25241da177e4SLinus Torvalds return 0; 25251da177e4SLinus Torvalds } 25261da177e4SLinus Torvalds 25279ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 25289ce8ade0SThomas Graf { 2529612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 25309ce8ade0SThomas Graf } 25319ce8ade0SThomas Graf 2532ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb) 25331da177e4SLinus Torvalds { 2534adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2535612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 25361da177e4SLinus Torvalds } 25371da177e4SLinus Torvalds 25389ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 25399ce8ade0SThomas Graf { 2540612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 25419ce8ade0SThomas Graf } 25429ce8ade0SThomas Graf 2543ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb) 25449ce8ade0SThomas Graf { 2545adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2546612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 25479ce8ade0SThomas Graf } 25489ce8ade0SThomas Graf 25491da177e4SLinus Torvalds /* 25501da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 25511da177e4SLinus Torvalds */ 25521da177e4SLinus Torvalds 25531da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 25541da177e4SLinus Torvalds const struct in6_addr *addr, 25558f031519SDavid S. Miller bool anycast) 25561da177e4SLinus Torvalds { 2557ca254490SDavid Ahern u32 tb_id; 2558c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 2559a3300ef4SHannes Frederic Sowa struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 2560ad706862SMartin KaFai Lau DST_NOCOUNT); 2561a3300ef4SHannes Frederic Sowa if (!rt) 25621da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 25631da177e4SLinus Torvalds 25641da177e4SLinus Torvalds in6_dev_hold(idev); 25651da177e4SLinus Torvalds 256611d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2567d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2568d8d1f30bSChangli Gao rt->dst.output = ip6_output; 25691da177e4SLinus Torvalds rt->rt6i_idev = idev; 25701da177e4SLinus Torvalds 25711da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 257258c4fb86SYOSHIFUJI Hideaki if (anycast) 257358c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 257458c4fb86SYOSHIFUJI Hideaki else 25751da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 25761da177e4SLinus Torvalds 2577550bab42SJulian Anastasov rt->rt6i_gateway = *addr; 25784e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 25791da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 2580ca254490SDavid Ahern tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL; 2581ca254490SDavid Ahern rt->rt6i_table = fib6_get_table(net, tb_id); 25828e3d5be7SMartin KaFai Lau rt->dst.flags |= DST_NOCACHE; 25831da177e4SLinus Torvalds 2584d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 25851da177e4SLinus Torvalds 25861da177e4SLinus Torvalds return rt; 25871da177e4SLinus Torvalds } 25881da177e4SLinus Torvalds 2589c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2590c3968a85SDaniel Walter struct rt6_info *rt, 2591b71d1d42SEric Dumazet const struct in6_addr *daddr, 2592c3968a85SDaniel Walter unsigned int prefs, 2593c3968a85SDaniel Walter struct in6_addr *saddr) 2594c3968a85SDaniel Walter { 2595e16e888bSMarkus Stenberg struct inet6_dev *idev = 2596e16e888bSMarkus Stenberg rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL; 2597c3968a85SDaniel Walter int err = 0; 2598e16e888bSMarkus Stenberg if (rt && rt->rt6i_prefsrc.plen) 25994e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2600c3968a85SDaniel Walter else 2601c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2602c3968a85SDaniel Walter daddr, prefs, saddr); 2603c3968a85SDaniel Walter return err; 2604c3968a85SDaniel Walter } 2605c3968a85SDaniel Walter 2606c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2607c3968a85SDaniel Walter struct arg_dev_net_ip { 2608c3968a85SDaniel Walter struct net_device *dev; 2609c3968a85SDaniel Walter struct net *net; 2610c3968a85SDaniel Walter struct in6_addr *addr; 2611c3968a85SDaniel Walter }; 2612c3968a85SDaniel Walter 2613c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2614c3968a85SDaniel Walter { 2615c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2616c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2617c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2618c3968a85SDaniel Walter 2619d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2620c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2621c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2622c3968a85SDaniel Walter /* remove prefsrc entry */ 2623c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2624c3968a85SDaniel Walter } 2625c3968a85SDaniel Walter return 0; 2626c3968a85SDaniel Walter } 2627c3968a85SDaniel Walter 2628c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2629c3968a85SDaniel Walter { 2630c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2631c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2632c3968a85SDaniel Walter .dev = ifp->idev->dev, 2633c3968a85SDaniel Walter .net = net, 2634c3968a85SDaniel Walter .addr = &ifp->addr, 2635c3968a85SDaniel Walter }; 26360c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 2637c3968a85SDaniel Walter } 2638c3968a85SDaniel Walter 2639be7a010dSDuan Jiong #define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) 2640be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 2641be7a010dSDuan Jiong 2642be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */ 2643be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg) 2644be7a010dSDuan Jiong { 2645be7a010dSDuan Jiong struct in6_addr *gateway = (struct in6_addr *)arg; 2646be7a010dSDuan Jiong 2647be7a010dSDuan Jiong if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) || 2648be7a010dSDuan Jiong ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) && 2649be7a010dSDuan Jiong ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { 2650be7a010dSDuan Jiong return -1; 2651be7a010dSDuan Jiong } 2652be7a010dSDuan Jiong return 0; 2653be7a010dSDuan Jiong } 2654be7a010dSDuan Jiong 2655be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 2656be7a010dSDuan Jiong { 2657be7a010dSDuan Jiong fib6_clean_all(net, fib6_clean_tohost, gateway); 2658be7a010dSDuan Jiong } 2659be7a010dSDuan Jiong 26608ed67789SDaniel Lezcano struct arg_dev_net { 26618ed67789SDaniel Lezcano struct net_device *dev; 26628ed67789SDaniel Lezcano struct net *net; 26638ed67789SDaniel Lezcano }; 26648ed67789SDaniel Lezcano 26651da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 26661da177e4SLinus Torvalds { 2667bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2668bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 26698ed67789SDaniel Lezcano 2670d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2671c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 26721da177e4SLinus Torvalds return -1; 2673c159d30cSDavid S. Miller 26741da177e4SLinus Torvalds return 0; 26751da177e4SLinus Torvalds } 26761da177e4SLinus Torvalds 2677f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 26781da177e4SLinus Torvalds { 26798ed67789SDaniel Lezcano struct arg_dev_net adn = { 26808ed67789SDaniel Lezcano .dev = dev, 26818ed67789SDaniel Lezcano .net = net, 26828ed67789SDaniel Lezcano }; 26838ed67789SDaniel Lezcano 26840c3584d5SLi RongQing fib6_clean_all(net, fib6_ifdown, &adn); 26851e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 2686e332bc67SEric W. Biederman if (dev) 26878d0b94afSMartin KaFai Lau rt6_uncached_list_flush_dev(net, dev); 26881da177e4SLinus Torvalds } 26891da177e4SLinus Torvalds 269095c96174SEric Dumazet struct rt6_mtu_change_arg { 26911da177e4SLinus Torvalds struct net_device *dev; 269295c96174SEric Dumazet unsigned int mtu; 26931da177e4SLinus Torvalds }; 26941da177e4SLinus Torvalds 26951da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 26961da177e4SLinus Torvalds { 26971da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 26981da177e4SLinus Torvalds struct inet6_dev *idev; 26991da177e4SLinus Torvalds 27001da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 27011da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 27021da177e4SLinus Torvalds We still use this lock to block changes 27031da177e4SLinus Torvalds caused by addrconf/ndisc. 27041da177e4SLinus Torvalds */ 27051da177e4SLinus Torvalds 27061da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 270738308473SDavid S. Miller if (!idev) 27081da177e4SLinus Torvalds return 0; 27091da177e4SLinus Torvalds 27101da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 27111da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 27121da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 27131da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 27141da177e4SLinus Torvalds */ 27151da177e4SLinus Torvalds /* 27161da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 27171da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 27181da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 27191da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 27201da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 27211da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 27221da177e4SLinus Torvalds PMTU discouvery. 27231da177e4SLinus Torvalds */ 2724d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 27254b32b5adSMartin KaFai Lau !dst_metric_locked(&rt->dst, RTAX_MTU)) { 27264b32b5adSMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) { 27274b32b5adSMartin KaFai Lau /* For RTF_CACHE with rt6i_pmtu == 0 27284b32b5adSMartin KaFai Lau * (i.e. a redirected route), 27294b32b5adSMartin KaFai Lau * the metrics of its rt->dst.from has already 27304b32b5adSMartin KaFai Lau * been updated. 27314b32b5adSMartin KaFai Lau */ 27324b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu) 27334b32b5adSMartin KaFai Lau rt->rt6i_pmtu = arg->mtu; 27344b32b5adSMartin KaFai Lau } else if (dst_mtu(&rt->dst) >= arg->mtu || 2735d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 27364b32b5adSMartin KaFai Lau dst_mtu(&rt->dst) == idev->cnf.mtu6)) { 2737defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2738566cfd8fSSimon Arlott } 27394b32b5adSMartin KaFai Lau } 27401da177e4SLinus Torvalds return 0; 27411da177e4SLinus Torvalds } 27421da177e4SLinus Torvalds 274395c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 27441da177e4SLinus Torvalds { 2745c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2746c71099acSThomas Graf .dev = dev, 2747c71099acSThomas Graf .mtu = mtu, 2748c71099acSThomas Graf }; 27491da177e4SLinus Torvalds 27500c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 27511da177e4SLinus Torvalds } 27521da177e4SLinus Torvalds 2753ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 27545176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 275586872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2756ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 275786872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 275886872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 275951ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2760c78ba6d6SLubomir Rintel [RTA_PREF] = { .type = NLA_U8 }, 276119e42e45SRoopa Prabhu [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, 276219e42e45SRoopa Prabhu [RTA_ENCAP] = { .type = NLA_NESTED }, 276332bc201eSXin Long [RTA_EXPIRES] = { .type = NLA_U32 }, 276486872cb5SThomas Graf }; 276586872cb5SThomas Graf 276686872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 276786872cb5SThomas Graf struct fib6_config *cfg) 27681da177e4SLinus Torvalds { 276986872cb5SThomas Graf struct rtmsg *rtm; 277086872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 2771c78ba6d6SLubomir Rintel unsigned int pref; 277286872cb5SThomas Graf int err; 27731da177e4SLinus Torvalds 277486872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 277586872cb5SThomas Graf if (err < 0) 277686872cb5SThomas Graf goto errout; 27771da177e4SLinus Torvalds 277886872cb5SThomas Graf err = -EINVAL; 277986872cb5SThomas Graf rtm = nlmsg_data(nlh); 278086872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 278186872cb5SThomas Graf 278286872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 278386872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 278486872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 278586872cb5SThomas Graf cfg->fc_flags = RTF_UP; 278686872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2787ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 278886872cb5SThomas Graf 2789ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2790ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2791b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2792b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 279386872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 279486872cb5SThomas Graf 2795ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2796ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2797ab79ad14SMaciej Żenczykowski 27981f56a01fSMartin KaFai Lau if (rtm->rtm_flags & RTM_F_CLONED) 27991f56a01fSMartin KaFai Lau cfg->fc_flags |= RTF_CACHE; 28001f56a01fSMartin KaFai Lau 280115e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 280286872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 28033b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 280486872cb5SThomas Graf 280586872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 280667b61f6cSJiri Benc cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 280786872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 28081da177e4SLinus Torvalds } 280986872cb5SThomas Graf 281086872cb5SThomas Graf if (tb[RTA_DST]) { 281186872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 281286872cb5SThomas Graf 281386872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 281486872cb5SThomas Graf goto errout; 281586872cb5SThomas Graf 281686872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 28171da177e4SLinus Torvalds } 281886872cb5SThomas Graf 281986872cb5SThomas Graf if (tb[RTA_SRC]) { 282086872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 282186872cb5SThomas Graf 282286872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 282386872cb5SThomas Graf goto errout; 282486872cb5SThomas Graf 282586872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 28261da177e4SLinus Torvalds } 282786872cb5SThomas Graf 2828c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 282967b61f6cSJiri Benc cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 2830c3968a85SDaniel Walter 283186872cb5SThomas Graf if (tb[RTA_OIF]) 283286872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 283386872cb5SThomas Graf 283486872cb5SThomas Graf if (tb[RTA_PRIORITY]) 283586872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 283686872cb5SThomas Graf 283786872cb5SThomas Graf if (tb[RTA_METRICS]) { 283886872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 283986872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 28401da177e4SLinus Torvalds } 284186872cb5SThomas Graf 284286872cb5SThomas Graf if (tb[RTA_TABLE]) 284386872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 284486872cb5SThomas Graf 284551ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 284651ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 284751ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 284851ebd318SNicolas Dichtel } 284951ebd318SNicolas Dichtel 2850c78ba6d6SLubomir Rintel if (tb[RTA_PREF]) { 2851c78ba6d6SLubomir Rintel pref = nla_get_u8(tb[RTA_PREF]); 2852c78ba6d6SLubomir Rintel if (pref != ICMPV6_ROUTER_PREF_LOW && 2853c78ba6d6SLubomir Rintel pref != ICMPV6_ROUTER_PREF_HIGH) 2854c78ba6d6SLubomir Rintel pref = ICMPV6_ROUTER_PREF_MEDIUM; 2855c78ba6d6SLubomir Rintel cfg->fc_flags |= RTF_PREF(pref); 2856c78ba6d6SLubomir Rintel } 2857c78ba6d6SLubomir Rintel 285819e42e45SRoopa Prabhu if (tb[RTA_ENCAP]) 285919e42e45SRoopa Prabhu cfg->fc_encap = tb[RTA_ENCAP]; 286019e42e45SRoopa Prabhu 286119e42e45SRoopa Prabhu if (tb[RTA_ENCAP_TYPE]) 286219e42e45SRoopa Prabhu cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]); 286319e42e45SRoopa Prabhu 286432bc201eSXin Long if (tb[RTA_EXPIRES]) { 286532bc201eSXin Long unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ); 286632bc201eSXin Long 286732bc201eSXin Long if (addrconf_finite_timeout(timeout)) { 286832bc201eSXin Long cfg->fc_expires = jiffies_to_clock_t(timeout * HZ); 286932bc201eSXin Long cfg->fc_flags |= RTF_EXPIRES; 287032bc201eSXin Long } 287132bc201eSXin Long } 287232bc201eSXin Long 287386872cb5SThomas Graf err = 0; 287486872cb5SThomas Graf errout: 287586872cb5SThomas Graf return err; 28761da177e4SLinus Torvalds } 28771da177e4SLinus Torvalds 28786b9ea5a6SRoopa Prabhu struct rt6_nh { 28796b9ea5a6SRoopa Prabhu struct rt6_info *rt6_info; 28806b9ea5a6SRoopa Prabhu struct fib6_config r_cfg; 28816b9ea5a6SRoopa Prabhu struct mx6_config mxc; 28826b9ea5a6SRoopa Prabhu struct list_head next; 28836b9ea5a6SRoopa Prabhu }; 28846b9ea5a6SRoopa Prabhu 28856b9ea5a6SRoopa Prabhu static void ip6_print_replace_route_err(struct list_head *rt6_nh_list) 28866b9ea5a6SRoopa Prabhu { 28876b9ea5a6SRoopa Prabhu struct rt6_nh *nh; 28886b9ea5a6SRoopa Prabhu 28896b9ea5a6SRoopa Prabhu list_for_each_entry(nh, rt6_nh_list, next) { 28906b9ea5a6SRoopa Prabhu pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6 nexthop %pI6 ifi %d\n", 28916b9ea5a6SRoopa Prabhu &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway, 28926b9ea5a6SRoopa Prabhu nh->r_cfg.fc_ifindex); 28936b9ea5a6SRoopa Prabhu } 28946b9ea5a6SRoopa Prabhu } 28956b9ea5a6SRoopa Prabhu 28966b9ea5a6SRoopa Prabhu static int ip6_route_info_append(struct list_head *rt6_nh_list, 28976b9ea5a6SRoopa Prabhu struct rt6_info *rt, struct fib6_config *r_cfg) 28986b9ea5a6SRoopa Prabhu { 28996b9ea5a6SRoopa Prabhu struct rt6_nh *nh; 29006b9ea5a6SRoopa Prabhu struct rt6_info *rtnh; 29016b9ea5a6SRoopa Prabhu int err = -EEXIST; 29026b9ea5a6SRoopa Prabhu 29036b9ea5a6SRoopa Prabhu list_for_each_entry(nh, rt6_nh_list, next) { 29046b9ea5a6SRoopa Prabhu /* check if rt6_info already exists */ 29056b9ea5a6SRoopa Prabhu rtnh = nh->rt6_info; 29066b9ea5a6SRoopa Prabhu 29076b9ea5a6SRoopa Prabhu if (rtnh->dst.dev == rt->dst.dev && 29086b9ea5a6SRoopa Prabhu rtnh->rt6i_idev == rt->rt6i_idev && 29096b9ea5a6SRoopa Prabhu ipv6_addr_equal(&rtnh->rt6i_gateway, 29106b9ea5a6SRoopa Prabhu &rt->rt6i_gateway)) 29116b9ea5a6SRoopa Prabhu return err; 29126b9ea5a6SRoopa Prabhu } 29136b9ea5a6SRoopa Prabhu 29146b9ea5a6SRoopa Prabhu nh = kzalloc(sizeof(*nh), GFP_KERNEL); 29156b9ea5a6SRoopa Prabhu if (!nh) 29166b9ea5a6SRoopa Prabhu return -ENOMEM; 29176b9ea5a6SRoopa Prabhu nh->rt6_info = rt; 29186b9ea5a6SRoopa Prabhu err = ip6_convert_metrics(&nh->mxc, r_cfg); 29196b9ea5a6SRoopa Prabhu if (err) { 29206b9ea5a6SRoopa Prabhu kfree(nh); 29216b9ea5a6SRoopa Prabhu return err; 29226b9ea5a6SRoopa Prabhu } 29236b9ea5a6SRoopa Prabhu memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg)); 29246b9ea5a6SRoopa Prabhu list_add_tail(&nh->next, rt6_nh_list); 29256b9ea5a6SRoopa Prabhu 29266b9ea5a6SRoopa Prabhu return 0; 29276b9ea5a6SRoopa Prabhu } 29286b9ea5a6SRoopa Prabhu 29296b9ea5a6SRoopa Prabhu static int ip6_route_multipath_add(struct fib6_config *cfg) 293051ebd318SNicolas Dichtel { 293151ebd318SNicolas Dichtel struct fib6_config r_cfg; 293251ebd318SNicolas Dichtel struct rtnexthop *rtnh; 29336b9ea5a6SRoopa Prabhu struct rt6_info *rt; 29346b9ea5a6SRoopa Prabhu struct rt6_nh *err_nh; 29356b9ea5a6SRoopa Prabhu struct rt6_nh *nh, *nh_safe; 293651ebd318SNicolas Dichtel int remaining; 293751ebd318SNicolas Dichtel int attrlen; 29386b9ea5a6SRoopa Prabhu int err = 1; 29396b9ea5a6SRoopa Prabhu int nhn = 0; 29406b9ea5a6SRoopa Prabhu int replace = (cfg->fc_nlinfo.nlh && 29416b9ea5a6SRoopa Prabhu (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); 29426b9ea5a6SRoopa Prabhu LIST_HEAD(rt6_nh_list); 294351ebd318SNicolas Dichtel 294435f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len; 294551ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 294651ebd318SNicolas Dichtel 29476b9ea5a6SRoopa Prabhu /* Parse a Multipath Entry and build a list (rt6_nh_list) of 29486b9ea5a6SRoopa Prabhu * rt6_info structs per nexthop 29496b9ea5a6SRoopa Prabhu */ 295051ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 295151ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 295251ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 295351ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 295451ebd318SNicolas Dichtel 295551ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 295651ebd318SNicolas Dichtel if (attrlen > 0) { 295751ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 295851ebd318SNicolas Dichtel 295951ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 296051ebd318SNicolas Dichtel if (nla) { 296167b61f6cSJiri Benc r_cfg.fc_gateway = nla_get_in6_addr(nla); 296251ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 296351ebd318SNicolas Dichtel } 296419e42e45SRoopa Prabhu r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 296519e42e45SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 296619e42e45SRoopa Prabhu if (nla) 296719e42e45SRoopa Prabhu r_cfg.fc_encap_type = nla_get_u16(nla); 296851ebd318SNicolas Dichtel } 29696b9ea5a6SRoopa Prabhu 29708c5b83f0SRoopa Prabhu rt = ip6_route_info_create(&r_cfg); 29718c5b83f0SRoopa Prabhu if (IS_ERR(rt)) { 29728c5b83f0SRoopa Prabhu err = PTR_ERR(rt); 29738c5b83f0SRoopa Prabhu rt = NULL; 29746b9ea5a6SRoopa Prabhu goto cleanup; 29758c5b83f0SRoopa Prabhu } 29766b9ea5a6SRoopa Prabhu 29776b9ea5a6SRoopa Prabhu err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg); 297851ebd318SNicolas Dichtel if (err) { 29796b9ea5a6SRoopa Prabhu dst_free(&rt->dst); 29806b9ea5a6SRoopa Prabhu goto cleanup; 298151ebd318SNicolas Dichtel } 29826b9ea5a6SRoopa Prabhu 29836b9ea5a6SRoopa Prabhu rtnh = rtnh_next(rtnh, &remaining); 298451ebd318SNicolas Dichtel } 29856b9ea5a6SRoopa Prabhu 29866b9ea5a6SRoopa Prabhu err_nh = NULL; 29876b9ea5a6SRoopa Prabhu list_for_each_entry(nh, &rt6_nh_list, next) { 29886b9ea5a6SRoopa Prabhu err = __ip6_ins_rt(nh->rt6_info, &cfg->fc_nlinfo, &nh->mxc); 29896b9ea5a6SRoopa Prabhu /* nh->rt6_info is used or freed at this point, reset to NULL*/ 29906b9ea5a6SRoopa Prabhu nh->rt6_info = NULL; 29916b9ea5a6SRoopa Prabhu if (err) { 29926b9ea5a6SRoopa Prabhu if (replace && nhn) 29936b9ea5a6SRoopa Prabhu ip6_print_replace_route_err(&rt6_nh_list); 29946b9ea5a6SRoopa Prabhu err_nh = nh; 29956b9ea5a6SRoopa Prabhu goto add_errout; 29966b9ea5a6SRoopa Prabhu } 29976b9ea5a6SRoopa Prabhu 29981a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 299927596472SMichal Kubeček * these flags after the first nexthop: if there is a collision, 300027596472SMichal Kubeček * we have already failed to add the first nexthop: 300127596472SMichal Kubeček * fib6_add_rt2node() has rejected it; when replacing, old 300227596472SMichal Kubeček * nexthops have been replaced by first new, the rest should 300327596472SMichal Kubeček * be added to it. 30041a72418bSNicolas Dichtel */ 300527596472SMichal Kubeček cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | 300627596472SMichal Kubeček NLM_F_REPLACE); 30076b9ea5a6SRoopa Prabhu nhn++; 30086b9ea5a6SRoopa Prabhu } 30096b9ea5a6SRoopa Prabhu 30106b9ea5a6SRoopa Prabhu goto cleanup; 30116b9ea5a6SRoopa Prabhu 30126b9ea5a6SRoopa Prabhu add_errout: 30136b9ea5a6SRoopa Prabhu /* Delete routes that were already added */ 30146b9ea5a6SRoopa Prabhu list_for_each_entry(nh, &rt6_nh_list, next) { 30156b9ea5a6SRoopa Prabhu if (err_nh == nh) 30166b9ea5a6SRoopa Prabhu break; 30176b9ea5a6SRoopa Prabhu ip6_route_del(&nh->r_cfg); 30186b9ea5a6SRoopa Prabhu } 30196b9ea5a6SRoopa Prabhu 30206b9ea5a6SRoopa Prabhu cleanup: 30216b9ea5a6SRoopa Prabhu list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) { 30226b9ea5a6SRoopa Prabhu if (nh->rt6_info) 30236b9ea5a6SRoopa Prabhu dst_free(&nh->rt6_info->dst); 30246b9ea5a6SRoopa Prabhu kfree(nh->mxc.mx); 30256b9ea5a6SRoopa Prabhu list_del(&nh->next); 30266b9ea5a6SRoopa Prabhu kfree(nh); 30276b9ea5a6SRoopa Prabhu } 30286b9ea5a6SRoopa Prabhu 30296b9ea5a6SRoopa Prabhu return err; 30306b9ea5a6SRoopa Prabhu } 30316b9ea5a6SRoopa Prabhu 30326b9ea5a6SRoopa Prabhu static int ip6_route_multipath_del(struct fib6_config *cfg) 30336b9ea5a6SRoopa Prabhu { 30346b9ea5a6SRoopa Prabhu struct fib6_config r_cfg; 30356b9ea5a6SRoopa Prabhu struct rtnexthop *rtnh; 30366b9ea5a6SRoopa Prabhu int remaining; 30376b9ea5a6SRoopa Prabhu int attrlen; 30386b9ea5a6SRoopa Prabhu int err = 1, last_err = 0; 30396b9ea5a6SRoopa Prabhu 30406b9ea5a6SRoopa Prabhu remaining = cfg->fc_mp_len; 30416b9ea5a6SRoopa Prabhu rtnh = (struct rtnexthop *)cfg->fc_mp; 30426b9ea5a6SRoopa Prabhu 30436b9ea5a6SRoopa Prabhu /* Parse a Multipath Entry */ 30446b9ea5a6SRoopa Prabhu while (rtnh_ok(rtnh, remaining)) { 30456b9ea5a6SRoopa Prabhu memcpy(&r_cfg, cfg, sizeof(*cfg)); 30466b9ea5a6SRoopa Prabhu if (rtnh->rtnh_ifindex) 30476b9ea5a6SRoopa Prabhu r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 30486b9ea5a6SRoopa Prabhu 30496b9ea5a6SRoopa Prabhu attrlen = rtnh_attrlen(rtnh); 30506b9ea5a6SRoopa Prabhu if (attrlen > 0) { 30516b9ea5a6SRoopa Prabhu struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 30526b9ea5a6SRoopa Prabhu 30536b9ea5a6SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_GATEWAY); 30546b9ea5a6SRoopa Prabhu if (nla) { 30556b9ea5a6SRoopa Prabhu nla_memcpy(&r_cfg.fc_gateway, nla, 16); 30566b9ea5a6SRoopa Prabhu r_cfg.fc_flags |= RTF_GATEWAY; 30576b9ea5a6SRoopa Prabhu } 30586b9ea5a6SRoopa Prabhu } 30596b9ea5a6SRoopa Prabhu err = ip6_route_del(&r_cfg); 30606b9ea5a6SRoopa Prabhu if (err) 30616b9ea5a6SRoopa Prabhu last_err = err; 30626b9ea5a6SRoopa Prabhu 306351ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 306451ebd318SNicolas Dichtel } 306551ebd318SNicolas Dichtel 306651ebd318SNicolas Dichtel return last_err; 306751ebd318SNicolas Dichtel } 306851ebd318SNicolas Dichtel 3069661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) 30701da177e4SLinus Torvalds { 307186872cb5SThomas Graf struct fib6_config cfg; 307286872cb5SThomas Graf int err; 30731da177e4SLinus Torvalds 307486872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 307586872cb5SThomas Graf if (err < 0) 307686872cb5SThomas Graf return err; 307786872cb5SThomas Graf 307851ebd318SNicolas Dichtel if (cfg.fc_mp) 30796b9ea5a6SRoopa Prabhu return ip6_route_multipath_del(&cfg); 308051ebd318SNicolas Dichtel else 308186872cb5SThomas Graf return ip6_route_del(&cfg); 30821da177e4SLinus Torvalds } 30831da177e4SLinus Torvalds 3084661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) 30851da177e4SLinus Torvalds { 308686872cb5SThomas Graf struct fib6_config cfg; 308786872cb5SThomas Graf int err; 30881da177e4SLinus Torvalds 308986872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 309086872cb5SThomas Graf if (err < 0) 309186872cb5SThomas Graf return err; 309286872cb5SThomas Graf 309351ebd318SNicolas Dichtel if (cfg.fc_mp) 30946b9ea5a6SRoopa Prabhu return ip6_route_multipath_add(&cfg); 309551ebd318SNicolas Dichtel else 309686872cb5SThomas Graf return ip6_route_add(&cfg); 30971da177e4SLinus Torvalds } 30981da177e4SLinus Torvalds 309919e42e45SRoopa Prabhu static inline size_t rt6_nlmsg_size(struct rt6_info *rt) 3100339bf98fSThomas Graf { 3101339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 3102339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 3103339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 3104339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 3105339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 3106339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 3107339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 3108339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 3109339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 31106a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 3111ea697639SDaniel Borkmann + nla_total_size(sizeof(struct rta_cacheinfo)) 3112c78ba6d6SLubomir Rintel + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 311319e42e45SRoopa Prabhu + nla_total_size(1) /* RTA_PREF */ 311461adedf3SJiri Benc + lwtunnel_get_encap_size(rt->dst.lwtstate); 3115339bf98fSThomas Graf } 3116339bf98fSThomas Graf 3117191cd582SBrian Haley static int rt6_fill_node(struct net *net, 3118191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 31190d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 312015e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 31217bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 31221da177e4SLinus Torvalds { 31234b32b5adSMartin KaFai Lau u32 metrics[RTAX_MAX]; 31241da177e4SLinus Torvalds struct rtmsg *rtm; 31251da177e4SLinus Torvalds struct nlmsghdr *nlh; 3126e3703b3dSThomas Graf long expires; 31279e762a4aSPatrick McHardy u32 table; 31281da177e4SLinus Torvalds 31291da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 31301da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 31311da177e4SLinus Torvalds /* success since this is not a prefix route */ 31321da177e4SLinus Torvalds return 1; 31331da177e4SLinus Torvalds } 31341da177e4SLinus Torvalds } 31351da177e4SLinus Torvalds 313615e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 313738308473SDavid S. Miller if (!nlh) 313826932566SPatrick McHardy return -EMSGSIZE; 31392d7202bfSThomas Graf 31402d7202bfSThomas Graf rtm = nlmsg_data(nlh); 31411da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 31421da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 31431da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 31441da177e4SLinus Torvalds rtm->rtm_tos = 0; 3145c71099acSThomas Graf if (rt->rt6i_table) 31469e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 3147c71099acSThomas Graf else 31489e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 31499e762a4aSPatrick McHardy rtm->rtm_table = table; 3150c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 3151c78679e8SDavid S. Miller goto nla_put_failure; 3152ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 3153ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 3154ef2c7d7bSNicolas Dichtel case -EINVAL: 3155ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 3156ef2c7d7bSNicolas Dichtel break; 3157ef2c7d7bSNicolas Dichtel case -EACCES: 3158ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 3159ef2c7d7bSNicolas Dichtel break; 3160b4949ab2SNicolas Dichtel case -EAGAIN: 3161b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 3162b4949ab2SNicolas Dichtel break; 3163ef2c7d7bSNicolas Dichtel default: 31641da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 3165ef2c7d7bSNicolas Dichtel break; 3166ef2c7d7bSNicolas Dichtel } 3167ef2c7d7bSNicolas Dichtel } 3168ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 3169ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 3170d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 31711da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 31721da177e4SLinus Torvalds else 31731da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 31741da177e4SLinus Torvalds rtm->rtm_flags = 0; 317535103d11SAndy Gospodarek if (!netif_carrier_ok(rt->dst.dev)) { 3176cea45e20SAndy Gospodarek rtm->rtm_flags |= RTNH_F_LINKDOWN; 317735103d11SAndy Gospodarek if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown) 317835103d11SAndy Gospodarek rtm->rtm_flags |= RTNH_F_DEAD; 317935103d11SAndy Gospodarek } 31801da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 31811da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 31821da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 31831da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 3184f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 3185f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 31861da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 3187f0396f60SDenis Ovsienko else 3188f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 3189f0396f60SDenis Ovsienko } 31901da177e4SLinus Torvalds 31911da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 31921da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 31931da177e4SLinus Torvalds 31941da177e4SLinus Torvalds if (dst) { 3195930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, dst)) 3196c78679e8SDavid S. Miller goto nla_put_failure; 31971da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 31981da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 3199930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr)) 3200c78679e8SDavid S. Miller goto nla_put_failure; 32011da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 32021da177e4SLinus Torvalds if (src) { 3203930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_SRC, src)) 3204c78679e8SDavid S. Miller goto nla_put_failure; 32051da177e4SLinus Torvalds rtm->rtm_src_len = 128; 3206c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 3207930345eaSJiri Benc nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr)) 3208c78679e8SDavid S. Miller goto nla_put_failure; 32091da177e4SLinus Torvalds #endif 32107bc570c8SYOSHIFUJI Hideaki if (iif) { 32117bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 32127bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 32138229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 32147bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 32157bc570c8SYOSHIFUJI Hideaki if (!nowait) { 32167bc570c8SYOSHIFUJI Hideaki if (err == 0) 32177bc570c8SYOSHIFUJI Hideaki return 0; 32187bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 32197bc570c8SYOSHIFUJI Hideaki } else { 32207bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 32217bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 32227bc570c8SYOSHIFUJI Hideaki } 32237bc570c8SYOSHIFUJI Hideaki } 32247bc570c8SYOSHIFUJI Hideaki } else 32257bc570c8SYOSHIFUJI Hideaki #endif 3226c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 3227c78679e8SDavid S. Miller goto nla_put_failure; 32287bc570c8SYOSHIFUJI Hideaki } else if (dst) { 32291da177e4SLinus Torvalds struct in6_addr saddr_buf; 3230c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 3231930345eaSJiri Benc nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 3232c78679e8SDavid S. Miller goto nla_put_failure; 3233c3968a85SDaniel Walter } 3234c3968a85SDaniel Walter 3235c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 3236c3968a85SDaniel Walter struct in6_addr saddr_buf; 32374e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 3238930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 3239c78679e8SDavid S. Miller goto nla_put_failure; 32401da177e4SLinus Torvalds } 32412d7202bfSThomas Graf 32424b32b5adSMartin KaFai Lau memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); 32434b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu) 32444b32b5adSMartin KaFai Lau metrics[RTAX_MTU - 1] = rt->rt6i_pmtu; 32454b32b5adSMartin KaFai Lau if (rtnetlink_put_metrics(skb, metrics) < 0) 32462d7202bfSThomas Graf goto nla_put_failure; 32472d7202bfSThomas Graf 3248dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_GATEWAY) { 3249930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0) 325094f826b8SEric Dumazet goto nla_put_failure; 325194f826b8SEric Dumazet } 32522d7202bfSThomas Graf 3253c78679e8SDavid S. Miller if (rt->dst.dev && 3254c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 3255c78679e8SDavid S. Miller goto nla_put_failure; 3256c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 3257c78679e8SDavid S. Miller goto nla_put_failure; 32588253947eSLi Wei 32598253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 326069cdf8f9SYOSHIFUJI Hideaki 326187a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 3262e3703b3dSThomas Graf goto nla_put_failure; 32631da177e4SLinus Torvalds 3264c78ba6d6SLubomir Rintel if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) 3265c78ba6d6SLubomir Rintel goto nla_put_failure; 3266c78ba6d6SLubomir Rintel 326761adedf3SJiri Benc lwtunnel_fill_encap(skb, rt->dst.lwtstate); 326819e42e45SRoopa Prabhu 3269053c095aSJohannes Berg nlmsg_end(skb, nlh); 3270053c095aSJohannes Berg return 0; 32712d7202bfSThomas Graf 32722d7202bfSThomas Graf nla_put_failure: 327326932566SPatrick McHardy nlmsg_cancel(skb, nlh); 327426932566SPatrick McHardy return -EMSGSIZE; 32751da177e4SLinus Torvalds } 32761da177e4SLinus Torvalds 32771b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 32781da177e4SLinus Torvalds { 32791da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 32801da177e4SLinus Torvalds int prefix; 32811da177e4SLinus Torvalds 32822d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 32832d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 32841da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 32851da177e4SLinus Torvalds } else 32861da177e4SLinus Torvalds prefix = 0; 32871da177e4SLinus Torvalds 3288191cd582SBrian Haley return rt6_fill_node(arg->net, 3289191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 329015e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 32917bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 32921da177e4SLinus Torvalds } 32931da177e4SLinus Torvalds 3294661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) 32951da177e4SLinus Torvalds { 32963b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 3297ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 32981da177e4SLinus Torvalds struct rt6_info *rt; 3299ab364a6fSThomas Graf struct sk_buff *skb; 3300ab364a6fSThomas Graf struct rtmsg *rtm; 33014c9483b2SDavid S. Miller struct flowi6 fl6; 330272331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 3303ab364a6fSThomas Graf 3304ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 3305ab364a6fSThomas Graf if (err < 0) 3306ab364a6fSThomas Graf goto errout; 3307ab364a6fSThomas Graf 3308ab364a6fSThomas Graf err = -EINVAL; 33094c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 331038b7097bSHannes Frederic Sowa rtm = nlmsg_data(nlh); 331138b7097bSHannes Frederic Sowa fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0); 3312ab364a6fSThomas Graf 3313ab364a6fSThomas Graf if (tb[RTA_SRC]) { 3314ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 3315ab364a6fSThomas Graf goto errout; 3316ab364a6fSThomas Graf 33174e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 3318ab364a6fSThomas Graf } 3319ab364a6fSThomas Graf 3320ab364a6fSThomas Graf if (tb[RTA_DST]) { 3321ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 3322ab364a6fSThomas Graf goto errout; 3323ab364a6fSThomas Graf 33244e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 3325ab364a6fSThomas Graf } 3326ab364a6fSThomas Graf 3327ab364a6fSThomas Graf if (tb[RTA_IIF]) 3328ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 3329ab364a6fSThomas Graf 3330ab364a6fSThomas Graf if (tb[RTA_OIF]) 333172331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 3332ab364a6fSThomas Graf 33332e47b291SLorenzo Colitti if (tb[RTA_MARK]) 33342e47b291SLorenzo Colitti fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 33352e47b291SLorenzo Colitti 3336ab364a6fSThomas Graf if (iif) { 3337ab364a6fSThomas Graf struct net_device *dev; 333872331bc0SShmulik Ladkani int flags = 0; 333972331bc0SShmulik Ladkani 33405578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 3341ab364a6fSThomas Graf if (!dev) { 3342ab364a6fSThomas Graf err = -ENODEV; 3343ab364a6fSThomas Graf goto errout; 3344ab364a6fSThomas Graf } 334572331bc0SShmulik Ladkani 334672331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 334772331bc0SShmulik Ladkani 334872331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 334972331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 335072331bc0SShmulik Ladkani 335172331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 335272331bc0SShmulik Ladkani flags); 335372331bc0SShmulik Ladkani } else { 335472331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 335572331bc0SShmulik Ladkani 3356ca254490SDavid Ahern if (netif_index_is_l3_master(net, oif)) { 3357ca254490SDavid Ahern fl6.flowi6_flags = FLOWI_FLAG_L3MDEV_SRC | 3358ca254490SDavid Ahern FLOWI_FLAG_SKIP_NH_OIF; 3359ca254490SDavid Ahern } 3360ca254490SDavid Ahern 336172331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 3362ab364a6fSThomas Graf } 33631da177e4SLinus Torvalds 33641da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 336538308473SDavid S. Miller if (!skb) { 336694e187c0SAmerigo Wang ip6_rt_put(rt); 3367ab364a6fSThomas Graf err = -ENOBUFS; 3368ab364a6fSThomas Graf goto errout; 3369ab364a6fSThomas Graf } 33701da177e4SLinus Torvalds 33711da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 33721da177e4SLinus Torvalds through good chunk of routing engine. 33731da177e4SLinus Torvalds */ 3374459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 33751da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 33761da177e4SLinus Torvalds 3377d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 33781da177e4SLinus Torvalds 33794c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 338015e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 33817bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 33821da177e4SLinus Torvalds if (err < 0) { 3383ab364a6fSThomas Graf kfree_skb(skb); 3384ab364a6fSThomas Graf goto errout; 33851da177e4SLinus Torvalds } 33861da177e4SLinus Torvalds 338715e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 3388ab364a6fSThomas Graf errout: 33891da177e4SLinus Torvalds return err; 33901da177e4SLinus Torvalds } 33911da177e4SLinus Torvalds 339237a1d361SRoopa Prabhu void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info, 339337a1d361SRoopa Prabhu unsigned int nlm_flags) 33941da177e4SLinus Torvalds { 33951da177e4SLinus Torvalds struct sk_buff *skb; 33965578689aSDaniel Lezcano struct net *net = info->nl_net; 3397528c4cebSDenis V. Lunev u32 seq; 3398528c4cebSDenis V. Lunev int err; 33990d51aa80SJamal Hadi Salim 3400528c4cebSDenis V. Lunev err = -ENOBUFS; 340138308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 340286872cb5SThomas Graf 340319e42e45SRoopa Prabhu skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 340438308473SDavid S. Miller if (!skb) 340521713ebcSThomas Graf goto errout; 34061da177e4SLinus Torvalds 3407191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 340837a1d361SRoopa Prabhu event, info->portid, seq, 0, 0, nlm_flags); 340926932566SPatrick McHardy if (err < 0) { 341026932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 341126932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 341226932566SPatrick McHardy kfree_skb(skb); 341326932566SPatrick McHardy goto errout; 341426932566SPatrick McHardy } 341515e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 34165578689aSDaniel Lezcano info->nlh, gfp_any()); 34171ce85fe4SPablo Neira Ayuso return; 341821713ebcSThomas Graf errout: 341921713ebcSThomas Graf if (err < 0) 34205578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 34211da177e4SLinus Torvalds } 34221da177e4SLinus Torvalds 34238ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 3424351638e7SJiri Pirko unsigned long event, void *ptr) 34258ed67789SDaniel Lezcano { 3426351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 3427c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 34288ed67789SDaniel Lezcano 34298ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 3430d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 34318ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 34328ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3433d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 34348ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 3435d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 34368ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 34378ed67789SDaniel Lezcano #endif 34388ed67789SDaniel Lezcano } 34398ed67789SDaniel Lezcano 34408ed67789SDaniel Lezcano return NOTIFY_OK; 34418ed67789SDaniel Lezcano } 34428ed67789SDaniel Lezcano 34431da177e4SLinus Torvalds /* 34441da177e4SLinus Torvalds * /proc 34451da177e4SLinus Torvalds */ 34461da177e4SLinus Torvalds 34471da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 34481da177e4SLinus Torvalds 344933120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 345033120b30SAlexey Dobriyan .owner = THIS_MODULE, 345133120b30SAlexey Dobriyan .open = ipv6_route_open, 345233120b30SAlexey Dobriyan .read = seq_read, 345333120b30SAlexey Dobriyan .llseek = seq_lseek, 34548d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 345533120b30SAlexey Dobriyan }; 345633120b30SAlexey Dobriyan 34571da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 34581da177e4SLinus Torvalds { 345969ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 34601da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 346169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 346269ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 346369ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 346469ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 346569ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 3466fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 346769ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 34681da177e4SLinus Torvalds 34691da177e4SLinus Torvalds return 0; 34701da177e4SLinus Torvalds } 34711da177e4SLinus Torvalds 34721da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 34731da177e4SLinus Torvalds { 3474de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 347569ddb805SDaniel Lezcano } 347669ddb805SDaniel Lezcano 34779a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 34781da177e4SLinus Torvalds .owner = THIS_MODULE, 34791da177e4SLinus Torvalds .open = rt6_stats_seq_open, 34801da177e4SLinus Torvalds .read = seq_read, 34811da177e4SLinus Torvalds .llseek = seq_lseek, 3482b6fcbdb4SPavel Emelyanov .release = single_release_net, 34831da177e4SLinus Torvalds }; 34841da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 34851da177e4SLinus Torvalds 34861da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 34871da177e4SLinus Torvalds 34881da177e4SLinus Torvalds static 3489fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 34901da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 34911da177e4SLinus Torvalds { 3492c486da34SLucian Adrian Grijincu struct net *net; 3493c486da34SLucian Adrian Grijincu int delay; 3494c486da34SLucian Adrian Grijincu if (!write) 3495c486da34SLucian Adrian Grijincu return -EINVAL; 3496c486da34SLucian Adrian Grijincu 3497c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 3498c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 34998d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 35002ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 35011da177e4SLinus Torvalds return 0; 35021da177e4SLinus Torvalds } 35031da177e4SLinus Torvalds 3504fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 35051da177e4SLinus Torvalds { 35061da177e4SLinus Torvalds .procname = "flush", 35074990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 35081da177e4SLinus Torvalds .maxlen = sizeof(int), 350989c8b3a1SDave Jones .mode = 0200, 35106d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 35111da177e4SLinus Torvalds }, 35121da177e4SLinus Torvalds { 35131da177e4SLinus Torvalds .procname = "gc_thresh", 35149a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 35151da177e4SLinus Torvalds .maxlen = sizeof(int), 35161da177e4SLinus Torvalds .mode = 0644, 35176d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 35181da177e4SLinus Torvalds }, 35191da177e4SLinus Torvalds { 35201da177e4SLinus Torvalds .procname = "max_size", 35214990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 35221da177e4SLinus Torvalds .maxlen = sizeof(int), 35231da177e4SLinus Torvalds .mode = 0644, 35246d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 35251da177e4SLinus Torvalds }, 35261da177e4SLinus Torvalds { 35271da177e4SLinus Torvalds .procname = "gc_min_interval", 35284990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 35291da177e4SLinus Torvalds .maxlen = sizeof(int), 35301da177e4SLinus Torvalds .mode = 0644, 35316d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 35321da177e4SLinus Torvalds }, 35331da177e4SLinus Torvalds { 35341da177e4SLinus Torvalds .procname = "gc_timeout", 35354990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 35361da177e4SLinus Torvalds .maxlen = sizeof(int), 35371da177e4SLinus Torvalds .mode = 0644, 35386d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 35391da177e4SLinus Torvalds }, 35401da177e4SLinus Torvalds { 35411da177e4SLinus Torvalds .procname = "gc_interval", 35424990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 35431da177e4SLinus Torvalds .maxlen = sizeof(int), 35441da177e4SLinus Torvalds .mode = 0644, 35456d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 35461da177e4SLinus Torvalds }, 35471da177e4SLinus Torvalds { 35481da177e4SLinus Torvalds .procname = "gc_elasticity", 35494990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 35501da177e4SLinus Torvalds .maxlen = sizeof(int), 35511da177e4SLinus Torvalds .mode = 0644, 3552f3d3f616SMin Zhang .proc_handler = proc_dointvec, 35531da177e4SLinus Torvalds }, 35541da177e4SLinus Torvalds { 35551da177e4SLinus Torvalds .procname = "mtu_expires", 35564990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 35571da177e4SLinus Torvalds .maxlen = sizeof(int), 35581da177e4SLinus Torvalds .mode = 0644, 35596d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 35601da177e4SLinus Torvalds }, 35611da177e4SLinus Torvalds { 35621da177e4SLinus Torvalds .procname = "min_adv_mss", 35634990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 35641da177e4SLinus Torvalds .maxlen = sizeof(int), 35651da177e4SLinus Torvalds .mode = 0644, 3566f3d3f616SMin Zhang .proc_handler = proc_dointvec, 35671da177e4SLinus Torvalds }, 35681da177e4SLinus Torvalds { 35691da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 35704990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 35711da177e4SLinus Torvalds .maxlen = sizeof(int), 35721da177e4SLinus Torvalds .mode = 0644, 35736d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 35741da177e4SLinus Torvalds }, 3575f8572d8fSEric W. Biederman { } 35761da177e4SLinus Torvalds }; 35771da177e4SLinus Torvalds 35782c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 3579760f2d01SDaniel Lezcano { 3580760f2d01SDaniel Lezcano struct ctl_table *table; 3581760f2d01SDaniel Lezcano 3582760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 3583760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 3584760f2d01SDaniel Lezcano GFP_KERNEL); 35855ee09105SYOSHIFUJI Hideaki 35865ee09105SYOSHIFUJI Hideaki if (table) { 35875ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 3588c486da34SLucian Adrian Grijincu table[0].extra1 = net; 358986393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 35905ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 35915ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 35925ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 35935ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 35945ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 35955ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 35965ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 35979c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 3598464dc801SEric W. Biederman 3599464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 3600464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 3601464dc801SEric W. Biederman table[0].procname = NULL; 36025ee09105SYOSHIFUJI Hideaki } 36035ee09105SYOSHIFUJI Hideaki 3604760f2d01SDaniel Lezcano return table; 3605760f2d01SDaniel Lezcano } 36061da177e4SLinus Torvalds #endif 36071da177e4SLinus Torvalds 36082c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 3609cdb18761SDaniel Lezcano { 3610633d424bSPavel Emelyanov int ret = -ENOMEM; 36118ed67789SDaniel Lezcano 361286393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 361386393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 3614f2fc6a54SBenjamin Thery 3615fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 3616fc66f95cSEric Dumazet goto out_ip6_dst_ops; 3617fc66f95cSEric Dumazet 36188ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 36198ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 36208ed67789SDaniel Lezcano GFP_KERNEL); 36218ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 3622fc66f95cSEric Dumazet goto out_ip6_dst_entries; 3623d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 36248ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 3625d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 362662fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 362762fa8a84SDavid S. Miller ip6_template_metrics, true); 36288ed67789SDaniel Lezcano 36298ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 36308ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 36318ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 36328ed67789SDaniel Lezcano GFP_KERNEL); 363368fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 363468fffc67SPeter Zijlstra goto out_ip6_null_entry; 3635d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 36368ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 3637d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 363862fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 363962fa8a84SDavid S. Miller ip6_template_metrics, true); 36408ed67789SDaniel Lezcano 36418ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 36428ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 36438ed67789SDaniel Lezcano GFP_KERNEL); 364468fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 364568fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 3646d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 36478ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 3648d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 364962fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 365062fa8a84SDavid S. Miller ip6_template_metrics, true); 36518ed67789SDaniel Lezcano #endif 36528ed67789SDaniel Lezcano 3653b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 3654b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 3655b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 3656b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 3657b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 3658b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 3659b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 3660b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 3661b339a47cSPeter Zijlstra 36626891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 36636891a346SBenjamin Thery 36648ed67789SDaniel Lezcano ret = 0; 36658ed67789SDaniel Lezcano out: 36668ed67789SDaniel Lezcano return ret; 3667f2fc6a54SBenjamin Thery 366868fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 366968fffc67SPeter Zijlstra out_ip6_prohibit_entry: 367068fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 367168fffc67SPeter Zijlstra out_ip6_null_entry: 367268fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 367368fffc67SPeter Zijlstra #endif 3674fc66f95cSEric Dumazet out_ip6_dst_entries: 3675fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3676f2fc6a54SBenjamin Thery out_ip6_dst_ops: 3677f2fc6a54SBenjamin Thery goto out; 3678cdb18761SDaniel Lezcano } 3679cdb18761SDaniel Lezcano 36802c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 3681cdb18761SDaniel Lezcano { 36828ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 36838ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 36848ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 36858ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 36868ed67789SDaniel Lezcano #endif 368741bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3688cdb18761SDaniel Lezcano } 3689cdb18761SDaniel Lezcano 3690d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 3691d189634eSThomas Graf { 3692d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3693d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 3694d4beaa66SGao feng proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops); 3695d189634eSThomas Graf #endif 3696d189634eSThomas Graf return 0; 3697d189634eSThomas Graf } 3698d189634eSThomas Graf 3699d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 3700d189634eSThomas Graf { 3701d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3702ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 3703ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 3704d189634eSThomas Graf #endif 3705d189634eSThomas Graf } 3706d189634eSThomas Graf 3707cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 3708cdb18761SDaniel Lezcano .init = ip6_route_net_init, 3709cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 3710cdb18761SDaniel Lezcano }; 3711cdb18761SDaniel Lezcano 3712c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 3713c3426b47SDavid S. Miller { 3714c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 3715c3426b47SDavid S. Miller 3716c3426b47SDavid S. Miller if (!bp) 3717c3426b47SDavid S. Miller return -ENOMEM; 3718c3426b47SDavid S. Miller inet_peer_base_init(bp); 3719c3426b47SDavid S. Miller net->ipv6.peers = bp; 3720c3426b47SDavid S. Miller return 0; 3721c3426b47SDavid S. Miller } 3722c3426b47SDavid S. Miller 3723c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 3724c3426b47SDavid S. Miller { 3725c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 3726c3426b47SDavid S. Miller 3727c3426b47SDavid S. Miller net->ipv6.peers = NULL; 372856a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3729c3426b47SDavid S. Miller kfree(bp); 3730c3426b47SDavid S. Miller } 3731c3426b47SDavid S. Miller 37322b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3733c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3734c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3735c3426b47SDavid S. Miller }; 3736c3426b47SDavid S. Miller 3737d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3738d189634eSThomas Graf .init = ip6_route_net_init_late, 3739d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3740d189634eSThomas Graf }; 3741d189634eSThomas Graf 37428ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 37438ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 37448ed67789SDaniel Lezcano .priority = 0, 37458ed67789SDaniel Lezcano }; 37468ed67789SDaniel Lezcano 3747433d49c3SDaniel Lezcano int __init ip6_route_init(void) 37481da177e4SLinus Torvalds { 3749433d49c3SDaniel Lezcano int ret; 37508d0b94afSMartin KaFai Lau int cpu; 3751433d49c3SDaniel Lezcano 37529a7ec3a9SDaniel Lezcano ret = -ENOMEM; 37539a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 37549a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 37559a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 37569a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3757c19a28e1SFernando Carrijo goto out; 375814e50e57SDavid S. Miller 3759fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 37608ed67789SDaniel Lezcano if (ret) 3761bdb3289fSDaniel Lezcano goto out_kmem_cache; 3762bdb3289fSDaniel Lezcano 3763c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3764c3426b47SDavid S. Miller if (ret) 3765e8803b6cSDavid S. Miller goto out_dst_entries; 37662a0c451aSThomas Graf 37677e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 37687e52b33bSDavid S. Miller if (ret) 37697e52b33bSDavid S. Miller goto out_register_inetpeer; 3770c3426b47SDavid S. Miller 37715dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 37725dc121e9SArnaud Ebalard 37738ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 37748ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 37758ed67789SDaniel Lezcano * manually for init_net */ 3776d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 37778ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3778bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3779d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 37808ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3781d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 37828ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3783bdb3289fSDaniel Lezcano #endif 3784e8803b6cSDavid S. Miller ret = fib6_init(); 3785433d49c3SDaniel Lezcano if (ret) 37868ed67789SDaniel Lezcano goto out_register_subsys; 3787433d49c3SDaniel Lezcano 3788433d49c3SDaniel Lezcano ret = xfrm6_init(); 3789433d49c3SDaniel Lezcano if (ret) 3790e8803b6cSDavid S. Miller goto out_fib6_init; 3791c35b7e72SDaniel Lezcano 3792433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3793433d49c3SDaniel Lezcano if (ret) 3794433d49c3SDaniel Lezcano goto xfrm6_init; 37957e5449c2SDaniel Lezcano 3796d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3797d189634eSThomas Graf if (ret) 3798d189634eSThomas Graf goto fib6_rules_init; 3799d189634eSThomas Graf 3800433d49c3SDaniel Lezcano ret = -ENOBUFS; 3801c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3802c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3803c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3804d189634eSThomas Graf goto out_register_late_subsys; 3805433d49c3SDaniel Lezcano 38068ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3807cdb18761SDaniel Lezcano if (ret) 3808d189634eSThomas Graf goto out_register_late_subsys; 38098ed67789SDaniel Lezcano 38108d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 38118d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 38128d0b94afSMartin KaFai Lau 38138d0b94afSMartin KaFai Lau INIT_LIST_HEAD(&ul->head); 38148d0b94afSMartin KaFai Lau spin_lock_init(&ul->lock); 38158d0b94afSMartin KaFai Lau } 38168d0b94afSMartin KaFai Lau 3817433d49c3SDaniel Lezcano out: 3818433d49c3SDaniel Lezcano return ret; 3819433d49c3SDaniel Lezcano 3820d189634eSThomas Graf out_register_late_subsys: 3821d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3822433d49c3SDaniel Lezcano fib6_rules_init: 3823433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3824433d49c3SDaniel Lezcano xfrm6_init: 3825433d49c3SDaniel Lezcano xfrm6_fini(); 38262a0c451aSThomas Graf out_fib6_init: 38272a0c451aSThomas Graf fib6_gc_cleanup(); 38288ed67789SDaniel Lezcano out_register_subsys: 38298ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 38307e52b33bSDavid S. Miller out_register_inetpeer: 38317e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3832fc66f95cSEric Dumazet out_dst_entries: 3833fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3834433d49c3SDaniel Lezcano out_kmem_cache: 3835f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3836433d49c3SDaniel Lezcano goto out; 38371da177e4SLinus Torvalds } 38381da177e4SLinus Torvalds 38391da177e4SLinus Torvalds void ip6_route_cleanup(void) 38401da177e4SLinus Torvalds { 38418ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3842d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3843101367c2SThomas Graf fib6_rules_cleanup(); 38441da177e4SLinus Torvalds xfrm6_fini(); 38451da177e4SLinus Torvalds fib6_gc_cleanup(); 3846c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 38478ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 384841bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3849f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 38501da177e4SLinus Torvalds } 3851