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> 571da177e4SLinus Torvalds #include <net/xfrm.h> 588d71740cSTom Tucker #include <net/netevent.h> 5921713ebcSThomas Graf #include <net/netlink.h> 6051ebd318SNicolas Dichtel #include <net/nexthop.h> 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds #include <asm/uaccess.h> 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 651da177e4SLinus Torvalds #include <linux/sysctl.h> 661da177e4SLinus Torvalds #endif 671da177e4SLinus Torvalds 68afc154e9SHannes Frederic Sowa enum rt6_nud_state { 697e980569SJiri Benc RT6_NUD_FAIL_HARD = -3, 707e980569SJiri Benc RT6_NUD_FAIL_PROBE = -2, 717e980569SJiri Benc RT6_NUD_FAIL_DO_RR = -1, 72afc154e9SHannes Frederic Sowa RT6_NUD_SUCCEED = 1 73afc154e9SHannes Frederic Sowa }; 74afc154e9SHannes Frederic Sowa 751716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 7621efcfa0SEric Dumazet const struct in6_addr *dest); 771da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 780dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst); 79ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst); 801da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *); 811da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *); 821da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *, 831da177e4SLinus Torvalds struct net_device *dev, int how); 84569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops); 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds static int ip6_pkt_discard(struct sk_buff *skb); 87aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb); 887150aedeSKamala R static int ip6_pkt_prohibit(struct sk_buff *skb); 89aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb); 901da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb); 916700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 926700c270SDavid S. Miller struct sk_buff *skb, u32 mtu); 936700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, 946700c270SDavid S. Miller struct sk_buff *skb); 954b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt); 9652bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict); 971da177e4SLinus Torvalds 9870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 99efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 100b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 101b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 10295c96174SEric Dumazet unsigned int pref); 103efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 104b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 105b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex); 10670ceb4f5SYOSHIFUJI Hideaki #endif 10770ceb4f5SYOSHIFUJI Hideaki 108*8d0b94afSMartin KaFai Lau struct uncached_list { 109*8d0b94afSMartin KaFai Lau spinlock_t lock; 110*8d0b94afSMartin KaFai Lau struct list_head head; 111*8d0b94afSMartin KaFai Lau }; 112*8d0b94afSMartin KaFai Lau 113*8d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list); 114*8d0b94afSMartin KaFai Lau 115*8d0b94afSMartin KaFai Lau static void rt6_uncached_list_add(struct rt6_info *rt) 116*8d0b94afSMartin KaFai Lau { 117*8d0b94afSMartin KaFai Lau struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list); 118*8d0b94afSMartin KaFai Lau 119*8d0b94afSMartin KaFai Lau rt->dst.flags |= DST_NOCACHE; 120*8d0b94afSMartin KaFai Lau rt->rt6i_uncached_list = ul; 121*8d0b94afSMartin KaFai Lau 122*8d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 123*8d0b94afSMartin KaFai Lau list_add_tail(&rt->rt6i_uncached, &ul->head); 124*8d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 125*8d0b94afSMartin KaFai Lau } 126*8d0b94afSMartin KaFai Lau 127*8d0b94afSMartin KaFai Lau static void rt6_uncached_list_del(struct rt6_info *rt) 128*8d0b94afSMartin KaFai Lau { 129*8d0b94afSMartin KaFai Lau if (!list_empty(&rt->rt6i_uncached)) { 130*8d0b94afSMartin KaFai Lau struct uncached_list *ul = rt->rt6i_uncached_list; 131*8d0b94afSMartin KaFai Lau 132*8d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 133*8d0b94afSMartin KaFai Lau list_del(&rt->rt6i_uncached); 134*8d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 135*8d0b94afSMartin KaFai Lau } 136*8d0b94afSMartin KaFai Lau } 137*8d0b94afSMartin KaFai Lau 138*8d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) 139*8d0b94afSMartin KaFai Lau { 140*8d0b94afSMartin KaFai Lau struct net_device *loopback_dev = net->loopback_dev; 141*8d0b94afSMartin KaFai Lau int cpu; 142*8d0b94afSMartin KaFai Lau 143*8d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 144*8d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 145*8d0b94afSMartin KaFai Lau struct rt6_info *rt; 146*8d0b94afSMartin KaFai Lau 147*8d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 148*8d0b94afSMartin KaFai Lau list_for_each_entry(rt, &ul->head, rt6i_uncached) { 149*8d0b94afSMartin KaFai Lau struct inet6_dev *rt_idev = rt->rt6i_idev; 150*8d0b94afSMartin KaFai Lau struct net_device *rt_dev = rt->dst.dev; 151*8d0b94afSMartin KaFai Lau 152*8d0b94afSMartin KaFai Lau if (rt_idev && (rt_idev->dev == dev || !dev) && 153*8d0b94afSMartin KaFai Lau rt_idev->dev != loopback_dev) { 154*8d0b94afSMartin KaFai Lau rt->rt6i_idev = in6_dev_get(loopback_dev); 155*8d0b94afSMartin KaFai Lau in6_dev_put(rt_idev); 156*8d0b94afSMartin KaFai Lau } 157*8d0b94afSMartin KaFai Lau 158*8d0b94afSMartin KaFai Lau if (rt_dev && (rt_dev == dev || !dev) && 159*8d0b94afSMartin KaFai Lau rt_dev != loopback_dev) { 160*8d0b94afSMartin KaFai Lau rt->dst.dev = loopback_dev; 161*8d0b94afSMartin KaFai Lau dev_hold(rt->dst.dev); 162*8d0b94afSMartin KaFai Lau dev_put(rt_dev); 163*8d0b94afSMartin KaFai Lau } 164*8d0b94afSMartin KaFai Lau } 165*8d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 166*8d0b94afSMartin KaFai Lau } 167*8d0b94afSMartin KaFai Lau } 168*8d0b94afSMartin KaFai Lau 16906582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) 17006582540SDavid S. Miller { 17106582540SDavid S. Miller struct rt6_info *rt = (struct rt6_info *)dst; 17206582540SDavid S. Miller 1734b32b5adSMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) 1744b32b5adSMartin KaFai Lau return NULL; 1754b32b5adSMartin KaFai Lau else 1763b471175SMartin KaFai Lau return dst_cow_metrics_generic(dst, old); 17706582540SDavid S. Miller } 17806582540SDavid S. Miller 179f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, 180f894cbf8SDavid S. Miller struct sk_buff *skb, 181f894cbf8SDavid S. Miller const void *daddr) 18239232973SDavid S. Miller { 18339232973SDavid S. Miller struct in6_addr *p = &rt->rt6i_gateway; 18439232973SDavid S. Miller 185a7563f34SDavid S. Miller if (!ipv6_addr_any(p)) 18639232973SDavid S. Miller return (const void *) p; 187f894cbf8SDavid S. Miller else if (skb) 188f894cbf8SDavid S. Miller return &ipv6_hdr(skb)->daddr; 18939232973SDavid S. Miller return daddr; 19039232973SDavid S. Miller } 19139232973SDavid S. Miller 192f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, 193f894cbf8SDavid S. Miller struct sk_buff *skb, 194f894cbf8SDavid S. Miller const void *daddr) 195d3aaeb38SDavid S. Miller { 19639232973SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 19739232973SDavid S. Miller struct neighbour *n; 19839232973SDavid S. Miller 199f894cbf8SDavid S. Miller daddr = choose_neigh_daddr(rt, skb, daddr); 2008e022ee6SYOSHIFUJI Hideaki / 吉藤英明 n = __ipv6_neigh_lookup(dst->dev, daddr); 201f83c7790SDavid S. Miller if (n) 202f83c7790SDavid S. Miller return n; 203f83c7790SDavid S. Miller return neigh_create(&nd_tbl, daddr, dst->dev); 204f83c7790SDavid S. Miller } 205f83c7790SDavid S. Miller 2069a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 2071da177e4SLinus Torvalds .family = AF_INET6, 2081da177e4SLinus Torvalds .gc = ip6_dst_gc, 2091da177e4SLinus Torvalds .gc_thresh = 1024, 2101da177e4SLinus Torvalds .check = ip6_dst_check, 2110dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 212ebb762f2SSteffen Klassert .mtu = ip6_mtu, 21306582540SDavid S. Miller .cow_metrics = ipv6_cow_metrics, 2141da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 2151da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 2161da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 2171da177e4SLinus Torvalds .link_failure = ip6_link_failure, 2181da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 2196e157b6aSDavid S. Miller .redirect = rt6_do_redirect, 2201ac06e03SHerbert Xu .local_out = __ip6_local_out, 221d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 2221da177e4SLinus Torvalds }; 2231da177e4SLinus Torvalds 224ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 225ec831ea7SRoland Dreier { 226618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 227618f9bc7SSteffen Klassert 228618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 229ec831ea7SRoland Dreier } 230ec831ea7SRoland Dreier 2316700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, 2326700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 23314e50e57SDavid S. Miller { 23414e50e57SDavid S. Miller } 23514e50e57SDavid S. Miller 2366700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, 2376700c270SDavid S. Miller struct sk_buff *skb) 238b587ee3bSDavid S. Miller { 239b587ee3bSDavid S. Miller } 240b587ee3bSDavid S. Miller 2410972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, 2420972ddb2SHeld Bernhard unsigned long old) 2430972ddb2SHeld Bernhard { 2440972ddb2SHeld Bernhard return NULL; 2450972ddb2SHeld Bernhard } 2460972ddb2SHeld Bernhard 24714e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 24814e50e57SDavid S. Miller .family = AF_INET6, 24914e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 25014e50e57SDavid S. Miller .check = ip6_dst_check, 251ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 252214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 25314e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 254b587ee3bSDavid S. Miller .redirect = ip6_rt_blackhole_redirect, 2550972ddb2SHeld Bernhard .cow_metrics = ip6_rt_blackhole_cow_metrics, 256d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 25714e50e57SDavid S. Miller }; 25814e50e57SDavid S. Miller 25962fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 26014edd87dSLi RongQing [RTAX_HOPLIMIT - 1] = 0, 26162fa8a84SDavid S. Miller }; 26262fa8a84SDavid S. Miller 263fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = { 2641da177e4SLinus Torvalds .dst = { 2651da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 2661da177e4SLinus Torvalds .__use = 1, 2672c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 2681da177e4SLinus Torvalds .error = -ENETUNREACH, 2691da177e4SLinus Torvalds .input = ip6_pkt_discard, 2701da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 2711da177e4SLinus Torvalds }, 2721da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2734f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 2741da177e4SLinus Torvalds .rt6i_metric = ~(u32) 0, 2751da177e4SLinus Torvalds .rt6i_ref = ATOMIC_INIT(1), 2761da177e4SLinus Torvalds }; 2771da177e4SLinus Torvalds 278101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 279101367c2SThomas Graf 280fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = { 281101367c2SThomas Graf .dst = { 282101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 283101367c2SThomas Graf .__use = 1, 2842c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 285101367c2SThomas Graf .error = -EACCES, 2869ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 2879ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 288101367c2SThomas Graf }, 289101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2904f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 291101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 292101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 293101367c2SThomas Graf }; 294101367c2SThomas Graf 295fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = { 296101367c2SThomas Graf .dst = { 297101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 298101367c2SThomas Graf .__use = 1, 2992c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 300101367c2SThomas Graf .error = -EINVAL, 301352e512cSHerbert Xu .input = dst_discard, 302aad88724SEric Dumazet .output = dst_discard_sk, 303101367c2SThomas Graf }, 304101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 3054f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 306101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 307101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 308101367c2SThomas Graf }; 309101367c2SThomas Graf 310101367c2SThomas Graf #endif 311101367c2SThomas Graf 3121da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 31397bab73fSDavid S. Miller static inline struct rt6_info *ip6_dst_alloc(struct net *net, 314957c665fSDavid S. Miller struct net_device *dev, 3158b96d22dSDavid S. Miller int flags, 3168b96d22dSDavid S. Miller struct fib6_table *table) 3171da177e4SLinus Torvalds { 31897bab73fSDavid S. Miller struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 3196f3118b5SNicolas Dichtel 0, DST_OBSOLETE_FORCE_CHK, flags); 320cf911662SDavid S. Miller 32197bab73fSDavid S. Miller if (rt) { 3228104891bSSteffen Klassert struct dst_entry *dst = &rt->dst; 3238104891bSSteffen Klassert 3248104891bSSteffen Klassert memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 32551ebd318SNicolas Dichtel INIT_LIST_HEAD(&rt->rt6i_siblings); 326*8d0b94afSMartin KaFai Lau INIT_LIST_HEAD(&rt->rt6i_uncached); 32797bab73fSDavid S. Miller } 328cf911662SDavid S. Miller return rt; 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 3321da177e4SLinus Torvalds { 3331da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 334ecd98837SYOSHIFUJI Hideaki / 吉藤英明 struct dst_entry *from = dst->from; 335*8d0b94afSMartin KaFai Lau struct inet6_dev *idev; 3361da177e4SLinus Torvalds 3378e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 3388e2ec639SYan, Zheng 339*8d0b94afSMartin KaFai Lau rt6_uncached_list_del(rt); 340*8d0b94afSMartin KaFai Lau 341*8d0b94afSMartin KaFai Lau idev = rt->rt6i_idev; 34238308473SDavid S. Miller if (idev) { 3431da177e4SLinus Torvalds rt->rt6i_idev = NULL; 3441da177e4SLinus Torvalds in6_dev_put(idev); 3451da177e4SLinus Torvalds } 3461716a961SGao feng 347ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst->from = NULL; 348ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst_release(from); 349b3419363SDavid S. Miller } 350b3419363SDavid S. Miller 3511da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 3521da177e4SLinus Torvalds int how) 3531da177e4SLinus Torvalds { 3541da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3551da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3565a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 357c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 3581da177e4SLinus Torvalds 35997cac082SDavid S. Miller if (dev != loopback_dev) { 36097cac082SDavid S. Miller if (idev && idev->dev == dev) { 3615a3e55d6SDenis V. Lunev struct inet6_dev *loopback_idev = 3625a3e55d6SDenis V. Lunev in6_dev_get(loopback_dev); 36338308473SDavid S. Miller if (loopback_idev) { 3641da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 3651da177e4SLinus Torvalds in6_dev_put(idev); 3661da177e4SLinus Torvalds } 3671da177e4SLinus Torvalds } 36897cac082SDavid S. Miller } 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds 371a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt) 3721da177e4SLinus Torvalds { 3731716a961SGao feng if (rt->rt6i_flags & RTF_EXPIRES) { 3741716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 375a50feda5SEric Dumazet return true; 3761716a961SGao feng } else if (rt->dst.from) { 3773fd91fb3SLi RongQing return rt6_check_expired((struct rt6_info *) rt->dst.from); 3781716a961SGao feng } 379a50feda5SEric Dumazet return false; 3801da177e4SLinus Torvalds } 3811da177e4SLinus Torvalds 38251ebd318SNicolas Dichtel /* Multipath route selection: 38351ebd318SNicolas Dichtel * Hash based function using packet header and flowlabel. 38451ebd318SNicolas Dichtel * Adapted from fib_info_hashfn() 38551ebd318SNicolas Dichtel */ 38651ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count, 38751ebd318SNicolas Dichtel const struct flowi6 *fl6) 38851ebd318SNicolas Dichtel { 38951ebd318SNicolas Dichtel unsigned int val = fl6->flowi6_proto; 39051ebd318SNicolas Dichtel 391c08977bbSYOSHIFUJI Hideaki / 吉藤英明 val ^= ipv6_addr_hash(&fl6->daddr); 392c08977bbSYOSHIFUJI Hideaki / 吉藤英明 val ^= ipv6_addr_hash(&fl6->saddr); 39351ebd318SNicolas Dichtel 39451ebd318SNicolas Dichtel /* Work only if this not encapsulated */ 39551ebd318SNicolas Dichtel switch (fl6->flowi6_proto) { 39651ebd318SNicolas Dichtel case IPPROTO_UDP: 39751ebd318SNicolas Dichtel case IPPROTO_TCP: 39851ebd318SNicolas Dichtel case IPPROTO_SCTP: 399b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_sport; 400b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_dport; 40151ebd318SNicolas Dichtel break; 40251ebd318SNicolas Dichtel 40351ebd318SNicolas Dichtel case IPPROTO_ICMPV6: 404b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_icmp_type; 405b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_icmp_code; 40651ebd318SNicolas Dichtel break; 40751ebd318SNicolas Dichtel } 40851ebd318SNicolas Dichtel /* RFC6438 recommands to use flowlabel */ 409b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->flowlabel; 41051ebd318SNicolas Dichtel 41151ebd318SNicolas Dichtel /* Perhaps, we need to tune, this function? */ 41251ebd318SNicolas Dichtel val = val ^ (val >> 7) ^ (val >> 12); 41351ebd318SNicolas Dichtel return val % candidate_count; 41451ebd318SNicolas Dichtel } 41551ebd318SNicolas Dichtel 41651ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match, 41752bd4c0cSNicolas Dichtel struct flowi6 *fl6, int oif, 41852bd4c0cSNicolas Dichtel int strict) 41951ebd318SNicolas Dichtel { 42051ebd318SNicolas Dichtel struct rt6_info *sibling, *next_sibling; 42151ebd318SNicolas Dichtel int route_choosen; 42251ebd318SNicolas Dichtel 42351ebd318SNicolas Dichtel route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6); 42451ebd318SNicolas Dichtel /* Don't change the route, if route_choosen == 0 42551ebd318SNicolas Dichtel * (siblings does not include ourself) 42651ebd318SNicolas Dichtel */ 42751ebd318SNicolas Dichtel if (route_choosen) 42851ebd318SNicolas Dichtel list_for_each_entry_safe(sibling, next_sibling, 42951ebd318SNicolas Dichtel &match->rt6i_siblings, rt6i_siblings) { 43051ebd318SNicolas Dichtel route_choosen--; 43151ebd318SNicolas Dichtel if (route_choosen == 0) { 43252bd4c0cSNicolas Dichtel if (rt6_score_route(sibling, oif, strict) < 0) 43352bd4c0cSNicolas Dichtel break; 43451ebd318SNicolas Dichtel match = sibling; 43551ebd318SNicolas Dichtel break; 43651ebd318SNicolas Dichtel } 43751ebd318SNicolas Dichtel } 43851ebd318SNicolas Dichtel return match; 43951ebd318SNicolas Dichtel } 44051ebd318SNicolas Dichtel 4411da177e4SLinus Torvalds /* 442c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 4431da177e4SLinus Torvalds */ 4441da177e4SLinus Torvalds 4458ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 4468ed67789SDaniel Lezcano struct rt6_info *rt, 447b71d1d42SEric Dumazet const struct in6_addr *saddr, 4481da177e4SLinus Torvalds int oif, 449d420895eSYOSHIFUJI Hideaki int flags) 4501da177e4SLinus Torvalds { 4511da177e4SLinus Torvalds struct rt6_info *local = NULL; 4521da177e4SLinus Torvalds struct rt6_info *sprt; 4531da177e4SLinus Torvalds 454dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 455dd3abc4eSYOSHIFUJI Hideaki goto out; 456dd3abc4eSYOSHIFUJI Hideaki 457d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 458d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 459dd3abc4eSYOSHIFUJI Hideaki 460dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4611da177e4SLinus Torvalds if (dev->ifindex == oif) 4621da177e4SLinus Torvalds return sprt; 4631da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 46438308473SDavid S. Miller if (!sprt->rt6i_idev || 4651da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 466d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE && oif) 4671da177e4SLinus Torvalds continue; 4681da177e4SLinus Torvalds if (local && (!oif || 4691da177e4SLinus Torvalds local->rt6i_idev->dev->ifindex == oif)) 4701da177e4SLinus Torvalds continue; 4711da177e4SLinus Torvalds } 4721da177e4SLinus Torvalds local = sprt; 4731da177e4SLinus Torvalds } 474dd3abc4eSYOSHIFUJI Hideaki } else { 475dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 476dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 477dd3abc4eSYOSHIFUJI Hideaki return sprt; 478dd3abc4eSYOSHIFUJI Hideaki } 4791da177e4SLinus Torvalds } 4801da177e4SLinus Torvalds 481dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4821da177e4SLinus Torvalds if (local) 4831da177e4SLinus Torvalds return local; 4841da177e4SLinus Torvalds 485d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 4868ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 4871da177e4SLinus Torvalds } 488dd3abc4eSYOSHIFUJI Hideaki out: 4891da177e4SLinus Torvalds return rt; 4901da177e4SLinus Torvalds } 4911da177e4SLinus Torvalds 49227097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 493c2f17e82SHannes Frederic Sowa struct __rt6_probe_work { 494c2f17e82SHannes Frederic Sowa struct work_struct work; 495c2f17e82SHannes Frederic Sowa struct in6_addr target; 496c2f17e82SHannes Frederic Sowa struct net_device *dev; 497c2f17e82SHannes Frederic Sowa }; 498c2f17e82SHannes Frederic Sowa 499c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w) 500c2f17e82SHannes Frederic Sowa { 501c2f17e82SHannes Frederic Sowa struct in6_addr mcaddr; 502c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work = 503c2f17e82SHannes Frederic Sowa container_of(w, struct __rt6_probe_work, work); 504c2f17e82SHannes Frederic Sowa 505c2f17e82SHannes Frederic Sowa addrconf_addr_solict_mult(&work->target, &mcaddr); 506c2f17e82SHannes Frederic Sowa ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL); 507c2f17e82SHannes Frederic Sowa dev_put(work->dev); 508662f5533SMichael Büsch kfree(work); 509c2f17e82SHannes Frederic Sowa } 510c2f17e82SHannes Frederic Sowa 51127097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 51227097255SYOSHIFUJI Hideaki { 513f2c31e32SEric Dumazet struct neighbour *neigh; 51427097255SYOSHIFUJI Hideaki /* 51527097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 51627097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 51727097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 51827097255SYOSHIFUJI Hideaki * 51927097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 52027097255SYOSHIFUJI Hideaki * to no more than one per minute. 52127097255SYOSHIFUJI Hideaki */ 5222152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!rt || !(rt->rt6i_flags & RTF_GATEWAY)) 523fdd6681dSAmerigo Wang return; 5242152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 5252152caeaSYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 5262152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 5272152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_lock(&neigh->lock); 5282152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh->nud_state & NUD_VALID) 5292152caeaSYOSHIFUJI Hideaki / 吉藤英明 goto out; 5307ff74a59SYOSHIFUJI Hideaki / 吉藤英明 } 5312152caeaSYOSHIFUJI Hideaki / 吉藤英明 5322152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!neigh || 53352e16356SYOSHIFUJI Hideaki time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { 534c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work; 53527097255SYOSHIFUJI Hideaki 536c2f17e82SHannes Frederic Sowa work = kmalloc(sizeof(*work), GFP_ATOMIC); 537c2f17e82SHannes Frederic Sowa 538c2f17e82SHannes Frederic Sowa if (neigh && work) 5397e980569SJiri Benc __neigh_set_probe_once(neigh); 5402152caeaSYOSHIFUJI Hideaki / 吉藤英明 541c2f17e82SHannes Frederic Sowa if (neigh) 542c2f17e82SHannes Frederic Sowa write_unlock(&neigh->lock); 543c2f17e82SHannes Frederic Sowa 544c2f17e82SHannes Frederic Sowa if (work) { 545c2f17e82SHannes Frederic Sowa INIT_WORK(&work->work, rt6_probe_deferred); 546c2f17e82SHannes Frederic Sowa work->target = rt->rt6i_gateway; 547c2f17e82SHannes Frederic Sowa dev_hold(rt->dst.dev); 548c2f17e82SHannes Frederic Sowa work->dev = rt->dst.dev; 549c2f17e82SHannes Frederic Sowa schedule_work(&work->work); 550c2f17e82SHannes Frederic Sowa } 551f2c31e32SEric Dumazet } else { 5522152caeaSYOSHIFUJI Hideaki / 吉藤英明 out: 5532152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_unlock(&neigh->lock); 55427097255SYOSHIFUJI Hideaki } 5552152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 556f2c31e32SEric Dumazet } 55727097255SYOSHIFUJI Hideaki #else 55827097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 55927097255SYOSHIFUJI Hideaki { 56027097255SYOSHIFUJI Hideaki } 56127097255SYOSHIFUJI Hideaki #endif 56227097255SYOSHIFUJI Hideaki 5631da177e4SLinus Torvalds /* 564554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 5651da177e4SLinus Torvalds */ 566b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 5671da177e4SLinus Torvalds { 568d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 569161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 570554cfb7eSYOSHIFUJI Hideaki return 2; 571161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 572161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 573161980f4SDavid S. Miller return 1; 574554cfb7eSYOSHIFUJI Hideaki return 0; 5751da177e4SLinus Torvalds } 5761da177e4SLinus Torvalds 577afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt) 5781da177e4SLinus Torvalds { 579f2c31e32SEric Dumazet struct neighbour *neigh; 580afc154e9SHannes Frederic Sowa enum rt6_nud_state ret = RT6_NUD_FAIL_HARD; 581f2c31e32SEric Dumazet 5824d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 5834d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 584afc154e9SHannes Frederic Sowa return RT6_NUD_SUCCEED; 585145a3621SYOSHIFUJI Hideaki / 吉藤英明 586145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 587145a3621SYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 588145a3621SYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 589145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_lock(&neigh->lock); 590554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 591afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 592398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 593a5a81f0bSPaul Marks else if (!(neigh->nud_state & NUD_FAILED)) 594afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 5957e980569SJiri Benc else 5967e980569SJiri Benc ret = RT6_NUD_FAIL_PROBE; 597398bcbebSYOSHIFUJI Hideaki #endif 598145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_unlock(&neigh->lock); 599afc154e9SHannes Frederic Sowa } else { 600afc154e9SHannes Frederic Sowa ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 6017e980569SJiri Benc RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; 602a5a81f0bSPaul Marks } 603145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 604145a3621SYOSHIFUJI Hideaki / 吉藤英明 605a5a81f0bSPaul Marks return ret; 6061da177e4SLinus Torvalds } 6071da177e4SLinus Torvalds 608554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 609554cfb7eSYOSHIFUJI Hideaki int strict) 610554cfb7eSYOSHIFUJI Hideaki { 611a5a81f0bSPaul Marks int m; 6124d0c5911SYOSHIFUJI Hideaki 6134d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 61477d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 615afc154e9SHannes Frederic Sowa return RT6_NUD_FAIL_HARD; 616ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 617ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 618ebacaaa0SYOSHIFUJI Hideaki #endif 619afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) { 620afc154e9SHannes Frederic Sowa int n = rt6_check_neigh(rt); 621afc154e9SHannes Frederic Sowa if (n < 0) 622afc154e9SHannes Frederic Sowa return n; 623afc154e9SHannes Frederic Sowa } 624554cfb7eSYOSHIFUJI Hideaki return m; 625554cfb7eSYOSHIFUJI Hideaki } 626554cfb7eSYOSHIFUJI Hideaki 627f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 628afc154e9SHannes Frederic Sowa int *mpri, struct rt6_info *match, 629afc154e9SHannes Frederic Sowa bool *do_rr) 630554cfb7eSYOSHIFUJI Hideaki { 631554cfb7eSYOSHIFUJI Hideaki int m; 632afc154e9SHannes Frederic Sowa bool match_do_rr = false; 633554cfb7eSYOSHIFUJI Hideaki 634554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 635f11e6659SDavid S. Miller goto out; 636554cfb7eSYOSHIFUJI Hideaki 637554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 6387e980569SJiri Benc if (m == RT6_NUD_FAIL_DO_RR) { 639afc154e9SHannes Frederic Sowa match_do_rr = true; 640afc154e9SHannes Frederic Sowa m = 0; /* lowest valid score */ 6417e980569SJiri Benc } else if (m == RT6_NUD_FAIL_HARD) { 642f11e6659SDavid S. Miller goto out; 6431da177e4SLinus Torvalds } 644f11e6659SDavid S. Miller 645afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) 646afc154e9SHannes Frederic Sowa rt6_probe(rt); 647afc154e9SHannes Frederic Sowa 6487e980569SJiri Benc /* note that m can be RT6_NUD_FAIL_PROBE at this point */ 649afc154e9SHannes Frederic Sowa if (m > *mpri) { 650afc154e9SHannes Frederic Sowa *do_rr = match_do_rr; 651afc154e9SHannes Frederic Sowa *mpri = m; 652afc154e9SHannes Frederic Sowa match = rt; 653afc154e9SHannes Frederic Sowa } 654f11e6659SDavid S. Miller out: 655f11e6659SDavid S. Miller return match; 6561da177e4SLinus Torvalds } 6571da177e4SLinus Torvalds 658f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 659f11e6659SDavid S. Miller struct rt6_info *rr_head, 660afc154e9SHannes Frederic Sowa u32 metric, int oif, int strict, 661afc154e9SHannes Frederic Sowa bool *do_rr) 662f11e6659SDavid S. Miller { 6639fbdcfafSSteffen Klassert struct rt6_info *rt, *match, *cont; 664f11e6659SDavid S. Miller int mpri = -1; 665f11e6659SDavid S. Miller 666f11e6659SDavid S. Miller match = NULL; 6679fbdcfafSSteffen Klassert cont = NULL; 6689fbdcfafSSteffen Klassert for (rt = rr_head; rt; rt = rt->dst.rt6_next) { 6699fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 6709fbdcfafSSteffen Klassert cont = rt; 6719fbdcfafSSteffen Klassert break; 6729fbdcfafSSteffen Klassert } 6739fbdcfafSSteffen Klassert 674afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 6759fbdcfafSSteffen Klassert } 6769fbdcfafSSteffen Klassert 6779fbdcfafSSteffen Klassert for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) { 6789fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 6799fbdcfafSSteffen Klassert cont = rt; 6809fbdcfafSSteffen Klassert break; 6819fbdcfafSSteffen Klassert } 6829fbdcfafSSteffen Klassert 6839fbdcfafSSteffen Klassert match = find_match(rt, oif, strict, &mpri, match, do_rr); 6849fbdcfafSSteffen Klassert } 6859fbdcfafSSteffen Klassert 6869fbdcfafSSteffen Klassert if (match || !cont) 6879fbdcfafSSteffen Klassert return match; 6889fbdcfafSSteffen Klassert 6899fbdcfafSSteffen Klassert for (rt = cont; rt; rt = rt->dst.rt6_next) 690afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 691f11e6659SDavid S. Miller 692f11e6659SDavid S. Miller return match; 693f11e6659SDavid S. Miller } 694f11e6659SDavid S. Miller 695f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 696f11e6659SDavid S. Miller { 697f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 6988ed67789SDaniel Lezcano struct net *net; 699afc154e9SHannes Frederic Sowa bool do_rr = false; 700f11e6659SDavid S. Miller 701f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 702f11e6659SDavid S. Miller if (!rt0) 703f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 704f11e6659SDavid S. Miller 705afc154e9SHannes Frederic Sowa match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict, 706afc154e9SHannes Frederic Sowa &do_rr); 707f11e6659SDavid S. Miller 708afc154e9SHannes Frederic Sowa if (do_rr) { 709d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 710f11e6659SDavid S. Miller 711554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 712f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 713f11e6659SDavid S. Miller next = fn->leaf; 714f11e6659SDavid S. Miller 715f11e6659SDavid S. Miller if (next != rt0) 716f11e6659SDavid S. Miller fn->rr_ptr = next; 717554cfb7eSYOSHIFUJI Hideaki } 718554cfb7eSYOSHIFUJI Hideaki 719d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 720a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 7211da177e4SLinus Torvalds } 7221da177e4SLinus Torvalds 7238b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt) 7248b9df265SMartin KaFai Lau { 7258b9df265SMartin KaFai Lau return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY)); 7268b9df265SMartin KaFai Lau } 7278b9df265SMartin KaFai Lau 72870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 72970ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 730b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 73170ceb4f5SYOSHIFUJI Hideaki { 732c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 73370ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 73470ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 73570ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 7364bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 73770ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 73870ceb4f5SYOSHIFUJI Hideaki 73970ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 74070ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 74170ceb4f5SYOSHIFUJI Hideaki } 74270ceb4f5SYOSHIFUJI Hideaki 74370ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 74470ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 74570ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 74670ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 74770ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 74870ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 74970ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 75070ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 75170ceb4f5SYOSHIFUJI Hideaki } 75270ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 75370ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 75470ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 75570ceb4f5SYOSHIFUJI Hideaki } 75670ceb4f5SYOSHIFUJI Hideaki } 75770ceb4f5SYOSHIFUJI Hideaki 75870ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 75970ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 7603933fc95SJens Rosenboom return -EINVAL; 76170ceb4f5SYOSHIFUJI Hideaki 7624bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 76370ceb4f5SYOSHIFUJI Hideaki 76470ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 76570ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 76670ceb4f5SYOSHIFUJI Hideaki else { 76770ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 76870ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 76970ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 77070ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 77170ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 77270ceb4f5SYOSHIFUJI Hideaki } 77370ceb4f5SYOSHIFUJI Hideaki 774f104a567SDuan Jiong if (rinfo->prefix_len == 0) 775f104a567SDuan Jiong rt = rt6_get_dflt_router(gwaddr, dev); 776f104a567SDuan Jiong else 777f104a567SDuan Jiong rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, 778f104a567SDuan Jiong gwaddr, dev->ifindex); 77970ceb4f5SYOSHIFUJI Hideaki 78070ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 781e0a1ad73SThomas Graf ip6_del_rt(rt); 78270ceb4f5SYOSHIFUJI Hideaki rt = NULL; 78370ceb4f5SYOSHIFUJI Hideaki } 78470ceb4f5SYOSHIFUJI Hideaki 78570ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 786efa2cea0SDaniel Lezcano rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 78770ceb4f5SYOSHIFUJI Hideaki pref); 78870ceb4f5SYOSHIFUJI Hideaki else if (rt) 78970ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 79070ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 79170ceb4f5SYOSHIFUJI Hideaki 79270ceb4f5SYOSHIFUJI Hideaki if (rt) { 7931716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 7941716a961SGao feng rt6_clean_expires(rt); 7951716a961SGao feng else 7961716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 7971716a961SGao feng 79894e187c0SAmerigo Wang ip6_rt_put(rt); 79970ceb4f5SYOSHIFUJI Hideaki } 80070ceb4f5SYOSHIFUJI Hideaki return 0; 80170ceb4f5SYOSHIFUJI Hideaki } 80270ceb4f5SYOSHIFUJI Hideaki #endif 80370ceb4f5SYOSHIFUJI Hideaki 804a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn, 805a3c00e46SMartin KaFai Lau struct in6_addr *saddr) 806a3c00e46SMartin KaFai Lau { 807a3c00e46SMartin KaFai Lau struct fib6_node *pn; 808a3c00e46SMartin KaFai Lau while (1) { 809a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_TL_ROOT) 810a3c00e46SMartin KaFai Lau return NULL; 811a3c00e46SMartin KaFai Lau pn = fn->parent; 812a3c00e46SMartin KaFai Lau if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) 813a3c00e46SMartin KaFai Lau fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); 814a3c00e46SMartin KaFai Lau else 815a3c00e46SMartin KaFai Lau fn = pn; 816a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_RTINFO) 817a3c00e46SMartin KaFai Lau return fn; 818a3c00e46SMartin KaFai Lau } 819a3c00e46SMartin KaFai Lau } 820c71099acSThomas Graf 8218ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 8228ed67789SDaniel Lezcano struct fib6_table *table, 8234c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 8241da177e4SLinus Torvalds { 8251da177e4SLinus Torvalds struct fib6_node *fn; 8261da177e4SLinus Torvalds struct rt6_info *rt; 8271da177e4SLinus Torvalds 828c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 8294c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 830c71099acSThomas Graf restart: 831c71099acSThomas Graf rt = fn->leaf; 8324c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 83351ebd318SNicolas Dichtel if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) 83452bd4c0cSNicolas Dichtel rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags); 835a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 836a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 837a3c00e46SMartin KaFai Lau if (fn) 838a3c00e46SMartin KaFai Lau goto restart; 839a3c00e46SMartin KaFai Lau } 840d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 841c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 8421da177e4SLinus Torvalds return rt; 843c71099acSThomas Graf 844c71099acSThomas Graf } 845c71099acSThomas Graf 846ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, 847ea6e574eSFlorian Westphal int flags) 848ea6e574eSFlorian Westphal { 849ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 850ea6e574eSFlorian Westphal } 851ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 852ea6e574eSFlorian Westphal 8539acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 8549acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 855c71099acSThomas Graf { 8564c9483b2SDavid S. Miller struct flowi6 fl6 = { 8574c9483b2SDavid S. Miller .flowi6_oif = oif, 8584c9483b2SDavid S. Miller .daddr = *daddr, 859c71099acSThomas Graf }; 860c71099acSThomas Graf struct dst_entry *dst; 86177d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 862c71099acSThomas Graf 863adaa70bbSThomas Graf if (saddr) { 8644c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 865adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 866adaa70bbSThomas Graf } 867adaa70bbSThomas Graf 8684c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 869c71099acSThomas Graf if (dst->error == 0) 870c71099acSThomas Graf return (struct rt6_info *) dst; 871c71099acSThomas Graf 872c71099acSThomas Graf dst_release(dst); 873c71099acSThomas Graf 8741da177e4SLinus Torvalds return NULL; 8751da177e4SLinus Torvalds } 8767159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 8777159039aSYOSHIFUJI Hideaki 878c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 8791da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 8801da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 8811da177e4SLinus Torvalds be destroyed. 8821da177e4SLinus Torvalds */ 8831da177e4SLinus Torvalds 884e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, 885e715b6d3SFlorian Westphal struct mx6_config *mxc) 8861da177e4SLinus Torvalds { 8871da177e4SLinus Torvalds int err; 888c71099acSThomas Graf struct fib6_table *table; 8891da177e4SLinus Torvalds 890c71099acSThomas Graf table = rt->rt6i_table; 891c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 892e715b6d3SFlorian Westphal err = fib6_add(&table->tb6_root, rt, info, mxc); 893c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 8941da177e4SLinus Torvalds 8951da177e4SLinus Torvalds return err; 8961da177e4SLinus Torvalds } 8971da177e4SLinus Torvalds 89840e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 89940e22e8fSThomas Graf { 900e715b6d3SFlorian Westphal struct nl_info info = { .nl_net = dev_net(rt->dst.dev), }; 901e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 902e715b6d3SFlorian Westphal 903e715b6d3SFlorian Westphal return __ip6_ins_rt(rt, &info, &mxc); 90440e22e8fSThomas Graf } 90540e22e8fSThomas Graf 9068b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, 90721efcfa0SEric Dumazet const struct in6_addr *daddr, 908b71d1d42SEric Dumazet const struct in6_addr *saddr) 9091da177e4SLinus Torvalds { 9101da177e4SLinus Torvalds struct rt6_info *rt; 9111da177e4SLinus Torvalds 9121da177e4SLinus Torvalds /* 9131da177e4SLinus Torvalds * Clone the route. 9141da177e4SLinus Torvalds */ 9151da177e4SLinus Torvalds 91621efcfa0SEric Dumazet rt = ip6_rt_copy(ort, daddr); 9171da177e4SLinus Torvalds 9181da177e4SLinus Torvalds if (rt) { 9198b9df265SMartin KaFai Lau rt->rt6i_flags |= RTF_CACHE; 9208b9df265SMartin KaFai Lau 9218b9df265SMartin KaFai Lau if (!rt6_is_gw_or_nonexthop(ort)) { 922bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 92321efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 92458c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 9251da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 9261da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 9274e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 9281da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 9291da177e4SLinus Torvalds } 9301da177e4SLinus Torvalds #endif 93195a9a5baSYOSHIFUJI Hideaki } 9321da177e4SLinus Torvalds } 93395a9a5baSYOSHIFUJI Hideaki 934299d9939SYOSHIFUJI Hideaki return rt; 935299d9939SYOSHIFUJI Hideaki } 936299d9939SYOSHIFUJI Hideaki 9378ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, 9384c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 9391da177e4SLinus Torvalds { 940367efcb9SMartin KaFai Lau struct fib6_node *fn, *saved_fn; 94145e4fd26SMartin KaFai Lau struct rt6_info *rt; 942c71099acSThomas Graf int strict = 0; 9431da177e4SLinus Torvalds 94477d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 945367efcb9SMartin KaFai Lau if (net->ipv6.devconf_all->forwarding == 0) 946367efcb9SMartin KaFai Lau strict |= RT6_LOOKUP_F_REACHABLE; 9471da177e4SLinus Torvalds 948c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 9491da177e4SLinus Torvalds 9504c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 951367efcb9SMartin KaFai Lau saved_fn = fn; 9521da177e4SLinus Torvalds 953a3c00e46SMartin KaFai Lau redo_rt6_select: 954367efcb9SMartin KaFai Lau rt = rt6_select(fn, oif, strict); 95552bd4c0cSNicolas Dichtel if (rt->rt6i_nsiblings) 956367efcb9SMartin KaFai Lau rt = rt6_multipath_select(rt, fl6, oif, strict); 957a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 958a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 959a3c00e46SMartin KaFai Lau if (fn) 960a3c00e46SMartin KaFai Lau goto redo_rt6_select; 961367efcb9SMartin KaFai Lau else if (strict & RT6_LOOKUP_F_REACHABLE) { 962367efcb9SMartin KaFai Lau /* also consider unreachable route */ 963367efcb9SMartin KaFai Lau strict &= ~RT6_LOOKUP_F_REACHABLE; 964367efcb9SMartin KaFai Lau fn = saved_fn; 965367efcb9SMartin KaFai Lau goto redo_rt6_select; 966367efcb9SMartin KaFai Lau } 967a3c00e46SMartin KaFai Lau } 968a3c00e46SMartin KaFai Lau 9693da59bd9SMartin KaFai Lau dst_use(&rt->dst, jiffies); 970c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 9711da177e4SLinus Torvalds 9723da59bd9SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) { 9733da59bd9SMartin KaFai Lau goto done; 9743da59bd9SMartin KaFai Lau } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && 9753da59bd9SMartin KaFai Lau !(rt->rt6i_flags & RTF_GATEWAY))) { 9763da59bd9SMartin KaFai Lau /* Create a RTF_CACHE clone which will not be 9773da59bd9SMartin KaFai Lau * owned by the fib6 tree. It is for the special case where 9783da59bd9SMartin KaFai Lau * the daddr in the skb during the neighbor look-up is different 9793da59bd9SMartin KaFai Lau * from the fl6->daddr used to look-up route here. 9803da59bd9SMartin KaFai Lau */ 981c71099acSThomas Graf 9823da59bd9SMartin KaFai Lau struct rt6_info *uncached_rt; 9833da59bd9SMartin KaFai Lau 9843da59bd9SMartin KaFai Lau uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL); 9853da59bd9SMartin KaFai Lau dst_release(&rt->dst); 9863da59bd9SMartin KaFai Lau 9873da59bd9SMartin KaFai Lau if (uncached_rt) 988*8d0b94afSMartin KaFai Lau rt6_uncached_list_add(uncached_rt); 9893da59bd9SMartin KaFai Lau else 9903da59bd9SMartin KaFai Lau uncached_rt = net->ipv6.ip6_null_entry; 9913da59bd9SMartin KaFai Lau dst_hold(&uncached_rt->dst); 9923da59bd9SMartin KaFai Lau return uncached_rt; 9933da59bd9SMartin KaFai Lau } 9943da59bd9SMartin KaFai Lau 9953da59bd9SMartin KaFai Lau done: 9963da59bd9SMartin KaFai Lau rt6_dst_from_metrics_check(rt); 997c71099acSThomas Graf return rt; 998c71099acSThomas Graf } 999c71099acSThomas Graf 10008ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 10014c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 10024acad72dSPavel Emelyanov { 10034c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 10044acad72dSPavel Emelyanov } 10054acad72dSPavel Emelyanov 100672331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 100772331bc0SShmulik Ladkani struct net_device *dev, 100872331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 100972331bc0SShmulik Ladkani { 101072331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 101172331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 101272331bc0SShmulik Ladkani 101372331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 101472331bc0SShmulik Ladkani } 101572331bc0SShmulik Ladkani 1016c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 1017c71099acSThomas Graf { 1018b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 1019c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 1020adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 10214c9483b2SDavid S. Miller struct flowi6 fl6 = { 10224c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 10234c9483b2SDavid S. Miller .daddr = iph->daddr, 10244c9483b2SDavid S. Miller .saddr = iph->saddr, 10256502ca52SYOSHIFUJI Hideaki / 吉藤英明 .flowlabel = ip6_flowinfo(iph), 10264c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 10274c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 1028c71099acSThomas Graf }; 1029adaa70bbSThomas Graf 103072331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 1031c71099acSThomas Graf } 1032c71099acSThomas Graf 10338ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 10344c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 1035c71099acSThomas Graf { 10364c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 1037c71099acSThomas Graf } 1038c71099acSThomas Graf 10399c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk, 10404c9483b2SDavid S. Miller struct flowi6 *fl6) 1041c71099acSThomas Graf { 1042c71099acSThomas Graf int flags = 0; 1043c71099acSThomas Graf 10441fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 10454dc27d1cSDavid McCullough 10464c9483b2SDavid S. Miller if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) 104777d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 1048c71099acSThomas Graf 10494c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 1050adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 10510c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 10520c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 1053adaa70bbSThomas Graf 10544c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 10551da177e4SLinus Torvalds } 10567159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 10571da177e4SLinus Torvalds 10582774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 105914e50e57SDavid S. Miller { 10605c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 106114e50e57SDavid S. Miller struct dst_entry *new = NULL; 106214e50e57SDavid S. Miller 1063f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 106414e50e57SDavid S. Miller if (rt) { 1065d8d1f30bSChangli Gao new = &rt->dst; 106614e50e57SDavid S. Miller 10678104891bSSteffen Klassert memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); 10688104891bSSteffen Klassert 106914e50e57SDavid S. Miller new->__use = 1; 1070352e512cSHerbert Xu new->input = dst_discard; 1071aad88724SEric Dumazet new->output = dst_discard_sk; 107214e50e57SDavid S. Miller 107321efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 107421efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 107521efcfa0SEric Dumazet else 1076defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 107714e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 107814e50e57SDavid S. Miller if (rt->rt6i_idev) 107914e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 108014e50e57SDavid S. Miller 10814e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 10821716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 108314e50e57SDavid S. Miller rt->rt6i_metric = 0; 108414e50e57SDavid S. Miller 108514e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 108614e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 108714e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 108814e50e57SDavid S. Miller #endif 108914e50e57SDavid S. Miller 109014e50e57SDavid S. Miller dst_free(new); 109114e50e57SDavid S. Miller } 109214e50e57SDavid S. Miller 109369ead7afSDavid S. Miller dst_release(dst_orig); 109469ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 109514e50e57SDavid S. Miller } 109614e50e57SDavid S. Miller 10971da177e4SLinus Torvalds /* 10981da177e4SLinus Torvalds * Destination cache support functions 10991da177e4SLinus Torvalds */ 11001da177e4SLinus Torvalds 11014b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt) 11024b32b5adSMartin KaFai Lau { 11034b32b5adSMartin KaFai Lau if (rt->dst.from && 11044b32b5adSMartin KaFai Lau dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from)) 11054b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true); 11064b32b5adSMartin KaFai Lau } 11074b32b5adSMartin KaFai Lau 11083da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie) 11093da59bd9SMartin KaFai Lau { 11103da59bd9SMartin KaFai Lau if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) 11113da59bd9SMartin KaFai Lau return NULL; 11123da59bd9SMartin KaFai Lau 11133da59bd9SMartin KaFai Lau if (rt6_check_expired(rt)) 11143da59bd9SMartin KaFai Lau return NULL; 11153da59bd9SMartin KaFai Lau 11163da59bd9SMartin KaFai Lau return &rt->dst; 11173da59bd9SMartin KaFai Lau } 11183da59bd9SMartin KaFai Lau 11193da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie) 11203da59bd9SMartin KaFai Lau { 11213da59bd9SMartin KaFai Lau if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && 11223da59bd9SMartin KaFai Lau rt6_check((struct rt6_info *)(rt->dst.from), cookie)) 11233da59bd9SMartin KaFai Lau return &rt->dst; 11243da59bd9SMartin KaFai Lau else 11253da59bd9SMartin KaFai Lau return NULL; 11263da59bd9SMartin KaFai Lau } 11273da59bd9SMartin KaFai Lau 11281da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 11291da177e4SLinus Torvalds { 11301da177e4SLinus Torvalds struct rt6_info *rt; 11311da177e4SLinus Torvalds 11321da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 11331da177e4SLinus Torvalds 11346f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 11356f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 11366f3118b5SNicolas Dichtel * into this function always. 11376f3118b5SNicolas Dichtel */ 1138e3bc10bdSHannes Frederic Sowa 11394b32b5adSMartin KaFai Lau rt6_dst_from_metrics_check(rt); 11404b32b5adSMartin KaFai Lau 11413da59bd9SMartin KaFai Lau if (unlikely(dst->flags & DST_NOCACHE)) 11423da59bd9SMartin KaFai Lau return rt6_dst_from_check(rt, cookie); 11433da59bd9SMartin KaFai Lau else 11443da59bd9SMartin KaFai Lau return rt6_check(rt, cookie); 11451da177e4SLinus Torvalds } 11461da177e4SLinus Torvalds 11471da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 11481da177e4SLinus Torvalds { 11491da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 11501da177e4SLinus Torvalds 11511da177e4SLinus Torvalds if (rt) { 115254c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 115354c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1154e0a1ad73SThomas Graf ip6_del_rt(rt); 115554c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 11561da177e4SLinus Torvalds } 115754c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 115854c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 115954c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 116054c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 116154c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 116254c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 11631da177e4SLinus Torvalds } 11641da177e4SLinus Torvalds 11651da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 11661da177e4SLinus Torvalds { 11671da177e4SLinus Torvalds struct rt6_info *rt; 11681da177e4SLinus Torvalds 11693ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 11701da177e4SLinus Torvalds 1171adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 11721da177e4SLinus Torvalds if (rt) { 11731eb4f758SHannes Frederic Sowa if (rt->rt6i_flags & RTF_CACHE) { 11741eb4f758SHannes Frederic Sowa dst_hold(&rt->dst); 11751eb4f758SHannes Frederic Sowa if (ip6_del_rt(rt)) 11761eb4f758SHannes Frederic Sowa dst_free(&rt->dst); 11771eb4f758SHannes Frederic Sowa } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) { 11781da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 11791da177e4SLinus Torvalds } 11801da177e4SLinus Torvalds } 11811eb4f758SHannes Frederic Sowa } 11821da177e4SLinus Torvalds 118345e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) 118445e4fd26SMartin KaFai Lau { 118545e4fd26SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 118645e4fd26SMartin KaFai Lau 118745e4fd26SMartin KaFai Lau rt->rt6i_flags |= RTF_MODIFIED; 118845e4fd26SMartin KaFai Lau rt->rt6i_pmtu = mtu; 118945e4fd26SMartin KaFai Lau rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); 119045e4fd26SMartin KaFai Lau } 119145e4fd26SMartin KaFai Lau 119245e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, 119345e4fd26SMartin KaFai Lau const struct ipv6hdr *iph, u32 mtu) 11941da177e4SLinus Torvalds { 11951da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info *)dst; 11961da177e4SLinus Torvalds 119745e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_LOCAL) 119845e4fd26SMartin KaFai Lau return; 119945e4fd26SMartin KaFai Lau 120081aded24SDavid S. Miller dst_confirm(dst); 120145e4fd26SMartin KaFai Lau mtu = max_t(u32, mtu, IPV6_MIN_MTU); 120245e4fd26SMartin KaFai Lau if (mtu >= dst_mtu(dst)) 120345e4fd26SMartin KaFai Lau return; 120481aded24SDavid S. Miller 120545e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_CACHE) { 120645e4fd26SMartin KaFai Lau rt6_do_update_pmtu(rt6, mtu); 120745e4fd26SMartin KaFai Lau } else { 120845e4fd26SMartin KaFai Lau const struct in6_addr *daddr, *saddr; 120945e4fd26SMartin KaFai Lau struct rt6_info *nrt6; 12109d289715SHagen Paul Pfeifer 121145e4fd26SMartin KaFai Lau if (iph) { 121245e4fd26SMartin KaFai Lau daddr = &iph->daddr; 121345e4fd26SMartin KaFai Lau saddr = &iph->saddr; 121445e4fd26SMartin KaFai Lau } else if (sk) { 121545e4fd26SMartin KaFai Lau daddr = &sk->sk_v6_daddr; 121645e4fd26SMartin KaFai Lau saddr = &inet6_sk(sk)->saddr; 121745e4fd26SMartin KaFai Lau } else { 121845e4fd26SMartin KaFai Lau return; 12191da177e4SLinus Torvalds } 122045e4fd26SMartin KaFai Lau nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr); 122145e4fd26SMartin KaFai Lau if (nrt6) { 122245e4fd26SMartin KaFai Lau rt6_do_update_pmtu(nrt6, mtu); 122345e4fd26SMartin KaFai Lau 122445e4fd26SMartin KaFai Lau /* ip6_ins_rt(nrt6) will bump the 122545e4fd26SMartin KaFai Lau * rt6->rt6i_node->fn_sernum 122645e4fd26SMartin KaFai Lau * which will fail the next rt6_check() and 122745e4fd26SMartin KaFai Lau * invalidate the sk->sk_dst_cache. 122845e4fd26SMartin KaFai Lau */ 122945e4fd26SMartin KaFai Lau ip6_ins_rt(nrt6); 123045e4fd26SMartin KaFai Lau } 123145e4fd26SMartin KaFai Lau } 123245e4fd26SMartin KaFai Lau } 123345e4fd26SMartin KaFai Lau 123445e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 123545e4fd26SMartin KaFai Lau struct sk_buff *skb, u32 mtu) 123645e4fd26SMartin KaFai Lau { 123745e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu); 12381da177e4SLinus Torvalds } 12391da177e4SLinus Torvalds 124042ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 124142ae66c8SDavid S. Miller int oif, u32 mark) 124281aded24SDavid S. Miller { 124381aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 124481aded24SDavid S. Miller struct dst_entry *dst; 124581aded24SDavid S. Miller struct flowi6 fl6; 124681aded24SDavid S. Miller 124781aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 124881aded24SDavid S. Miller fl6.flowi6_oif = oif; 12491b3c61dcSLorenzo Colitti fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark); 125081aded24SDavid S. Miller fl6.daddr = iph->daddr; 125181aded24SDavid S. Miller fl6.saddr = iph->saddr; 12526502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 125381aded24SDavid S. Miller 125481aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 125581aded24SDavid S. Miller if (!dst->error) 125645e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu)); 125781aded24SDavid S. Miller dst_release(dst); 125881aded24SDavid S. Miller } 125981aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 126081aded24SDavid S. Miller 126181aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 126281aded24SDavid S. Miller { 126381aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 126481aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 126581aded24SDavid S. Miller } 126681aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 126781aded24SDavid S. Miller 1268b55b76b2SDuan Jiong /* Handle redirects */ 1269b55b76b2SDuan Jiong struct ip6rd_flowi { 1270b55b76b2SDuan Jiong struct flowi6 fl6; 1271b55b76b2SDuan Jiong struct in6_addr gateway; 1272b55b76b2SDuan Jiong }; 1273b55b76b2SDuan Jiong 1274b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net, 1275b55b76b2SDuan Jiong struct fib6_table *table, 1276b55b76b2SDuan Jiong struct flowi6 *fl6, 1277b55b76b2SDuan Jiong int flags) 1278b55b76b2SDuan Jiong { 1279b55b76b2SDuan Jiong struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 1280b55b76b2SDuan Jiong struct rt6_info *rt; 1281b55b76b2SDuan Jiong struct fib6_node *fn; 1282b55b76b2SDuan Jiong 1283b55b76b2SDuan Jiong /* Get the "current" route for this destination and 1284b55b76b2SDuan Jiong * check if the redirect has come from approriate router. 1285b55b76b2SDuan Jiong * 1286b55b76b2SDuan Jiong * RFC 4861 specifies that redirects should only be 1287b55b76b2SDuan Jiong * accepted if they come from the nexthop to the target. 1288b55b76b2SDuan Jiong * Due to the way the routes are chosen, this notion 1289b55b76b2SDuan Jiong * is a bit fuzzy and one might need to check all possible 1290b55b76b2SDuan Jiong * routes. 1291b55b76b2SDuan Jiong */ 1292b55b76b2SDuan Jiong 1293b55b76b2SDuan Jiong read_lock_bh(&table->tb6_lock); 1294b55b76b2SDuan Jiong fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1295b55b76b2SDuan Jiong restart: 1296b55b76b2SDuan Jiong for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1297b55b76b2SDuan Jiong if (rt6_check_expired(rt)) 1298b55b76b2SDuan Jiong continue; 1299b55b76b2SDuan Jiong if (rt->dst.error) 1300b55b76b2SDuan Jiong break; 1301b55b76b2SDuan Jiong if (!(rt->rt6i_flags & RTF_GATEWAY)) 1302b55b76b2SDuan Jiong continue; 1303b55b76b2SDuan Jiong if (fl6->flowi6_oif != rt->dst.dev->ifindex) 1304b55b76b2SDuan Jiong continue; 1305b55b76b2SDuan Jiong if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) 1306b55b76b2SDuan Jiong continue; 1307b55b76b2SDuan Jiong break; 1308b55b76b2SDuan Jiong } 1309b55b76b2SDuan Jiong 1310b55b76b2SDuan Jiong if (!rt) 1311b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1312b55b76b2SDuan Jiong else if (rt->dst.error) { 1313b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1314b0a1ba59SMartin KaFai Lau goto out; 1315b0a1ba59SMartin KaFai Lau } 1316b0a1ba59SMartin KaFai Lau 1317b0a1ba59SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1318a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1319a3c00e46SMartin KaFai Lau if (fn) 1320a3c00e46SMartin KaFai Lau goto restart; 1321b55b76b2SDuan Jiong } 1322a3c00e46SMartin KaFai Lau 1323b0a1ba59SMartin KaFai Lau out: 1324b55b76b2SDuan Jiong dst_hold(&rt->dst); 1325b55b76b2SDuan Jiong 1326b55b76b2SDuan Jiong read_unlock_bh(&table->tb6_lock); 1327b55b76b2SDuan Jiong 1328b55b76b2SDuan Jiong return rt; 1329b55b76b2SDuan Jiong }; 1330b55b76b2SDuan Jiong 1331b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net, 1332b55b76b2SDuan Jiong const struct flowi6 *fl6, 1333b55b76b2SDuan Jiong const struct in6_addr *gateway) 1334b55b76b2SDuan Jiong { 1335b55b76b2SDuan Jiong int flags = RT6_LOOKUP_F_HAS_SADDR; 1336b55b76b2SDuan Jiong struct ip6rd_flowi rdfl; 1337b55b76b2SDuan Jiong 1338b55b76b2SDuan Jiong rdfl.fl6 = *fl6; 1339b55b76b2SDuan Jiong rdfl.gateway = *gateway; 1340b55b76b2SDuan Jiong 1341b55b76b2SDuan Jiong return fib6_rule_lookup(net, &rdfl.fl6, 1342b55b76b2SDuan Jiong flags, __ip6_route_redirect); 1343b55b76b2SDuan Jiong } 1344b55b76b2SDuan Jiong 13453a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 13463a5ad2eeSDavid S. Miller { 13473a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 13483a5ad2eeSDavid S. Miller struct dst_entry *dst; 13493a5ad2eeSDavid S. Miller struct flowi6 fl6; 13503a5ad2eeSDavid S. Miller 13513a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 1352e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 13533a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 13543a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 13553a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 13563a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 13576502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 13583a5ad2eeSDavid S. Miller 1359b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); 13606700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 13613a5ad2eeSDavid S. Miller dst_release(dst); 13623a5ad2eeSDavid S. Miller } 13633a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 13643a5ad2eeSDavid S. Miller 1365c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, 1366c92a59ecSDuan Jiong u32 mark) 1367c92a59ecSDuan Jiong { 1368c92a59ecSDuan Jiong const struct ipv6hdr *iph = ipv6_hdr(skb); 1369c92a59ecSDuan Jiong const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 1370c92a59ecSDuan Jiong struct dst_entry *dst; 1371c92a59ecSDuan Jiong struct flowi6 fl6; 1372c92a59ecSDuan Jiong 1373c92a59ecSDuan Jiong memset(&fl6, 0, sizeof(fl6)); 1374e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 1375c92a59ecSDuan Jiong fl6.flowi6_oif = oif; 1376c92a59ecSDuan Jiong fl6.flowi6_mark = mark; 1377c92a59ecSDuan Jiong fl6.daddr = msg->dest; 1378c92a59ecSDuan Jiong fl6.saddr = iph->daddr; 1379c92a59ecSDuan Jiong 1380b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &iph->saddr); 1381c92a59ecSDuan Jiong rt6_do_redirect(dst, NULL, skb); 1382c92a59ecSDuan Jiong dst_release(dst); 1383c92a59ecSDuan Jiong } 1384c92a59ecSDuan Jiong 13853a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 13863a5ad2eeSDavid S. Miller { 13873a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 13883a5ad2eeSDavid S. Miller } 13893a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 13903a5ad2eeSDavid S. Miller 13910dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 13921da177e4SLinus Torvalds { 13930dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 13940dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 13950dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 13960dbaee3bSDavid S. Miller 13971da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 13981da177e4SLinus Torvalds 13995578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 14005578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 14011da177e4SLinus Torvalds 14021da177e4SLinus Torvalds /* 14031da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 14041da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 14051da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 14061da177e4SLinus Torvalds * rely only on pmtu discovery" 14071da177e4SLinus Torvalds */ 14081da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 14091da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 14101da177e4SLinus Torvalds return mtu; 14111da177e4SLinus Torvalds } 14121da177e4SLinus Torvalds 1413ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1414d33e4553SDavid S. Miller { 14154b32b5adSMartin KaFai Lau const struct rt6_info *rt = (const struct rt6_info *)dst; 14164b32b5adSMartin KaFai Lau unsigned int mtu = rt->rt6i_pmtu; 1417d33e4553SDavid S. Miller struct inet6_dev *idev; 1418618f9bc7SSteffen Klassert 1419618f9bc7SSteffen Klassert if (mtu) 142030f78d8eSEric Dumazet goto out; 1421618f9bc7SSteffen Klassert 14224b32b5adSMartin KaFai Lau mtu = dst_metric_raw(dst, RTAX_MTU); 14234b32b5adSMartin KaFai Lau if (mtu) 14244b32b5adSMartin KaFai Lau goto out; 14254b32b5adSMartin KaFai Lau 1426618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1427d33e4553SDavid S. Miller 1428d33e4553SDavid S. Miller rcu_read_lock(); 1429d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1430d33e4553SDavid S. Miller if (idev) 1431d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1432d33e4553SDavid S. Miller rcu_read_unlock(); 1433d33e4553SDavid S. Miller 143430f78d8eSEric Dumazet out: 143530f78d8eSEric Dumazet return min_t(unsigned int, mtu, IP6_MAX_MTU); 1436d33e4553SDavid S. Miller } 1437d33e4553SDavid S. Miller 14383b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 14393b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 14405d0bbeebSThomas Graf 14413b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 144287a11578SDavid S. Miller struct flowi6 *fl6) 14431da177e4SLinus Torvalds { 144487a11578SDavid S. Miller struct dst_entry *dst; 14451da177e4SLinus Torvalds struct rt6_info *rt; 14461da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1447c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 14481da177e4SLinus Torvalds 144938308473SDavid S. Miller if (unlikely(!idev)) 1450122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 14511da177e4SLinus Torvalds 14528b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, dev, 0, NULL); 145338308473SDavid S. Miller if (unlikely(!rt)) { 14541da177e4SLinus Torvalds in6_dev_put(idev); 145587a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 14561da177e4SLinus Torvalds goto out; 14571da177e4SLinus Torvalds } 14581da177e4SLinus Torvalds 14598e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 14608e2ec639SYan, Zheng rt->dst.output = ip6_output; 1461d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 1462550bab42SJulian Anastasov rt->rt6i_gateway = fl6->daddr; 146387a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 14648e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 14658e2ec639SYan, Zheng rt->rt6i_idev = idev; 146614edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 14671da177e4SLinus Torvalds 14683b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1469d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1470d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 14713b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 14721da177e4SLinus Torvalds 14735578689aSDaniel Lezcano fib6_force_start_gc(net); 14741da177e4SLinus Torvalds 147587a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 147687a11578SDavid S. Miller 14771da177e4SLinus Torvalds out: 147887a11578SDavid S. Miller return dst; 14791da177e4SLinus Torvalds } 14801da177e4SLinus Torvalds 14813d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 14821da177e4SLinus Torvalds { 1483e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 14843d0f24a7SStephen Hemminger int more = 0; 14851da177e4SLinus Torvalds 14863b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 14873b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 14885d0bbeebSThomas Graf 14891da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 14901da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 14911da177e4SLinus Torvalds *pprev = dst->next; 14921da177e4SLinus Torvalds dst_free(dst); 14931da177e4SLinus Torvalds } else { 14941da177e4SLinus Torvalds pprev = &dst->next; 14953d0f24a7SStephen Hemminger ++more; 14961da177e4SLinus Torvalds } 14971da177e4SLinus Torvalds } 14981da177e4SLinus Torvalds 14993b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 15005d0bbeebSThomas Graf 15013d0f24a7SStephen Hemminger return more; 15021da177e4SLinus Torvalds } 15031da177e4SLinus Torvalds 15041e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 15051e493d19SDavid S. Miller void *arg) 15061e493d19SDavid S. Miller { 15071e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 15081e493d19SDavid S. Miller 15091e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 15101e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 15111e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 15121e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 15131e493d19SDavid S. Miller if (func(rt, arg)) { 15141e493d19SDavid S. Miller *pprev = dst->next; 15151e493d19SDavid S. Miller dst_free(dst); 15161e493d19SDavid S. Miller } else { 15171e493d19SDavid S. Miller pprev = &dst->next; 15181e493d19SDavid S. Miller } 15191e493d19SDavid S. Miller } 15201e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 15211e493d19SDavid S. Miller } 15221e493d19SDavid S. Miller 1523569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 15241da177e4SLinus Torvalds { 152586393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 15267019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 15277019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 15287019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 15297019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 15307019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1531fc66f95cSEric Dumazet int entries; 15321da177e4SLinus Torvalds 1533fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 153449a18d86SMichal Kubeček if (time_after(rt_last_gc + rt_min_interval, jiffies) && 1535fc66f95cSEric Dumazet entries <= rt_max_size) 15361da177e4SLinus Torvalds goto out; 15371da177e4SLinus Torvalds 15386891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 153914956643SLi RongQing fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true); 1540fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1541fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 15427019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 15431da177e4SLinus Torvalds out: 15447019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1545fc66f95cSEric Dumazet return entries > rt_max_size; 15461da177e4SLinus Torvalds } 15471da177e4SLinus Torvalds 1548e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc, 1549e715b6d3SFlorian Westphal const struct fib6_config *cfg) 1550e715b6d3SFlorian Westphal { 1551e715b6d3SFlorian Westphal struct nlattr *nla; 1552e715b6d3SFlorian Westphal int remaining; 1553e715b6d3SFlorian Westphal u32 *mp; 1554e715b6d3SFlorian Westphal 155563159f29SIan Morris if (!cfg->fc_mx) 1556e715b6d3SFlorian Westphal return 0; 1557e715b6d3SFlorian Westphal 1558e715b6d3SFlorian Westphal mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 1559e715b6d3SFlorian Westphal if (unlikely(!mp)) 1560e715b6d3SFlorian Westphal return -ENOMEM; 1561e715b6d3SFlorian Westphal 1562e715b6d3SFlorian Westphal nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 1563e715b6d3SFlorian Westphal int type = nla_type(nla); 1564e715b6d3SFlorian Westphal 1565e715b6d3SFlorian Westphal if (type) { 1566ea697639SDaniel Borkmann u32 val; 1567ea697639SDaniel Borkmann 1568e715b6d3SFlorian Westphal if (unlikely(type > RTAX_MAX)) 1569e715b6d3SFlorian Westphal goto err; 1570ea697639SDaniel Borkmann if (type == RTAX_CC_ALGO) { 1571ea697639SDaniel Borkmann char tmp[TCP_CA_NAME_MAX]; 1572e715b6d3SFlorian Westphal 1573ea697639SDaniel Borkmann nla_strlcpy(tmp, nla, sizeof(tmp)); 1574ea697639SDaniel Borkmann val = tcp_ca_get_key_by_name(tmp); 1575ea697639SDaniel Borkmann if (val == TCP_CA_UNSPEC) 1576ea697639SDaniel Borkmann goto err; 1577ea697639SDaniel Borkmann } else { 1578ea697639SDaniel Borkmann val = nla_get_u32(nla); 1579ea697639SDaniel Borkmann } 1580ea697639SDaniel Borkmann 1581ea697639SDaniel Borkmann mp[type - 1] = val; 1582e715b6d3SFlorian Westphal __set_bit(type - 1, mxc->mx_valid); 1583e715b6d3SFlorian Westphal } 1584e715b6d3SFlorian Westphal } 1585e715b6d3SFlorian Westphal 1586e715b6d3SFlorian Westphal mxc->mx = mp; 1587e715b6d3SFlorian Westphal 1588e715b6d3SFlorian Westphal return 0; 1589e715b6d3SFlorian Westphal err: 1590e715b6d3SFlorian Westphal kfree(mp); 1591e715b6d3SFlorian Westphal return -EINVAL; 1592e715b6d3SFlorian Westphal } 15931da177e4SLinus Torvalds 159486872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg) 15951da177e4SLinus Torvalds { 15961da177e4SLinus Torvalds int err; 15975578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 15981da177e4SLinus Torvalds struct rt6_info *rt = NULL; 15991da177e4SLinus Torvalds struct net_device *dev = NULL; 16001da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1601c71099acSThomas Graf struct fib6_table *table; 1602e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 16031da177e4SLinus Torvalds int addr_type; 16041da177e4SLinus Torvalds 160586872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 16061da177e4SLinus Torvalds return -EINVAL; 16071da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 160886872cb5SThomas Graf if (cfg->fc_src_len) 16091da177e4SLinus Torvalds return -EINVAL; 16101da177e4SLinus Torvalds #endif 161186872cb5SThomas Graf if (cfg->fc_ifindex) { 16121da177e4SLinus Torvalds err = -ENODEV; 16135578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 16141da177e4SLinus Torvalds if (!dev) 16151da177e4SLinus Torvalds goto out; 16161da177e4SLinus Torvalds idev = in6_dev_get(dev); 16171da177e4SLinus Torvalds if (!idev) 16181da177e4SLinus Torvalds goto out; 16191da177e4SLinus Torvalds } 16201da177e4SLinus Torvalds 162186872cb5SThomas Graf if (cfg->fc_metric == 0) 162286872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 16231da177e4SLinus Torvalds 1624c71099acSThomas Graf err = -ENOBUFS; 162538308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1626d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1627d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 162838308473SDavid S. Miller if (!table) { 1629f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1630d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1631d71314b4SMatti Vaittinen } 1632d71314b4SMatti Vaittinen } else { 1633d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1634d71314b4SMatti Vaittinen } 163538308473SDavid S. Miller 163638308473SDavid S. Miller if (!table) 1637c71099acSThomas Graf goto out; 1638c71099acSThomas Graf 1639c88507fbSSabrina Dubroca rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table); 16401da177e4SLinus Torvalds 164138308473SDavid S. Miller if (!rt) { 16421da177e4SLinus Torvalds err = -ENOMEM; 16431da177e4SLinus Torvalds goto out; 16441da177e4SLinus Torvalds } 16451da177e4SLinus Torvalds 16461716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 16471716a961SGao feng rt6_set_expires(rt, jiffies + 16481716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 16491716a961SGao feng else 16501716a961SGao feng rt6_clean_expires(rt); 16511da177e4SLinus Torvalds 165286872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 165386872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 165486872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 165586872cb5SThomas Graf 165686872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 16571da177e4SLinus Torvalds 16581da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1659d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1660ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1661ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 16621da177e4SLinus Torvalds else 1663d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 16641da177e4SLinus Torvalds 1665d8d1f30bSChangli Gao rt->dst.output = ip6_output; 16661da177e4SLinus Torvalds 166786872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 166886872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 1669afc4eef8SMartin KaFai Lau if (rt->rt6i_dst.plen == 128) 167011d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 16711da177e4SLinus Torvalds 16721da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 167386872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 167486872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 16751da177e4SLinus Torvalds #endif 16761da177e4SLinus Torvalds 167786872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 16781da177e4SLinus Torvalds 16791da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 16801da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 16811da177e4SLinus Torvalds */ 168286872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 168338308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 168438308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 168538308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 16861da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 16875578689aSDaniel Lezcano if (dev != net->loopback_dev) { 16881da177e4SLinus Torvalds if (dev) { 16891da177e4SLinus Torvalds dev_put(dev); 16901da177e4SLinus Torvalds in6_dev_put(idev); 16911da177e4SLinus Torvalds } 16925578689aSDaniel Lezcano dev = net->loopback_dev; 16931da177e4SLinus Torvalds dev_hold(dev); 16941da177e4SLinus Torvalds idev = in6_dev_get(dev); 16951da177e4SLinus Torvalds if (!idev) { 16961da177e4SLinus Torvalds err = -ENODEV; 16971da177e4SLinus Torvalds goto out; 16981da177e4SLinus Torvalds } 16991da177e4SLinus Torvalds } 17001da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1701ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1702ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1703ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 1704aad88724SEric Dumazet rt->dst.output = dst_discard_sk; 17057150aedeSKamala R rt->dst.input = dst_discard; 1706ef2c7d7bSNicolas Dichtel break; 1707ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1708ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 17097150aedeSKamala R rt->dst.output = ip6_pkt_prohibit_out; 17107150aedeSKamala R rt->dst.input = ip6_pkt_prohibit; 1711ef2c7d7bSNicolas Dichtel break; 1712b4949ab2SNicolas Dichtel case RTN_THROW: 1713ef2c7d7bSNicolas Dichtel default: 17147150aedeSKamala R rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN 17157150aedeSKamala R : -ENETUNREACH; 17167150aedeSKamala R rt->dst.output = ip6_pkt_discard_out; 17177150aedeSKamala R rt->dst.input = ip6_pkt_discard; 1718ef2c7d7bSNicolas Dichtel break; 1719ef2c7d7bSNicolas Dichtel } 17201da177e4SLinus Torvalds goto install_route; 17211da177e4SLinus Torvalds } 17221da177e4SLinus Torvalds 172386872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1724b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 17251da177e4SLinus Torvalds int gwa_type; 17261da177e4SLinus Torvalds 172786872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 172848ed7b26SFlorian Westphal 172948ed7b26SFlorian Westphal /* if gw_addr is local we will fail to detect this in case 173048ed7b26SFlorian Westphal * address is still TENTATIVE (DAD in progress). rt6_lookup() 173148ed7b26SFlorian Westphal * will return already-added prefix route via interface that 173248ed7b26SFlorian Westphal * prefix route was assigned to, which might be non-loopback. 173348ed7b26SFlorian Westphal */ 173448ed7b26SFlorian Westphal err = -EINVAL; 173548ed7b26SFlorian Westphal if (ipv6_chk_addr_and_flags(net, gw_addr, NULL, 0, 0)) 173648ed7b26SFlorian Westphal goto out; 173748ed7b26SFlorian Westphal 17384e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 17391da177e4SLinus Torvalds gwa_type = ipv6_addr_type(gw_addr); 17401da177e4SLinus Torvalds 17411da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 17421da177e4SLinus Torvalds struct rt6_info *grt; 17431da177e4SLinus Torvalds 17441da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 17451da177e4SLinus Torvalds addresses as nexthop address. 17461da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 17471da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 17481da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 17491da177e4SLinus Torvalds some exceptions. --ANK 17501da177e4SLinus Torvalds */ 17511da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 17521da177e4SLinus Torvalds goto out; 17531da177e4SLinus Torvalds 17545578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 17551da177e4SLinus Torvalds 17561da177e4SLinus Torvalds err = -EHOSTUNREACH; 175738308473SDavid S. Miller if (!grt) 17581da177e4SLinus Torvalds goto out; 17591da177e4SLinus Torvalds if (dev) { 1760d1918542SDavid S. Miller if (dev != grt->dst.dev) { 176194e187c0SAmerigo Wang ip6_rt_put(grt); 17621da177e4SLinus Torvalds goto out; 17631da177e4SLinus Torvalds } 17641da177e4SLinus Torvalds } else { 1765d1918542SDavid S. Miller dev = grt->dst.dev; 17661da177e4SLinus Torvalds idev = grt->rt6i_idev; 17671da177e4SLinus Torvalds dev_hold(dev); 17681da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 17691da177e4SLinus Torvalds } 17701da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 17711da177e4SLinus Torvalds err = 0; 177294e187c0SAmerigo Wang ip6_rt_put(grt); 17731da177e4SLinus Torvalds 17741da177e4SLinus Torvalds if (err) 17751da177e4SLinus Torvalds goto out; 17761da177e4SLinus Torvalds } 17771da177e4SLinus Torvalds err = -EINVAL; 177838308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 17791da177e4SLinus Torvalds goto out; 17801da177e4SLinus Torvalds } 17811da177e4SLinus Torvalds 17821da177e4SLinus Torvalds err = -ENODEV; 178338308473SDavid S. Miller if (!dev) 17841da177e4SLinus Torvalds goto out; 17851da177e4SLinus Torvalds 1786c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1787c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1788c3968a85SDaniel Walter err = -EINVAL; 1789c3968a85SDaniel Walter goto out; 1790c3968a85SDaniel Walter } 17914e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1792c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1793c3968a85SDaniel Walter } else 1794c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1795c3968a85SDaniel Walter 179686872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 17971da177e4SLinus Torvalds 17981da177e4SLinus Torvalds install_route: 1799d8d1f30bSChangli Gao rt->dst.dev = dev; 18001da177e4SLinus Torvalds rt->rt6i_idev = idev; 1801c71099acSThomas Graf rt->rt6i_table = table; 180263152fc0SDaniel Lezcano 1803c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 180463152fc0SDaniel Lezcano 1805e715b6d3SFlorian Westphal err = ip6_convert_metrics(&mxc, cfg); 1806e715b6d3SFlorian Westphal if (err) 1807e715b6d3SFlorian Westphal goto out; 18081da177e4SLinus Torvalds 1809e715b6d3SFlorian Westphal err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); 1810e715b6d3SFlorian Westphal 1811e715b6d3SFlorian Westphal kfree(mxc.mx); 1812e715b6d3SFlorian Westphal return err; 18131da177e4SLinus Torvalds out: 18141da177e4SLinus Torvalds if (dev) 18151da177e4SLinus Torvalds dev_put(dev); 18161da177e4SLinus Torvalds if (idev) 18171da177e4SLinus Torvalds in6_dev_put(idev); 18181da177e4SLinus Torvalds if (rt) 1819d8d1f30bSChangli Gao dst_free(&rt->dst); 18201da177e4SLinus Torvalds return err; 18211da177e4SLinus Torvalds } 18221da177e4SLinus Torvalds 182386872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 18241da177e4SLinus Torvalds { 18251da177e4SLinus Torvalds int err; 1826c71099acSThomas Graf struct fib6_table *table; 1827d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 18281da177e4SLinus Torvalds 18296825a26cSGao feng if (rt == net->ipv6.ip6_null_entry) { 18306825a26cSGao feng err = -ENOENT; 18316825a26cSGao feng goto out; 18326825a26cSGao feng } 18336c813a72SPatrick McHardy 1834c71099acSThomas Graf table = rt->rt6i_table; 1835c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 183686872cb5SThomas Graf err = fib6_del(rt, info); 1837c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 18381da177e4SLinus Torvalds 18396825a26cSGao feng out: 184094e187c0SAmerigo Wang ip6_rt_put(rt); 18411da177e4SLinus Torvalds return err; 18421da177e4SLinus Torvalds } 18431da177e4SLinus Torvalds 1844e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 1845e0a1ad73SThomas Graf { 18464d1169c1SDenis V. Lunev struct nl_info info = { 1847d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 18484d1169c1SDenis V. Lunev }; 1849528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 1850e0a1ad73SThomas Graf } 1851e0a1ad73SThomas Graf 185286872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 18531da177e4SLinus Torvalds { 1854c71099acSThomas Graf struct fib6_table *table; 18551da177e4SLinus Torvalds struct fib6_node *fn; 18561da177e4SLinus Torvalds struct rt6_info *rt; 18571da177e4SLinus Torvalds int err = -ESRCH; 18581da177e4SLinus Torvalds 18595578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 186038308473SDavid S. Miller if (!table) 1861c71099acSThomas Graf return err; 18621da177e4SLinus Torvalds 1863c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1864c71099acSThomas Graf 1865c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 186686872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 186786872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 18681da177e4SLinus Torvalds 18691da177e4SLinus Torvalds if (fn) { 1870d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 18711f56a01fSMartin KaFai Lau if ((rt->rt6i_flags & RTF_CACHE) && 18721f56a01fSMartin KaFai Lau !(cfg->fc_flags & RTF_CACHE)) 18731f56a01fSMartin KaFai Lau continue; 187486872cb5SThomas Graf if (cfg->fc_ifindex && 1875d1918542SDavid S. Miller (!rt->dst.dev || 1876d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 18771da177e4SLinus Torvalds continue; 187886872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 187986872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 18801da177e4SLinus Torvalds continue; 188186872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 18821da177e4SLinus Torvalds continue; 1883d8d1f30bSChangli Gao dst_hold(&rt->dst); 1884c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 18851da177e4SLinus Torvalds 188686872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 18871da177e4SLinus Torvalds } 18881da177e4SLinus Torvalds } 1889c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 18901da177e4SLinus Torvalds 18911da177e4SLinus Torvalds return err; 18921da177e4SLinus Torvalds } 18931da177e4SLinus Torvalds 18946700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 1895a6279458SYOSHIFUJI Hideaki { 1896e8599ff4SDavid S. Miller struct net *net = dev_net(skb->dev); 1897a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 1898e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 1899e8599ff4SDavid S. Miller struct ndisc_options ndopts; 1900e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 1901e8599ff4SDavid S. Miller struct neighbour *neigh; 190271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 struct rd_msg *msg; 19036e157b6aSDavid S. Miller int optlen, on_link; 19046e157b6aSDavid S. Miller u8 *lladdr; 1905e8599ff4SDavid S. Miller 190629a3cad5SSimon Horman optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 190771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 optlen -= sizeof(*msg); 1908e8599ff4SDavid S. Miller 1909e8599ff4SDavid S. Miller if (optlen < 0) { 19106e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 1911e8599ff4SDavid S. Miller return; 1912e8599ff4SDavid S. Miller } 1913e8599ff4SDavid S. Miller 191471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 msg = (struct rd_msg *)icmp6_hdr(skb); 1915e8599ff4SDavid S. Miller 191671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_is_multicast(&msg->dest)) { 19176e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 1918e8599ff4SDavid S. Miller return; 1919e8599ff4SDavid S. Miller } 1920e8599ff4SDavid S. Miller 19216e157b6aSDavid S. Miller on_link = 0; 192271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_equal(&msg->dest, &msg->target)) { 1923e8599ff4SDavid S. Miller on_link = 1; 192471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 } else if (ipv6_addr_type(&msg->target) != 1925e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 19266e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 1927e8599ff4SDavid S. Miller return; 1928e8599ff4SDavid S. Miller } 1929e8599ff4SDavid S. Miller 1930e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 1931e8599ff4SDavid S. Miller if (!in6_dev) 1932e8599ff4SDavid S. Miller return; 1933e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 1934e8599ff4SDavid S. Miller return; 1935e8599ff4SDavid S. Miller 1936e8599ff4SDavid S. Miller /* RFC2461 8.1: 1937e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 1938e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 1939e8599ff4SDavid S. Miller */ 1940e8599ff4SDavid S. Miller 194171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) { 1942e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 1943e8599ff4SDavid S. Miller return; 1944e8599ff4SDavid S. Miller } 19456e157b6aSDavid S. Miller 19466e157b6aSDavid S. Miller lladdr = NULL; 1947e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 1948e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 1949e8599ff4SDavid S. Miller skb->dev); 1950e8599ff4SDavid S. Miller if (!lladdr) { 1951e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 1952e8599ff4SDavid S. Miller return; 1953e8599ff4SDavid S. Miller } 1954e8599ff4SDavid S. Miller } 1955e8599ff4SDavid S. Miller 19566e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 19576e157b6aSDavid S. Miller if (rt == net->ipv6.ip6_null_entry) { 19586e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 19596e157b6aSDavid S. Miller return; 19606e157b6aSDavid S. Miller } 19616e157b6aSDavid S. Miller 19626e157b6aSDavid S. Miller /* Redirect received -> path was valid. 19636e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 19646e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 19656e157b6aSDavid S. Miller */ 19666e157b6aSDavid S. Miller dst_confirm(&rt->dst); 19676e157b6aSDavid S. Miller 196871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 1969e8599ff4SDavid S. Miller if (!neigh) 1970e8599ff4SDavid S. Miller return; 1971e8599ff4SDavid S. Miller 19721da177e4SLinus Torvalds /* 19731da177e4SLinus Torvalds * We have finally decided to accept it. 19741da177e4SLinus Torvalds */ 19751da177e4SLinus Torvalds 19761da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 19771da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 19781da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 19791da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 19801da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 19811da177e4SLinus Torvalds ); 19821da177e4SLinus Torvalds 198371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 nrt = ip6_rt_copy(rt, &msg->dest); 198438308473SDavid S. Miller if (!nrt) 19851da177e4SLinus Torvalds goto out; 19861da177e4SLinus Torvalds 19871da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 19881da177e4SLinus Torvalds if (on_link) 19891da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 19901da177e4SLinus Torvalds 19914e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 19921da177e4SLinus Torvalds 199340e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 19941da177e4SLinus Torvalds goto out; 19951da177e4SLinus Torvalds 1996d8d1f30bSChangli Gao netevent.old = &rt->dst; 1997d8d1f30bSChangli Gao netevent.new = &nrt->dst; 199871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 netevent.daddr = &msg->dest; 199960592833SYOSHIFUJI Hideaki / 吉藤英明 netevent.neigh = neigh; 20008d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 20018d71740cSTom Tucker 20021da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 20036e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 2004e0a1ad73SThomas Graf ip6_del_rt(rt); 20051da177e4SLinus Torvalds } 20061da177e4SLinus Torvalds 20071da177e4SLinus Torvalds out: 2008e8599ff4SDavid S. Miller neigh_release(neigh); 20096e157b6aSDavid S. Miller } 20106e157b6aSDavid S. Miller 20111da177e4SLinus Torvalds /* 20121da177e4SLinus Torvalds * Misc support functions 20131da177e4SLinus Torvalds */ 20141da177e4SLinus Torvalds 20154b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) 20164b32b5adSMartin KaFai Lau { 20174b32b5adSMartin KaFai Lau BUG_ON(from->dst.from); 20184b32b5adSMartin KaFai Lau 20194b32b5adSMartin KaFai Lau rt->rt6i_flags &= ~RTF_EXPIRES; 20204b32b5adSMartin KaFai Lau dst_hold(&from->dst); 20214b32b5adSMartin KaFai Lau rt->dst.from = &from->dst; 20224b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true); 20234b32b5adSMartin KaFai Lau } 20244b32b5adSMartin KaFai Lau 20251716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 202621efcfa0SEric Dumazet const struct in6_addr *dest) 20271da177e4SLinus Torvalds { 2028d1918542SDavid S. Miller struct net *net = dev_net(ort->dst.dev); 20294b32b5adSMartin KaFai Lau struct rt6_info *rt; 20304b32b5adSMartin KaFai Lau 20314b32b5adSMartin KaFai Lau if (ort->rt6i_flags & RTF_CACHE) 20324b32b5adSMartin KaFai Lau ort = (struct rt6_info *)ort->dst.from; 20334b32b5adSMartin KaFai Lau 20344b32b5adSMartin KaFai Lau rt = ip6_dst_alloc(net, ort->dst.dev, 0, 20358b96d22dSDavid S. Miller ort->rt6i_table); 20361da177e4SLinus Torvalds 20371da177e4SLinus Torvalds if (rt) { 2038d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 2039d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 20408e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 20411da177e4SLinus Torvalds 20424e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *dest; 20438e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 2044d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 20451da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 20461da177e4SLinus Torvalds if (rt->rt6i_idev) 20471da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 2048d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 20494e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 20501716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 20511716a961SGao feng rt6_set_from(rt, ort); 20521da177e4SLinus Torvalds rt->rt6i_metric = 0; 20531da177e4SLinus Torvalds 20541da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 20551da177e4SLinus Torvalds memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 20561da177e4SLinus Torvalds #endif 20570f6c6392SFlorian Westphal memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key)); 2058c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 20591da177e4SLinus Torvalds } 20601da177e4SLinus Torvalds return rt; 20611da177e4SLinus Torvalds } 20621da177e4SLinus Torvalds 206370ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 2064efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 2065b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2066b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 206770ceb4f5SYOSHIFUJI Hideaki { 206870ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 206970ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 2070c71099acSThomas Graf struct fib6_table *table; 207170ceb4f5SYOSHIFUJI Hideaki 2072efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 207338308473SDavid S. Miller if (!table) 2074c71099acSThomas Graf return NULL; 2075c71099acSThomas Graf 20765744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2077c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0); 207870ceb4f5SYOSHIFUJI Hideaki if (!fn) 207970ceb4f5SYOSHIFUJI Hideaki goto out; 208070ceb4f5SYOSHIFUJI Hideaki 2081d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 2082d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 208370ceb4f5SYOSHIFUJI Hideaki continue; 208470ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 208570ceb4f5SYOSHIFUJI Hideaki continue; 208670ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 208770ceb4f5SYOSHIFUJI Hideaki continue; 2088d8d1f30bSChangli Gao dst_hold(&rt->dst); 208970ceb4f5SYOSHIFUJI Hideaki break; 209070ceb4f5SYOSHIFUJI Hideaki } 209170ceb4f5SYOSHIFUJI Hideaki out: 20925744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 209370ceb4f5SYOSHIFUJI Hideaki return rt; 209470ceb4f5SYOSHIFUJI Hideaki } 209570ceb4f5SYOSHIFUJI Hideaki 2096efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 2097b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2098b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 209995c96174SEric Dumazet unsigned int pref) 210070ceb4f5SYOSHIFUJI Hideaki { 210186872cb5SThomas Graf struct fib6_config cfg = { 210286872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 2103238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 210486872cb5SThomas Graf .fc_ifindex = ifindex, 210586872cb5SThomas Graf .fc_dst_len = prefixlen, 210686872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 210786872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 210815e47304SEric W. Biederman .fc_nlinfo.portid = 0, 2109efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 2110efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 211186872cb5SThomas Graf }; 211270ceb4f5SYOSHIFUJI Hideaki 21134e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 21144e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 211586872cb5SThomas Graf 2116e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 2117e317da96SYOSHIFUJI Hideaki if (!prefixlen) 211886872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 211970ceb4f5SYOSHIFUJI Hideaki 212086872cb5SThomas Graf ip6_route_add(&cfg); 212170ceb4f5SYOSHIFUJI Hideaki 2122efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 212370ceb4f5SYOSHIFUJI Hideaki } 212470ceb4f5SYOSHIFUJI Hideaki #endif 212570ceb4f5SYOSHIFUJI Hideaki 2126b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 21271da177e4SLinus Torvalds { 21281da177e4SLinus Torvalds struct rt6_info *rt; 2129c71099acSThomas Graf struct fib6_table *table; 21301da177e4SLinus Torvalds 2131c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 213238308473SDavid S. Miller if (!table) 2133c71099acSThomas Graf return NULL; 21341da177e4SLinus Torvalds 21355744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2136d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 2137d1918542SDavid S. Miller if (dev == rt->dst.dev && 2138045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 21391da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 21401da177e4SLinus Torvalds break; 21411da177e4SLinus Torvalds } 21421da177e4SLinus Torvalds if (rt) 2143d8d1f30bSChangli Gao dst_hold(&rt->dst); 21445744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 21451da177e4SLinus Torvalds return rt; 21461da177e4SLinus Torvalds } 21471da177e4SLinus Torvalds 2148b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 2149ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 2150ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 21511da177e4SLinus Torvalds { 215286872cb5SThomas Graf struct fib6_config cfg = { 215386872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 2154238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 215586872cb5SThomas Graf .fc_ifindex = dev->ifindex, 215686872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 215786872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 215815e47304SEric W. Biederman .fc_nlinfo.portid = 0, 21595578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 2160c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 216186872cb5SThomas Graf }; 21621da177e4SLinus Torvalds 21634e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 21641da177e4SLinus Torvalds 216586872cb5SThomas Graf ip6_route_add(&cfg); 21661da177e4SLinus Torvalds 21671da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 21681da177e4SLinus Torvalds } 21691da177e4SLinus Torvalds 21707b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 21711da177e4SLinus Torvalds { 21721da177e4SLinus Torvalds struct rt6_info *rt; 2173c71099acSThomas Graf struct fib6_table *table; 2174c71099acSThomas Graf 2175c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 21767b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 217738308473SDavid S. Miller if (!table) 2178c71099acSThomas Graf return; 21791da177e4SLinus Torvalds 21801da177e4SLinus Torvalds restart: 2181c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2182d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 21833e8b0ac3SLorenzo Colitti if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 21843e8b0ac3SLorenzo Colitti (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { 2185d8d1f30bSChangli Gao dst_hold(&rt->dst); 2186c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2187e0a1ad73SThomas Graf ip6_del_rt(rt); 21881da177e4SLinus Torvalds goto restart; 21891da177e4SLinus Torvalds } 21901da177e4SLinus Torvalds } 2191c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 21921da177e4SLinus Torvalds } 21931da177e4SLinus Torvalds 21945578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 21955578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 219686872cb5SThomas Graf struct fib6_config *cfg) 219786872cb5SThomas Graf { 219886872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 219986872cb5SThomas Graf 220086872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 220186872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 220286872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 220386872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 220486872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 220586872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 220686872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 220786872cb5SThomas Graf 22085578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 2209f1243c2dSBenjamin Thery 22104e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 22114e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 22124e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 221386872cb5SThomas Graf } 221486872cb5SThomas Graf 22155578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 22161da177e4SLinus Torvalds { 221786872cb5SThomas Graf struct fib6_config cfg; 22181da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 22191da177e4SLinus Torvalds int err; 22201da177e4SLinus Torvalds 22211da177e4SLinus Torvalds switch (cmd) { 22221da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 22231da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 2224af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 22251da177e4SLinus Torvalds return -EPERM; 22261da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 22271da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 22281da177e4SLinus Torvalds if (err) 22291da177e4SLinus Torvalds return -EFAULT; 22301da177e4SLinus Torvalds 22315578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 223286872cb5SThomas Graf 22331da177e4SLinus Torvalds rtnl_lock(); 22341da177e4SLinus Torvalds switch (cmd) { 22351da177e4SLinus Torvalds case SIOCADDRT: 223686872cb5SThomas Graf err = ip6_route_add(&cfg); 22371da177e4SLinus Torvalds break; 22381da177e4SLinus Torvalds case SIOCDELRT: 223986872cb5SThomas Graf err = ip6_route_del(&cfg); 22401da177e4SLinus Torvalds break; 22411da177e4SLinus Torvalds default: 22421da177e4SLinus Torvalds err = -EINVAL; 22431da177e4SLinus Torvalds } 22441da177e4SLinus Torvalds rtnl_unlock(); 22451da177e4SLinus Torvalds 22461da177e4SLinus Torvalds return err; 22473ff50b79SStephen Hemminger } 22481da177e4SLinus Torvalds 22491da177e4SLinus Torvalds return -EINVAL; 22501da177e4SLinus Torvalds } 22511da177e4SLinus Torvalds 22521da177e4SLinus Torvalds /* 22531da177e4SLinus Torvalds * Drop the packet on the floor 22541da177e4SLinus Torvalds */ 22551da177e4SLinus Torvalds 2256d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 22571da177e4SLinus Torvalds { 2258612f09e8SYOSHIFUJI Hideaki int type; 2259adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2260612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2261612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 22620660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 226345bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 22643bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 22653bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2266612f09e8SYOSHIFUJI Hideaki break; 2267612f09e8SYOSHIFUJI Hideaki } 2268612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2269612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 22703bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 22713bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2272612f09e8SYOSHIFUJI Hideaki break; 2273612f09e8SYOSHIFUJI Hideaki } 22743ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 22751da177e4SLinus Torvalds kfree_skb(skb); 22761da177e4SLinus Torvalds return 0; 22771da177e4SLinus Torvalds } 22781da177e4SLinus Torvalds 22799ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 22809ce8ade0SThomas Graf { 2281612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 22829ce8ade0SThomas Graf } 22839ce8ade0SThomas Graf 2284aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb) 22851da177e4SLinus Torvalds { 2286adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2287612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 22881da177e4SLinus Torvalds } 22891da177e4SLinus Torvalds 22909ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 22919ce8ade0SThomas Graf { 2292612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 22939ce8ade0SThomas Graf } 22949ce8ade0SThomas Graf 2295aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb) 22969ce8ade0SThomas Graf { 2297adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2298612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 22999ce8ade0SThomas Graf } 23009ce8ade0SThomas Graf 23011da177e4SLinus Torvalds /* 23021da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 23031da177e4SLinus Torvalds */ 23041da177e4SLinus Torvalds 23051da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 23061da177e4SLinus Torvalds const struct in6_addr *addr, 23078f031519SDavid S. Miller bool anycast) 23081da177e4SLinus Torvalds { 2309c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 2310a3300ef4SHannes Frederic Sowa struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 2311a3300ef4SHannes Frederic Sowa DST_NOCOUNT, NULL); 2312a3300ef4SHannes Frederic Sowa if (!rt) 23131da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 23141da177e4SLinus Torvalds 23151da177e4SLinus Torvalds in6_dev_hold(idev); 23161da177e4SLinus Torvalds 231711d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2318d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2319d8d1f30bSChangli Gao rt->dst.output = ip6_output; 23201da177e4SLinus Torvalds rt->rt6i_idev = idev; 23211da177e4SLinus Torvalds 23221da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 232358c4fb86SYOSHIFUJI Hideaki if (anycast) 232458c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 232558c4fb86SYOSHIFUJI Hideaki else 23261da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 23271da177e4SLinus Torvalds 2328550bab42SJulian Anastasov rt->rt6i_gateway = *addr; 23294e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 23301da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 23315578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 23321da177e4SLinus Torvalds 2333d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 23341da177e4SLinus Torvalds 23351da177e4SLinus Torvalds return rt; 23361da177e4SLinus Torvalds } 23371da177e4SLinus Torvalds 2338c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2339c3968a85SDaniel Walter struct rt6_info *rt, 2340b71d1d42SEric Dumazet const struct in6_addr *daddr, 2341c3968a85SDaniel Walter unsigned int prefs, 2342c3968a85SDaniel Walter struct in6_addr *saddr) 2343c3968a85SDaniel Walter { 2344e16e888bSMarkus Stenberg struct inet6_dev *idev = 2345e16e888bSMarkus Stenberg rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL; 2346c3968a85SDaniel Walter int err = 0; 2347e16e888bSMarkus Stenberg if (rt && rt->rt6i_prefsrc.plen) 23484e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2349c3968a85SDaniel Walter else 2350c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2351c3968a85SDaniel Walter daddr, prefs, saddr); 2352c3968a85SDaniel Walter return err; 2353c3968a85SDaniel Walter } 2354c3968a85SDaniel Walter 2355c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2356c3968a85SDaniel Walter struct arg_dev_net_ip { 2357c3968a85SDaniel Walter struct net_device *dev; 2358c3968a85SDaniel Walter struct net *net; 2359c3968a85SDaniel Walter struct in6_addr *addr; 2360c3968a85SDaniel Walter }; 2361c3968a85SDaniel Walter 2362c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2363c3968a85SDaniel Walter { 2364c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2365c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2366c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2367c3968a85SDaniel Walter 2368d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2369c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2370c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2371c3968a85SDaniel Walter /* remove prefsrc entry */ 2372c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2373c3968a85SDaniel Walter } 2374c3968a85SDaniel Walter return 0; 2375c3968a85SDaniel Walter } 2376c3968a85SDaniel Walter 2377c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2378c3968a85SDaniel Walter { 2379c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2380c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2381c3968a85SDaniel Walter .dev = ifp->idev->dev, 2382c3968a85SDaniel Walter .net = net, 2383c3968a85SDaniel Walter .addr = &ifp->addr, 2384c3968a85SDaniel Walter }; 23850c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 2386c3968a85SDaniel Walter } 2387c3968a85SDaniel Walter 2388be7a010dSDuan Jiong #define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) 2389be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 2390be7a010dSDuan Jiong 2391be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */ 2392be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg) 2393be7a010dSDuan Jiong { 2394be7a010dSDuan Jiong struct in6_addr *gateway = (struct in6_addr *)arg; 2395be7a010dSDuan Jiong 2396be7a010dSDuan Jiong if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) || 2397be7a010dSDuan Jiong ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) && 2398be7a010dSDuan Jiong ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { 2399be7a010dSDuan Jiong return -1; 2400be7a010dSDuan Jiong } 2401be7a010dSDuan Jiong return 0; 2402be7a010dSDuan Jiong } 2403be7a010dSDuan Jiong 2404be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 2405be7a010dSDuan Jiong { 2406be7a010dSDuan Jiong fib6_clean_all(net, fib6_clean_tohost, gateway); 2407be7a010dSDuan Jiong } 2408be7a010dSDuan Jiong 24098ed67789SDaniel Lezcano struct arg_dev_net { 24108ed67789SDaniel Lezcano struct net_device *dev; 24118ed67789SDaniel Lezcano struct net *net; 24128ed67789SDaniel Lezcano }; 24138ed67789SDaniel Lezcano 24141da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 24151da177e4SLinus Torvalds { 2416bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2417bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 24188ed67789SDaniel Lezcano 2419d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2420c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 24211da177e4SLinus Torvalds return -1; 2422c159d30cSDavid S. Miller 24231da177e4SLinus Torvalds return 0; 24241da177e4SLinus Torvalds } 24251da177e4SLinus Torvalds 2426f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 24271da177e4SLinus Torvalds { 24288ed67789SDaniel Lezcano struct arg_dev_net adn = { 24298ed67789SDaniel Lezcano .dev = dev, 24308ed67789SDaniel Lezcano .net = net, 24318ed67789SDaniel Lezcano }; 24328ed67789SDaniel Lezcano 24330c3584d5SLi RongQing fib6_clean_all(net, fib6_ifdown, &adn); 24341e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 2435*8d0b94afSMartin KaFai Lau rt6_uncached_list_flush_dev(net, dev); 24361da177e4SLinus Torvalds } 24371da177e4SLinus Torvalds 243895c96174SEric Dumazet struct rt6_mtu_change_arg { 24391da177e4SLinus Torvalds struct net_device *dev; 244095c96174SEric Dumazet unsigned int mtu; 24411da177e4SLinus Torvalds }; 24421da177e4SLinus Torvalds 24431da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 24441da177e4SLinus Torvalds { 24451da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 24461da177e4SLinus Torvalds struct inet6_dev *idev; 24471da177e4SLinus Torvalds 24481da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 24491da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 24501da177e4SLinus Torvalds We still use this lock to block changes 24511da177e4SLinus Torvalds caused by addrconf/ndisc. 24521da177e4SLinus Torvalds */ 24531da177e4SLinus Torvalds 24541da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 245538308473SDavid S. Miller if (!idev) 24561da177e4SLinus Torvalds return 0; 24571da177e4SLinus Torvalds 24581da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 24591da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 24601da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 24611da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 24621da177e4SLinus Torvalds */ 24631da177e4SLinus Torvalds /* 24641da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 24651da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 24661da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 24671da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 24681da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 24691da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 24701da177e4SLinus Torvalds PMTU discouvery. 24711da177e4SLinus Torvalds */ 2472d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 24734b32b5adSMartin KaFai Lau !dst_metric_locked(&rt->dst, RTAX_MTU)) { 24744b32b5adSMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) { 24754b32b5adSMartin KaFai Lau /* For RTF_CACHE with rt6i_pmtu == 0 24764b32b5adSMartin KaFai Lau * (i.e. a redirected route), 24774b32b5adSMartin KaFai Lau * the metrics of its rt->dst.from has already 24784b32b5adSMartin KaFai Lau * been updated. 24794b32b5adSMartin KaFai Lau */ 24804b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu) 24814b32b5adSMartin KaFai Lau rt->rt6i_pmtu = arg->mtu; 24824b32b5adSMartin KaFai Lau } else if (dst_mtu(&rt->dst) >= arg->mtu || 2483d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 24844b32b5adSMartin KaFai Lau dst_mtu(&rt->dst) == idev->cnf.mtu6)) { 2485defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2486566cfd8fSSimon Arlott } 24874b32b5adSMartin KaFai Lau } 24881da177e4SLinus Torvalds return 0; 24891da177e4SLinus Torvalds } 24901da177e4SLinus Torvalds 249195c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 24921da177e4SLinus Torvalds { 2493c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2494c71099acSThomas Graf .dev = dev, 2495c71099acSThomas Graf .mtu = mtu, 2496c71099acSThomas Graf }; 24971da177e4SLinus Torvalds 24980c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 24991da177e4SLinus Torvalds } 25001da177e4SLinus Torvalds 2501ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 25025176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 250386872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2504ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 250586872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 250686872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 250751ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2508c78ba6d6SLubomir Rintel [RTA_PREF] = { .type = NLA_U8 }, 250986872cb5SThomas Graf }; 251086872cb5SThomas Graf 251186872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 251286872cb5SThomas Graf struct fib6_config *cfg) 25131da177e4SLinus Torvalds { 251486872cb5SThomas Graf struct rtmsg *rtm; 251586872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 2516c78ba6d6SLubomir Rintel unsigned int pref; 251786872cb5SThomas Graf int err; 25181da177e4SLinus Torvalds 251986872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 252086872cb5SThomas Graf if (err < 0) 252186872cb5SThomas Graf goto errout; 25221da177e4SLinus Torvalds 252386872cb5SThomas Graf err = -EINVAL; 252486872cb5SThomas Graf rtm = nlmsg_data(nlh); 252586872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 252686872cb5SThomas Graf 252786872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 252886872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 252986872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 253086872cb5SThomas Graf cfg->fc_flags = RTF_UP; 253186872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2532ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 253386872cb5SThomas Graf 2534ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2535ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2536b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2537b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 253886872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 253986872cb5SThomas Graf 2540ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2541ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2542ab79ad14SMaciej Żenczykowski 25431f56a01fSMartin KaFai Lau if (rtm->rtm_flags & RTM_F_CLONED) 25441f56a01fSMartin KaFai Lau cfg->fc_flags |= RTF_CACHE; 25451f56a01fSMartin KaFai Lau 254615e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 254786872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 25483b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 254986872cb5SThomas Graf 255086872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 255167b61f6cSJiri Benc cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 255286872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 25531da177e4SLinus Torvalds } 255486872cb5SThomas Graf 255586872cb5SThomas Graf if (tb[RTA_DST]) { 255686872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 255786872cb5SThomas Graf 255886872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 255986872cb5SThomas Graf goto errout; 256086872cb5SThomas Graf 256186872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 25621da177e4SLinus Torvalds } 256386872cb5SThomas Graf 256486872cb5SThomas Graf if (tb[RTA_SRC]) { 256586872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 256686872cb5SThomas Graf 256786872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 256886872cb5SThomas Graf goto errout; 256986872cb5SThomas Graf 257086872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 25711da177e4SLinus Torvalds } 257286872cb5SThomas Graf 2573c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 257467b61f6cSJiri Benc cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 2575c3968a85SDaniel Walter 257686872cb5SThomas Graf if (tb[RTA_OIF]) 257786872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 257886872cb5SThomas Graf 257986872cb5SThomas Graf if (tb[RTA_PRIORITY]) 258086872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 258186872cb5SThomas Graf 258286872cb5SThomas Graf if (tb[RTA_METRICS]) { 258386872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 258486872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 25851da177e4SLinus Torvalds } 258686872cb5SThomas Graf 258786872cb5SThomas Graf if (tb[RTA_TABLE]) 258886872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 258986872cb5SThomas Graf 259051ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 259151ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 259251ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 259351ebd318SNicolas Dichtel } 259451ebd318SNicolas Dichtel 2595c78ba6d6SLubomir Rintel if (tb[RTA_PREF]) { 2596c78ba6d6SLubomir Rintel pref = nla_get_u8(tb[RTA_PREF]); 2597c78ba6d6SLubomir Rintel if (pref != ICMPV6_ROUTER_PREF_LOW && 2598c78ba6d6SLubomir Rintel pref != ICMPV6_ROUTER_PREF_HIGH) 2599c78ba6d6SLubomir Rintel pref = ICMPV6_ROUTER_PREF_MEDIUM; 2600c78ba6d6SLubomir Rintel cfg->fc_flags |= RTF_PREF(pref); 2601c78ba6d6SLubomir Rintel } 2602c78ba6d6SLubomir Rintel 260386872cb5SThomas Graf err = 0; 260486872cb5SThomas Graf errout: 260586872cb5SThomas Graf return err; 26061da177e4SLinus Torvalds } 26071da177e4SLinus Torvalds 260851ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add) 260951ebd318SNicolas Dichtel { 261051ebd318SNicolas Dichtel struct fib6_config r_cfg; 261151ebd318SNicolas Dichtel struct rtnexthop *rtnh; 261251ebd318SNicolas Dichtel int remaining; 261351ebd318SNicolas Dichtel int attrlen; 261451ebd318SNicolas Dichtel int err = 0, last_err = 0; 261551ebd318SNicolas Dichtel 261635f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len; 261751ebd318SNicolas Dichtel beginning: 261851ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 261951ebd318SNicolas Dichtel 262051ebd318SNicolas Dichtel /* Parse a Multipath Entry */ 262151ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 262251ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 262351ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 262451ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 262551ebd318SNicolas Dichtel 262651ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 262751ebd318SNicolas Dichtel if (attrlen > 0) { 262851ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 262951ebd318SNicolas Dichtel 263051ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 263151ebd318SNicolas Dichtel if (nla) { 263267b61f6cSJiri Benc r_cfg.fc_gateway = nla_get_in6_addr(nla); 263351ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 263451ebd318SNicolas Dichtel } 263551ebd318SNicolas Dichtel } 263651ebd318SNicolas Dichtel err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg); 263751ebd318SNicolas Dichtel if (err) { 263851ebd318SNicolas Dichtel last_err = err; 263951ebd318SNicolas Dichtel /* If we are trying to remove a route, do not stop the 264051ebd318SNicolas Dichtel * loop when ip6_route_del() fails (because next hop is 264151ebd318SNicolas Dichtel * already gone), we should try to remove all next hops. 264251ebd318SNicolas Dichtel */ 264351ebd318SNicolas Dichtel if (add) { 264451ebd318SNicolas Dichtel /* If add fails, we should try to delete all 264551ebd318SNicolas Dichtel * next hops that have been already added. 264651ebd318SNicolas Dichtel */ 264751ebd318SNicolas Dichtel add = 0; 264835f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len - remaining; 264951ebd318SNicolas Dichtel goto beginning; 265051ebd318SNicolas Dichtel } 265151ebd318SNicolas Dichtel } 26521a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 265327596472SMichal Kubeček * these flags after the first nexthop: if there is a collision, 265427596472SMichal Kubeček * we have already failed to add the first nexthop: 265527596472SMichal Kubeček * fib6_add_rt2node() has rejected it; when replacing, old 265627596472SMichal Kubeček * nexthops have been replaced by first new, the rest should 265727596472SMichal Kubeček * be added to it. 26581a72418bSNicolas Dichtel */ 265927596472SMichal Kubeček cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | 266027596472SMichal Kubeček NLM_F_REPLACE); 266151ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 266251ebd318SNicolas Dichtel } 266351ebd318SNicolas Dichtel 266451ebd318SNicolas Dichtel return last_err; 266551ebd318SNicolas Dichtel } 266651ebd318SNicolas Dichtel 2667661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) 26681da177e4SLinus Torvalds { 266986872cb5SThomas Graf struct fib6_config cfg; 267086872cb5SThomas Graf int err; 26711da177e4SLinus Torvalds 267286872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 267386872cb5SThomas Graf if (err < 0) 267486872cb5SThomas Graf return err; 267586872cb5SThomas Graf 267651ebd318SNicolas Dichtel if (cfg.fc_mp) 267751ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 0); 267851ebd318SNicolas Dichtel else 267986872cb5SThomas Graf return ip6_route_del(&cfg); 26801da177e4SLinus Torvalds } 26811da177e4SLinus Torvalds 2682661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) 26831da177e4SLinus Torvalds { 268486872cb5SThomas Graf struct fib6_config cfg; 268586872cb5SThomas Graf int err; 26861da177e4SLinus Torvalds 268786872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 268886872cb5SThomas Graf if (err < 0) 268986872cb5SThomas Graf return err; 269086872cb5SThomas Graf 269151ebd318SNicolas Dichtel if (cfg.fc_mp) 269251ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 1); 269351ebd318SNicolas Dichtel else 269486872cb5SThomas Graf return ip6_route_add(&cfg); 26951da177e4SLinus Torvalds } 26961da177e4SLinus Torvalds 2697339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void) 2698339bf98fSThomas Graf { 2699339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 2700339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 2701339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 2702339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 2703339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 2704339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 2705339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 2706339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 2707339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 27086a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 2709ea697639SDaniel Borkmann + nla_total_size(sizeof(struct rta_cacheinfo)) 2710c78ba6d6SLubomir Rintel + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 2711c78ba6d6SLubomir Rintel + nla_total_size(1); /* RTA_PREF */ 2712339bf98fSThomas Graf } 2713339bf98fSThomas Graf 2714191cd582SBrian Haley static int rt6_fill_node(struct net *net, 2715191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 27160d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 271715e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 27187bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 27191da177e4SLinus Torvalds { 27204b32b5adSMartin KaFai Lau u32 metrics[RTAX_MAX]; 27211da177e4SLinus Torvalds struct rtmsg *rtm; 27221da177e4SLinus Torvalds struct nlmsghdr *nlh; 2723e3703b3dSThomas Graf long expires; 27249e762a4aSPatrick McHardy u32 table; 27251da177e4SLinus Torvalds 27261da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 27271da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 27281da177e4SLinus Torvalds /* success since this is not a prefix route */ 27291da177e4SLinus Torvalds return 1; 27301da177e4SLinus Torvalds } 27311da177e4SLinus Torvalds } 27321da177e4SLinus Torvalds 273315e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 273438308473SDavid S. Miller if (!nlh) 273526932566SPatrick McHardy return -EMSGSIZE; 27362d7202bfSThomas Graf 27372d7202bfSThomas Graf rtm = nlmsg_data(nlh); 27381da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 27391da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 27401da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 27411da177e4SLinus Torvalds rtm->rtm_tos = 0; 2742c71099acSThomas Graf if (rt->rt6i_table) 27439e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 2744c71099acSThomas Graf else 27459e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 27469e762a4aSPatrick McHardy rtm->rtm_table = table; 2747c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 2748c78679e8SDavid S. Miller goto nla_put_failure; 2749ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 2750ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 2751ef2c7d7bSNicolas Dichtel case -EINVAL: 2752ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 2753ef2c7d7bSNicolas Dichtel break; 2754ef2c7d7bSNicolas Dichtel case -EACCES: 2755ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 2756ef2c7d7bSNicolas Dichtel break; 2757b4949ab2SNicolas Dichtel case -EAGAIN: 2758b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 2759b4949ab2SNicolas Dichtel break; 2760ef2c7d7bSNicolas Dichtel default: 27611da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 2762ef2c7d7bSNicolas Dichtel break; 2763ef2c7d7bSNicolas Dichtel } 2764ef2c7d7bSNicolas Dichtel } 2765ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 2766ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 2767d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 27681da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 27691da177e4SLinus Torvalds else 27701da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 27711da177e4SLinus Torvalds rtm->rtm_flags = 0; 27721da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 27731da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 27741da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 27751da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 2776f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 2777f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 27781da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 2779f0396f60SDenis Ovsienko else 2780f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 2781f0396f60SDenis Ovsienko } 27821da177e4SLinus Torvalds 27831da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 27841da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 27851da177e4SLinus Torvalds 27861da177e4SLinus Torvalds if (dst) { 2787930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, dst)) 2788c78679e8SDavid S. Miller goto nla_put_failure; 27891da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 27901da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 2791930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr)) 2792c78679e8SDavid S. Miller goto nla_put_failure; 27931da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 27941da177e4SLinus Torvalds if (src) { 2795930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_SRC, src)) 2796c78679e8SDavid S. Miller goto nla_put_failure; 27971da177e4SLinus Torvalds rtm->rtm_src_len = 128; 2798c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 2799930345eaSJiri Benc nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr)) 2800c78679e8SDavid S. Miller goto nla_put_failure; 28011da177e4SLinus Torvalds #endif 28027bc570c8SYOSHIFUJI Hideaki if (iif) { 28037bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 28047bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 28058229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 28067bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 28077bc570c8SYOSHIFUJI Hideaki if (!nowait) { 28087bc570c8SYOSHIFUJI Hideaki if (err == 0) 28097bc570c8SYOSHIFUJI Hideaki return 0; 28107bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 28117bc570c8SYOSHIFUJI Hideaki } else { 28127bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 28137bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 28147bc570c8SYOSHIFUJI Hideaki } 28157bc570c8SYOSHIFUJI Hideaki } 28167bc570c8SYOSHIFUJI Hideaki } else 28177bc570c8SYOSHIFUJI Hideaki #endif 2818c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 2819c78679e8SDavid S. Miller goto nla_put_failure; 28207bc570c8SYOSHIFUJI Hideaki } else if (dst) { 28211da177e4SLinus Torvalds struct in6_addr saddr_buf; 2822c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 2823930345eaSJiri Benc nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 2824c78679e8SDavid S. Miller goto nla_put_failure; 2825c3968a85SDaniel Walter } 2826c3968a85SDaniel Walter 2827c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 2828c3968a85SDaniel Walter struct in6_addr saddr_buf; 28294e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 2830930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 2831c78679e8SDavid S. Miller goto nla_put_failure; 28321da177e4SLinus Torvalds } 28332d7202bfSThomas Graf 28344b32b5adSMartin KaFai Lau memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); 28354b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu) 28364b32b5adSMartin KaFai Lau metrics[RTAX_MTU - 1] = rt->rt6i_pmtu; 28374b32b5adSMartin KaFai Lau if (rtnetlink_put_metrics(skb, metrics) < 0) 28382d7202bfSThomas Graf goto nla_put_failure; 28392d7202bfSThomas Graf 2840dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_GATEWAY) { 2841930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0) 284294f826b8SEric Dumazet goto nla_put_failure; 284394f826b8SEric Dumazet } 28442d7202bfSThomas Graf 2845c78679e8SDavid S. Miller if (rt->dst.dev && 2846c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 2847c78679e8SDavid S. Miller goto nla_put_failure; 2848c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 2849c78679e8SDavid S. Miller goto nla_put_failure; 28508253947eSLi Wei 28518253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 285269cdf8f9SYOSHIFUJI Hideaki 285387a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 2854e3703b3dSThomas Graf goto nla_put_failure; 28551da177e4SLinus Torvalds 2856c78ba6d6SLubomir Rintel if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) 2857c78ba6d6SLubomir Rintel goto nla_put_failure; 2858c78ba6d6SLubomir Rintel 2859053c095aSJohannes Berg nlmsg_end(skb, nlh); 2860053c095aSJohannes Berg return 0; 28612d7202bfSThomas Graf 28622d7202bfSThomas Graf nla_put_failure: 286326932566SPatrick McHardy nlmsg_cancel(skb, nlh); 286426932566SPatrick McHardy return -EMSGSIZE; 28651da177e4SLinus Torvalds } 28661da177e4SLinus Torvalds 28671b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 28681da177e4SLinus Torvalds { 28691da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 28701da177e4SLinus Torvalds int prefix; 28711da177e4SLinus Torvalds 28722d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 28732d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 28741da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 28751da177e4SLinus Torvalds } else 28761da177e4SLinus Torvalds prefix = 0; 28771da177e4SLinus Torvalds 2878191cd582SBrian Haley return rt6_fill_node(arg->net, 2879191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 288015e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 28817bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 28821da177e4SLinus Torvalds } 28831da177e4SLinus Torvalds 2884661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) 28851da177e4SLinus Torvalds { 28863b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 2887ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 28881da177e4SLinus Torvalds struct rt6_info *rt; 2889ab364a6fSThomas Graf struct sk_buff *skb; 2890ab364a6fSThomas Graf struct rtmsg *rtm; 28914c9483b2SDavid S. Miller struct flowi6 fl6; 289272331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 2893ab364a6fSThomas Graf 2894ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 2895ab364a6fSThomas Graf if (err < 0) 2896ab364a6fSThomas Graf goto errout; 2897ab364a6fSThomas Graf 2898ab364a6fSThomas Graf err = -EINVAL; 28994c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2900ab364a6fSThomas Graf 2901ab364a6fSThomas Graf if (tb[RTA_SRC]) { 2902ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 2903ab364a6fSThomas Graf goto errout; 2904ab364a6fSThomas Graf 29054e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 2906ab364a6fSThomas Graf } 2907ab364a6fSThomas Graf 2908ab364a6fSThomas Graf if (tb[RTA_DST]) { 2909ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 2910ab364a6fSThomas Graf goto errout; 2911ab364a6fSThomas Graf 29124e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 2913ab364a6fSThomas Graf } 2914ab364a6fSThomas Graf 2915ab364a6fSThomas Graf if (tb[RTA_IIF]) 2916ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 2917ab364a6fSThomas Graf 2918ab364a6fSThomas Graf if (tb[RTA_OIF]) 291972331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 2920ab364a6fSThomas Graf 29212e47b291SLorenzo Colitti if (tb[RTA_MARK]) 29222e47b291SLorenzo Colitti fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 29232e47b291SLorenzo Colitti 2924ab364a6fSThomas Graf if (iif) { 2925ab364a6fSThomas Graf struct net_device *dev; 292672331bc0SShmulik Ladkani int flags = 0; 292772331bc0SShmulik Ladkani 29285578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 2929ab364a6fSThomas Graf if (!dev) { 2930ab364a6fSThomas Graf err = -ENODEV; 2931ab364a6fSThomas Graf goto errout; 2932ab364a6fSThomas Graf } 293372331bc0SShmulik Ladkani 293472331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 293572331bc0SShmulik Ladkani 293672331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 293772331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 293872331bc0SShmulik Ladkani 293972331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 294072331bc0SShmulik Ladkani flags); 294172331bc0SShmulik Ladkani } else { 294272331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 294372331bc0SShmulik Ladkani 294472331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 2945ab364a6fSThomas Graf } 29461da177e4SLinus Torvalds 29471da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 294838308473SDavid S. Miller if (!skb) { 294994e187c0SAmerigo Wang ip6_rt_put(rt); 2950ab364a6fSThomas Graf err = -ENOBUFS; 2951ab364a6fSThomas Graf goto errout; 2952ab364a6fSThomas Graf } 29531da177e4SLinus Torvalds 29541da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 29551da177e4SLinus Torvalds through good chunk of routing engine. 29561da177e4SLinus Torvalds */ 2957459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 29581da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 29591da177e4SLinus Torvalds 2960d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 29611da177e4SLinus Torvalds 29624c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 296315e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 29647bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 29651da177e4SLinus Torvalds if (err < 0) { 2966ab364a6fSThomas Graf kfree_skb(skb); 2967ab364a6fSThomas Graf goto errout; 29681da177e4SLinus Torvalds } 29691da177e4SLinus Torvalds 297015e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 2971ab364a6fSThomas Graf errout: 29721da177e4SLinus Torvalds return err; 29731da177e4SLinus Torvalds } 29741da177e4SLinus Torvalds 297586872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) 29761da177e4SLinus Torvalds { 29771da177e4SLinus Torvalds struct sk_buff *skb; 29785578689aSDaniel Lezcano struct net *net = info->nl_net; 2979528c4cebSDenis V. Lunev u32 seq; 2980528c4cebSDenis V. Lunev int err; 29810d51aa80SJamal Hadi Salim 2982528c4cebSDenis V. Lunev err = -ENOBUFS; 298338308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 298486872cb5SThomas Graf 2985339bf98fSThomas Graf skb = nlmsg_new(rt6_nlmsg_size(), gfp_any()); 298638308473SDavid S. Miller if (!skb) 298721713ebcSThomas Graf goto errout; 29881da177e4SLinus Torvalds 2989191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 299015e47304SEric W. Biederman event, info->portid, seq, 0, 0, 0); 299126932566SPatrick McHardy if (err < 0) { 299226932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 299326932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 299426932566SPatrick McHardy kfree_skb(skb); 299526932566SPatrick McHardy goto errout; 299626932566SPatrick McHardy } 299715e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 29985578689aSDaniel Lezcano info->nlh, gfp_any()); 29991ce85fe4SPablo Neira Ayuso return; 300021713ebcSThomas Graf errout: 300121713ebcSThomas Graf if (err < 0) 30025578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 30031da177e4SLinus Torvalds } 30041da177e4SLinus Torvalds 30058ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 3006351638e7SJiri Pirko unsigned long event, void *ptr) 30078ed67789SDaniel Lezcano { 3008351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 3009c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 30108ed67789SDaniel Lezcano 30118ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 3012d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 30138ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 30148ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3015d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 30168ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 3017d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 30188ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 30198ed67789SDaniel Lezcano #endif 30208ed67789SDaniel Lezcano } 30218ed67789SDaniel Lezcano 30228ed67789SDaniel Lezcano return NOTIFY_OK; 30238ed67789SDaniel Lezcano } 30248ed67789SDaniel Lezcano 30251da177e4SLinus Torvalds /* 30261da177e4SLinus Torvalds * /proc 30271da177e4SLinus Torvalds */ 30281da177e4SLinus Torvalds 30291da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 30301da177e4SLinus Torvalds 303133120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 303233120b30SAlexey Dobriyan .owner = THIS_MODULE, 303333120b30SAlexey Dobriyan .open = ipv6_route_open, 303433120b30SAlexey Dobriyan .read = seq_read, 303533120b30SAlexey Dobriyan .llseek = seq_lseek, 30368d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 303733120b30SAlexey Dobriyan }; 303833120b30SAlexey Dobriyan 30391da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 30401da177e4SLinus Torvalds { 304169ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 30421da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 304369ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 304469ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 304569ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 304669ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 304769ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 3048fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 304969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 30501da177e4SLinus Torvalds 30511da177e4SLinus Torvalds return 0; 30521da177e4SLinus Torvalds } 30531da177e4SLinus Torvalds 30541da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 30551da177e4SLinus Torvalds { 3056de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 305769ddb805SDaniel Lezcano } 305869ddb805SDaniel Lezcano 30599a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 30601da177e4SLinus Torvalds .owner = THIS_MODULE, 30611da177e4SLinus Torvalds .open = rt6_stats_seq_open, 30621da177e4SLinus Torvalds .read = seq_read, 30631da177e4SLinus Torvalds .llseek = seq_lseek, 3064b6fcbdb4SPavel Emelyanov .release = single_release_net, 30651da177e4SLinus Torvalds }; 30661da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 30671da177e4SLinus Torvalds 30681da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 30691da177e4SLinus Torvalds 30701da177e4SLinus Torvalds static 3071fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 30721da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 30731da177e4SLinus Torvalds { 3074c486da34SLucian Adrian Grijincu struct net *net; 3075c486da34SLucian Adrian Grijincu int delay; 3076c486da34SLucian Adrian Grijincu if (!write) 3077c486da34SLucian Adrian Grijincu return -EINVAL; 3078c486da34SLucian Adrian Grijincu 3079c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 3080c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 30818d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 30822ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 30831da177e4SLinus Torvalds return 0; 30841da177e4SLinus Torvalds } 30851da177e4SLinus Torvalds 3086fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 30871da177e4SLinus Torvalds { 30881da177e4SLinus Torvalds .procname = "flush", 30894990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 30901da177e4SLinus Torvalds .maxlen = sizeof(int), 309189c8b3a1SDave Jones .mode = 0200, 30926d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 30931da177e4SLinus Torvalds }, 30941da177e4SLinus Torvalds { 30951da177e4SLinus Torvalds .procname = "gc_thresh", 30969a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 30971da177e4SLinus Torvalds .maxlen = sizeof(int), 30981da177e4SLinus Torvalds .mode = 0644, 30996d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 31001da177e4SLinus Torvalds }, 31011da177e4SLinus Torvalds { 31021da177e4SLinus Torvalds .procname = "max_size", 31034990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 31041da177e4SLinus Torvalds .maxlen = sizeof(int), 31051da177e4SLinus Torvalds .mode = 0644, 31066d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 31071da177e4SLinus Torvalds }, 31081da177e4SLinus Torvalds { 31091da177e4SLinus Torvalds .procname = "gc_min_interval", 31104990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 31111da177e4SLinus Torvalds .maxlen = sizeof(int), 31121da177e4SLinus Torvalds .mode = 0644, 31136d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 31141da177e4SLinus Torvalds }, 31151da177e4SLinus Torvalds { 31161da177e4SLinus Torvalds .procname = "gc_timeout", 31174990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 31181da177e4SLinus Torvalds .maxlen = sizeof(int), 31191da177e4SLinus Torvalds .mode = 0644, 31206d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 31211da177e4SLinus Torvalds }, 31221da177e4SLinus Torvalds { 31231da177e4SLinus Torvalds .procname = "gc_interval", 31244990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 31251da177e4SLinus Torvalds .maxlen = sizeof(int), 31261da177e4SLinus Torvalds .mode = 0644, 31276d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 31281da177e4SLinus Torvalds }, 31291da177e4SLinus Torvalds { 31301da177e4SLinus Torvalds .procname = "gc_elasticity", 31314990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 31321da177e4SLinus Torvalds .maxlen = sizeof(int), 31331da177e4SLinus Torvalds .mode = 0644, 3134f3d3f616SMin Zhang .proc_handler = proc_dointvec, 31351da177e4SLinus Torvalds }, 31361da177e4SLinus Torvalds { 31371da177e4SLinus Torvalds .procname = "mtu_expires", 31384990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 31391da177e4SLinus Torvalds .maxlen = sizeof(int), 31401da177e4SLinus Torvalds .mode = 0644, 31416d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 31421da177e4SLinus Torvalds }, 31431da177e4SLinus Torvalds { 31441da177e4SLinus Torvalds .procname = "min_adv_mss", 31454990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 31461da177e4SLinus Torvalds .maxlen = sizeof(int), 31471da177e4SLinus Torvalds .mode = 0644, 3148f3d3f616SMin Zhang .proc_handler = proc_dointvec, 31491da177e4SLinus Torvalds }, 31501da177e4SLinus Torvalds { 31511da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 31524990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 31531da177e4SLinus Torvalds .maxlen = sizeof(int), 31541da177e4SLinus Torvalds .mode = 0644, 31556d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 31561da177e4SLinus Torvalds }, 3157f8572d8fSEric W. Biederman { } 31581da177e4SLinus Torvalds }; 31591da177e4SLinus Torvalds 31602c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 3161760f2d01SDaniel Lezcano { 3162760f2d01SDaniel Lezcano struct ctl_table *table; 3163760f2d01SDaniel Lezcano 3164760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 3165760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 3166760f2d01SDaniel Lezcano GFP_KERNEL); 31675ee09105SYOSHIFUJI Hideaki 31685ee09105SYOSHIFUJI Hideaki if (table) { 31695ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 3170c486da34SLucian Adrian Grijincu table[0].extra1 = net; 317186393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 31725ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 31735ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 31745ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 31755ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 31765ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 31775ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 31785ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 31799c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 3180464dc801SEric W. Biederman 3181464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 3182464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 3183464dc801SEric W. Biederman table[0].procname = NULL; 31845ee09105SYOSHIFUJI Hideaki } 31855ee09105SYOSHIFUJI Hideaki 3186760f2d01SDaniel Lezcano return table; 3187760f2d01SDaniel Lezcano } 31881da177e4SLinus Torvalds #endif 31891da177e4SLinus Torvalds 31902c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 3191cdb18761SDaniel Lezcano { 3192633d424bSPavel Emelyanov int ret = -ENOMEM; 31938ed67789SDaniel Lezcano 319486393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 319586393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 3196f2fc6a54SBenjamin Thery 3197fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 3198fc66f95cSEric Dumazet goto out_ip6_dst_ops; 3199fc66f95cSEric Dumazet 32008ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 32018ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 32028ed67789SDaniel Lezcano GFP_KERNEL); 32038ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 3204fc66f95cSEric Dumazet goto out_ip6_dst_entries; 3205d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 32068ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 3207d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 320862fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 320962fa8a84SDavid S. Miller ip6_template_metrics, true); 32108ed67789SDaniel Lezcano 32118ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 32128ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 32138ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 32148ed67789SDaniel Lezcano GFP_KERNEL); 321568fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 321668fffc67SPeter Zijlstra goto out_ip6_null_entry; 3217d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 32188ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 3219d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 322062fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 322162fa8a84SDavid S. Miller ip6_template_metrics, true); 32228ed67789SDaniel Lezcano 32238ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 32248ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 32258ed67789SDaniel Lezcano GFP_KERNEL); 322668fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 322768fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 3228d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 32298ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 3230d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 323162fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 323262fa8a84SDavid S. Miller ip6_template_metrics, true); 32338ed67789SDaniel Lezcano #endif 32348ed67789SDaniel Lezcano 3235b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 3236b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 3237b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 3238b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 3239b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 3240b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 3241b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 3242b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 3243b339a47cSPeter Zijlstra 32446891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 32456891a346SBenjamin Thery 32468ed67789SDaniel Lezcano ret = 0; 32478ed67789SDaniel Lezcano out: 32488ed67789SDaniel Lezcano return ret; 3249f2fc6a54SBenjamin Thery 325068fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 325168fffc67SPeter Zijlstra out_ip6_prohibit_entry: 325268fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 325368fffc67SPeter Zijlstra out_ip6_null_entry: 325468fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 325568fffc67SPeter Zijlstra #endif 3256fc66f95cSEric Dumazet out_ip6_dst_entries: 3257fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3258f2fc6a54SBenjamin Thery out_ip6_dst_ops: 3259f2fc6a54SBenjamin Thery goto out; 3260cdb18761SDaniel Lezcano } 3261cdb18761SDaniel Lezcano 32622c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 3263cdb18761SDaniel Lezcano { 32648ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 32658ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 32668ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 32678ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 32688ed67789SDaniel Lezcano #endif 326941bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3270cdb18761SDaniel Lezcano } 3271cdb18761SDaniel Lezcano 3272d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 3273d189634eSThomas Graf { 3274d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3275d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 3276d4beaa66SGao feng proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops); 3277d189634eSThomas Graf #endif 3278d189634eSThomas Graf return 0; 3279d189634eSThomas Graf } 3280d189634eSThomas Graf 3281d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 3282d189634eSThomas Graf { 3283d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3284ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 3285ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 3286d189634eSThomas Graf #endif 3287d189634eSThomas Graf } 3288d189634eSThomas Graf 3289cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 3290cdb18761SDaniel Lezcano .init = ip6_route_net_init, 3291cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 3292cdb18761SDaniel Lezcano }; 3293cdb18761SDaniel Lezcano 3294c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 3295c3426b47SDavid S. Miller { 3296c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 3297c3426b47SDavid S. Miller 3298c3426b47SDavid S. Miller if (!bp) 3299c3426b47SDavid S. Miller return -ENOMEM; 3300c3426b47SDavid S. Miller inet_peer_base_init(bp); 3301c3426b47SDavid S. Miller net->ipv6.peers = bp; 3302c3426b47SDavid S. Miller return 0; 3303c3426b47SDavid S. Miller } 3304c3426b47SDavid S. Miller 3305c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 3306c3426b47SDavid S. Miller { 3307c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 3308c3426b47SDavid S. Miller 3309c3426b47SDavid S. Miller net->ipv6.peers = NULL; 331056a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3311c3426b47SDavid S. Miller kfree(bp); 3312c3426b47SDavid S. Miller } 3313c3426b47SDavid S. Miller 33142b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3315c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3316c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3317c3426b47SDavid S. Miller }; 3318c3426b47SDavid S. Miller 3319d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3320d189634eSThomas Graf .init = ip6_route_net_init_late, 3321d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3322d189634eSThomas Graf }; 3323d189634eSThomas Graf 33248ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 33258ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 33268ed67789SDaniel Lezcano .priority = 0, 33278ed67789SDaniel Lezcano }; 33288ed67789SDaniel Lezcano 3329433d49c3SDaniel Lezcano int __init ip6_route_init(void) 33301da177e4SLinus Torvalds { 3331433d49c3SDaniel Lezcano int ret; 3332*8d0b94afSMartin KaFai Lau int cpu; 3333433d49c3SDaniel Lezcano 33349a7ec3a9SDaniel Lezcano ret = -ENOMEM; 33359a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 33369a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 33379a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 33389a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3339c19a28e1SFernando Carrijo goto out; 334014e50e57SDavid S. Miller 3341fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 33428ed67789SDaniel Lezcano if (ret) 3343bdb3289fSDaniel Lezcano goto out_kmem_cache; 3344bdb3289fSDaniel Lezcano 3345c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3346c3426b47SDavid S. Miller if (ret) 3347e8803b6cSDavid S. Miller goto out_dst_entries; 33482a0c451aSThomas Graf 33497e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 33507e52b33bSDavid S. Miller if (ret) 33517e52b33bSDavid S. Miller goto out_register_inetpeer; 3352c3426b47SDavid S. Miller 33535dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 33545dc121e9SArnaud Ebalard 33558ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 33568ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 33578ed67789SDaniel Lezcano * manually for init_net */ 3358d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 33598ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3360bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3361d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 33628ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3363d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 33648ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3365bdb3289fSDaniel Lezcano #endif 3366e8803b6cSDavid S. Miller ret = fib6_init(); 3367433d49c3SDaniel Lezcano if (ret) 33688ed67789SDaniel Lezcano goto out_register_subsys; 3369433d49c3SDaniel Lezcano 3370433d49c3SDaniel Lezcano ret = xfrm6_init(); 3371433d49c3SDaniel Lezcano if (ret) 3372e8803b6cSDavid S. Miller goto out_fib6_init; 3373c35b7e72SDaniel Lezcano 3374433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3375433d49c3SDaniel Lezcano if (ret) 3376433d49c3SDaniel Lezcano goto xfrm6_init; 33777e5449c2SDaniel Lezcano 3378d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3379d189634eSThomas Graf if (ret) 3380d189634eSThomas Graf goto fib6_rules_init; 3381d189634eSThomas Graf 3382433d49c3SDaniel Lezcano ret = -ENOBUFS; 3383c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3384c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3385c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3386d189634eSThomas Graf goto out_register_late_subsys; 3387433d49c3SDaniel Lezcano 33888ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3389cdb18761SDaniel Lezcano if (ret) 3390d189634eSThomas Graf goto out_register_late_subsys; 33918ed67789SDaniel Lezcano 3392*8d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 3393*8d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 3394*8d0b94afSMartin KaFai Lau 3395*8d0b94afSMartin KaFai Lau INIT_LIST_HEAD(&ul->head); 3396*8d0b94afSMartin KaFai Lau spin_lock_init(&ul->lock); 3397*8d0b94afSMartin KaFai Lau } 3398*8d0b94afSMartin KaFai Lau 3399433d49c3SDaniel Lezcano out: 3400433d49c3SDaniel Lezcano return ret; 3401433d49c3SDaniel Lezcano 3402d189634eSThomas Graf out_register_late_subsys: 3403d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3404433d49c3SDaniel Lezcano fib6_rules_init: 3405433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3406433d49c3SDaniel Lezcano xfrm6_init: 3407433d49c3SDaniel Lezcano xfrm6_fini(); 34082a0c451aSThomas Graf out_fib6_init: 34092a0c451aSThomas Graf fib6_gc_cleanup(); 34108ed67789SDaniel Lezcano out_register_subsys: 34118ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 34127e52b33bSDavid S. Miller out_register_inetpeer: 34137e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3414fc66f95cSEric Dumazet out_dst_entries: 3415fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3416433d49c3SDaniel Lezcano out_kmem_cache: 3417f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3418433d49c3SDaniel Lezcano goto out; 34191da177e4SLinus Torvalds } 34201da177e4SLinus Torvalds 34211da177e4SLinus Torvalds void ip6_route_cleanup(void) 34221da177e4SLinus Torvalds { 34238ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3424d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3425101367c2SThomas Graf fib6_rules_cleanup(); 34261da177e4SLinus Torvalds xfrm6_fini(); 34271da177e4SLinus Torvalds fib6_gc_cleanup(); 3428c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 34298ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 343041bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3431f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 34321da177e4SLinus Torvalds } 3433