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> 6119e42e45SRoopa Prabhu #include <net/lwtunnel.h> 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds #include <asm/uaccess.h> 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 661da177e4SLinus Torvalds #include <linux/sysctl.h> 671da177e4SLinus Torvalds #endif 681da177e4SLinus Torvalds 69afc154e9SHannes Frederic Sowa enum rt6_nud_state { 707e980569SJiri Benc RT6_NUD_FAIL_HARD = -3, 717e980569SJiri Benc RT6_NUD_FAIL_PROBE = -2, 727e980569SJiri Benc RT6_NUD_FAIL_DO_RR = -1, 73afc154e9SHannes Frederic Sowa RT6_NUD_SUCCEED = 1 74afc154e9SHannes Frederic Sowa }; 75afc154e9SHannes Frederic Sowa 7683a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort); 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 1088d0b94afSMartin KaFai Lau struct uncached_list { 1098d0b94afSMartin KaFai Lau spinlock_t lock; 1108d0b94afSMartin KaFai Lau struct list_head head; 1118d0b94afSMartin KaFai Lau }; 1128d0b94afSMartin KaFai Lau 1138d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list); 1148d0b94afSMartin KaFai Lau 1158d0b94afSMartin KaFai Lau static void rt6_uncached_list_add(struct rt6_info *rt) 1168d0b94afSMartin KaFai Lau { 1178d0b94afSMartin KaFai Lau struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list); 1188d0b94afSMartin KaFai Lau 1198d0b94afSMartin KaFai Lau rt->dst.flags |= DST_NOCACHE; 1208d0b94afSMartin KaFai Lau rt->rt6i_uncached_list = ul; 1218d0b94afSMartin KaFai Lau 1228d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1238d0b94afSMartin KaFai Lau list_add_tail(&rt->rt6i_uncached, &ul->head); 1248d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1258d0b94afSMartin KaFai Lau } 1268d0b94afSMartin KaFai Lau 1278d0b94afSMartin KaFai Lau static void rt6_uncached_list_del(struct rt6_info *rt) 1288d0b94afSMartin KaFai Lau { 1298d0b94afSMartin KaFai Lau if (!list_empty(&rt->rt6i_uncached)) { 1308d0b94afSMartin KaFai Lau struct uncached_list *ul = rt->rt6i_uncached_list; 1318d0b94afSMartin KaFai Lau 1328d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1338d0b94afSMartin KaFai Lau list_del(&rt->rt6i_uncached); 1348d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1358d0b94afSMartin KaFai Lau } 1368d0b94afSMartin KaFai Lau } 1378d0b94afSMartin KaFai Lau 1388d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) 1398d0b94afSMartin KaFai Lau { 1408d0b94afSMartin KaFai Lau struct net_device *loopback_dev = net->loopback_dev; 1418d0b94afSMartin KaFai Lau int cpu; 1428d0b94afSMartin KaFai Lau 1438d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 1448d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 1458d0b94afSMartin KaFai Lau struct rt6_info *rt; 1468d0b94afSMartin KaFai Lau 1478d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1488d0b94afSMartin KaFai Lau list_for_each_entry(rt, &ul->head, rt6i_uncached) { 1498d0b94afSMartin KaFai Lau struct inet6_dev *rt_idev = rt->rt6i_idev; 1508d0b94afSMartin KaFai Lau struct net_device *rt_dev = rt->dst.dev; 1518d0b94afSMartin KaFai Lau 1528d0b94afSMartin KaFai Lau if (rt_idev && (rt_idev->dev == dev || !dev) && 1538d0b94afSMartin KaFai Lau rt_idev->dev != loopback_dev) { 1548d0b94afSMartin KaFai Lau rt->rt6i_idev = in6_dev_get(loopback_dev); 1558d0b94afSMartin KaFai Lau in6_dev_put(rt_idev); 1568d0b94afSMartin KaFai Lau } 1578d0b94afSMartin KaFai Lau 1588d0b94afSMartin KaFai Lau if (rt_dev && (rt_dev == dev || !dev) && 1598d0b94afSMartin KaFai Lau rt_dev != loopback_dev) { 1608d0b94afSMartin KaFai Lau rt->dst.dev = loopback_dev; 1618d0b94afSMartin KaFai Lau dev_hold(rt->dst.dev); 1628d0b94afSMartin KaFai Lau dev_put(rt_dev); 1638d0b94afSMartin KaFai Lau } 1648d0b94afSMartin KaFai Lau } 1658d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1668d0b94afSMartin KaFai Lau } 1678d0b94afSMartin KaFai Lau } 1688d0b94afSMartin KaFai Lau 169d52d3997SMartin KaFai Lau static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt) 170d52d3997SMartin KaFai Lau { 171d52d3997SMartin KaFai Lau return dst_metrics_write_ptr(rt->dst.from); 172d52d3997SMartin KaFai Lau } 173d52d3997SMartin KaFai Lau 17406582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) 17506582540SDavid S. Miller { 17606582540SDavid S. Miller struct rt6_info *rt = (struct rt6_info *)dst; 17706582540SDavid S. Miller 178d52d3997SMartin KaFai Lau if (rt->rt6i_flags & RTF_PCPU) 179d52d3997SMartin KaFai Lau return rt6_pcpu_cow_metrics(rt); 180d52d3997SMartin KaFai Lau else if (rt->rt6i_flags & RTF_CACHE) 1814b32b5adSMartin KaFai Lau return NULL; 1824b32b5adSMartin KaFai Lau else 1833b471175SMartin KaFai Lau return dst_cow_metrics_generic(dst, old); 18406582540SDavid S. Miller } 18506582540SDavid S. Miller 186f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, 187f894cbf8SDavid S. Miller struct sk_buff *skb, 188f894cbf8SDavid S. Miller const void *daddr) 18939232973SDavid S. Miller { 19039232973SDavid S. Miller struct in6_addr *p = &rt->rt6i_gateway; 19139232973SDavid S. Miller 192a7563f34SDavid S. Miller if (!ipv6_addr_any(p)) 19339232973SDavid S. Miller return (const void *) p; 194f894cbf8SDavid S. Miller else if (skb) 195f894cbf8SDavid S. Miller return &ipv6_hdr(skb)->daddr; 19639232973SDavid S. Miller return daddr; 19739232973SDavid S. Miller } 19839232973SDavid S. Miller 199f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, 200f894cbf8SDavid S. Miller struct sk_buff *skb, 201f894cbf8SDavid S. Miller const void *daddr) 202d3aaeb38SDavid S. Miller { 20339232973SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 20439232973SDavid S. Miller struct neighbour *n; 20539232973SDavid S. Miller 206f894cbf8SDavid S. Miller daddr = choose_neigh_daddr(rt, skb, daddr); 2078e022ee6SYOSHIFUJI Hideaki / 吉藤英明 n = __ipv6_neigh_lookup(dst->dev, daddr); 208f83c7790SDavid S. Miller if (n) 209f83c7790SDavid S. Miller return n; 210f83c7790SDavid S. Miller return neigh_create(&nd_tbl, daddr, dst->dev); 211f83c7790SDavid S. Miller } 212f83c7790SDavid S. Miller 2139a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 2141da177e4SLinus Torvalds .family = AF_INET6, 2151da177e4SLinus Torvalds .gc = ip6_dst_gc, 2161da177e4SLinus Torvalds .gc_thresh = 1024, 2171da177e4SLinus Torvalds .check = ip6_dst_check, 2180dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 219ebb762f2SSteffen Klassert .mtu = ip6_mtu, 22006582540SDavid S. Miller .cow_metrics = ipv6_cow_metrics, 2211da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 2221da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 2231da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 2241da177e4SLinus Torvalds .link_failure = ip6_link_failure, 2251da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 2266e157b6aSDavid S. Miller .redirect = rt6_do_redirect, 2271ac06e03SHerbert Xu .local_out = __ip6_local_out, 228d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 2291da177e4SLinus Torvalds }; 2301da177e4SLinus Torvalds 231ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 232ec831ea7SRoland Dreier { 233618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 234618f9bc7SSteffen Klassert 235618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 236ec831ea7SRoland Dreier } 237ec831ea7SRoland Dreier 2386700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, 2396700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 24014e50e57SDavid S. Miller { 24114e50e57SDavid S. Miller } 24214e50e57SDavid S. Miller 2436700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, 2446700c270SDavid S. Miller struct sk_buff *skb) 245b587ee3bSDavid S. Miller { 246b587ee3bSDavid S. Miller } 247b587ee3bSDavid S. Miller 2480972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, 2490972ddb2SHeld Bernhard unsigned long old) 2500972ddb2SHeld Bernhard { 2510972ddb2SHeld Bernhard return NULL; 2520972ddb2SHeld Bernhard } 2530972ddb2SHeld Bernhard 25414e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 25514e50e57SDavid S. Miller .family = AF_INET6, 25614e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 25714e50e57SDavid S. Miller .check = ip6_dst_check, 258ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 259214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 26014e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 261b587ee3bSDavid S. Miller .redirect = ip6_rt_blackhole_redirect, 2620972ddb2SHeld Bernhard .cow_metrics = ip6_rt_blackhole_cow_metrics, 263d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 26414e50e57SDavid S. Miller }; 26514e50e57SDavid S. Miller 26662fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 26714edd87dSLi RongQing [RTAX_HOPLIMIT - 1] = 0, 26862fa8a84SDavid S. Miller }; 26962fa8a84SDavid S. Miller 270fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = { 2711da177e4SLinus Torvalds .dst = { 2721da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 2731da177e4SLinus Torvalds .__use = 1, 2742c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 2751da177e4SLinus Torvalds .error = -ENETUNREACH, 2761da177e4SLinus Torvalds .input = ip6_pkt_discard, 2771da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 2781da177e4SLinus Torvalds }, 2791da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2804f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 2811da177e4SLinus Torvalds .rt6i_metric = ~(u32) 0, 2821da177e4SLinus Torvalds .rt6i_ref = ATOMIC_INIT(1), 2831da177e4SLinus Torvalds }; 2841da177e4SLinus Torvalds 285101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 286101367c2SThomas Graf 287fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = { 288101367c2SThomas Graf .dst = { 289101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 290101367c2SThomas Graf .__use = 1, 2912c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 292101367c2SThomas Graf .error = -EACCES, 2939ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 2949ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 295101367c2SThomas Graf }, 296101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2974f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 298101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 299101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 300101367c2SThomas Graf }; 301101367c2SThomas Graf 302fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = { 303101367c2SThomas Graf .dst = { 304101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 305101367c2SThomas Graf .__use = 1, 3062c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 307101367c2SThomas Graf .error = -EINVAL, 308352e512cSHerbert Xu .input = dst_discard, 309aad88724SEric Dumazet .output = dst_discard_sk, 310101367c2SThomas Graf }, 311101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 3124f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 313101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 314101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 315101367c2SThomas Graf }; 316101367c2SThomas Graf 317101367c2SThomas Graf #endif 318101367c2SThomas Graf 3191da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 320d52d3997SMartin KaFai Lau static struct rt6_info *__ip6_dst_alloc(struct net *net, 321957c665fSDavid S. Miller struct net_device *dev, 3228b96d22dSDavid S. Miller int flags, 3238b96d22dSDavid S. Miller struct fib6_table *table) 3241da177e4SLinus Torvalds { 32597bab73fSDavid S. Miller struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 3266f3118b5SNicolas Dichtel 0, DST_OBSOLETE_FORCE_CHK, flags); 327cf911662SDavid S. Miller 32897bab73fSDavid S. Miller if (rt) { 3298104891bSSteffen Klassert struct dst_entry *dst = &rt->dst; 3308104891bSSteffen Klassert 3318104891bSSteffen Klassert memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 33251ebd318SNicolas Dichtel INIT_LIST_HEAD(&rt->rt6i_siblings); 3338d0b94afSMartin KaFai Lau INIT_LIST_HEAD(&rt->rt6i_uncached); 33497bab73fSDavid S. Miller } 335cf911662SDavid S. Miller return rt; 3361da177e4SLinus Torvalds } 3371da177e4SLinus Torvalds 338d52d3997SMartin KaFai Lau static struct rt6_info *ip6_dst_alloc(struct net *net, 339d52d3997SMartin KaFai Lau struct net_device *dev, 340d52d3997SMartin KaFai Lau int flags, 341d52d3997SMartin KaFai Lau struct fib6_table *table) 342d52d3997SMartin KaFai Lau { 343d52d3997SMartin KaFai Lau struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags, table); 344d52d3997SMartin KaFai Lau 345d52d3997SMartin KaFai Lau if (rt) { 346d52d3997SMartin KaFai Lau rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC); 347d52d3997SMartin KaFai Lau if (rt->rt6i_pcpu) { 348d52d3997SMartin KaFai Lau int cpu; 349d52d3997SMartin KaFai Lau 350d52d3997SMartin KaFai Lau for_each_possible_cpu(cpu) { 351d52d3997SMartin KaFai Lau struct rt6_info **p; 352d52d3997SMartin KaFai Lau 353d52d3997SMartin KaFai Lau p = per_cpu_ptr(rt->rt6i_pcpu, cpu); 354d52d3997SMartin KaFai Lau /* no one shares rt */ 355d52d3997SMartin KaFai Lau *p = NULL; 356d52d3997SMartin KaFai Lau } 357d52d3997SMartin KaFai Lau } else { 358d52d3997SMartin KaFai Lau dst_destroy((struct dst_entry *)rt); 359d52d3997SMartin KaFai Lau return NULL; 360d52d3997SMartin KaFai Lau } 361d52d3997SMartin KaFai Lau } 362d52d3997SMartin KaFai Lau 363d52d3997SMartin KaFai Lau return rt; 364d52d3997SMartin KaFai Lau } 365d52d3997SMartin KaFai Lau 3661da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 3671da177e4SLinus Torvalds { 3681da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 369ecd98837SYOSHIFUJI Hideaki / 吉藤英明 struct dst_entry *from = dst->from; 3708d0b94afSMartin KaFai Lau struct inet6_dev *idev; 3711da177e4SLinus Torvalds 3728e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 373d52d3997SMartin KaFai Lau free_percpu(rt->rt6i_pcpu); 3748d0b94afSMartin KaFai Lau rt6_uncached_list_del(rt); 3758d0b94afSMartin KaFai Lau 3768d0b94afSMartin KaFai Lau idev = rt->rt6i_idev; 37738308473SDavid S. Miller if (idev) { 3781da177e4SLinus Torvalds rt->rt6i_idev = NULL; 3791da177e4SLinus Torvalds in6_dev_put(idev); 3801da177e4SLinus Torvalds } 3811716a961SGao feng 382ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst->from = NULL; 383ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst_release(from); 384b3419363SDavid S. Miller } 385b3419363SDavid S. Miller 3861da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 3871da177e4SLinus Torvalds int how) 3881da177e4SLinus Torvalds { 3891da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3901da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3915a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 392c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 3931da177e4SLinus Torvalds 39497cac082SDavid S. Miller if (dev != loopback_dev) { 39597cac082SDavid S. Miller if (idev && idev->dev == dev) { 3965a3e55d6SDenis V. Lunev struct inet6_dev *loopback_idev = 3975a3e55d6SDenis V. Lunev in6_dev_get(loopback_dev); 39838308473SDavid S. Miller if (loopback_idev) { 3991da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 4001da177e4SLinus Torvalds in6_dev_put(idev); 4011da177e4SLinus Torvalds } 4021da177e4SLinus Torvalds } 40397cac082SDavid S. Miller } 4041da177e4SLinus Torvalds } 4051da177e4SLinus Torvalds 406a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt) 4071da177e4SLinus Torvalds { 4081716a961SGao feng if (rt->rt6i_flags & RTF_EXPIRES) { 4091716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 410a50feda5SEric Dumazet return true; 4111716a961SGao feng } else if (rt->dst.from) { 4123fd91fb3SLi RongQing return rt6_check_expired((struct rt6_info *) rt->dst.from); 4131716a961SGao feng } 414a50feda5SEric Dumazet return false; 4151da177e4SLinus Torvalds } 4161da177e4SLinus Torvalds 41751ebd318SNicolas Dichtel /* Multipath route selection: 41851ebd318SNicolas Dichtel * Hash based function using packet header and flowlabel. 41951ebd318SNicolas Dichtel * Adapted from fib_info_hashfn() 42051ebd318SNicolas Dichtel */ 42151ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count, 42251ebd318SNicolas Dichtel const struct flowi6 *fl6) 42351ebd318SNicolas Dichtel { 42451ebd318SNicolas Dichtel unsigned int val = fl6->flowi6_proto; 42551ebd318SNicolas Dichtel 426c08977bbSYOSHIFUJI Hideaki / 吉藤英明 val ^= ipv6_addr_hash(&fl6->daddr); 427c08977bbSYOSHIFUJI Hideaki / 吉藤英明 val ^= ipv6_addr_hash(&fl6->saddr); 42851ebd318SNicolas Dichtel 42951ebd318SNicolas Dichtel /* Work only if this not encapsulated */ 43051ebd318SNicolas Dichtel switch (fl6->flowi6_proto) { 43151ebd318SNicolas Dichtel case IPPROTO_UDP: 43251ebd318SNicolas Dichtel case IPPROTO_TCP: 43351ebd318SNicolas Dichtel case IPPROTO_SCTP: 434b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_sport; 435b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_dport; 43651ebd318SNicolas Dichtel break; 43751ebd318SNicolas Dichtel 43851ebd318SNicolas Dichtel case IPPROTO_ICMPV6: 439b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_icmp_type; 440b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_icmp_code; 44151ebd318SNicolas Dichtel break; 44251ebd318SNicolas Dichtel } 44351ebd318SNicolas Dichtel /* RFC6438 recommands to use flowlabel */ 444b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->flowlabel; 44551ebd318SNicolas Dichtel 44651ebd318SNicolas Dichtel /* Perhaps, we need to tune, this function? */ 44751ebd318SNicolas Dichtel val = val ^ (val >> 7) ^ (val >> 12); 44851ebd318SNicolas Dichtel return val % candidate_count; 44951ebd318SNicolas Dichtel } 45051ebd318SNicolas Dichtel 45151ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match, 45252bd4c0cSNicolas Dichtel struct flowi6 *fl6, int oif, 45352bd4c0cSNicolas Dichtel int strict) 45451ebd318SNicolas Dichtel { 45551ebd318SNicolas Dichtel struct rt6_info *sibling, *next_sibling; 45651ebd318SNicolas Dichtel int route_choosen; 45751ebd318SNicolas Dichtel 45851ebd318SNicolas Dichtel route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6); 45951ebd318SNicolas Dichtel /* Don't change the route, if route_choosen == 0 46051ebd318SNicolas Dichtel * (siblings does not include ourself) 46151ebd318SNicolas Dichtel */ 46251ebd318SNicolas Dichtel if (route_choosen) 46351ebd318SNicolas Dichtel list_for_each_entry_safe(sibling, next_sibling, 46451ebd318SNicolas Dichtel &match->rt6i_siblings, rt6i_siblings) { 46551ebd318SNicolas Dichtel route_choosen--; 46651ebd318SNicolas Dichtel if (route_choosen == 0) { 46752bd4c0cSNicolas Dichtel if (rt6_score_route(sibling, oif, strict) < 0) 46852bd4c0cSNicolas Dichtel break; 46951ebd318SNicolas Dichtel match = sibling; 47051ebd318SNicolas Dichtel break; 47151ebd318SNicolas Dichtel } 47251ebd318SNicolas Dichtel } 47351ebd318SNicolas Dichtel return match; 47451ebd318SNicolas Dichtel } 47551ebd318SNicolas Dichtel 4761da177e4SLinus Torvalds /* 477c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 4781da177e4SLinus Torvalds */ 4791da177e4SLinus Torvalds 4808ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 4818ed67789SDaniel Lezcano struct rt6_info *rt, 482b71d1d42SEric Dumazet const struct in6_addr *saddr, 4831da177e4SLinus Torvalds int oif, 484d420895eSYOSHIFUJI Hideaki int flags) 4851da177e4SLinus Torvalds { 4861da177e4SLinus Torvalds struct rt6_info *local = NULL; 4871da177e4SLinus Torvalds struct rt6_info *sprt; 4881da177e4SLinus Torvalds 489dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 490dd3abc4eSYOSHIFUJI Hideaki goto out; 491dd3abc4eSYOSHIFUJI Hideaki 492d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 493d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 494dd3abc4eSYOSHIFUJI Hideaki 495dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4961da177e4SLinus Torvalds if (dev->ifindex == oif) 4971da177e4SLinus Torvalds return sprt; 4981da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 49938308473SDavid S. Miller if (!sprt->rt6i_idev || 5001da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 501d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE && oif) 5021da177e4SLinus Torvalds continue; 5031da177e4SLinus Torvalds if (local && (!oif || 5041da177e4SLinus Torvalds local->rt6i_idev->dev->ifindex == oif)) 5051da177e4SLinus Torvalds continue; 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds local = sprt; 5081da177e4SLinus Torvalds } 509dd3abc4eSYOSHIFUJI Hideaki } else { 510dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 511dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 512dd3abc4eSYOSHIFUJI Hideaki return sprt; 513dd3abc4eSYOSHIFUJI Hideaki } 5141da177e4SLinus Torvalds } 5151da177e4SLinus Torvalds 516dd3abc4eSYOSHIFUJI Hideaki if (oif) { 5171da177e4SLinus Torvalds if (local) 5181da177e4SLinus Torvalds return local; 5191da177e4SLinus Torvalds 520d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 5218ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 5221da177e4SLinus Torvalds } 523dd3abc4eSYOSHIFUJI Hideaki out: 5241da177e4SLinus Torvalds return rt; 5251da177e4SLinus Torvalds } 5261da177e4SLinus Torvalds 52727097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 528c2f17e82SHannes Frederic Sowa struct __rt6_probe_work { 529c2f17e82SHannes Frederic Sowa struct work_struct work; 530c2f17e82SHannes Frederic Sowa struct in6_addr target; 531c2f17e82SHannes Frederic Sowa struct net_device *dev; 532c2f17e82SHannes Frederic Sowa }; 533c2f17e82SHannes Frederic Sowa 534c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w) 535c2f17e82SHannes Frederic Sowa { 536c2f17e82SHannes Frederic Sowa struct in6_addr mcaddr; 537c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work = 538c2f17e82SHannes Frederic Sowa container_of(w, struct __rt6_probe_work, work); 539c2f17e82SHannes Frederic Sowa 540c2f17e82SHannes Frederic Sowa addrconf_addr_solict_mult(&work->target, &mcaddr); 541c2f17e82SHannes Frederic Sowa ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL); 542c2f17e82SHannes Frederic Sowa dev_put(work->dev); 543662f5533SMichael Büsch kfree(work); 544c2f17e82SHannes Frederic Sowa } 545c2f17e82SHannes Frederic Sowa 54627097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 54727097255SYOSHIFUJI Hideaki { 548990edb42SMartin KaFai Lau struct __rt6_probe_work *work; 549f2c31e32SEric Dumazet struct neighbour *neigh; 55027097255SYOSHIFUJI Hideaki /* 55127097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 55227097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 55327097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 55427097255SYOSHIFUJI Hideaki * 55527097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 55627097255SYOSHIFUJI Hideaki * to no more than one per minute. 55727097255SYOSHIFUJI Hideaki */ 5582152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!rt || !(rt->rt6i_flags & RTF_GATEWAY)) 559fdd6681dSAmerigo Wang return; 5602152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 5612152caeaSYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 5622152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 5638d6c31bfSMartin KaFai Lau if (neigh->nud_state & NUD_VALID) 5648d6c31bfSMartin KaFai Lau goto out; 5658d6c31bfSMartin KaFai Lau 566990edb42SMartin KaFai Lau work = NULL; 5672152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_lock(&neigh->lock); 568990edb42SMartin KaFai Lau if (!(neigh->nud_state & NUD_VALID) && 569990edb42SMartin KaFai Lau time_after(jiffies, 570990edb42SMartin KaFai Lau neigh->updated + 571990edb42SMartin KaFai Lau rt->rt6i_idev->cnf.rtr_probe_interval)) { 572c2f17e82SHannes Frederic Sowa work = kmalloc(sizeof(*work), GFP_ATOMIC); 573990edb42SMartin KaFai Lau if (work) 5747e980569SJiri Benc __neigh_set_probe_once(neigh); 575990edb42SMartin KaFai Lau } 576c2f17e82SHannes Frederic Sowa write_unlock(&neigh->lock); 577990edb42SMartin KaFai Lau } else { 578990edb42SMartin KaFai Lau work = kmalloc(sizeof(*work), GFP_ATOMIC); 579990edb42SMartin KaFai Lau } 580c2f17e82SHannes Frederic Sowa 581c2f17e82SHannes Frederic Sowa if (work) { 582c2f17e82SHannes Frederic Sowa INIT_WORK(&work->work, rt6_probe_deferred); 583c2f17e82SHannes Frederic Sowa work->target = rt->rt6i_gateway; 584c2f17e82SHannes Frederic Sowa dev_hold(rt->dst.dev); 585c2f17e82SHannes Frederic Sowa work->dev = rt->dst.dev; 586c2f17e82SHannes Frederic Sowa schedule_work(&work->work); 587c2f17e82SHannes Frederic Sowa } 588990edb42SMartin KaFai Lau 5898d6c31bfSMartin KaFai Lau out: 5902152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 591f2c31e32SEric Dumazet } 59227097255SYOSHIFUJI Hideaki #else 59327097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 59427097255SYOSHIFUJI Hideaki { 59527097255SYOSHIFUJI Hideaki } 59627097255SYOSHIFUJI Hideaki #endif 59727097255SYOSHIFUJI Hideaki 5981da177e4SLinus Torvalds /* 599554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 6001da177e4SLinus Torvalds */ 601b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 6021da177e4SLinus Torvalds { 603d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 604161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 605554cfb7eSYOSHIFUJI Hideaki return 2; 606161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 607161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 608161980f4SDavid S. Miller return 1; 609554cfb7eSYOSHIFUJI Hideaki return 0; 6101da177e4SLinus Torvalds } 6111da177e4SLinus Torvalds 612afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt) 6131da177e4SLinus Torvalds { 614f2c31e32SEric Dumazet struct neighbour *neigh; 615afc154e9SHannes Frederic Sowa enum rt6_nud_state ret = RT6_NUD_FAIL_HARD; 616f2c31e32SEric Dumazet 6174d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 6184d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 619afc154e9SHannes Frederic Sowa return RT6_NUD_SUCCEED; 620145a3621SYOSHIFUJI Hideaki / 吉藤英明 621145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 622145a3621SYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 623145a3621SYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 624145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_lock(&neigh->lock); 625554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 626afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 627398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 628a5a81f0bSPaul Marks else if (!(neigh->nud_state & NUD_FAILED)) 629afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 6307e980569SJiri Benc else 6317e980569SJiri Benc ret = RT6_NUD_FAIL_PROBE; 632398bcbebSYOSHIFUJI Hideaki #endif 633145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_unlock(&neigh->lock); 634afc154e9SHannes Frederic Sowa } else { 635afc154e9SHannes Frederic Sowa ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 6367e980569SJiri Benc RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; 637a5a81f0bSPaul Marks } 638145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 639145a3621SYOSHIFUJI Hideaki / 吉藤英明 640a5a81f0bSPaul Marks return ret; 6411da177e4SLinus Torvalds } 6421da177e4SLinus Torvalds 643554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 644554cfb7eSYOSHIFUJI Hideaki int strict) 645554cfb7eSYOSHIFUJI Hideaki { 646a5a81f0bSPaul Marks int m; 6474d0c5911SYOSHIFUJI Hideaki 6484d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 64977d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 650afc154e9SHannes Frederic Sowa return RT6_NUD_FAIL_HARD; 651ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 652ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 653ebacaaa0SYOSHIFUJI Hideaki #endif 654afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) { 655afc154e9SHannes Frederic Sowa int n = rt6_check_neigh(rt); 656afc154e9SHannes Frederic Sowa if (n < 0) 657afc154e9SHannes Frederic Sowa return n; 658afc154e9SHannes Frederic Sowa } 659554cfb7eSYOSHIFUJI Hideaki return m; 660554cfb7eSYOSHIFUJI Hideaki } 661554cfb7eSYOSHIFUJI Hideaki 662f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 663afc154e9SHannes Frederic Sowa int *mpri, struct rt6_info *match, 664afc154e9SHannes Frederic Sowa bool *do_rr) 665554cfb7eSYOSHIFUJI Hideaki { 666554cfb7eSYOSHIFUJI Hideaki int m; 667afc154e9SHannes Frederic Sowa bool match_do_rr = false; 66835103d11SAndy Gospodarek struct inet6_dev *idev = rt->rt6i_idev; 66935103d11SAndy Gospodarek struct net_device *dev = rt->dst.dev; 67035103d11SAndy Gospodarek 67135103d11SAndy Gospodarek if (dev && !netif_carrier_ok(dev) && 67235103d11SAndy Gospodarek idev->cnf.ignore_routes_with_linkdown) 67335103d11SAndy Gospodarek goto out; 674554cfb7eSYOSHIFUJI Hideaki 675554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 676f11e6659SDavid S. Miller goto out; 677554cfb7eSYOSHIFUJI Hideaki 678554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 6797e980569SJiri Benc if (m == RT6_NUD_FAIL_DO_RR) { 680afc154e9SHannes Frederic Sowa match_do_rr = true; 681afc154e9SHannes Frederic Sowa m = 0; /* lowest valid score */ 6827e980569SJiri Benc } else if (m == RT6_NUD_FAIL_HARD) { 683f11e6659SDavid S. Miller goto out; 6841da177e4SLinus Torvalds } 685f11e6659SDavid S. Miller 686afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) 687afc154e9SHannes Frederic Sowa rt6_probe(rt); 688afc154e9SHannes Frederic Sowa 6897e980569SJiri Benc /* note that m can be RT6_NUD_FAIL_PROBE at this point */ 690afc154e9SHannes Frederic Sowa if (m > *mpri) { 691afc154e9SHannes Frederic Sowa *do_rr = match_do_rr; 692afc154e9SHannes Frederic Sowa *mpri = m; 693afc154e9SHannes Frederic Sowa match = rt; 694afc154e9SHannes Frederic Sowa } 695f11e6659SDavid S. Miller out: 696f11e6659SDavid S. Miller return match; 6971da177e4SLinus Torvalds } 6981da177e4SLinus Torvalds 699f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 700f11e6659SDavid S. Miller struct rt6_info *rr_head, 701afc154e9SHannes Frederic Sowa u32 metric, int oif, int strict, 702afc154e9SHannes Frederic Sowa bool *do_rr) 703f11e6659SDavid S. Miller { 7049fbdcfafSSteffen Klassert struct rt6_info *rt, *match, *cont; 705f11e6659SDavid S. Miller int mpri = -1; 706f11e6659SDavid S. Miller 707f11e6659SDavid S. Miller match = NULL; 7089fbdcfafSSteffen Klassert cont = NULL; 7099fbdcfafSSteffen Klassert for (rt = rr_head; rt; rt = rt->dst.rt6_next) { 7109fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 7119fbdcfafSSteffen Klassert cont = rt; 7129fbdcfafSSteffen Klassert break; 7139fbdcfafSSteffen Klassert } 7149fbdcfafSSteffen Klassert 715afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 7169fbdcfafSSteffen Klassert } 7179fbdcfafSSteffen Klassert 7189fbdcfafSSteffen Klassert for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) { 7199fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 7209fbdcfafSSteffen Klassert cont = rt; 7219fbdcfafSSteffen Klassert break; 7229fbdcfafSSteffen Klassert } 7239fbdcfafSSteffen Klassert 7249fbdcfafSSteffen Klassert match = find_match(rt, oif, strict, &mpri, match, do_rr); 7259fbdcfafSSteffen Klassert } 7269fbdcfafSSteffen Klassert 7279fbdcfafSSteffen Klassert if (match || !cont) 7289fbdcfafSSteffen Klassert return match; 7299fbdcfafSSteffen Klassert 7309fbdcfafSSteffen Klassert for (rt = cont; rt; rt = rt->dst.rt6_next) 731afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 732f11e6659SDavid S. Miller 733f11e6659SDavid S. Miller return match; 734f11e6659SDavid S. Miller } 735f11e6659SDavid S. Miller 736f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 737f11e6659SDavid S. Miller { 738f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 7398ed67789SDaniel Lezcano struct net *net; 740afc154e9SHannes Frederic Sowa bool do_rr = false; 741f11e6659SDavid S. Miller 742f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 743f11e6659SDavid S. Miller if (!rt0) 744f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 745f11e6659SDavid S. Miller 746afc154e9SHannes Frederic Sowa match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict, 747afc154e9SHannes Frederic Sowa &do_rr); 748f11e6659SDavid S. Miller 749afc154e9SHannes Frederic Sowa if (do_rr) { 750d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 751f11e6659SDavid S. Miller 752554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 753f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 754f11e6659SDavid S. Miller next = fn->leaf; 755f11e6659SDavid S. Miller 756f11e6659SDavid S. Miller if (next != rt0) 757f11e6659SDavid S. Miller fn->rr_ptr = next; 758554cfb7eSYOSHIFUJI Hideaki } 759554cfb7eSYOSHIFUJI Hideaki 760d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 761a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 7621da177e4SLinus Torvalds } 7631da177e4SLinus Torvalds 7648b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt) 7658b9df265SMartin KaFai Lau { 7668b9df265SMartin KaFai Lau return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY)); 7678b9df265SMartin KaFai Lau } 7688b9df265SMartin KaFai Lau 76970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 77070ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 771b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 77270ceb4f5SYOSHIFUJI Hideaki { 773c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 77470ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 77570ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 77670ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 7774bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 77870ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 77970ceb4f5SYOSHIFUJI Hideaki 78070ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 78170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 78270ceb4f5SYOSHIFUJI Hideaki } 78370ceb4f5SYOSHIFUJI Hideaki 78470ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 78570ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 78670ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 78770ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 78870ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 78970ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 79070ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 79170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 79270ceb4f5SYOSHIFUJI Hideaki } 79370ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 79470ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 79570ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 79670ceb4f5SYOSHIFUJI Hideaki } 79770ceb4f5SYOSHIFUJI Hideaki } 79870ceb4f5SYOSHIFUJI Hideaki 79970ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 80070ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 8013933fc95SJens Rosenboom return -EINVAL; 80270ceb4f5SYOSHIFUJI Hideaki 8034bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 80470ceb4f5SYOSHIFUJI Hideaki 80570ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 80670ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 80770ceb4f5SYOSHIFUJI Hideaki else { 80870ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 80970ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 81070ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 81170ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 81270ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 81370ceb4f5SYOSHIFUJI Hideaki } 81470ceb4f5SYOSHIFUJI Hideaki 815f104a567SDuan Jiong if (rinfo->prefix_len == 0) 816f104a567SDuan Jiong rt = rt6_get_dflt_router(gwaddr, dev); 817f104a567SDuan Jiong else 818f104a567SDuan Jiong rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, 819f104a567SDuan Jiong gwaddr, dev->ifindex); 82070ceb4f5SYOSHIFUJI Hideaki 82170ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 822e0a1ad73SThomas Graf ip6_del_rt(rt); 82370ceb4f5SYOSHIFUJI Hideaki rt = NULL; 82470ceb4f5SYOSHIFUJI Hideaki } 82570ceb4f5SYOSHIFUJI Hideaki 82670ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 827efa2cea0SDaniel Lezcano rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 82870ceb4f5SYOSHIFUJI Hideaki pref); 82970ceb4f5SYOSHIFUJI Hideaki else if (rt) 83070ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 83170ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 83270ceb4f5SYOSHIFUJI Hideaki 83370ceb4f5SYOSHIFUJI Hideaki if (rt) { 8341716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 8351716a961SGao feng rt6_clean_expires(rt); 8361716a961SGao feng else 8371716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 8381716a961SGao feng 83994e187c0SAmerigo Wang ip6_rt_put(rt); 84070ceb4f5SYOSHIFUJI Hideaki } 84170ceb4f5SYOSHIFUJI Hideaki return 0; 84270ceb4f5SYOSHIFUJI Hideaki } 84370ceb4f5SYOSHIFUJI Hideaki #endif 84470ceb4f5SYOSHIFUJI Hideaki 845a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn, 846a3c00e46SMartin KaFai Lau struct in6_addr *saddr) 847a3c00e46SMartin KaFai Lau { 848a3c00e46SMartin KaFai Lau struct fib6_node *pn; 849a3c00e46SMartin KaFai Lau while (1) { 850a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_TL_ROOT) 851a3c00e46SMartin KaFai Lau return NULL; 852a3c00e46SMartin KaFai Lau pn = fn->parent; 853a3c00e46SMartin KaFai Lau if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) 854a3c00e46SMartin KaFai Lau fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); 855a3c00e46SMartin KaFai Lau else 856a3c00e46SMartin KaFai Lau fn = pn; 857a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_RTINFO) 858a3c00e46SMartin KaFai Lau return fn; 859a3c00e46SMartin KaFai Lau } 860a3c00e46SMartin KaFai Lau } 861c71099acSThomas Graf 8628ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 8638ed67789SDaniel Lezcano struct fib6_table *table, 8644c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 8651da177e4SLinus Torvalds { 8661da177e4SLinus Torvalds struct fib6_node *fn; 8671da177e4SLinus Torvalds struct rt6_info *rt; 8681da177e4SLinus Torvalds 869c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 8704c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 871c71099acSThomas Graf restart: 872c71099acSThomas Graf rt = fn->leaf; 8734c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 87451ebd318SNicolas Dichtel if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) 87552bd4c0cSNicolas Dichtel rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags); 876a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 877a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 878a3c00e46SMartin KaFai Lau if (fn) 879a3c00e46SMartin KaFai Lau goto restart; 880a3c00e46SMartin KaFai Lau } 881d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 882c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 8831da177e4SLinus Torvalds return rt; 884c71099acSThomas Graf 885c71099acSThomas Graf } 886c71099acSThomas Graf 887ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, 888ea6e574eSFlorian Westphal int flags) 889ea6e574eSFlorian Westphal { 890ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 891ea6e574eSFlorian Westphal } 892ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 893ea6e574eSFlorian Westphal 8949acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 8959acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 896c71099acSThomas Graf { 8974c9483b2SDavid S. Miller struct flowi6 fl6 = { 8984c9483b2SDavid S. Miller .flowi6_oif = oif, 8994c9483b2SDavid S. Miller .daddr = *daddr, 900c71099acSThomas Graf }; 901c71099acSThomas Graf struct dst_entry *dst; 90277d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 903c71099acSThomas Graf 904adaa70bbSThomas Graf if (saddr) { 9054c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 906adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 907adaa70bbSThomas Graf } 908adaa70bbSThomas Graf 9094c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 910c71099acSThomas Graf if (dst->error == 0) 911c71099acSThomas Graf return (struct rt6_info *) dst; 912c71099acSThomas Graf 913c71099acSThomas Graf dst_release(dst); 914c71099acSThomas Graf 9151da177e4SLinus Torvalds return NULL; 9161da177e4SLinus Torvalds } 9177159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 9187159039aSYOSHIFUJI Hideaki 919c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 9201da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 9211da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 9221da177e4SLinus Torvalds be destroyed. 9231da177e4SLinus Torvalds */ 9241da177e4SLinus Torvalds 925e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, 926e715b6d3SFlorian Westphal struct mx6_config *mxc) 9271da177e4SLinus Torvalds { 9281da177e4SLinus Torvalds int err; 929c71099acSThomas Graf struct fib6_table *table; 9301da177e4SLinus Torvalds 931c71099acSThomas Graf table = rt->rt6i_table; 932c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 933e715b6d3SFlorian Westphal err = fib6_add(&table->tb6_root, rt, info, mxc); 934c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 9351da177e4SLinus Torvalds 9361da177e4SLinus Torvalds return err; 9371da177e4SLinus Torvalds } 9381da177e4SLinus Torvalds 93940e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 94040e22e8fSThomas Graf { 941e715b6d3SFlorian Westphal struct nl_info info = { .nl_net = dev_net(rt->dst.dev), }; 942e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 943e715b6d3SFlorian Westphal 944e715b6d3SFlorian Westphal return __ip6_ins_rt(rt, &info, &mxc); 94540e22e8fSThomas Graf } 94640e22e8fSThomas Graf 9478b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, 94821efcfa0SEric Dumazet const struct in6_addr *daddr, 949b71d1d42SEric Dumazet const struct in6_addr *saddr) 9501da177e4SLinus Torvalds { 9511da177e4SLinus Torvalds struct rt6_info *rt; 9521da177e4SLinus Torvalds 9531da177e4SLinus Torvalds /* 9541da177e4SLinus Torvalds * Clone the route. 9551da177e4SLinus Torvalds */ 9561da177e4SLinus Torvalds 957d52d3997SMartin KaFai Lau if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)) 95883a09abdSMartin KaFai Lau ort = (struct rt6_info *)ort->dst.from; 9591da177e4SLinus Torvalds 960d52d3997SMartin KaFai Lau rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 96183a09abdSMartin KaFai Lau 0, ort->rt6i_table); 96283a09abdSMartin KaFai Lau 96383a09abdSMartin KaFai Lau if (!rt) 96483a09abdSMartin KaFai Lau return NULL; 96583a09abdSMartin KaFai Lau 96683a09abdSMartin KaFai Lau ip6_rt_copy_init(rt, ort); 9678b9df265SMartin KaFai Lau rt->rt6i_flags |= RTF_CACHE; 96883a09abdSMartin KaFai Lau rt->rt6i_metric = 0; 96983a09abdSMartin KaFai Lau rt->dst.flags |= DST_HOST; 97083a09abdSMartin KaFai Lau rt->rt6i_dst.addr = *daddr; 97183a09abdSMartin KaFai Lau rt->rt6i_dst.plen = 128; 9728b9df265SMartin KaFai Lau 9738b9df265SMartin KaFai Lau if (!rt6_is_gw_or_nonexthop(ort)) { 974bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 97521efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 97658c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 9771da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 9781da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 9794e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 9801da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 9811da177e4SLinus Torvalds } 9821da177e4SLinus Torvalds #endif 98395a9a5baSYOSHIFUJI Hideaki } 98495a9a5baSYOSHIFUJI Hideaki 985299d9939SYOSHIFUJI Hideaki return rt; 986299d9939SYOSHIFUJI Hideaki } 987299d9939SYOSHIFUJI Hideaki 988d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt) 989d52d3997SMartin KaFai Lau { 990d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 991d52d3997SMartin KaFai Lau 992d52d3997SMartin KaFai Lau pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev), 993d52d3997SMartin KaFai Lau rt->dst.dev, rt->dst.flags, 994d52d3997SMartin KaFai Lau rt->rt6i_table); 995d52d3997SMartin KaFai Lau 996d52d3997SMartin KaFai Lau if (!pcpu_rt) 997d52d3997SMartin KaFai Lau return NULL; 998d52d3997SMartin KaFai Lau ip6_rt_copy_init(pcpu_rt, rt); 999d52d3997SMartin KaFai Lau pcpu_rt->rt6i_protocol = rt->rt6i_protocol; 1000d52d3997SMartin KaFai Lau pcpu_rt->rt6i_flags |= RTF_PCPU; 1001d52d3997SMartin KaFai Lau return pcpu_rt; 1002d52d3997SMartin KaFai Lau } 1003d52d3997SMartin KaFai Lau 1004d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */ 1005d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt) 1006d52d3997SMartin KaFai Lau { 1007d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt, *prev, **p; 1008d52d3997SMartin KaFai Lau 1009d52d3997SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 1010d52d3997SMartin KaFai Lau pcpu_rt = *p; 1011d52d3997SMartin KaFai Lau 1012d52d3997SMartin KaFai Lau if (pcpu_rt) 1013d52d3997SMartin KaFai Lau goto done; 1014d52d3997SMartin KaFai Lau 1015d52d3997SMartin KaFai Lau pcpu_rt = ip6_rt_pcpu_alloc(rt); 1016d52d3997SMartin KaFai Lau if (!pcpu_rt) { 1017d52d3997SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 1018d52d3997SMartin KaFai Lau 1019d52d3997SMartin KaFai Lau pcpu_rt = net->ipv6.ip6_null_entry; 1020d52d3997SMartin KaFai Lau goto done; 1021d52d3997SMartin KaFai Lau } 1022d52d3997SMartin KaFai Lau 1023d52d3997SMartin KaFai Lau prev = cmpxchg(p, NULL, pcpu_rt); 1024d52d3997SMartin KaFai Lau if (prev) { 1025d52d3997SMartin KaFai Lau /* If someone did it before us, return prev instead */ 1026d52d3997SMartin KaFai Lau dst_destroy(&pcpu_rt->dst); 1027d52d3997SMartin KaFai Lau pcpu_rt = prev; 1028d52d3997SMartin KaFai Lau } 1029d52d3997SMartin KaFai Lau 1030d52d3997SMartin KaFai Lau done: 1031d52d3997SMartin KaFai Lau dst_hold(&pcpu_rt->dst); 1032d52d3997SMartin KaFai Lau rt6_dst_from_metrics_check(pcpu_rt); 1033d52d3997SMartin KaFai Lau return pcpu_rt; 1034d52d3997SMartin KaFai Lau } 1035d52d3997SMartin KaFai Lau 10368ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, 10374c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 10381da177e4SLinus Torvalds { 1039367efcb9SMartin KaFai Lau struct fib6_node *fn, *saved_fn; 104045e4fd26SMartin KaFai Lau struct rt6_info *rt; 1041c71099acSThomas Graf int strict = 0; 10421da177e4SLinus Torvalds 104377d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 1044367efcb9SMartin KaFai Lau if (net->ipv6.devconf_all->forwarding == 0) 1045367efcb9SMartin KaFai Lau strict |= RT6_LOOKUP_F_REACHABLE; 10461da177e4SLinus Torvalds 1047c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 10481da177e4SLinus Torvalds 10494c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1050367efcb9SMartin KaFai Lau saved_fn = fn; 10511da177e4SLinus Torvalds 1052a3c00e46SMartin KaFai Lau redo_rt6_select: 1053367efcb9SMartin KaFai Lau rt = rt6_select(fn, oif, strict); 105452bd4c0cSNicolas Dichtel if (rt->rt6i_nsiblings) 1055367efcb9SMartin KaFai Lau rt = rt6_multipath_select(rt, fl6, oif, strict); 1056a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1057a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1058a3c00e46SMartin KaFai Lau if (fn) 1059a3c00e46SMartin KaFai Lau goto redo_rt6_select; 1060367efcb9SMartin KaFai Lau else if (strict & RT6_LOOKUP_F_REACHABLE) { 1061367efcb9SMartin KaFai Lau /* also consider unreachable route */ 1062367efcb9SMartin KaFai Lau strict &= ~RT6_LOOKUP_F_REACHABLE; 1063367efcb9SMartin KaFai Lau fn = saved_fn; 1064367efcb9SMartin KaFai Lau goto redo_rt6_select; 1065367efcb9SMartin KaFai Lau } 1066a3c00e46SMartin KaFai Lau } 1067a3c00e46SMartin KaFai Lau 1068d52d3997SMartin KaFai Lau 1069d52d3997SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) { 10703da59bd9SMartin KaFai Lau dst_use(&rt->dst, jiffies); 1071c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 10721da177e4SLinus Torvalds 1073d52d3997SMartin KaFai Lau rt6_dst_from_metrics_check(rt); 1074d52d3997SMartin KaFai Lau return rt; 10753da59bd9SMartin KaFai Lau } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && 10763da59bd9SMartin KaFai Lau !(rt->rt6i_flags & RTF_GATEWAY))) { 10773da59bd9SMartin KaFai Lau /* Create a RTF_CACHE clone which will not be 10783da59bd9SMartin KaFai Lau * owned by the fib6 tree. It is for the special case where 10793da59bd9SMartin KaFai Lau * the daddr in the skb during the neighbor look-up is different 10803da59bd9SMartin KaFai Lau * from the fl6->daddr used to look-up route here. 10813da59bd9SMartin KaFai Lau */ 1082c71099acSThomas Graf 10833da59bd9SMartin KaFai Lau struct rt6_info *uncached_rt; 10843da59bd9SMartin KaFai Lau 1085d52d3997SMartin KaFai Lau dst_use(&rt->dst, jiffies); 1086d52d3997SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 1087d52d3997SMartin KaFai Lau 10883da59bd9SMartin KaFai Lau uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL); 10893da59bd9SMartin KaFai Lau dst_release(&rt->dst); 10903da59bd9SMartin KaFai Lau 10913da59bd9SMartin KaFai Lau if (uncached_rt) 10928d0b94afSMartin KaFai Lau rt6_uncached_list_add(uncached_rt); 10933da59bd9SMartin KaFai Lau else 10943da59bd9SMartin KaFai Lau uncached_rt = net->ipv6.ip6_null_entry; 1095d52d3997SMartin KaFai Lau 10963da59bd9SMartin KaFai Lau dst_hold(&uncached_rt->dst); 10973da59bd9SMartin KaFai Lau return uncached_rt; 10983da59bd9SMartin KaFai Lau 1099d52d3997SMartin KaFai Lau } else { 1100d52d3997SMartin KaFai Lau /* Get a percpu copy */ 1101d52d3997SMartin KaFai Lau 1102d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 1103d52d3997SMartin KaFai Lau 1104d52d3997SMartin KaFai Lau rt->dst.lastuse = jiffies; 1105d52d3997SMartin KaFai Lau rt->dst.__use++; 1106d52d3997SMartin KaFai Lau pcpu_rt = rt6_get_pcpu_route(rt); 1107d52d3997SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 1108d52d3997SMartin KaFai Lau 1109d52d3997SMartin KaFai Lau return pcpu_rt; 1110d52d3997SMartin KaFai Lau } 1111c71099acSThomas Graf } 1112c71099acSThomas Graf 11138ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 11144c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 11154acad72dSPavel Emelyanov { 11164c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 11174acad72dSPavel Emelyanov } 11184acad72dSPavel Emelyanov 111972331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 112072331bc0SShmulik Ladkani struct net_device *dev, 112172331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 112272331bc0SShmulik Ladkani { 112372331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 112472331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 112572331bc0SShmulik Ladkani 112672331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 112772331bc0SShmulik Ladkani } 112872331bc0SShmulik Ladkani 1129c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 1130c71099acSThomas Graf { 1131b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 1132c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 1133adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 11344c9483b2SDavid S. Miller struct flowi6 fl6 = { 11354c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 11364c9483b2SDavid S. Miller .daddr = iph->daddr, 11374c9483b2SDavid S. Miller .saddr = iph->saddr, 11386502ca52SYOSHIFUJI Hideaki / 吉藤英明 .flowlabel = ip6_flowinfo(iph), 11394c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 11404c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 1141c71099acSThomas Graf }; 1142adaa70bbSThomas Graf 1143*06e9d040SJiri Benc skb_dst_drop(skb); 114472331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 1145c71099acSThomas Graf } 1146c71099acSThomas Graf 11478ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 11484c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 1149c71099acSThomas Graf { 11504c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 1151c71099acSThomas Graf } 1152c71099acSThomas Graf 11539c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk, 11544c9483b2SDavid S. Miller struct flowi6 *fl6) 1155c71099acSThomas Graf { 1156c71099acSThomas Graf int flags = 0; 1157c71099acSThomas Graf 11581fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 11594dc27d1cSDavid McCullough 11604c9483b2SDavid S. Miller if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) 116177d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 1162c71099acSThomas Graf 11634c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 1164adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 11650c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 11660c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 1167adaa70bbSThomas Graf 11684c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 11691da177e4SLinus Torvalds } 11707159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 11711da177e4SLinus Torvalds 11722774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 117314e50e57SDavid S. Miller { 11745c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 117514e50e57SDavid S. Miller struct dst_entry *new = NULL; 117614e50e57SDavid S. Miller 1177f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 117814e50e57SDavid S. Miller if (rt) { 1179d8d1f30bSChangli Gao new = &rt->dst; 118014e50e57SDavid S. Miller 11818104891bSSteffen Klassert memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); 11828104891bSSteffen Klassert 118314e50e57SDavid S. Miller new->__use = 1; 1184352e512cSHerbert Xu new->input = dst_discard; 1185aad88724SEric Dumazet new->output = dst_discard_sk; 118614e50e57SDavid S. Miller 118721efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 118821efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 118921efcfa0SEric Dumazet else 1190defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 119114e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 119214e50e57SDavid S. Miller if (rt->rt6i_idev) 119314e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 119414e50e57SDavid S. Miller 11954e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 11961716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 119714e50e57SDavid S. Miller rt->rt6i_metric = 0; 119814e50e57SDavid S. Miller 119914e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 120014e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 120114e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 120214e50e57SDavid S. Miller #endif 120314e50e57SDavid S. Miller 120414e50e57SDavid S. Miller dst_free(new); 120514e50e57SDavid S. Miller } 120614e50e57SDavid S. Miller 120769ead7afSDavid S. Miller dst_release(dst_orig); 120869ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 120914e50e57SDavid S. Miller } 121014e50e57SDavid S. Miller 12111da177e4SLinus Torvalds /* 12121da177e4SLinus Torvalds * Destination cache support functions 12131da177e4SLinus Torvalds */ 12141da177e4SLinus Torvalds 12154b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt) 12164b32b5adSMartin KaFai Lau { 12174b32b5adSMartin KaFai Lau if (rt->dst.from && 12184b32b5adSMartin KaFai Lau dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from)) 12194b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true); 12204b32b5adSMartin KaFai Lau } 12214b32b5adSMartin KaFai Lau 12223da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie) 12233da59bd9SMartin KaFai Lau { 12243da59bd9SMartin KaFai Lau if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) 12253da59bd9SMartin KaFai Lau return NULL; 12263da59bd9SMartin KaFai Lau 12273da59bd9SMartin KaFai Lau if (rt6_check_expired(rt)) 12283da59bd9SMartin KaFai Lau return NULL; 12293da59bd9SMartin KaFai Lau 12303da59bd9SMartin KaFai Lau return &rt->dst; 12313da59bd9SMartin KaFai Lau } 12323da59bd9SMartin KaFai Lau 12333da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie) 12343da59bd9SMartin KaFai Lau { 12353da59bd9SMartin KaFai Lau if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && 12363da59bd9SMartin KaFai Lau rt6_check((struct rt6_info *)(rt->dst.from), cookie)) 12373da59bd9SMartin KaFai Lau return &rt->dst; 12383da59bd9SMartin KaFai Lau else 12393da59bd9SMartin KaFai Lau return NULL; 12403da59bd9SMartin KaFai Lau } 12413da59bd9SMartin KaFai Lau 12421da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 12431da177e4SLinus Torvalds { 12441da177e4SLinus Torvalds struct rt6_info *rt; 12451da177e4SLinus Torvalds 12461da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 12471da177e4SLinus Torvalds 12486f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 12496f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 12506f3118b5SNicolas Dichtel * into this function always. 12516f3118b5SNicolas Dichtel */ 1252e3bc10bdSHannes Frederic Sowa 12534b32b5adSMartin KaFai Lau rt6_dst_from_metrics_check(rt); 12544b32b5adSMartin KaFai Lau 1255d52d3997SMartin KaFai Lau if ((rt->rt6i_flags & RTF_PCPU) || unlikely(dst->flags & DST_NOCACHE)) 12563da59bd9SMartin KaFai Lau return rt6_dst_from_check(rt, cookie); 12573da59bd9SMartin KaFai Lau else 12583da59bd9SMartin KaFai Lau return rt6_check(rt, cookie); 12591da177e4SLinus Torvalds } 12601da177e4SLinus Torvalds 12611da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 12621da177e4SLinus Torvalds { 12631da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 12641da177e4SLinus Torvalds 12651da177e4SLinus Torvalds if (rt) { 126654c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 126754c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1268e0a1ad73SThomas Graf ip6_del_rt(rt); 126954c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 12701da177e4SLinus Torvalds } 127154c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 127254c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 127354c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 127454c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 127554c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 127654c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 12771da177e4SLinus Torvalds } 12781da177e4SLinus Torvalds 12791da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 12801da177e4SLinus Torvalds { 12811da177e4SLinus Torvalds struct rt6_info *rt; 12821da177e4SLinus Torvalds 12833ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 12841da177e4SLinus Torvalds 1285adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 12861da177e4SLinus Torvalds if (rt) { 12871eb4f758SHannes Frederic Sowa if (rt->rt6i_flags & RTF_CACHE) { 12881eb4f758SHannes Frederic Sowa dst_hold(&rt->dst); 12891eb4f758SHannes Frederic Sowa if (ip6_del_rt(rt)) 12901eb4f758SHannes Frederic Sowa dst_free(&rt->dst); 12911eb4f758SHannes Frederic Sowa } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) { 12921da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 12931da177e4SLinus Torvalds } 12941da177e4SLinus Torvalds } 12951eb4f758SHannes Frederic Sowa } 12961da177e4SLinus Torvalds 129745e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) 129845e4fd26SMartin KaFai Lau { 129945e4fd26SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 130045e4fd26SMartin KaFai Lau 130145e4fd26SMartin KaFai Lau rt->rt6i_flags |= RTF_MODIFIED; 130245e4fd26SMartin KaFai Lau rt->rt6i_pmtu = mtu; 130345e4fd26SMartin KaFai Lau rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); 130445e4fd26SMartin KaFai Lau } 130545e4fd26SMartin KaFai Lau 130645e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, 130745e4fd26SMartin KaFai Lau const struct ipv6hdr *iph, u32 mtu) 13081da177e4SLinus Torvalds { 13091da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info *)dst; 13101da177e4SLinus Torvalds 131145e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_LOCAL) 131245e4fd26SMartin KaFai Lau return; 131345e4fd26SMartin KaFai Lau 131481aded24SDavid S. Miller dst_confirm(dst); 131545e4fd26SMartin KaFai Lau mtu = max_t(u32, mtu, IPV6_MIN_MTU); 131645e4fd26SMartin KaFai Lau if (mtu >= dst_mtu(dst)) 131745e4fd26SMartin KaFai Lau return; 131881aded24SDavid S. Miller 131945e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_CACHE) { 132045e4fd26SMartin KaFai Lau rt6_do_update_pmtu(rt6, mtu); 132145e4fd26SMartin KaFai Lau } else { 132245e4fd26SMartin KaFai Lau const struct in6_addr *daddr, *saddr; 132345e4fd26SMartin KaFai Lau struct rt6_info *nrt6; 13249d289715SHagen Paul Pfeifer 132545e4fd26SMartin KaFai Lau if (iph) { 132645e4fd26SMartin KaFai Lau daddr = &iph->daddr; 132745e4fd26SMartin KaFai Lau saddr = &iph->saddr; 132845e4fd26SMartin KaFai Lau } else if (sk) { 132945e4fd26SMartin KaFai Lau daddr = &sk->sk_v6_daddr; 133045e4fd26SMartin KaFai Lau saddr = &inet6_sk(sk)->saddr; 133145e4fd26SMartin KaFai Lau } else { 133245e4fd26SMartin KaFai Lau return; 13331da177e4SLinus Torvalds } 133445e4fd26SMartin KaFai Lau nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr); 133545e4fd26SMartin KaFai Lau if (nrt6) { 133645e4fd26SMartin KaFai Lau rt6_do_update_pmtu(nrt6, mtu); 133745e4fd26SMartin KaFai Lau 133845e4fd26SMartin KaFai Lau /* ip6_ins_rt(nrt6) will bump the 133945e4fd26SMartin KaFai Lau * rt6->rt6i_node->fn_sernum 134045e4fd26SMartin KaFai Lau * which will fail the next rt6_check() and 134145e4fd26SMartin KaFai Lau * invalidate the sk->sk_dst_cache. 134245e4fd26SMartin KaFai Lau */ 134345e4fd26SMartin KaFai Lau ip6_ins_rt(nrt6); 134445e4fd26SMartin KaFai Lau } 134545e4fd26SMartin KaFai Lau } 134645e4fd26SMartin KaFai Lau } 134745e4fd26SMartin KaFai Lau 134845e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 134945e4fd26SMartin KaFai Lau struct sk_buff *skb, u32 mtu) 135045e4fd26SMartin KaFai Lau { 135145e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu); 13521da177e4SLinus Torvalds } 13531da177e4SLinus Torvalds 135442ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 135542ae66c8SDavid S. Miller int oif, u32 mark) 135681aded24SDavid S. Miller { 135781aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 135881aded24SDavid S. Miller struct dst_entry *dst; 135981aded24SDavid S. Miller struct flowi6 fl6; 136081aded24SDavid S. Miller 136181aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 136281aded24SDavid S. Miller fl6.flowi6_oif = oif; 13631b3c61dcSLorenzo Colitti fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark); 136481aded24SDavid S. Miller fl6.daddr = iph->daddr; 136581aded24SDavid S. Miller fl6.saddr = iph->saddr; 13666502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 136781aded24SDavid S. Miller 136881aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 136981aded24SDavid S. Miller if (!dst->error) 137045e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu)); 137181aded24SDavid S. Miller dst_release(dst); 137281aded24SDavid S. Miller } 137381aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 137481aded24SDavid S. Miller 137581aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 137681aded24SDavid S. Miller { 137781aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 137881aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 137981aded24SDavid S. Miller } 138081aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 138181aded24SDavid S. Miller 1382b55b76b2SDuan Jiong /* Handle redirects */ 1383b55b76b2SDuan Jiong struct ip6rd_flowi { 1384b55b76b2SDuan Jiong struct flowi6 fl6; 1385b55b76b2SDuan Jiong struct in6_addr gateway; 1386b55b76b2SDuan Jiong }; 1387b55b76b2SDuan Jiong 1388b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net, 1389b55b76b2SDuan Jiong struct fib6_table *table, 1390b55b76b2SDuan Jiong struct flowi6 *fl6, 1391b55b76b2SDuan Jiong int flags) 1392b55b76b2SDuan Jiong { 1393b55b76b2SDuan Jiong struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 1394b55b76b2SDuan Jiong struct rt6_info *rt; 1395b55b76b2SDuan Jiong struct fib6_node *fn; 1396b55b76b2SDuan Jiong 1397b55b76b2SDuan Jiong /* Get the "current" route for this destination and 1398b55b76b2SDuan Jiong * check if the redirect has come from approriate router. 1399b55b76b2SDuan Jiong * 1400b55b76b2SDuan Jiong * RFC 4861 specifies that redirects should only be 1401b55b76b2SDuan Jiong * accepted if they come from the nexthop to the target. 1402b55b76b2SDuan Jiong * Due to the way the routes are chosen, this notion 1403b55b76b2SDuan Jiong * is a bit fuzzy and one might need to check all possible 1404b55b76b2SDuan Jiong * routes. 1405b55b76b2SDuan Jiong */ 1406b55b76b2SDuan Jiong 1407b55b76b2SDuan Jiong read_lock_bh(&table->tb6_lock); 1408b55b76b2SDuan Jiong fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1409b55b76b2SDuan Jiong restart: 1410b55b76b2SDuan Jiong for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1411b55b76b2SDuan Jiong if (rt6_check_expired(rt)) 1412b55b76b2SDuan Jiong continue; 1413b55b76b2SDuan Jiong if (rt->dst.error) 1414b55b76b2SDuan Jiong break; 1415b55b76b2SDuan Jiong if (!(rt->rt6i_flags & RTF_GATEWAY)) 1416b55b76b2SDuan Jiong continue; 1417b55b76b2SDuan Jiong if (fl6->flowi6_oif != rt->dst.dev->ifindex) 1418b55b76b2SDuan Jiong continue; 1419b55b76b2SDuan Jiong if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) 1420b55b76b2SDuan Jiong continue; 1421b55b76b2SDuan Jiong break; 1422b55b76b2SDuan Jiong } 1423b55b76b2SDuan Jiong 1424b55b76b2SDuan Jiong if (!rt) 1425b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1426b55b76b2SDuan Jiong else if (rt->dst.error) { 1427b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1428b0a1ba59SMartin KaFai Lau goto out; 1429b0a1ba59SMartin KaFai Lau } 1430b0a1ba59SMartin KaFai Lau 1431b0a1ba59SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1432a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1433a3c00e46SMartin KaFai Lau if (fn) 1434a3c00e46SMartin KaFai Lau goto restart; 1435b55b76b2SDuan Jiong } 1436a3c00e46SMartin KaFai Lau 1437b0a1ba59SMartin KaFai Lau out: 1438b55b76b2SDuan Jiong dst_hold(&rt->dst); 1439b55b76b2SDuan Jiong 1440b55b76b2SDuan Jiong read_unlock_bh(&table->tb6_lock); 1441b55b76b2SDuan Jiong 1442b55b76b2SDuan Jiong return rt; 1443b55b76b2SDuan Jiong }; 1444b55b76b2SDuan Jiong 1445b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net, 1446b55b76b2SDuan Jiong const struct flowi6 *fl6, 1447b55b76b2SDuan Jiong const struct in6_addr *gateway) 1448b55b76b2SDuan Jiong { 1449b55b76b2SDuan Jiong int flags = RT6_LOOKUP_F_HAS_SADDR; 1450b55b76b2SDuan Jiong struct ip6rd_flowi rdfl; 1451b55b76b2SDuan Jiong 1452b55b76b2SDuan Jiong rdfl.fl6 = *fl6; 1453b55b76b2SDuan Jiong rdfl.gateway = *gateway; 1454b55b76b2SDuan Jiong 1455b55b76b2SDuan Jiong return fib6_rule_lookup(net, &rdfl.fl6, 1456b55b76b2SDuan Jiong flags, __ip6_route_redirect); 1457b55b76b2SDuan Jiong } 1458b55b76b2SDuan Jiong 14593a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 14603a5ad2eeSDavid S. Miller { 14613a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 14623a5ad2eeSDavid S. Miller struct dst_entry *dst; 14633a5ad2eeSDavid S. Miller struct flowi6 fl6; 14643a5ad2eeSDavid S. Miller 14653a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 1466e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 14673a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 14683a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 14693a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 14703a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 14716502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 14723a5ad2eeSDavid S. Miller 1473b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); 14746700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 14753a5ad2eeSDavid S. Miller dst_release(dst); 14763a5ad2eeSDavid S. Miller } 14773a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 14783a5ad2eeSDavid S. Miller 1479c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, 1480c92a59ecSDuan Jiong u32 mark) 1481c92a59ecSDuan Jiong { 1482c92a59ecSDuan Jiong const struct ipv6hdr *iph = ipv6_hdr(skb); 1483c92a59ecSDuan Jiong const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 1484c92a59ecSDuan Jiong struct dst_entry *dst; 1485c92a59ecSDuan Jiong struct flowi6 fl6; 1486c92a59ecSDuan Jiong 1487c92a59ecSDuan Jiong memset(&fl6, 0, sizeof(fl6)); 1488e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 1489c92a59ecSDuan Jiong fl6.flowi6_oif = oif; 1490c92a59ecSDuan Jiong fl6.flowi6_mark = mark; 1491c92a59ecSDuan Jiong fl6.daddr = msg->dest; 1492c92a59ecSDuan Jiong fl6.saddr = iph->daddr; 1493c92a59ecSDuan Jiong 1494b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &iph->saddr); 1495c92a59ecSDuan Jiong rt6_do_redirect(dst, NULL, skb); 1496c92a59ecSDuan Jiong dst_release(dst); 1497c92a59ecSDuan Jiong } 1498c92a59ecSDuan Jiong 14993a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 15003a5ad2eeSDavid S. Miller { 15013a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 15023a5ad2eeSDavid S. Miller } 15033a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 15043a5ad2eeSDavid S. Miller 15050dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 15061da177e4SLinus Torvalds { 15070dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 15080dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 15090dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 15100dbaee3bSDavid S. Miller 15111da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 15121da177e4SLinus Torvalds 15135578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 15145578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 15151da177e4SLinus Torvalds 15161da177e4SLinus Torvalds /* 15171da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 15181da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 15191da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 15201da177e4SLinus Torvalds * rely only on pmtu discovery" 15211da177e4SLinus Torvalds */ 15221da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 15231da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 15241da177e4SLinus Torvalds return mtu; 15251da177e4SLinus Torvalds } 15261da177e4SLinus Torvalds 1527ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1528d33e4553SDavid S. Miller { 15294b32b5adSMartin KaFai Lau const struct rt6_info *rt = (const struct rt6_info *)dst; 15304b32b5adSMartin KaFai Lau unsigned int mtu = rt->rt6i_pmtu; 1531d33e4553SDavid S. Miller struct inet6_dev *idev; 1532618f9bc7SSteffen Klassert 1533618f9bc7SSteffen Klassert if (mtu) 153430f78d8eSEric Dumazet goto out; 1535618f9bc7SSteffen Klassert 15364b32b5adSMartin KaFai Lau mtu = dst_metric_raw(dst, RTAX_MTU); 15374b32b5adSMartin KaFai Lau if (mtu) 15384b32b5adSMartin KaFai Lau goto out; 15394b32b5adSMartin KaFai Lau 1540618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1541d33e4553SDavid S. Miller 1542d33e4553SDavid S. Miller rcu_read_lock(); 1543d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1544d33e4553SDavid S. Miller if (idev) 1545d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1546d33e4553SDavid S. Miller rcu_read_unlock(); 1547d33e4553SDavid S. Miller 154830f78d8eSEric Dumazet out: 154930f78d8eSEric Dumazet return min_t(unsigned int, mtu, IP6_MAX_MTU); 1550d33e4553SDavid S. Miller } 1551d33e4553SDavid S. Miller 15523b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 15533b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 15545d0bbeebSThomas Graf 15553b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 155687a11578SDavid S. Miller struct flowi6 *fl6) 15571da177e4SLinus Torvalds { 155887a11578SDavid S. Miller struct dst_entry *dst; 15591da177e4SLinus Torvalds struct rt6_info *rt; 15601da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1561c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 15621da177e4SLinus Torvalds 156338308473SDavid S. Miller if (unlikely(!idev)) 1564122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 15651da177e4SLinus Torvalds 15668b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, dev, 0, NULL); 156738308473SDavid S. Miller if (unlikely(!rt)) { 15681da177e4SLinus Torvalds in6_dev_put(idev); 156987a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 15701da177e4SLinus Torvalds goto out; 15711da177e4SLinus Torvalds } 15721da177e4SLinus Torvalds 15738e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 15748e2ec639SYan, Zheng rt->dst.output = ip6_output; 1575d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 1576550bab42SJulian Anastasov rt->rt6i_gateway = fl6->daddr; 157787a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 15788e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 15798e2ec639SYan, Zheng rt->rt6i_idev = idev; 158014edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 15811da177e4SLinus Torvalds 15823b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1583d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1584d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 15853b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 15861da177e4SLinus Torvalds 15875578689aSDaniel Lezcano fib6_force_start_gc(net); 15881da177e4SLinus Torvalds 158987a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 159087a11578SDavid S. Miller 15911da177e4SLinus Torvalds out: 159287a11578SDavid S. Miller return dst; 15931da177e4SLinus Torvalds } 15941da177e4SLinus Torvalds 15953d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 15961da177e4SLinus Torvalds { 1597e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 15983d0f24a7SStephen Hemminger int more = 0; 15991da177e4SLinus Torvalds 16003b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 16013b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 16025d0bbeebSThomas Graf 16031da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 16041da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 16051da177e4SLinus Torvalds *pprev = dst->next; 16061da177e4SLinus Torvalds dst_free(dst); 16071da177e4SLinus Torvalds } else { 16081da177e4SLinus Torvalds pprev = &dst->next; 16093d0f24a7SStephen Hemminger ++more; 16101da177e4SLinus Torvalds } 16111da177e4SLinus Torvalds } 16121da177e4SLinus Torvalds 16133b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 16145d0bbeebSThomas Graf 16153d0f24a7SStephen Hemminger return more; 16161da177e4SLinus Torvalds } 16171da177e4SLinus Torvalds 16181e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 16191e493d19SDavid S. Miller void *arg) 16201e493d19SDavid S. Miller { 16211e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 16221e493d19SDavid S. Miller 16231e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 16241e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 16251e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 16261e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 16271e493d19SDavid S. Miller if (func(rt, arg)) { 16281e493d19SDavid S. Miller *pprev = dst->next; 16291e493d19SDavid S. Miller dst_free(dst); 16301e493d19SDavid S. Miller } else { 16311e493d19SDavid S. Miller pprev = &dst->next; 16321e493d19SDavid S. Miller } 16331e493d19SDavid S. Miller } 16341e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 16351e493d19SDavid S. Miller } 16361e493d19SDavid S. Miller 1637569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 16381da177e4SLinus Torvalds { 163986393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 16407019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 16417019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 16427019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 16437019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 16447019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1645fc66f95cSEric Dumazet int entries; 16461da177e4SLinus Torvalds 1647fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 164849a18d86SMichal Kubeček if (time_after(rt_last_gc + rt_min_interval, jiffies) && 1649fc66f95cSEric Dumazet entries <= rt_max_size) 16501da177e4SLinus Torvalds goto out; 16511da177e4SLinus Torvalds 16526891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 165314956643SLi RongQing fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true); 1654fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1655fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 16567019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 16571da177e4SLinus Torvalds out: 16587019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1659fc66f95cSEric Dumazet return entries > rt_max_size; 16601da177e4SLinus Torvalds } 16611da177e4SLinus Torvalds 1662e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc, 1663e715b6d3SFlorian Westphal const struct fib6_config *cfg) 1664e715b6d3SFlorian Westphal { 1665e715b6d3SFlorian Westphal struct nlattr *nla; 1666e715b6d3SFlorian Westphal int remaining; 1667e715b6d3SFlorian Westphal u32 *mp; 1668e715b6d3SFlorian Westphal 166963159f29SIan Morris if (!cfg->fc_mx) 1670e715b6d3SFlorian Westphal return 0; 1671e715b6d3SFlorian Westphal 1672e715b6d3SFlorian Westphal mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 1673e715b6d3SFlorian Westphal if (unlikely(!mp)) 1674e715b6d3SFlorian Westphal return -ENOMEM; 1675e715b6d3SFlorian Westphal 1676e715b6d3SFlorian Westphal nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 1677e715b6d3SFlorian Westphal int type = nla_type(nla); 1678e715b6d3SFlorian Westphal 1679e715b6d3SFlorian Westphal if (type) { 1680ea697639SDaniel Borkmann u32 val; 1681ea697639SDaniel Borkmann 1682e715b6d3SFlorian Westphal if (unlikely(type > RTAX_MAX)) 1683e715b6d3SFlorian Westphal goto err; 1684ea697639SDaniel Borkmann if (type == RTAX_CC_ALGO) { 1685ea697639SDaniel Borkmann char tmp[TCP_CA_NAME_MAX]; 1686e715b6d3SFlorian Westphal 1687ea697639SDaniel Borkmann nla_strlcpy(tmp, nla, sizeof(tmp)); 1688ea697639SDaniel Borkmann val = tcp_ca_get_key_by_name(tmp); 1689ea697639SDaniel Borkmann if (val == TCP_CA_UNSPEC) 1690ea697639SDaniel Borkmann goto err; 1691ea697639SDaniel Borkmann } else { 1692ea697639SDaniel Borkmann val = nla_get_u32(nla); 1693ea697639SDaniel Borkmann } 1694ea697639SDaniel Borkmann 1695ea697639SDaniel Borkmann mp[type - 1] = val; 1696e715b6d3SFlorian Westphal __set_bit(type - 1, mxc->mx_valid); 1697e715b6d3SFlorian Westphal } 1698e715b6d3SFlorian Westphal } 1699e715b6d3SFlorian Westphal 1700e715b6d3SFlorian Westphal mxc->mx = mp; 1701e715b6d3SFlorian Westphal 1702e715b6d3SFlorian Westphal return 0; 1703e715b6d3SFlorian Westphal err: 1704e715b6d3SFlorian Westphal kfree(mp); 1705e715b6d3SFlorian Westphal return -EINVAL; 1706e715b6d3SFlorian Westphal } 17071da177e4SLinus Torvalds 170886872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg) 17091da177e4SLinus Torvalds { 17101da177e4SLinus Torvalds int err; 17115578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 17121da177e4SLinus Torvalds struct rt6_info *rt = NULL; 17131da177e4SLinus Torvalds struct net_device *dev = NULL; 17141da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1715c71099acSThomas Graf struct fib6_table *table; 1716e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 17171da177e4SLinus Torvalds int addr_type; 17181da177e4SLinus Torvalds 171986872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 17201da177e4SLinus Torvalds return -EINVAL; 17211da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 172286872cb5SThomas Graf if (cfg->fc_src_len) 17231da177e4SLinus Torvalds return -EINVAL; 17241da177e4SLinus Torvalds #endif 172586872cb5SThomas Graf if (cfg->fc_ifindex) { 17261da177e4SLinus Torvalds err = -ENODEV; 17275578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 17281da177e4SLinus Torvalds if (!dev) 17291da177e4SLinus Torvalds goto out; 17301da177e4SLinus Torvalds idev = in6_dev_get(dev); 17311da177e4SLinus Torvalds if (!idev) 17321da177e4SLinus Torvalds goto out; 17331da177e4SLinus Torvalds } 17341da177e4SLinus Torvalds 173586872cb5SThomas Graf if (cfg->fc_metric == 0) 173686872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 17371da177e4SLinus Torvalds 1738c71099acSThomas Graf err = -ENOBUFS; 173938308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1740d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1741d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 174238308473SDavid S. Miller if (!table) { 1743f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1744d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1745d71314b4SMatti Vaittinen } 1746d71314b4SMatti Vaittinen } else { 1747d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1748d71314b4SMatti Vaittinen } 174938308473SDavid S. Miller 175038308473SDavid S. Miller if (!table) 1751c71099acSThomas Graf goto out; 1752c71099acSThomas Graf 1753c88507fbSSabrina Dubroca rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table); 17541da177e4SLinus Torvalds 175538308473SDavid S. Miller if (!rt) { 17561da177e4SLinus Torvalds err = -ENOMEM; 17571da177e4SLinus Torvalds goto out; 17581da177e4SLinus Torvalds } 17591da177e4SLinus Torvalds 17601716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 17611716a961SGao feng rt6_set_expires(rt, jiffies + 17621716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 17631716a961SGao feng else 17641716a961SGao feng rt6_clean_expires(rt); 17651da177e4SLinus Torvalds 176686872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 176786872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 176886872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 176986872cb5SThomas Graf 177086872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 17711da177e4SLinus Torvalds 17721da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1773d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1774ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1775ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 17761da177e4SLinus Torvalds else 1777d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 17781da177e4SLinus Torvalds 1779d8d1f30bSChangli Gao rt->dst.output = ip6_output; 17801da177e4SLinus Torvalds 178119e42e45SRoopa Prabhu if (cfg->fc_encap) { 178219e42e45SRoopa Prabhu struct lwtunnel_state *lwtstate; 178319e42e45SRoopa Prabhu 178419e42e45SRoopa Prabhu err = lwtunnel_build_state(dev, cfg->fc_encap_type, 178519e42e45SRoopa Prabhu cfg->fc_encap, &lwtstate); 178619e42e45SRoopa Prabhu if (err) 178719e42e45SRoopa Prabhu goto out; 178861adedf3SJiri Benc rt->dst.lwtstate = lwtstate_get(lwtstate); 178961adedf3SJiri Benc if (lwtunnel_output_redirect(rt->dst.lwtstate)) { 179061adedf3SJiri Benc rt->dst.lwtstate->orig_output = rt->dst.output; 179161adedf3SJiri Benc rt->dst.output = lwtunnel_output; 179219e42e45SRoopa Prabhu } 179361adedf3SJiri Benc if (lwtunnel_input_redirect(rt->dst.lwtstate)) { 179461adedf3SJiri Benc rt->dst.lwtstate->orig_input = rt->dst.input; 179561adedf3SJiri Benc rt->dst.input = lwtunnel_input; 179625368623STom Herbert } 179725368623STom Herbert } 179819e42e45SRoopa Prabhu 179986872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 180086872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 1801afc4eef8SMartin KaFai Lau if (rt->rt6i_dst.plen == 128) 180211d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 18031da177e4SLinus Torvalds 18041da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 180586872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 180686872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 18071da177e4SLinus Torvalds #endif 18081da177e4SLinus Torvalds 180986872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 18101da177e4SLinus Torvalds 18111da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 18121da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 18131da177e4SLinus Torvalds */ 181486872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 181538308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 181638308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 181738308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 18181da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 18195578689aSDaniel Lezcano if (dev != net->loopback_dev) { 18201da177e4SLinus Torvalds if (dev) { 18211da177e4SLinus Torvalds dev_put(dev); 18221da177e4SLinus Torvalds in6_dev_put(idev); 18231da177e4SLinus Torvalds } 18245578689aSDaniel Lezcano dev = net->loopback_dev; 18251da177e4SLinus Torvalds dev_hold(dev); 18261da177e4SLinus Torvalds idev = in6_dev_get(dev); 18271da177e4SLinus Torvalds if (!idev) { 18281da177e4SLinus Torvalds err = -ENODEV; 18291da177e4SLinus Torvalds goto out; 18301da177e4SLinus Torvalds } 18311da177e4SLinus Torvalds } 18321da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1833ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1834ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1835ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 1836aad88724SEric Dumazet rt->dst.output = dst_discard_sk; 18377150aedeSKamala R rt->dst.input = dst_discard; 1838ef2c7d7bSNicolas Dichtel break; 1839ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1840ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 18417150aedeSKamala R rt->dst.output = ip6_pkt_prohibit_out; 18427150aedeSKamala R rt->dst.input = ip6_pkt_prohibit; 1843ef2c7d7bSNicolas Dichtel break; 1844b4949ab2SNicolas Dichtel case RTN_THROW: 1845ef2c7d7bSNicolas Dichtel default: 18467150aedeSKamala R rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN 18477150aedeSKamala R : -ENETUNREACH; 18487150aedeSKamala R rt->dst.output = ip6_pkt_discard_out; 18497150aedeSKamala R rt->dst.input = ip6_pkt_discard; 1850ef2c7d7bSNicolas Dichtel break; 1851ef2c7d7bSNicolas Dichtel } 18521da177e4SLinus Torvalds goto install_route; 18531da177e4SLinus Torvalds } 18541da177e4SLinus Torvalds 185586872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1856b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 18571da177e4SLinus Torvalds int gwa_type; 18581da177e4SLinus Torvalds 185986872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 1860330567b7SFlorian Westphal gwa_type = ipv6_addr_type(gw_addr); 186148ed7b26SFlorian Westphal 186248ed7b26SFlorian Westphal /* if gw_addr is local we will fail to detect this in case 186348ed7b26SFlorian Westphal * address is still TENTATIVE (DAD in progress). rt6_lookup() 186448ed7b26SFlorian Westphal * will return already-added prefix route via interface that 186548ed7b26SFlorian Westphal * prefix route was assigned to, which might be non-loopback. 186648ed7b26SFlorian Westphal */ 186748ed7b26SFlorian Westphal err = -EINVAL; 1868330567b7SFlorian Westphal if (ipv6_chk_addr_and_flags(net, gw_addr, 1869330567b7SFlorian Westphal gwa_type & IPV6_ADDR_LINKLOCAL ? 1870330567b7SFlorian Westphal dev : NULL, 0, 0)) 187148ed7b26SFlorian Westphal goto out; 187248ed7b26SFlorian Westphal 18734e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 18741da177e4SLinus Torvalds 18751da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 18761da177e4SLinus Torvalds struct rt6_info *grt; 18771da177e4SLinus Torvalds 18781da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 18791da177e4SLinus Torvalds addresses as nexthop address. 18801da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 18811da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 18821da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 18831da177e4SLinus Torvalds some exceptions. --ANK 18841da177e4SLinus Torvalds */ 18851da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 18861da177e4SLinus Torvalds goto out; 18871da177e4SLinus Torvalds 18885578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 18891da177e4SLinus Torvalds 18901da177e4SLinus Torvalds err = -EHOSTUNREACH; 189138308473SDavid S. Miller if (!grt) 18921da177e4SLinus Torvalds goto out; 18931da177e4SLinus Torvalds if (dev) { 1894d1918542SDavid S. Miller if (dev != grt->dst.dev) { 189594e187c0SAmerigo Wang ip6_rt_put(grt); 18961da177e4SLinus Torvalds goto out; 18971da177e4SLinus Torvalds } 18981da177e4SLinus Torvalds } else { 1899d1918542SDavid S. Miller dev = grt->dst.dev; 19001da177e4SLinus Torvalds idev = grt->rt6i_idev; 19011da177e4SLinus Torvalds dev_hold(dev); 19021da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 19031da177e4SLinus Torvalds } 19041da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 19051da177e4SLinus Torvalds err = 0; 190694e187c0SAmerigo Wang ip6_rt_put(grt); 19071da177e4SLinus Torvalds 19081da177e4SLinus Torvalds if (err) 19091da177e4SLinus Torvalds goto out; 19101da177e4SLinus Torvalds } 19111da177e4SLinus Torvalds err = -EINVAL; 191238308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 19131da177e4SLinus Torvalds goto out; 19141da177e4SLinus Torvalds } 19151da177e4SLinus Torvalds 19161da177e4SLinus Torvalds err = -ENODEV; 191738308473SDavid S. Miller if (!dev) 19181da177e4SLinus Torvalds goto out; 19191da177e4SLinus Torvalds 1920c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1921c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1922c3968a85SDaniel Walter err = -EINVAL; 1923c3968a85SDaniel Walter goto out; 1924c3968a85SDaniel Walter } 19254e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1926c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1927c3968a85SDaniel Walter } else 1928c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1929c3968a85SDaniel Walter 193086872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 19311da177e4SLinus Torvalds 19321da177e4SLinus Torvalds install_route: 1933d8d1f30bSChangli Gao rt->dst.dev = dev; 19341da177e4SLinus Torvalds rt->rt6i_idev = idev; 1935c71099acSThomas Graf rt->rt6i_table = table; 193663152fc0SDaniel Lezcano 1937c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 193863152fc0SDaniel Lezcano 1939e715b6d3SFlorian Westphal err = ip6_convert_metrics(&mxc, cfg); 1940e715b6d3SFlorian Westphal if (err) 1941e715b6d3SFlorian Westphal goto out; 19421da177e4SLinus Torvalds 1943e715b6d3SFlorian Westphal err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); 1944e715b6d3SFlorian Westphal 1945e715b6d3SFlorian Westphal kfree(mxc.mx); 1946e715b6d3SFlorian Westphal return err; 19471da177e4SLinus Torvalds out: 19481da177e4SLinus Torvalds if (dev) 19491da177e4SLinus Torvalds dev_put(dev); 19501da177e4SLinus Torvalds if (idev) 19511da177e4SLinus Torvalds in6_dev_put(idev); 19521da177e4SLinus Torvalds if (rt) 1953d8d1f30bSChangli Gao dst_free(&rt->dst); 19541da177e4SLinus Torvalds return err; 19551da177e4SLinus Torvalds } 19561da177e4SLinus Torvalds 195786872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 19581da177e4SLinus Torvalds { 19591da177e4SLinus Torvalds int err; 1960c71099acSThomas Graf struct fib6_table *table; 1961d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 19621da177e4SLinus Torvalds 19636825a26cSGao feng if (rt == net->ipv6.ip6_null_entry) { 19646825a26cSGao feng err = -ENOENT; 19656825a26cSGao feng goto out; 19666825a26cSGao feng } 19676c813a72SPatrick McHardy 1968c71099acSThomas Graf table = rt->rt6i_table; 1969c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 197086872cb5SThomas Graf err = fib6_del(rt, info); 1971c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 19721da177e4SLinus Torvalds 19736825a26cSGao feng out: 197494e187c0SAmerigo Wang ip6_rt_put(rt); 19751da177e4SLinus Torvalds return err; 19761da177e4SLinus Torvalds } 19771da177e4SLinus Torvalds 1978e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 1979e0a1ad73SThomas Graf { 19804d1169c1SDenis V. Lunev struct nl_info info = { 1981d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 19824d1169c1SDenis V. Lunev }; 1983528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 1984e0a1ad73SThomas Graf } 1985e0a1ad73SThomas Graf 198686872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 19871da177e4SLinus Torvalds { 1988c71099acSThomas Graf struct fib6_table *table; 19891da177e4SLinus Torvalds struct fib6_node *fn; 19901da177e4SLinus Torvalds struct rt6_info *rt; 19911da177e4SLinus Torvalds int err = -ESRCH; 19921da177e4SLinus Torvalds 19935578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 199438308473SDavid S. Miller if (!table) 1995c71099acSThomas Graf return err; 19961da177e4SLinus Torvalds 1997c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1998c71099acSThomas Graf 1999c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 200086872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 200186872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 20021da177e4SLinus Torvalds 20031da177e4SLinus Torvalds if (fn) { 2004d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 20051f56a01fSMartin KaFai Lau if ((rt->rt6i_flags & RTF_CACHE) && 20061f56a01fSMartin KaFai Lau !(cfg->fc_flags & RTF_CACHE)) 20071f56a01fSMartin KaFai Lau continue; 200886872cb5SThomas Graf if (cfg->fc_ifindex && 2009d1918542SDavid S. Miller (!rt->dst.dev || 2010d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 20111da177e4SLinus Torvalds continue; 201286872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 201386872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 20141da177e4SLinus Torvalds continue; 201586872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 20161da177e4SLinus Torvalds continue; 2017d8d1f30bSChangli Gao dst_hold(&rt->dst); 2018c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 20191da177e4SLinus Torvalds 202086872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 20211da177e4SLinus Torvalds } 20221da177e4SLinus Torvalds } 2023c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 20241da177e4SLinus Torvalds 20251da177e4SLinus Torvalds return err; 20261da177e4SLinus Torvalds } 20271da177e4SLinus Torvalds 20286700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 2029a6279458SYOSHIFUJI Hideaki { 2030e8599ff4SDavid S. Miller struct net *net = dev_net(skb->dev); 2031a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 2032e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 2033e8599ff4SDavid S. Miller struct ndisc_options ndopts; 2034e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 2035e8599ff4SDavid S. Miller struct neighbour *neigh; 203671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 struct rd_msg *msg; 20376e157b6aSDavid S. Miller int optlen, on_link; 20386e157b6aSDavid S. Miller u8 *lladdr; 2039e8599ff4SDavid S. Miller 204029a3cad5SSimon Horman optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 204171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 optlen -= sizeof(*msg); 2042e8599ff4SDavid S. Miller 2043e8599ff4SDavid S. Miller if (optlen < 0) { 20446e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 2045e8599ff4SDavid S. Miller return; 2046e8599ff4SDavid S. Miller } 2047e8599ff4SDavid S. Miller 204871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 msg = (struct rd_msg *)icmp6_hdr(skb); 2049e8599ff4SDavid S. Miller 205071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_is_multicast(&msg->dest)) { 20516e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 2052e8599ff4SDavid S. Miller return; 2053e8599ff4SDavid S. Miller } 2054e8599ff4SDavid S. Miller 20556e157b6aSDavid S. Miller on_link = 0; 205671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_equal(&msg->dest, &msg->target)) { 2057e8599ff4SDavid S. Miller on_link = 1; 205871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 } else if (ipv6_addr_type(&msg->target) != 2059e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 20606e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 2061e8599ff4SDavid S. Miller return; 2062e8599ff4SDavid S. Miller } 2063e8599ff4SDavid S. Miller 2064e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 2065e8599ff4SDavid S. Miller if (!in6_dev) 2066e8599ff4SDavid S. Miller return; 2067e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 2068e8599ff4SDavid S. Miller return; 2069e8599ff4SDavid S. Miller 2070e8599ff4SDavid S. Miller /* RFC2461 8.1: 2071e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 2072e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 2073e8599ff4SDavid S. Miller */ 2074e8599ff4SDavid S. Miller 207571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) { 2076e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 2077e8599ff4SDavid S. Miller return; 2078e8599ff4SDavid S. Miller } 20796e157b6aSDavid S. Miller 20806e157b6aSDavid S. Miller lladdr = NULL; 2081e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 2082e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 2083e8599ff4SDavid S. Miller skb->dev); 2084e8599ff4SDavid S. Miller if (!lladdr) { 2085e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 2086e8599ff4SDavid S. Miller return; 2087e8599ff4SDavid S. Miller } 2088e8599ff4SDavid S. Miller } 2089e8599ff4SDavid S. Miller 20906e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 20916e157b6aSDavid S. Miller if (rt == net->ipv6.ip6_null_entry) { 20926e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 20936e157b6aSDavid S. Miller return; 20946e157b6aSDavid S. Miller } 20956e157b6aSDavid S. Miller 20966e157b6aSDavid S. Miller /* Redirect received -> path was valid. 20976e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 20986e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 20996e157b6aSDavid S. Miller */ 21006e157b6aSDavid S. Miller dst_confirm(&rt->dst); 21016e157b6aSDavid S. Miller 210271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 2103e8599ff4SDavid S. Miller if (!neigh) 2104e8599ff4SDavid S. Miller return; 2105e8599ff4SDavid S. Miller 21061da177e4SLinus Torvalds /* 21071da177e4SLinus Torvalds * We have finally decided to accept it. 21081da177e4SLinus Torvalds */ 21091da177e4SLinus Torvalds 21101da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 21111da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 21121da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 21131da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 21141da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 21151da177e4SLinus Torvalds ); 21161da177e4SLinus Torvalds 211783a09abdSMartin KaFai Lau nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL); 211838308473SDavid S. Miller if (!nrt) 21191da177e4SLinus Torvalds goto out; 21201da177e4SLinus Torvalds 21211da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 21221da177e4SLinus Torvalds if (on_link) 21231da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 21241da177e4SLinus Torvalds 21254e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 21261da177e4SLinus Torvalds 212740e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 21281da177e4SLinus Torvalds goto out; 21291da177e4SLinus Torvalds 2130d8d1f30bSChangli Gao netevent.old = &rt->dst; 2131d8d1f30bSChangli Gao netevent.new = &nrt->dst; 213271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 netevent.daddr = &msg->dest; 213360592833SYOSHIFUJI Hideaki / 吉藤英明 netevent.neigh = neigh; 21348d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 21358d71740cSTom Tucker 21361da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 21376e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 2138e0a1ad73SThomas Graf ip6_del_rt(rt); 21391da177e4SLinus Torvalds } 21401da177e4SLinus Torvalds 21411da177e4SLinus Torvalds out: 2142e8599ff4SDavid S. Miller neigh_release(neigh); 21436e157b6aSDavid S. Miller } 21446e157b6aSDavid S. Miller 21451da177e4SLinus Torvalds /* 21461da177e4SLinus Torvalds * Misc support functions 21471da177e4SLinus Torvalds */ 21481da177e4SLinus Torvalds 21494b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) 21504b32b5adSMartin KaFai Lau { 21514b32b5adSMartin KaFai Lau BUG_ON(from->dst.from); 21524b32b5adSMartin KaFai Lau 21534b32b5adSMartin KaFai Lau rt->rt6i_flags &= ~RTF_EXPIRES; 21544b32b5adSMartin KaFai Lau dst_hold(&from->dst); 21554b32b5adSMartin KaFai Lau rt->dst.from = &from->dst; 21564b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true); 21574b32b5adSMartin KaFai Lau } 21584b32b5adSMartin KaFai Lau 215983a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort) 21601da177e4SLinus Torvalds { 2161d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 2162d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 216383a09abdSMartin KaFai Lau rt->rt6i_dst = ort->rt6i_dst; 2164d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 21651da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 21661da177e4SLinus Torvalds if (rt->rt6i_idev) 21671da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 2168d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 21694e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 21701716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 21711716a961SGao feng rt6_set_from(rt, ort); 217283a09abdSMartin KaFai Lau rt->rt6i_metric = ort->rt6i_metric; 21731da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 217483a09abdSMartin KaFai Lau rt->rt6i_src = ort->rt6i_src; 21751da177e4SLinus Torvalds #endif 217683a09abdSMartin KaFai Lau rt->rt6i_prefsrc = ort->rt6i_prefsrc; 2177c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 217861adedf3SJiri Benc rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate); 21791da177e4SLinus Torvalds } 21801da177e4SLinus Torvalds 218170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 2182efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 2183b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2184b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 218570ceb4f5SYOSHIFUJI Hideaki { 218670ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 218770ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 2188c71099acSThomas Graf struct fib6_table *table; 218970ceb4f5SYOSHIFUJI Hideaki 2190efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 219138308473SDavid S. Miller if (!table) 2192c71099acSThomas Graf return NULL; 2193c71099acSThomas Graf 21945744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2195c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0); 219670ceb4f5SYOSHIFUJI Hideaki if (!fn) 219770ceb4f5SYOSHIFUJI Hideaki goto out; 219870ceb4f5SYOSHIFUJI Hideaki 2199d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 2200d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 220170ceb4f5SYOSHIFUJI Hideaki continue; 220270ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 220370ceb4f5SYOSHIFUJI Hideaki continue; 220470ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 220570ceb4f5SYOSHIFUJI Hideaki continue; 2206d8d1f30bSChangli Gao dst_hold(&rt->dst); 220770ceb4f5SYOSHIFUJI Hideaki break; 220870ceb4f5SYOSHIFUJI Hideaki } 220970ceb4f5SYOSHIFUJI Hideaki out: 22105744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 221170ceb4f5SYOSHIFUJI Hideaki return rt; 221270ceb4f5SYOSHIFUJI Hideaki } 221370ceb4f5SYOSHIFUJI Hideaki 2214efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 2215b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2216b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 221795c96174SEric Dumazet unsigned int pref) 221870ceb4f5SYOSHIFUJI Hideaki { 221986872cb5SThomas Graf struct fib6_config cfg = { 222086872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 2221238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 222286872cb5SThomas Graf .fc_ifindex = ifindex, 222386872cb5SThomas Graf .fc_dst_len = prefixlen, 222486872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 222586872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 222615e47304SEric W. Biederman .fc_nlinfo.portid = 0, 2227efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 2228efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 222986872cb5SThomas Graf }; 223070ceb4f5SYOSHIFUJI Hideaki 22314e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 22324e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 223386872cb5SThomas Graf 2234e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 2235e317da96SYOSHIFUJI Hideaki if (!prefixlen) 223686872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 223770ceb4f5SYOSHIFUJI Hideaki 223886872cb5SThomas Graf ip6_route_add(&cfg); 223970ceb4f5SYOSHIFUJI Hideaki 2240efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 224170ceb4f5SYOSHIFUJI Hideaki } 224270ceb4f5SYOSHIFUJI Hideaki #endif 224370ceb4f5SYOSHIFUJI Hideaki 2244b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 22451da177e4SLinus Torvalds { 22461da177e4SLinus Torvalds struct rt6_info *rt; 2247c71099acSThomas Graf struct fib6_table *table; 22481da177e4SLinus Torvalds 2249c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 225038308473SDavid S. Miller if (!table) 2251c71099acSThomas Graf return NULL; 22521da177e4SLinus Torvalds 22535744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2254d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 2255d1918542SDavid S. Miller if (dev == rt->dst.dev && 2256045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 22571da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 22581da177e4SLinus Torvalds break; 22591da177e4SLinus Torvalds } 22601da177e4SLinus Torvalds if (rt) 2261d8d1f30bSChangli Gao dst_hold(&rt->dst); 22625744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 22631da177e4SLinus Torvalds return rt; 22641da177e4SLinus Torvalds } 22651da177e4SLinus Torvalds 2266b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 2267ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 2268ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 22691da177e4SLinus Torvalds { 227086872cb5SThomas Graf struct fib6_config cfg = { 227186872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 2272238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 227386872cb5SThomas Graf .fc_ifindex = dev->ifindex, 227486872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 227586872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 227615e47304SEric W. Biederman .fc_nlinfo.portid = 0, 22775578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 2278c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 227986872cb5SThomas Graf }; 22801da177e4SLinus Torvalds 22814e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 22821da177e4SLinus Torvalds 228386872cb5SThomas Graf ip6_route_add(&cfg); 22841da177e4SLinus Torvalds 22851da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 22861da177e4SLinus Torvalds } 22871da177e4SLinus Torvalds 22887b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 22891da177e4SLinus Torvalds { 22901da177e4SLinus Torvalds struct rt6_info *rt; 2291c71099acSThomas Graf struct fib6_table *table; 2292c71099acSThomas Graf 2293c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 22947b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 229538308473SDavid S. Miller if (!table) 2296c71099acSThomas Graf return; 22971da177e4SLinus Torvalds 22981da177e4SLinus Torvalds restart: 2299c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2300d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 23013e8b0ac3SLorenzo Colitti if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 23023e8b0ac3SLorenzo Colitti (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { 2303d8d1f30bSChangli Gao dst_hold(&rt->dst); 2304c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2305e0a1ad73SThomas Graf ip6_del_rt(rt); 23061da177e4SLinus Torvalds goto restart; 23071da177e4SLinus Torvalds } 23081da177e4SLinus Torvalds } 2309c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 23101da177e4SLinus Torvalds } 23111da177e4SLinus Torvalds 23125578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 23135578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 231486872cb5SThomas Graf struct fib6_config *cfg) 231586872cb5SThomas Graf { 231686872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 231786872cb5SThomas Graf 231886872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 231986872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 232086872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 232186872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 232286872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 232386872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 232486872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 232586872cb5SThomas Graf 23265578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 2327f1243c2dSBenjamin Thery 23284e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 23294e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 23304e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 233186872cb5SThomas Graf } 233286872cb5SThomas Graf 23335578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 23341da177e4SLinus Torvalds { 233586872cb5SThomas Graf struct fib6_config cfg; 23361da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 23371da177e4SLinus Torvalds int err; 23381da177e4SLinus Torvalds 23391da177e4SLinus Torvalds switch (cmd) { 23401da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 23411da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 2342af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 23431da177e4SLinus Torvalds return -EPERM; 23441da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 23451da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 23461da177e4SLinus Torvalds if (err) 23471da177e4SLinus Torvalds return -EFAULT; 23481da177e4SLinus Torvalds 23495578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 235086872cb5SThomas Graf 23511da177e4SLinus Torvalds rtnl_lock(); 23521da177e4SLinus Torvalds switch (cmd) { 23531da177e4SLinus Torvalds case SIOCADDRT: 235486872cb5SThomas Graf err = ip6_route_add(&cfg); 23551da177e4SLinus Torvalds break; 23561da177e4SLinus Torvalds case SIOCDELRT: 235786872cb5SThomas Graf err = ip6_route_del(&cfg); 23581da177e4SLinus Torvalds break; 23591da177e4SLinus Torvalds default: 23601da177e4SLinus Torvalds err = -EINVAL; 23611da177e4SLinus Torvalds } 23621da177e4SLinus Torvalds rtnl_unlock(); 23631da177e4SLinus Torvalds 23641da177e4SLinus Torvalds return err; 23653ff50b79SStephen Hemminger } 23661da177e4SLinus Torvalds 23671da177e4SLinus Torvalds return -EINVAL; 23681da177e4SLinus Torvalds } 23691da177e4SLinus Torvalds 23701da177e4SLinus Torvalds /* 23711da177e4SLinus Torvalds * Drop the packet on the floor 23721da177e4SLinus Torvalds */ 23731da177e4SLinus Torvalds 2374d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 23751da177e4SLinus Torvalds { 2376612f09e8SYOSHIFUJI Hideaki int type; 2377adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2378612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2379612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 23800660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 238145bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 23823bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 23833bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2384612f09e8SYOSHIFUJI Hideaki break; 2385612f09e8SYOSHIFUJI Hideaki } 2386612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2387612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 23883bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 23893bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2390612f09e8SYOSHIFUJI Hideaki break; 2391612f09e8SYOSHIFUJI Hideaki } 23923ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 23931da177e4SLinus Torvalds kfree_skb(skb); 23941da177e4SLinus Torvalds return 0; 23951da177e4SLinus Torvalds } 23961da177e4SLinus Torvalds 23979ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 23989ce8ade0SThomas Graf { 2399612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 24009ce8ade0SThomas Graf } 24019ce8ade0SThomas Graf 2402aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb) 24031da177e4SLinus Torvalds { 2404adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2405612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 24061da177e4SLinus Torvalds } 24071da177e4SLinus Torvalds 24089ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 24099ce8ade0SThomas Graf { 2410612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 24119ce8ade0SThomas Graf } 24129ce8ade0SThomas Graf 2413aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb) 24149ce8ade0SThomas Graf { 2415adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2416612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 24179ce8ade0SThomas Graf } 24189ce8ade0SThomas Graf 24191da177e4SLinus Torvalds /* 24201da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 24211da177e4SLinus Torvalds */ 24221da177e4SLinus Torvalds 24231da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 24241da177e4SLinus Torvalds const struct in6_addr *addr, 24258f031519SDavid S. Miller bool anycast) 24261da177e4SLinus Torvalds { 2427c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 2428a3300ef4SHannes Frederic Sowa struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 2429a3300ef4SHannes Frederic Sowa DST_NOCOUNT, NULL); 2430a3300ef4SHannes Frederic Sowa if (!rt) 24311da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 24321da177e4SLinus Torvalds 24331da177e4SLinus Torvalds in6_dev_hold(idev); 24341da177e4SLinus Torvalds 243511d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2436d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2437d8d1f30bSChangli Gao rt->dst.output = ip6_output; 24381da177e4SLinus Torvalds rt->rt6i_idev = idev; 24391da177e4SLinus Torvalds 24401da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 244158c4fb86SYOSHIFUJI Hideaki if (anycast) 244258c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 244358c4fb86SYOSHIFUJI Hideaki else 24441da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 24451da177e4SLinus Torvalds 2446550bab42SJulian Anastasov rt->rt6i_gateway = *addr; 24474e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 24481da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 24495578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 24501da177e4SLinus Torvalds 2451d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 24521da177e4SLinus Torvalds 24531da177e4SLinus Torvalds return rt; 24541da177e4SLinus Torvalds } 24551da177e4SLinus Torvalds 2456c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2457c3968a85SDaniel Walter struct rt6_info *rt, 2458b71d1d42SEric Dumazet const struct in6_addr *daddr, 2459c3968a85SDaniel Walter unsigned int prefs, 2460c3968a85SDaniel Walter struct in6_addr *saddr) 2461c3968a85SDaniel Walter { 2462e16e888bSMarkus Stenberg struct inet6_dev *idev = 2463e16e888bSMarkus Stenberg rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL; 2464c3968a85SDaniel Walter int err = 0; 2465e16e888bSMarkus Stenberg if (rt && rt->rt6i_prefsrc.plen) 24664e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2467c3968a85SDaniel Walter else 2468c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2469c3968a85SDaniel Walter daddr, prefs, saddr); 2470c3968a85SDaniel Walter return err; 2471c3968a85SDaniel Walter } 2472c3968a85SDaniel Walter 2473c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2474c3968a85SDaniel Walter struct arg_dev_net_ip { 2475c3968a85SDaniel Walter struct net_device *dev; 2476c3968a85SDaniel Walter struct net *net; 2477c3968a85SDaniel Walter struct in6_addr *addr; 2478c3968a85SDaniel Walter }; 2479c3968a85SDaniel Walter 2480c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2481c3968a85SDaniel Walter { 2482c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2483c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2484c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2485c3968a85SDaniel Walter 2486d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2487c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2488c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2489c3968a85SDaniel Walter /* remove prefsrc entry */ 2490c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2491c3968a85SDaniel Walter } 2492c3968a85SDaniel Walter return 0; 2493c3968a85SDaniel Walter } 2494c3968a85SDaniel Walter 2495c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2496c3968a85SDaniel Walter { 2497c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2498c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2499c3968a85SDaniel Walter .dev = ifp->idev->dev, 2500c3968a85SDaniel Walter .net = net, 2501c3968a85SDaniel Walter .addr = &ifp->addr, 2502c3968a85SDaniel Walter }; 25030c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 2504c3968a85SDaniel Walter } 2505c3968a85SDaniel Walter 2506be7a010dSDuan Jiong #define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) 2507be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 2508be7a010dSDuan Jiong 2509be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */ 2510be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg) 2511be7a010dSDuan Jiong { 2512be7a010dSDuan Jiong struct in6_addr *gateway = (struct in6_addr *)arg; 2513be7a010dSDuan Jiong 2514be7a010dSDuan Jiong if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) || 2515be7a010dSDuan Jiong ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) && 2516be7a010dSDuan Jiong ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { 2517be7a010dSDuan Jiong return -1; 2518be7a010dSDuan Jiong } 2519be7a010dSDuan Jiong return 0; 2520be7a010dSDuan Jiong } 2521be7a010dSDuan Jiong 2522be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 2523be7a010dSDuan Jiong { 2524be7a010dSDuan Jiong fib6_clean_all(net, fib6_clean_tohost, gateway); 2525be7a010dSDuan Jiong } 2526be7a010dSDuan Jiong 25278ed67789SDaniel Lezcano struct arg_dev_net { 25288ed67789SDaniel Lezcano struct net_device *dev; 25298ed67789SDaniel Lezcano struct net *net; 25308ed67789SDaniel Lezcano }; 25318ed67789SDaniel Lezcano 25321da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 25331da177e4SLinus Torvalds { 2534bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2535bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 25368ed67789SDaniel Lezcano 2537d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2538c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 25391da177e4SLinus Torvalds return -1; 2540c159d30cSDavid S. Miller 25411da177e4SLinus Torvalds return 0; 25421da177e4SLinus Torvalds } 25431da177e4SLinus Torvalds 2544f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 25451da177e4SLinus Torvalds { 25468ed67789SDaniel Lezcano struct arg_dev_net adn = { 25478ed67789SDaniel Lezcano .dev = dev, 25488ed67789SDaniel Lezcano .net = net, 25498ed67789SDaniel Lezcano }; 25508ed67789SDaniel Lezcano 25510c3584d5SLi RongQing fib6_clean_all(net, fib6_ifdown, &adn); 25521e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 25538d0b94afSMartin KaFai Lau rt6_uncached_list_flush_dev(net, dev); 25541da177e4SLinus Torvalds } 25551da177e4SLinus Torvalds 255695c96174SEric Dumazet struct rt6_mtu_change_arg { 25571da177e4SLinus Torvalds struct net_device *dev; 255895c96174SEric Dumazet unsigned int mtu; 25591da177e4SLinus Torvalds }; 25601da177e4SLinus Torvalds 25611da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 25621da177e4SLinus Torvalds { 25631da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 25641da177e4SLinus Torvalds struct inet6_dev *idev; 25651da177e4SLinus Torvalds 25661da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 25671da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 25681da177e4SLinus Torvalds We still use this lock to block changes 25691da177e4SLinus Torvalds caused by addrconf/ndisc. 25701da177e4SLinus Torvalds */ 25711da177e4SLinus Torvalds 25721da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 257338308473SDavid S. Miller if (!idev) 25741da177e4SLinus Torvalds return 0; 25751da177e4SLinus Torvalds 25761da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 25771da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 25781da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 25791da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 25801da177e4SLinus Torvalds */ 25811da177e4SLinus Torvalds /* 25821da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 25831da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 25841da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 25851da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 25861da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 25871da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 25881da177e4SLinus Torvalds PMTU discouvery. 25891da177e4SLinus Torvalds */ 2590d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 25914b32b5adSMartin KaFai Lau !dst_metric_locked(&rt->dst, RTAX_MTU)) { 25924b32b5adSMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) { 25934b32b5adSMartin KaFai Lau /* For RTF_CACHE with rt6i_pmtu == 0 25944b32b5adSMartin KaFai Lau * (i.e. a redirected route), 25954b32b5adSMartin KaFai Lau * the metrics of its rt->dst.from has already 25964b32b5adSMartin KaFai Lau * been updated. 25974b32b5adSMartin KaFai Lau */ 25984b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu) 25994b32b5adSMartin KaFai Lau rt->rt6i_pmtu = arg->mtu; 26004b32b5adSMartin KaFai Lau } else if (dst_mtu(&rt->dst) >= arg->mtu || 2601d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 26024b32b5adSMartin KaFai Lau dst_mtu(&rt->dst) == idev->cnf.mtu6)) { 2603defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2604566cfd8fSSimon Arlott } 26054b32b5adSMartin KaFai Lau } 26061da177e4SLinus Torvalds return 0; 26071da177e4SLinus Torvalds } 26081da177e4SLinus Torvalds 260995c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 26101da177e4SLinus Torvalds { 2611c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2612c71099acSThomas Graf .dev = dev, 2613c71099acSThomas Graf .mtu = mtu, 2614c71099acSThomas Graf }; 26151da177e4SLinus Torvalds 26160c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 26171da177e4SLinus Torvalds } 26181da177e4SLinus Torvalds 2619ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 26205176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 262186872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2622ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 262386872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 262486872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 262551ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2626c78ba6d6SLubomir Rintel [RTA_PREF] = { .type = NLA_U8 }, 262719e42e45SRoopa Prabhu [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, 262819e42e45SRoopa Prabhu [RTA_ENCAP] = { .type = NLA_NESTED }, 262986872cb5SThomas Graf }; 263086872cb5SThomas Graf 263186872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 263286872cb5SThomas Graf struct fib6_config *cfg) 26331da177e4SLinus Torvalds { 263486872cb5SThomas Graf struct rtmsg *rtm; 263586872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 2636c78ba6d6SLubomir Rintel unsigned int pref; 263786872cb5SThomas Graf int err; 26381da177e4SLinus Torvalds 263986872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 264086872cb5SThomas Graf if (err < 0) 264186872cb5SThomas Graf goto errout; 26421da177e4SLinus Torvalds 264386872cb5SThomas Graf err = -EINVAL; 264486872cb5SThomas Graf rtm = nlmsg_data(nlh); 264586872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 264686872cb5SThomas Graf 264786872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 264886872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 264986872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 265086872cb5SThomas Graf cfg->fc_flags = RTF_UP; 265186872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2652ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 265386872cb5SThomas Graf 2654ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2655ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2656b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2657b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 265886872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 265986872cb5SThomas Graf 2660ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2661ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2662ab79ad14SMaciej Żenczykowski 26631f56a01fSMartin KaFai Lau if (rtm->rtm_flags & RTM_F_CLONED) 26641f56a01fSMartin KaFai Lau cfg->fc_flags |= RTF_CACHE; 26651f56a01fSMartin KaFai Lau 266615e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 266786872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 26683b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 266986872cb5SThomas Graf 267086872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 267167b61f6cSJiri Benc cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 267286872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 26731da177e4SLinus Torvalds } 267486872cb5SThomas Graf 267586872cb5SThomas Graf if (tb[RTA_DST]) { 267686872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 267786872cb5SThomas Graf 267886872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 267986872cb5SThomas Graf goto errout; 268086872cb5SThomas Graf 268186872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 26821da177e4SLinus Torvalds } 268386872cb5SThomas Graf 268486872cb5SThomas Graf if (tb[RTA_SRC]) { 268586872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 268686872cb5SThomas Graf 268786872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 268886872cb5SThomas Graf goto errout; 268986872cb5SThomas Graf 269086872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 26911da177e4SLinus Torvalds } 269286872cb5SThomas Graf 2693c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 269467b61f6cSJiri Benc cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 2695c3968a85SDaniel Walter 269686872cb5SThomas Graf if (tb[RTA_OIF]) 269786872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 269886872cb5SThomas Graf 269986872cb5SThomas Graf if (tb[RTA_PRIORITY]) 270086872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 270186872cb5SThomas Graf 270286872cb5SThomas Graf if (tb[RTA_METRICS]) { 270386872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 270486872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 27051da177e4SLinus Torvalds } 270686872cb5SThomas Graf 270786872cb5SThomas Graf if (tb[RTA_TABLE]) 270886872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 270986872cb5SThomas Graf 271051ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 271151ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 271251ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 271351ebd318SNicolas Dichtel } 271451ebd318SNicolas Dichtel 2715c78ba6d6SLubomir Rintel if (tb[RTA_PREF]) { 2716c78ba6d6SLubomir Rintel pref = nla_get_u8(tb[RTA_PREF]); 2717c78ba6d6SLubomir Rintel if (pref != ICMPV6_ROUTER_PREF_LOW && 2718c78ba6d6SLubomir Rintel pref != ICMPV6_ROUTER_PREF_HIGH) 2719c78ba6d6SLubomir Rintel pref = ICMPV6_ROUTER_PREF_MEDIUM; 2720c78ba6d6SLubomir Rintel cfg->fc_flags |= RTF_PREF(pref); 2721c78ba6d6SLubomir Rintel } 2722c78ba6d6SLubomir Rintel 272319e42e45SRoopa Prabhu if (tb[RTA_ENCAP]) 272419e42e45SRoopa Prabhu cfg->fc_encap = tb[RTA_ENCAP]; 272519e42e45SRoopa Prabhu 272619e42e45SRoopa Prabhu if (tb[RTA_ENCAP_TYPE]) 272719e42e45SRoopa Prabhu cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]); 272819e42e45SRoopa Prabhu 272986872cb5SThomas Graf err = 0; 273086872cb5SThomas Graf errout: 273186872cb5SThomas Graf return err; 27321da177e4SLinus Torvalds } 27331da177e4SLinus Torvalds 273451ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add) 273551ebd318SNicolas Dichtel { 273651ebd318SNicolas Dichtel struct fib6_config r_cfg; 273751ebd318SNicolas Dichtel struct rtnexthop *rtnh; 273851ebd318SNicolas Dichtel int remaining; 273951ebd318SNicolas Dichtel int attrlen; 274051ebd318SNicolas Dichtel int err = 0, last_err = 0; 274151ebd318SNicolas Dichtel 274235f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len; 274351ebd318SNicolas Dichtel beginning: 274451ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 274551ebd318SNicolas Dichtel 274651ebd318SNicolas Dichtel /* Parse a Multipath Entry */ 274751ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 274851ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 274951ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 275051ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 275151ebd318SNicolas Dichtel 275251ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 275351ebd318SNicolas Dichtel if (attrlen > 0) { 275451ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 275551ebd318SNicolas Dichtel 275651ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 275751ebd318SNicolas Dichtel if (nla) { 275867b61f6cSJiri Benc r_cfg.fc_gateway = nla_get_in6_addr(nla); 275951ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 276051ebd318SNicolas Dichtel } 276119e42e45SRoopa Prabhu r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 276219e42e45SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 276319e42e45SRoopa Prabhu if (nla) 276419e42e45SRoopa Prabhu r_cfg.fc_encap_type = nla_get_u16(nla); 276551ebd318SNicolas Dichtel } 276651ebd318SNicolas Dichtel err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg); 276751ebd318SNicolas Dichtel if (err) { 276851ebd318SNicolas Dichtel last_err = err; 276951ebd318SNicolas Dichtel /* If we are trying to remove a route, do not stop the 277051ebd318SNicolas Dichtel * loop when ip6_route_del() fails (because next hop is 277151ebd318SNicolas Dichtel * already gone), we should try to remove all next hops. 277251ebd318SNicolas Dichtel */ 277351ebd318SNicolas Dichtel if (add) { 277451ebd318SNicolas Dichtel /* If add fails, we should try to delete all 277551ebd318SNicolas Dichtel * next hops that have been already added. 277651ebd318SNicolas Dichtel */ 277751ebd318SNicolas Dichtel add = 0; 277835f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len - remaining; 277951ebd318SNicolas Dichtel goto beginning; 278051ebd318SNicolas Dichtel } 278151ebd318SNicolas Dichtel } 27821a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 278327596472SMichal Kubeček * these flags after the first nexthop: if there is a collision, 278427596472SMichal Kubeček * we have already failed to add the first nexthop: 278527596472SMichal Kubeček * fib6_add_rt2node() has rejected it; when replacing, old 278627596472SMichal Kubeček * nexthops have been replaced by first new, the rest should 278727596472SMichal Kubeček * be added to it. 27881a72418bSNicolas Dichtel */ 278927596472SMichal Kubeček cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | 279027596472SMichal Kubeček NLM_F_REPLACE); 279151ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 279251ebd318SNicolas Dichtel } 279351ebd318SNicolas Dichtel 279451ebd318SNicolas Dichtel return last_err; 279551ebd318SNicolas Dichtel } 279651ebd318SNicolas Dichtel 2797661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) 27981da177e4SLinus Torvalds { 279986872cb5SThomas Graf struct fib6_config cfg; 280086872cb5SThomas Graf int err; 28011da177e4SLinus Torvalds 280286872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 280386872cb5SThomas Graf if (err < 0) 280486872cb5SThomas Graf return err; 280586872cb5SThomas Graf 280651ebd318SNicolas Dichtel if (cfg.fc_mp) 280751ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 0); 280851ebd318SNicolas Dichtel else 280986872cb5SThomas Graf return ip6_route_del(&cfg); 28101da177e4SLinus Torvalds } 28111da177e4SLinus Torvalds 2812661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) 28131da177e4SLinus Torvalds { 281486872cb5SThomas Graf struct fib6_config cfg; 281586872cb5SThomas Graf int err; 28161da177e4SLinus Torvalds 281786872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 281886872cb5SThomas Graf if (err < 0) 281986872cb5SThomas Graf return err; 282086872cb5SThomas Graf 282151ebd318SNicolas Dichtel if (cfg.fc_mp) 282251ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 1); 282351ebd318SNicolas Dichtel else 282486872cb5SThomas Graf return ip6_route_add(&cfg); 28251da177e4SLinus Torvalds } 28261da177e4SLinus Torvalds 282719e42e45SRoopa Prabhu static inline size_t rt6_nlmsg_size(struct rt6_info *rt) 2828339bf98fSThomas Graf { 2829339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 2830339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 2831339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 2832339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 2833339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 2834339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 2835339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 2836339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 2837339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 28386a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 2839ea697639SDaniel Borkmann + nla_total_size(sizeof(struct rta_cacheinfo)) 2840c78ba6d6SLubomir Rintel + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 284119e42e45SRoopa Prabhu + nla_total_size(1) /* RTA_PREF */ 284261adedf3SJiri Benc + lwtunnel_get_encap_size(rt->dst.lwtstate); 2843339bf98fSThomas Graf } 2844339bf98fSThomas Graf 2845191cd582SBrian Haley static int rt6_fill_node(struct net *net, 2846191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 28470d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 284815e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 28497bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 28501da177e4SLinus Torvalds { 28514b32b5adSMartin KaFai Lau u32 metrics[RTAX_MAX]; 28521da177e4SLinus Torvalds struct rtmsg *rtm; 28531da177e4SLinus Torvalds struct nlmsghdr *nlh; 2854e3703b3dSThomas Graf long expires; 28559e762a4aSPatrick McHardy u32 table; 28561da177e4SLinus Torvalds 28571da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 28581da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 28591da177e4SLinus Torvalds /* success since this is not a prefix route */ 28601da177e4SLinus Torvalds return 1; 28611da177e4SLinus Torvalds } 28621da177e4SLinus Torvalds } 28631da177e4SLinus Torvalds 286415e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 286538308473SDavid S. Miller if (!nlh) 286626932566SPatrick McHardy return -EMSGSIZE; 28672d7202bfSThomas Graf 28682d7202bfSThomas Graf rtm = nlmsg_data(nlh); 28691da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 28701da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 28711da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 28721da177e4SLinus Torvalds rtm->rtm_tos = 0; 2873c71099acSThomas Graf if (rt->rt6i_table) 28749e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 2875c71099acSThomas Graf else 28769e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 28779e762a4aSPatrick McHardy rtm->rtm_table = table; 2878c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 2879c78679e8SDavid S. Miller goto nla_put_failure; 2880ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 2881ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 2882ef2c7d7bSNicolas Dichtel case -EINVAL: 2883ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 2884ef2c7d7bSNicolas Dichtel break; 2885ef2c7d7bSNicolas Dichtel case -EACCES: 2886ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 2887ef2c7d7bSNicolas Dichtel break; 2888b4949ab2SNicolas Dichtel case -EAGAIN: 2889b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 2890b4949ab2SNicolas Dichtel break; 2891ef2c7d7bSNicolas Dichtel default: 28921da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 2893ef2c7d7bSNicolas Dichtel break; 2894ef2c7d7bSNicolas Dichtel } 2895ef2c7d7bSNicolas Dichtel } 2896ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 2897ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 2898d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 28991da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 29001da177e4SLinus Torvalds else 29011da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 29021da177e4SLinus Torvalds rtm->rtm_flags = 0; 290335103d11SAndy Gospodarek if (!netif_carrier_ok(rt->dst.dev)) { 2904cea45e20SAndy Gospodarek rtm->rtm_flags |= RTNH_F_LINKDOWN; 290535103d11SAndy Gospodarek if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown) 290635103d11SAndy Gospodarek rtm->rtm_flags |= RTNH_F_DEAD; 290735103d11SAndy Gospodarek } 29081da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 29091da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 29101da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 29111da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 2912f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 2913f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 29141da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 2915f0396f60SDenis Ovsienko else 2916f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 2917f0396f60SDenis Ovsienko } 29181da177e4SLinus Torvalds 29191da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 29201da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 29211da177e4SLinus Torvalds 29221da177e4SLinus Torvalds if (dst) { 2923930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, dst)) 2924c78679e8SDavid S. Miller goto nla_put_failure; 29251da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 29261da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 2927930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr)) 2928c78679e8SDavid S. Miller goto nla_put_failure; 29291da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 29301da177e4SLinus Torvalds if (src) { 2931930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_SRC, src)) 2932c78679e8SDavid S. Miller goto nla_put_failure; 29331da177e4SLinus Torvalds rtm->rtm_src_len = 128; 2934c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 2935930345eaSJiri Benc nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr)) 2936c78679e8SDavid S. Miller goto nla_put_failure; 29371da177e4SLinus Torvalds #endif 29387bc570c8SYOSHIFUJI Hideaki if (iif) { 29397bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 29407bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 29418229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 29427bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 29437bc570c8SYOSHIFUJI Hideaki if (!nowait) { 29447bc570c8SYOSHIFUJI Hideaki if (err == 0) 29457bc570c8SYOSHIFUJI Hideaki return 0; 29467bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 29477bc570c8SYOSHIFUJI Hideaki } else { 29487bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 29497bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 29507bc570c8SYOSHIFUJI Hideaki } 29517bc570c8SYOSHIFUJI Hideaki } 29527bc570c8SYOSHIFUJI Hideaki } else 29537bc570c8SYOSHIFUJI Hideaki #endif 2954c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 2955c78679e8SDavid S. Miller goto nla_put_failure; 29567bc570c8SYOSHIFUJI Hideaki } else if (dst) { 29571da177e4SLinus Torvalds struct in6_addr saddr_buf; 2958c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 2959930345eaSJiri Benc nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 2960c78679e8SDavid S. Miller goto nla_put_failure; 2961c3968a85SDaniel Walter } 2962c3968a85SDaniel Walter 2963c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 2964c3968a85SDaniel Walter struct in6_addr saddr_buf; 29654e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 2966930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 2967c78679e8SDavid S. Miller goto nla_put_failure; 29681da177e4SLinus Torvalds } 29692d7202bfSThomas Graf 29704b32b5adSMartin KaFai Lau memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); 29714b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu) 29724b32b5adSMartin KaFai Lau metrics[RTAX_MTU - 1] = rt->rt6i_pmtu; 29734b32b5adSMartin KaFai Lau if (rtnetlink_put_metrics(skb, metrics) < 0) 29742d7202bfSThomas Graf goto nla_put_failure; 29752d7202bfSThomas Graf 2976dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_GATEWAY) { 2977930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0) 297894f826b8SEric Dumazet goto nla_put_failure; 297994f826b8SEric Dumazet } 29802d7202bfSThomas Graf 2981c78679e8SDavid S. Miller if (rt->dst.dev && 2982c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 2983c78679e8SDavid S. Miller goto nla_put_failure; 2984c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 2985c78679e8SDavid S. Miller goto nla_put_failure; 29868253947eSLi Wei 29878253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 298869cdf8f9SYOSHIFUJI Hideaki 298987a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 2990e3703b3dSThomas Graf goto nla_put_failure; 29911da177e4SLinus Torvalds 2992c78ba6d6SLubomir Rintel if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) 2993c78ba6d6SLubomir Rintel goto nla_put_failure; 2994c78ba6d6SLubomir Rintel 299561adedf3SJiri Benc lwtunnel_fill_encap(skb, rt->dst.lwtstate); 299619e42e45SRoopa Prabhu 2997053c095aSJohannes Berg nlmsg_end(skb, nlh); 2998053c095aSJohannes Berg return 0; 29992d7202bfSThomas Graf 30002d7202bfSThomas Graf nla_put_failure: 300126932566SPatrick McHardy nlmsg_cancel(skb, nlh); 300226932566SPatrick McHardy return -EMSGSIZE; 30031da177e4SLinus Torvalds } 30041da177e4SLinus Torvalds 30051b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 30061da177e4SLinus Torvalds { 30071da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 30081da177e4SLinus Torvalds int prefix; 30091da177e4SLinus Torvalds 30102d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 30112d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 30121da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 30131da177e4SLinus Torvalds } else 30141da177e4SLinus Torvalds prefix = 0; 30151da177e4SLinus Torvalds 3016191cd582SBrian Haley return rt6_fill_node(arg->net, 3017191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 301815e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 30197bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 30201da177e4SLinus Torvalds } 30211da177e4SLinus Torvalds 3022661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) 30231da177e4SLinus Torvalds { 30243b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 3025ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 30261da177e4SLinus Torvalds struct rt6_info *rt; 3027ab364a6fSThomas Graf struct sk_buff *skb; 3028ab364a6fSThomas Graf struct rtmsg *rtm; 30294c9483b2SDavid S. Miller struct flowi6 fl6; 303072331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 3031ab364a6fSThomas Graf 3032ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 3033ab364a6fSThomas Graf if (err < 0) 3034ab364a6fSThomas Graf goto errout; 3035ab364a6fSThomas Graf 3036ab364a6fSThomas Graf err = -EINVAL; 30374c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 3038ab364a6fSThomas Graf 3039ab364a6fSThomas Graf if (tb[RTA_SRC]) { 3040ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 3041ab364a6fSThomas Graf goto errout; 3042ab364a6fSThomas Graf 30434e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 3044ab364a6fSThomas Graf } 3045ab364a6fSThomas Graf 3046ab364a6fSThomas Graf if (tb[RTA_DST]) { 3047ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 3048ab364a6fSThomas Graf goto errout; 3049ab364a6fSThomas Graf 30504e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 3051ab364a6fSThomas Graf } 3052ab364a6fSThomas Graf 3053ab364a6fSThomas Graf if (tb[RTA_IIF]) 3054ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 3055ab364a6fSThomas Graf 3056ab364a6fSThomas Graf if (tb[RTA_OIF]) 305772331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 3058ab364a6fSThomas Graf 30592e47b291SLorenzo Colitti if (tb[RTA_MARK]) 30602e47b291SLorenzo Colitti fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 30612e47b291SLorenzo Colitti 3062ab364a6fSThomas Graf if (iif) { 3063ab364a6fSThomas Graf struct net_device *dev; 306472331bc0SShmulik Ladkani int flags = 0; 306572331bc0SShmulik Ladkani 30665578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 3067ab364a6fSThomas Graf if (!dev) { 3068ab364a6fSThomas Graf err = -ENODEV; 3069ab364a6fSThomas Graf goto errout; 3070ab364a6fSThomas Graf } 307172331bc0SShmulik Ladkani 307272331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 307372331bc0SShmulik Ladkani 307472331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 307572331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 307672331bc0SShmulik Ladkani 307772331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 307872331bc0SShmulik Ladkani flags); 307972331bc0SShmulik Ladkani } else { 308072331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 308172331bc0SShmulik Ladkani 308272331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 3083ab364a6fSThomas Graf } 30841da177e4SLinus Torvalds 30851da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 308638308473SDavid S. Miller if (!skb) { 308794e187c0SAmerigo Wang ip6_rt_put(rt); 3088ab364a6fSThomas Graf err = -ENOBUFS; 3089ab364a6fSThomas Graf goto errout; 3090ab364a6fSThomas Graf } 30911da177e4SLinus Torvalds 30921da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 30931da177e4SLinus Torvalds through good chunk of routing engine. 30941da177e4SLinus Torvalds */ 3095459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 30961da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 30971da177e4SLinus Torvalds 3098d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 30991da177e4SLinus Torvalds 31004c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 310115e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 31027bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 31031da177e4SLinus Torvalds if (err < 0) { 3104ab364a6fSThomas Graf kfree_skb(skb); 3105ab364a6fSThomas Graf goto errout; 31061da177e4SLinus Torvalds } 31071da177e4SLinus Torvalds 310815e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 3109ab364a6fSThomas Graf errout: 31101da177e4SLinus Torvalds return err; 31111da177e4SLinus Torvalds } 31121da177e4SLinus Torvalds 311386872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) 31141da177e4SLinus Torvalds { 31151da177e4SLinus Torvalds struct sk_buff *skb; 31165578689aSDaniel Lezcano struct net *net = info->nl_net; 3117528c4cebSDenis V. Lunev u32 seq; 3118528c4cebSDenis V. Lunev int err; 31190d51aa80SJamal Hadi Salim 3120528c4cebSDenis V. Lunev err = -ENOBUFS; 312138308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 312286872cb5SThomas Graf 312319e42e45SRoopa Prabhu skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 312438308473SDavid S. Miller if (!skb) 312521713ebcSThomas Graf goto errout; 31261da177e4SLinus Torvalds 3127191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 312815e47304SEric W. Biederman event, info->portid, seq, 0, 0, 0); 312926932566SPatrick McHardy if (err < 0) { 313026932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 313126932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 313226932566SPatrick McHardy kfree_skb(skb); 313326932566SPatrick McHardy goto errout; 313426932566SPatrick McHardy } 313515e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 31365578689aSDaniel Lezcano info->nlh, gfp_any()); 31371ce85fe4SPablo Neira Ayuso return; 313821713ebcSThomas Graf errout: 313921713ebcSThomas Graf if (err < 0) 31405578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 31411da177e4SLinus Torvalds } 31421da177e4SLinus Torvalds 31438ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 3144351638e7SJiri Pirko unsigned long event, void *ptr) 31458ed67789SDaniel Lezcano { 3146351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 3147c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 31488ed67789SDaniel Lezcano 31498ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 3150d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 31518ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 31528ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3153d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 31548ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 3155d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 31568ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 31578ed67789SDaniel Lezcano #endif 31588ed67789SDaniel Lezcano } 31598ed67789SDaniel Lezcano 31608ed67789SDaniel Lezcano return NOTIFY_OK; 31618ed67789SDaniel Lezcano } 31628ed67789SDaniel Lezcano 31631da177e4SLinus Torvalds /* 31641da177e4SLinus Torvalds * /proc 31651da177e4SLinus Torvalds */ 31661da177e4SLinus Torvalds 31671da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 31681da177e4SLinus Torvalds 316933120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 317033120b30SAlexey Dobriyan .owner = THIS_MODULE, 317133120b30SAlexey Dobriyan .open = ipv6_route_open, 317233120b30SAlexey Dobriyan .read = seq_read, 317333120b30SAlexey Dobriyan .llseek = seq_lseek, 31748d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 317533120b30SAlexey Dobriyan }; 317633120b30SAlexey Dobriyan 31771da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 31781da177e4SLinus Torvalds { 317969ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 31801da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 318169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 318269ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 318369ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 318469ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 318569ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 3186fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 318769ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 31881da177e4SLinus Torvalds 31891da177e4SLinus Torvalds return 0; 31901da177e4SLinus Torvalds } 31911da177e4SLinus Torvalds 31921da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 31931da177e4SLinus Torvalds { 3194de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 319569ddb805SDaniel Lezcano } 319669ddb805SDaniel Lezcano 31979a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 31981da177e4SLinus Torvalds .owner = THIS_MODULE, 31991da177e4SLinus Torvalds .open = rt6_stats_seq_open, 32001da177e4SLinus Torvalds .read = seq_read, 32011da177e4SLinus Torvalds .llseek = seq_lseek, 3202b6fcbdb4SPavel Emelyanov .release = single_release_net, 32031da177e4SLinus Torvalds }; 32041da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 32051da177e4SLinus Torvalds 32061da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 32071da177e4SLinus Torvalds 32081da177e4SLinus Torvalds static 3209fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 32101da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 32111da177e4SLinus Torvalds { 3212c486da34SLucian Adrian Grijincu struct net *net; 3213c486da34SLucian Adrian Grijincu int delay; 3214c486da34SLucian Adrian Grijincu if (!write) 3215c486da34SLucian Adrian Grijincu return -EINVAL; 3216c486da34SLucian Adrian Grijincu 3217c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 3218c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 32198d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 32202ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 32211da177e4SLinus Torvalds return 0; 32221da177e4SLinus Torvalds } 32231da177e4SLinus Torvalds 3224fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 32251da177e4SLinus Torvalds { 32261da177e4SLinus Torvalds .procname = "flush", 32274990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 32281da177e4SLinus Torvalds .maxlen = sizeof(int), 322989c8b3a1SDave Jones .mode = 0200, 32306d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 32311da177e4SLinus Torvalds }, 32321da177e4SLinus Torvalds { 32331da177e4SLinus Torvalds .procname = "gc_thresh", 32349a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 32351da177e4SLinus Torvalds .maxlen = sizeof(int), 32361da177e4SLinus Torvalds .mode = 0644, 32376d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 32381da177e4SLinus Torvalds }, 32391da177e4SLinus Torvalds { 32401da177e4SLinus Torvalds .procname = "max_size", 32414990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 32421da177e4SLinus Torvalds .maxlen = sizeof(int), 32431da177e4SLinus Torvalds .mode = 0644, 32446d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 32451da177e4SLinus Torvalds }, 32461da177e4SLinus Torvalds { 32471da177e4SLinus Torvalds .procname = "gc_min_interval", 32484990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 32491da177e4SLinus Torvalds .maxlen = sizeof(int), 32501da177e4SLinus Torvalds .mode = 0644, 32516d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 32521da177e4SLinus Torvalds }, 32531da177e4SLinus Torvalds { 32541da177e4SLinus Torvalds .procname = "gc_timeout", 32554990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 32561da177e4SLinus Torvalds .maxlen = sizeof(int), 32571da177e4SLinus Torvalds .mode = 0644, 32586d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 32591da177e4SLinus Torvalds }, 32601da177e4SLinus Torvalds { 32611da177e4SLinus Torvalds .procname = "gc_interval", 32624990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 32631da177e4SLinus Torvalds .maxlen = sizeof(int), 32641da177e4SLinus Torvalds .mode = 0644, 32656d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 32661da177e4SLinus Torvalds }, 32671da177e4SLinus Torvalds { 32681da177e4SLinus Torvalds .procname = "gc_elasticity", 32694990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 32701da177e4SLinus Torvalds .maxlen = sizeof(int), 32711da177e4SLinus Torvalds .mode = 0644, 3272f3d3f616SMin Zhang .proc_handler = proc_dointvec, 32731da177e4SLinus Torvalds }, 32741da177e4SLinus Torvalds { 32751da177e4SLinus Torvalds .procname = "mtu_expires", 32764990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 32771da177e4SLinus Torvalds .maxlen = sizeof(int), 32781da177e4SLinus Torvalds .mode = 0644, 32796d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 32801da177e4SLinus Torvalds }, 32811da177e4SLinus Torvalds { 32821da177e4SLinus Torvalds .procname = "min_adv_mss", 32834990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 32841da177e4SLinus Torvalds .maxlen = sizeof(int), 32851da177e4SLinus Torvalds .mode = 0644, 3286f3d3f616SMin Zhang .proc_handler = proc_dointvec, 32871da177e4SLinus Torvalds }, 32881da177e4SLinus Torvalds { 32891da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 32904990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 32911da177e4SLinus Torvalds .maxlen = sizeof(int), 32921da177e4SLinus Torvalds .mode = 0644, 32936d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 32941da177e4SLinus Torvalds }, 3295f8572d8fSEric W. Biederman { } 32961da177e4SLinus Torvalds }; 32971da177e4SLinus Torvalds 32982c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 3299760f2d01SDaniel Lezcano { 3300760f2d01SDaniel Lezcano struct ctl_table *table; 3301760f2d01SDaniel Lezcano 3302760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 3303760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 3304760f2d01SDaniel Lezcano GFP_KERNEL); 33055ee09105SYOSHIFUJI Hideaki 33065ee09105SYOSHIFUJI Hideaki if (table) { 33075ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 3308c486da34SLucian Adrian Grijincu table[0].extra1 = net; 330986393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 33105ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 33115ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 33125ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 33135ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 33145ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 33155ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 33165ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 33179c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 3318464dc801SEric W. Biederman 3319464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 3320464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 3321464dc801SEric W. Biederman table[0].procname = NULL; 33225ee09105SYOSHIFUJI Hideaki } 33235ee09105SYOSHIFUJI Hideaki 3324760f2d01SDaniel Lezcano return table; 3325760f2d01SDaniel Lezcano } 33261da177e4SLinus Torvalds #endif 33271da177e4SLinus Torvalds 33282c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 3329cdb18761SDaniel Lezcano { 3330633d424bSPavel Emelyanov int ret = -ENOMEM; 33318ed67789SDaniel Lezcano 333286393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 333386393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 3334f2fc6a54SBenjamin Thery 3335fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 3336fc66f95cSEric Dumazet goto out_ip6_dst_ops; 3337fc66f95cSEric Dumazet 33388ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 33398ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 33408ed67789SDaniel Lezcano GFP_KERNEL); 33418ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 3342fc66f95cSEric Dumazet goto out_ip6_dst_entries; 3343d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 33448ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 3345d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 334662fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 334762fa8a84SDavid S. Miller ip6_template_metrics, true); 33488ed67789SDaniel Lezcano 33498ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 33508ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 33518ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 33528ed67789SDaniel Lezcano GFP_KERNEL); 335368fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 335468fffc67SPeter Zijlstra goto out_ip6_null_entry; 3355d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 33568ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 3357d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 335862fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 335962fa8a84SDavid S. Miller ip6_template_metrics, true); 33608ed67789SDaniel Lezcano 33618ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 33628ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 33638ed67789SDaniel Lezcano GFP_KERNEL); 336468fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 336568fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 3366d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 33678ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 3368d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 336962fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 337062fa8a84SDavid S. Miller ip6_template_metrics, true); 33718ed67789SDaniel Lezcano #endif 33728ed67789SDaniel Lezcano 3373b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 3374b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 3375b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 3376b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 3377b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 3378b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 3379b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 3380b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 3381b339a47cSPeter Zijlstra 33826891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 33836891a346SBenjamin Thery 33848ed67789SDaniel Lezcano ret = 0; 33858ed67789SDaniel Lezcano out: 33868ed67789SDaniel Lezcano return ret; 3387f2fc6a54SBenjamin Thery 338868fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 338968fffc67SPeter Zijlstra out_ip6_prohibit_entry: 339068fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 339168fffc67SPeter Zijlstra out_ip6_null_entry: 339268fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 339368fffc67SPeter Zijlstra #endif 3394fc66f95cSEric Dumazet out_ip6_dst_entries: 3395fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3396f2fc6a54SBenjamin Thery out_ip6_dst_ops: 3397f2fc6a54SBenjamin Thery goto out; 3398cdb18761SDaniel Lezcano } 3399cdb18761SDaniel Lezcano 34002c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 3401cdb18761SDaniel Lezcano { 34028ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 34038ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 34048ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 34058ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 34068ed67789SDaniel Lezcano #endif 340741bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3408cdb18761SDaniel Lezcano } 3409cdb18761SDaniel Lezcano 3410d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 3411d189634eSThomas Graf { 3412d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3413d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 3414d4beaa66SGao feng proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops); 3415d189634eSThomas Graf #endif 3416d189634eSThomas Graf return 0; 3417d189634eSThomas Graf } 3418d189634eSThomas Graf 3419d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 3420d189634eSThomas Graf { 3421d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3422ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 3423ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 3424d189634eSThomas Graf #endif 3425d189634eSThomas Graf } 3426d189634eSThomas Graf 3427cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 3428cdb18761SDaniel Lezcano .init = ip6_route_net_init, 3429cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 3430cdb18761SDaniel Lezcano }; 3431cdb18761SDaniel Lezcano 3432c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 3433c3426b47SDavid S. Miller { 3434c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 3435c3426b47SDavid S. Miller 3436c3426b47SDavid S. Miller if (!bp) 3437c3426b47SDavid S. Miller return -ENOMEM; 3438c3426b47SDavid S. Miller inet_peer_base_init(bp); 3439c3426b47SDavid S. Miller net->ipv6.peers = bp; 3440c3426b47SDavid S. Miller return 0; 3441c3426b47SDavid S. Miller } 3442c3426b47SDavid S. Miller 3443c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 3444c3426b47SDavid S. Miller { 3445c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 3446c3426b47SDavid S. Miller 3447c3426b47SDavid S. Miller net->ipv6.peers = NULL; 344856a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3449c3426b47SDavid S. Miller kfree(bp); 3450c3426b47SDavid S. Miller } 3451c3426b47SDavid S. Miller 34522b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3453c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3454c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3455c3426b47SDavid S. Miller }; 3456c3426b47SDavid S. Miller 3457d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3458d189634eSThomas Graf .init = ip6_route_net_init_late, 3459d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3460d189634eSThomas Graf }; 3461d189634eSThomas Graf 34628ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 34638ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 34648ed67789SDaniel Lezcano .priority = 0, 34658ed67789SDaniel Lezcano }; 34668ed67789SDaniel Lezcano 3467433d49c3SDaniel Lezcano int __init ip6_route_init(void) 34681da177e4SLinus Torvalds { 3469433d49c3SDaniel Lezcano int ret; 34708d0b94afSMartin KaFai Lau int cpu; 3471433d49c3SDaniel Lezcano 34729a7ec3a9SDaniel Lezcano ret = -ENOMEM; 34739a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 34749a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 34759a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 34769a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3477c19a28e1SFernando Carrijo goto out; 347814e50e57SDavid S. Miller 3479fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 34808ed67789SDaniel Lezcano if (ret) 3481bdb3289fSDaniel Lezcano goto out_kmem_cache; 3482bdb3289fSDaniel Lezcano 3483c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3484c3426b47SDavid S. Miller if (ret) 3485e8803b6cSDavid S. Miller goto out_dst_entries; 34862a0c451aSThomas Graf 34877e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 34887e52b33bSDavid S. Miller if (ret) 34897e52b33bSDavid S. Miller goto out_register_inetpeer; 3490c3426b47SDavid S. Miller 34915dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 34925dc121e9SArnaud Ebalard 34938ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 34948ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 34958ed67789SDaniel Lezcano * manually for init_net */ 3496d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 34978ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3498bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3499d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 35008ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3501d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 35028ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3503bdb3289fSDaniel Lezcano #endif 3504e8803b6cSDavid S. Miller ret = fib6_init(); 3505433d49c3SDaniel Lezcano if (ret) 35068ed67789SDaniel Lezcano goto out_register_subsys; 3507433d49c3SDaniel Lezcano 3508433d49c3SDaniel Lezcano ret = xfrm6_init(); 3509433d49c3SDaniel Lezcano if (ret) 3510e8803b6cSDavid S. Miller goto out_fib6_init; 3511c35b7e72SDaniel Lezcano 3512433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3513433d49c3SDaniel Lezcano if (ret) 3514433d49c3SDaniel Lezcano goto xfrm6_init; 35157e5449c2SDaniel Lezcano 3516d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3517d189634eSThomas Graf if (ret) 3518d189634eSThomas Graf goto fib6_rules_init; 3519d189634eSThomas Graf 3520433d49c3SDaniel Lezcano ret = -ENOBUFS; 3521c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3522c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3523c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3524d189634eSThomas Graf goto out_register_late_subsys; 3525433d49c3SDaniel Lezcano 35268ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3527cdb18761SDaniel Lezcano if (ret) 3528d189634eSThomas Graf goto out_register_late_subsys; 35298ed67789SDaniel Lezcano 35308d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 35318d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 35328d0b94afSMartin KaFai Lau 35338d0b94afSMartin KaFai Lau INIT_LIST_HEAD(&ul->head); 35348d0b94afSMartin KaFai Lau spin_lock_init(&ul->lock); 35358d0b94afSMartin KaFai Lau } 35368d0b94afSMartin KaFai Lau 3537433d49c3SDaniel Lezcano out: 3538433d49c3SDaniel Lezcano return ret; 3539433d49c3SDaniel Lezcano 3540d189634eSThomas Graf out_register_late_subsys: 3541d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3542433d49c3SDaniel Lezcano fib6_rules_init: 3543433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3544433d49c3SDaniel Lezcano xfrm6_init: 3545433d49c3SDaniel Lezcano xfrm6_fini(); 35462a0c451aSThomas Graf out_fib6_init: 35472a0c451aSThomas Graf fib6_gc_cleanup(); 35488ed67789SDaniel Lezcano out_register_subsys: 35498ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 35507e52b33bSDavid S. Miller out_register_inetpeer: 35517e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3552fc66f95cSEric Dumazet out_dst_entries: 3553fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3554433d49c3SDaniel Lezcano out_kmem_cache: 3555f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3556433d49c3SDaniel Lezcano goto out; 35571da177e4SLinus Torvalds } 35581da177e4SLinus Torvalds 35591da177e4SLinus Torvalds void ip6_route_cleanup(void) 35601da177e4SLinus Torvalds { 35618ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3562d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3563101367c2SThomas Graf fib6_rules_cleanup(); 35641da177e4SLinus Torvalds xfrm6_fini(); 35651da177e4SLinus Torvalds fib6_gc_cleanup(); 3566c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 35678ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 356841bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3569f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 35701da177e4SLinus Torvalds } 3571