11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Linux INET6 implementation 31da177e4SLinus Torvalds * FIB front-end. 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Authors: 61da177e4SLinus Torvalds * Pedro Roque <roque@di.fc.ul.pt> 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 91da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 101da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 111da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds /* Changes: 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * YOSHIFUJI Hideaki @USAGI 171da177e4SLinus Torvalds * reworked default router selection. 181da177e4SLinus Torvalds * - respect outgoing interface 191da177e4SLinus Torvalds * - select from (probably) reachable routers (i.e. 201da177e4SLinus Torvalds * routers in REACHABLE, STALE, DELAY or PROBE states). 211da177e4SLinus Torvalds * - always select the same router if it is (probably) 221da177e4SLinus Torvalds * reachable. otherwise, round-robin the list. 23c0bece9fSYOSHIFUJI Hideaki * Ville Nuorvala 24c0bece9fSYOSHIFUJI Hideaki * Fixed routing subtrees. 251da177e4SLinus Torvalds */ 261da177e4SLinus Torvalds 27f3213831SJoe Perches #define pr_fmt(fmt) "IPv6: " fmt 28f3213831SJoe Perches 294fc268d2SRandy Dunlap #include <linux/capability.h> 301da177e4SLinus Torvalds #include <linux/errno.h> 31bc3b2d7fSPaul Gortmaker #include <linux/export.h> 321da177e4SLinus Torvalds #include <linux/types.h> 331da177e4SLinus Torvalds #include <linux/times.h> 341da177e4SLinus Torvalds #include <linux/socket.h> 351da177e4SLinus Torvalds #include <linux/sockios.h> 361da177e4SLinus Torvalds #include <linux/net.h> 371da177e4SLinus Torvalds #include <linux/route.h> 381da177e4SLinus Torvalds #include <linux/netdevice.h> 391da177e4SLinus Torvalds #include <linux/in6.h> 407bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h> 411da177e4SLinus Torvalds #include <linux/init.h> 421da177e4SLinus Torvalds #include <linux/if_arp.h> 431da177e4SLinus Torvalds #include <linux/proc_fs.h> 441da177e4SLinus Torvalds #include <linux/seq_file.h> 455b7c931dSDaniel Lezcano #include <linux/nsproxy.h> 465a0e3ad6STejun Heo #include <linux/slab.h> 47457c4cbcSEric W. Biederman #include <net/net_namespace.h> 481da177e4SLinus Torvalds #include <net/snmp.h> 491da177e4SLinus Torvalds #include <net/ipv6.h> 501da177e4SLinus Torvalds #include <net/ip6_fib.h> 511da177e4SLinus Torvalds #include <net/ip6_route.h> 521da177e4SLinus Torvalds #include <net/ndisc.h> 531da177e4SLinus Torvalds #include <net/addrconf.h> 541da177e4SLinus Torvalds #include <net/tcp.h> 551da177e4SLinus Torvalds #include <linux/rtnetlink.h> 561da177e4SLinus Torvalds #include <net/dst.h> 57904af04dSJiri Benc #include <net/dst_metadata.h> 581da177e4SLinus Torvalds #include <net/xfrm.h> 598d71740cSTom Tucker #include <net/netevent.h> 6021713ebcSThomas Graf #include <net/netlink.h> 6151ebd318SNicolas Dichtel #include <net/nexthop.h> 6219e42e45SRoopa Prabhu #include <net/lwtunnel.h> 63904af04dSJiri Benc #include <net/ip_tunnels.h> 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds #include <asm/uaccess.h> 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 681da177e4SLinus Torvalds #include <linux/sysctl.h> 691da177e4SLinus Torvalds #endif 701da177e4SLinus Torvalds 71afc154e9SHannes Frederic Sowa enum rt6_nud_state { 727e980569SJiri Benc RT6_NUD_FAIL_HARD = -3, 737e980569SJiri Benc RT6_NUD_FAIL_PROBE = -2, 747e980569SJiri Benc RT6_NUD_FAIL_DO_RR = -1, 75afc154e9SHannes Frederic Sowa RT6_NUD_SUCCEED = 1 76afc154e9SHannes Frederic Sowa }; 77afc154e9SHannes Frederic Sowa 7883a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort); 791da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 800dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst); 81ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst); 821da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *); 831da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *); 841da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *, 851da177e4SLinus Torvalds struct net_device *dev, int how); 86569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops); 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds static int ip6_pkt_discard(struct sk_buff *skb); 89aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb); 907150aedeSKamala R static int ip6_pkt_prohibit(struct sk_buff *skb); 91aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb); 921da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb); 936700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 946700c270SDavid S. Miller struct sk_buff *skb, u32 mtu); 956700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, 966700c270SDavid S. Miller struct sk_buff *skb); 974b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt); 9852bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict); 991da177e4SLinus Torvalds 10070ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 101efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 102b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 103b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 10495c96174SEric Dumazet unsigned int pref); 105efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 106b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 107b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex); 10870ceb4f5SYOSHIFUJI Hideaki #endif 10970ceb4f5SYOSHIFUJI Hideaki 1108d0b94afSMartin KaFai Lau struct uncached_list { 1118d0b94afSMartin KaFai Lau spinlock_t lock; 1128d0b94afSMartin KaFai Lau struct list_head head; 1138d0b94afSMartin KaFai Lau }; 1148d0b94afSMartin KaFai Lau 1158d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list); 1168d0b94afSMartin KaFai Lau 1178d0b94afSMartin KaFai Lau static void rt6_uncached_list_add(struct rt6_info *rt) 1188d0b94afSMartin KaFai Lau { 1198d0b94afSMartin KaFai Lau struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list); 1208d0b94afSMartin KaFai Lau 1218d0b94afSMartin KaFai Lau rt->dst.flags |= DST_NOCACHE; 1228d0b94afSMartin KaFai Lau rt->rt6i_uncached_list = ul; 1238d0b94afSMartin KaFai Lau 1248d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1258d0b94afSMartin KaFai Lau list_add_tail(&rt->rt6i_uncached, &ul->head); 1268d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1278d0b94afSMartin KaFai Lau } 1288d0b94afSMartin KaFai Lau 1298d0b94afSMartin KaFai Lau static void rt6_uncached_list_del(struct rt6_info *rt) 1308d0b94afSMartin KaFai Lau { 1318d0b94afSMartin KaFai Lau if (!list_empty(&rt->rt6i_uncached)) { 1328d0b94afSMartin KaFai Lau struct uncached_list *ul = rt->rt6i_uncached_list; 1338d0b94afSMartin KaFai Lau 1348d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1358d0b94afSMartin KaFai Lau list_del(&rt->rt6i_uncached); 1368d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1378d0b94afSMartin KaFai Lau } 1388d0b94afSMartin KaFai Lau } 1398d0b94afSMartin KaFai Lau 1408d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) 1418d0b94afSMartin KaFai Lau { 1428d0b94afSMartin KaFai Lau struct net_device *loopback_dev = net->loopback_dev; 1438d0b94afSMartin KaFai Lau int cpu; 1448d0b94afSMartin KaFai Lau 1458d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 1468d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 1478d0b94afSMartin KaFai Lau struct rt6_info *rt; 1488d0b94afSMartin KaFai Lau 1498d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1508d0b94afSMartin KaFai Lau list_for_each_entry(rt, &ul->head, rt6i_uncached) { 1518d0b94afSMartin KaFai Lau struct inet6_dev *rt_idev = rt->rt6i_idev; 1528d0b94afSMartin KaFai Lau struct net_device *rt_dev = rt->dst.dev; 1538d0b94afSMartin KaFai Lau 1548d0b94afSMartin KaFai Lau if (rt_idev && (rt_idev->dev == dev || !dev) && 1558d0b94afSMartin KaFai Lau rt_idev->dev != loopback_dev) { 1568d0b94afSMartin KaFai Lau rt->rt6i_idev = in6_dev_get(loopback_dev); 1578d0b94afSMartin KaFai Lau in6_dev_put(rt_idev); 1588d0b94afSMartin KaFai Lau } 1598d0b94afSMartin KaFai Lau 1608d0b94afSMartin KaFai Lau if (rt_dev && (rt_dev == dev || !dev) && 1618d0b94afSMartin KaFai Lau rt_dev != loopback_dev) { 1628d0b94afSMartin KaFai Lau rt->dst.dev = loopback_dev; 1638d0b94afSMartin KaFai Lau dev_hold(rt->dst.dev); 1648d0b94afSMartin KaFai Lau dev_put(rt_dev); 1658d0b94afSMartin KaFai Lau } 1668d0b94afSMartin KaFai Lau } 1678d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1688d0b94afSMartin KaFai Lau } 1698d0b94afSMartin KaFai Lau } 1708d0b94afSMartin KaFai Lau 171d52d3997SMartin KaFai Lau static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt) 172d52d3997SMartin KaFai Lau { 173d52d3997SMartin KaFai Lau return dst_metrics_write_ptr(rt->dst.from); 174d52d3997SMartin KaFai Lau } 175d52d3997SMartin KaFai Lau 17606582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) 17706582540SDavid S. Miller { 17806582540SDavid S. Miller struct rt6_info *rt = (struct rt6_info *)dst; 17906582540SDavid S. Miller 180d52d3997SMartin KaFai Lau if (rt->rt6i_flags & RTF_PCPU) 181d52d3997SMartin KaFai Lau return rt6_pcpu_cow_metrics(rt); 182d52d3997SMartin KaFai Lau else if (rt->rt6i_flags & RTF_CACHE) 1834b32b5adSMartin KaFai Lau return NULL; 1844b32b5adSMartin KaFai Lau else 1853b471175SMartin KaFai Lau return dst_cow_metrics_generic(dst, old); 18606582540SDavid S. Miller } 18706582540SDavid S. Miller 188f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, 189f894cbf8SDavid S. Miller struct sk_buff *skb, 190f894cbf8SDavid S. Miller const void *daddr) 19139232973SDavid S. Miller { 19239232973SDavid S. Miller struct in6_addr *p = &rt->rt6i_gateway; 19339232973SDavid S. Miller 194a7563f34SDavid S. Miller if (!ipv6_addr_any(p)) 19539232973SDavid S. Miller return (const void *) p; 196f894cbf8SDavid S. Miller else if (skb) 197f894cbf8SDavid S. Miller return &ipv6_hdr(skb)->daddr; 19839232973SDavid S. Miller return daddr; 19939232973SDavid S. Miller } 20039232973SDavid S. Miller 201f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, 202f894cbf8SDavid S. Miller struct sk_buff *skb, 203f894cbf8SDavid S. Miller const void *daddr) 204d3aaeb38SDavid S. Miller { 20539232973SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 20639232973SDavid S. Miller struct neighbour *n; 20739232973SDavid S. Miller 208f894cbf8SDavid S. Miller daddr = choose_neigh_daddr(rt, skb, daddr); 2098e022ee6SYOSHIFUJI Hideaki / 吉藤英明 n = __ipv6_neigh_lookup(dst->dev, daddr); 210f83c7790SDavid S. Miller if (n) 211f83c7790SDavid S. Miller return n; 212f83c7790SDavid S. Miller return neigh_create(&nd_tbl, daddr, dst->dev); 213f83c7790SDavid S. Miller } 214f83c7790SDavid S. Miller 2159a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 2161da177e4SLinus Torvalds .family = AF_INET6, 2171da177e4SLinus Torvalds .gc = ip6_dst_gc, 2181da177e4SLinus Torvalds .gc_thresh = 1024, 2191da177e4SLinus Torvalds .check = ip6_dst_check, 2200dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 221ebb762f2SSteffen Klassert .mtu = ip6_mtu, 22206582540SDavid S. Miller .cow_metrics = ipv6_cow_metrics, 2231da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 2241da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 2251da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 2261da177e4SLinus Torvalds .link_failure = ip6_link_failure, 2271da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 2286e157b6aSDavid S. Miller .redirect = rt6_do_redirect, 2291ac06e03SHerbert Xu .local_out = __ip6_local_out, 230d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 2311da177e4SLinus Torvalds }; 2321da177e4SLinus Torvalds 233ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 234ec831ea7SRoland Dreier { 235618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 236618f9bc7SSteffen Klassert 237618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 238ec831ea7SRoland Dreier } 239ec831ea7SRoland Dreier 2406700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, 2416700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 24214e50e57SDavid S. Miller { 24314e50e57SDavid S. Miller } 24414e50e57SDavid S. Miller 2456700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, 2466700c270SDavid S. Miller struct sk_buff *skb) 247b587ee3bSDavid S. Miller { 248b587ee3bSDavid S. Miller } 249b587ee3bSDavid S. Miller 2500972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, 2510972ddb2SHeld Bernhard unsigned long old) 2520972ddb2SHeld Bernhard { 2530972ddb2SHeld Bernhard return NULL; 2540972ddb2SHeld Bernhard } 2550972ddb2SHeld Bernhard 25614e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 25714e50e57SDavid S. Miller .family = AF_INET6, 25814e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 25914e50e57SDavid S. Miller .check = ip6_dst_check, 260ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 261214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 26214e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 263b587ee3bSDavid S. Miller .redirect = ip6_rt_blackhole_redirect, 2640972ddb2SHeld Bernhard .cow_metrics = ip6_rt_blackhole_cow_metrics, 265d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 26614e50e57SDavid S. Miller }; 26714e50e57SDavid S. Miller 26862fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 26914edd87dSLi RongQing [RTAX_HOPLIMIT - 1] = 0, 27062fa8a84SDavid S. Miller }; 27162fa8a84SDavid S. Miller 272fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = { 2731da177e4SLinus Torvalds .dst = { 2741da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 2751da177e4SLinus Torvalds .__use = 1, 2762c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 2771da177e4SLinus Torvalds .error = -ENETUNREACH, 2781da177e4SLinus Torvalds .input = ip6_pkt_discard, 2791da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 2801da177e4SLinus Torvalds }, 2811da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2824f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 2831da177e4SLinus Torvalds .rt6i_metric = ~(u32) 0, 2841da177e4SLinus Torvalds .rt6i_ref = ATOMIC_INIT(1), 2851da177e4SLinus Torvalds }; 2861da177e4SLinus Torvalds 287101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 288101367c2SThomas Graf 289fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = { 290101367c2SThomas Graf .dst = { 291101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 292101367c2SThomas Graf .__use = 1, 2932c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 294101367c2SThomas Graf .error = -EACCES, 2959ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 2969ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 297101367c2SThomas Graf }, 298101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2994f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 300101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 301101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 302101367c2SThomas Graf }; 303101367c2SThomas Graf 304fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = { 305101367c2SThomas Graf .dst = { 306101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 307101367c2SThomas Graf .__use = 1, 3082c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 309101367c2SThomas Graf .error = -EINVAL, 310352e512cSHerbert Xu .input = dst_discard, 311aad88724SEric Dumazet .output = dst_discard_sk, 312101367c2SThomas Graf }, 313101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 3144f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 315101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 316101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 317101367c2SThomas Graf }; 318101367c2SThomas Graf 319101367c2SThomas Graf #endif 320101367c2SThomas Graf 3211da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 322d52d3997SMartin KaFai Lau static struct rt6_info *__ip6_dst_alloc(struct net *net, 323957c665fSDavid S. Miller struct net_device *dev, 324ad706862SMartin KaFai Lau int flags) 3251da177e4SLinus Torvalds { 32697bab73fSDavid S. Miller struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 3276f3118b5SNicolas Dichtel 0, DST_OBSOLETE_FORCE_CHK, flags); 328cf911662SDavid S. Miller 32997bab73fSDavid S. Miller if (rt) { 3308104891bSSteffen Klassert struct dst_entry *dst = &rt->dst; 3318104891bSSteffen Klassert 3328104891bSSteffen Klassert memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 33351ebd318SNicolas Dichtel INIT_LIST_HEAD(&rt->rt6i_siblings); 3348d0b94afSMartin KaFai Lau INIT_LIST_HEAD(&rt->rt6i_uncached); 33597bab73fSDavid S. Miller } 336cf911662SDavid S. Miller return rt; 3371da177e4SLinus Torvalds } 3381da177e4SLinus Torvalds 339d52d3997SMartin KaFai Lau static struct rt6_info *ip6_dst_alloc(struct net *net, 340d52d3997SMartin KaFai Lau struct net_device *dev, 341ad706862SMartin KaFai Lau int flags) 342d52d3997SMartin KaFai Lau { 343ad706862SMartin KaFai Lau struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags); 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); 541ab450605SJiri Benc ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL, 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 960ad706862SMartin KaFai Lau rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0); 96183a09abdSMartin KaFai Lau 96283a09abdSMartin KaFai Lau if (!rt) 96383a09abdSMartin KaFai Lau return NULL; 96483a09abdSMartin KaFai Lau 96583a09abdSMartin KaFai Lau ip6_rt_copy_init(rt, ort); 9668b9df265SMartin KaFai Lau rt->rt6i_flags |= RTF_CACHE; 96783a09abdSMartin KaFai Lau rt->rt6i_metric = 0; 96883a09abdSMartin KaFai Lau rt->dst.flags |= DST_HOST; 96983a09abdSMartin KaFai Lau rt->rt6i_dst.addr = *daddr; 97083a09abdSMartin KaFai Lau rt->rt6i_dst.plen = 128; 9718b9df265SMartin KaFai Lau 9728b9df265SMartin KaFai Lau if (!rt6_is_gw_or_nonexthop(ort)) { 973bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 97421efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 97558c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 9761da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 9771da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 9784e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 9791da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 9801da177e4SLinus Torvalds } 9811da177e4SLinus Torvalds #endif 98295a9a5baSYOSHIFUJI Hideaki } 98395a9a5baSYOSHIFUJI Hideaki 984299d9939SYOSHIFUJI Hideaki return rt; 985299d9939SYOSHIFUJI Hideaki } 986299d9939SYOSHIFUJI Hideaki 987d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt) 988d52d3997SMartin KaFai Lau { 989d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 990d52d3997SMartin KaFai Lau 991d52d3997SMartin KaFai Lau pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev), 992ad706862SMartin KaFai Lau rt->dst.dev, rt->dst.flags); 993d52d3997SMartin KaFai Lau 994d52d3997SMartin KaFai Lau if (!pcpu_rt) 995d52d3997SMartin KaFai Lau return NULL; 996d52d3997SMartin KaFai Lau ip6_rt_copy_init(pcpu_rt, rt); 997d52d3997SMartin KaFai Lau pcpu_rt->rt6i_protocol = rt->rt6i_protocol; 998d52d3997SMartin KaFai Lau pcpu_rt->rt6i_flags |= RTF_PCPU; 999d52d3997SMartin KaFai Lau return pcpu_rt; 1000d52d3997SMartin KaFai Lau } 1001d52d3997SMartin KaFai Lau 1002d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */ 1003d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt) 1004d52d3997SMartin KaFai Lau { 1005a73e4195SMartin KaFai Lau struct rt6_info *pcpu_rt, **p; 1006d52d3997SMartin KaFai Lau 1007d52d3997SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 1008d52d3997SMartin KaFai Lau pcpu_rt = *p; 1009d52d3997SMartin KaFai Lau 1010a73e4195SMartin KaFai Lau if (pcpu_rt) { 1011a73e4195SMartin KaFai Lau dst_hold(&pcpu_rt->dst); 1012a73e4195SMartin KaFai Lau rt6_dst_from_metrics_check(pcpu_rt); 1013a73e4195SMartin KaFai Lau } 1014a73e4195SMartin KaFai Lau return pcpu_rt; 1015a73e4195SMartin KaFai Lau } 1016a73e4195SMartin KaFai Lau 1017a73e4195SMartin KaFai Lau static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt) 1018a73e4195SMartin KaFai Lau { 10199c7370a1SMartin KaFai Lau struct fib6_table *table = rt->rt6i_table; 1020a73e4195SMartin KaFai Lau struct rt6_info *pcpu_rt, *prev, **p; 1021d52d3997SMartin KaFai Lau 1022d52d3997SMartin KaFai Lau pcpu_rt = ip6_rt_pcpu_alloc(rt); 1023d52d3997SMartin KaFai Lau if (!pcpu_rt) { 1024d52d3997SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 1025d52d3997SMartin KaFai Lau 10269c7370a1SMartin KaFai Lau dst_hold(&net->ipv6.ip6_null_entry->dst); 10279c7370a1SMartin KaFai Lau return net->ipv6.ip6_null_entry; 1028d52d3997SMartin KaFai Lau } 1029d52d3997SMartin KaFai Lau 10309c7370a1SMartin KaFai Lau read_lock_bh(&table->tb6_lock); 10319c7370a1SMartin KaFai Lau if (rt->rt6i_pcpu) { 1032a73e4195SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 1033d52d3997SMartin KaFai Lau prev = cmpxchg(p, NULL, pcpu_rt); 1034d52d3997SMartin KaFai Lau if (prev) { 1035d52d3997SMartin KaFai Lau /* If someone did it before us, return prev instead */ 1036d52d3997SMartin KaFai Lau dst_destroy(&pcpu_rt->dst); 1037d52d3997SMartin KaFai Lau pcpu_rt = prev; 1038d52d3997SMartin KaFai Lau } 10399c7370a1SMartin KaFai Lau } else { 10409c7370a1SMartin KaFai Lau /* rt has been removed from the fib6 tree 10419c7370a1SMartin KaFai Lau * before we have a chance to acquire the read_lock. 10429c7370a1SMartin KaFai Lau * In this case, don't brother to create a pcpu rt 10439c7370a1SMartin KaFai Lau * since rt is going away anyway. The next 10449c7370a1SMartin KaFai Lau * dst_check() will trigger a re-lookup. 10459c7370a1SMartin KaFai Lau */ 10469c7370a1SMartin KaFai Lau dst_destroy(&pcpu_rt->dst); 10479c7370a1SMartin KaFai Lau pcpu_rt = rt; 10489c7370a1SMartin KaFai Lau } 1049d52d3997SMartin KaFai Lau dst_hold(&pcpu_rt->dst); 1050d52d3997SMartin KaFai Lau rt6_dst_from_metrics_check(pcpu_rt); 10519c7370a1SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 1052d52d3997SMartin KaFai Lau return pcpu_rt; 1053d52d3997SMartin KaFai Lau } 1054d52d3997SMartin KaFai Lau 10558ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, 10564c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 10571da177e4SLinus Torvalds { 1058367efcb9SMartin KaFai Lau struct fib6_node *fn, *saved_fn; 105945e4fd26SMartin KaFai Lau struct rt6_info *rt; 1060c71099acSThomas Graf int strict = 0; 10611da177e4SLinus Torvalds 106277d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 1063367efcb9SMartin KaFai Lau if (net->ipv6.devconf_all->forwarding == 0) 1064367efcb9SMartin KaFai Lau strict |= RT6_LOOKUP_F_REACHABLE; 10651da177e4SLinus Torvalds 1066c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 10671da177e4SLinus Torvalds 10684c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1069367efcb9SMartin KaFai Lau saved_fn = fn; 10701da177e4SLinus Torvalds 1071a3c00e46SMartin KaFai Lau redo_rt6_select: 1072367efcb9SMartin KaFai Lau rt = rt6_select(fn, oif, strict); 107352bd4c0cSNicolas Dichtel if (rt->rt6i_nsiblings) 1074367efcb9SMartin KaFai Lau rt = rt6_multipath_select(rt, fl6, oif, strict); 1075a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1076a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1077a3c00e46SMartin KaFai Lau if (fn) 1078a3c00e46SMartin KaFai Lau goto redo_rt6_select; 1079367efcb9SMartin KaFai Lau else if (strict & RT6_LOOKUP_F_REACHABLE) { 1080367efcb9SMartin KaFai Lau /* also consider unreachable route */ 1081367efcb9SMartin KaFai Lau strict &= ~RT6_LOOKUP_F_REACHABLE; 1082367efcb9SMartin KaFai Lau fn = saved_fn; 1083367efcb9SMartin KaFai Lau goto redo_rt6_select; 1084367efcb9SMartin KaFai Lau } 1085a3c00e46SMartin KaFai Lau } 1086a3c00e46SMartin KaFai Lau 1087d52d3997SMartin KaFai Lau 1088d52d3997SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) { 10893da59bd9SMartin KaFai Lau dst_use(&rt->dst, jiffies); 1090c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 10911da177e4SLinus Torvalds 1092d52d3997SMartin KaFai Lau rt6_dst_from_metrics_check(rt); 1093d52d3997SMartin KaFai Lau return rt; 10943da59bd9SMartin KaFai Lau } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && 10953da59bd9SMartin KaFai Lau !(rt->rt6i_flags & RTF_GATEWAY))) { 10963da59bd9SMartin KaFai Lau /* Create a RTF_CACHE clone which will not be 10973da59bd9SMartin KaFai Lau * owned by the fib6 tree. It is for the special case where 10983da59bd9SMartin KaFai Lau * the daddr in the skb during the neighbor look-up is different 10993da59bd9SMartin KaFai Lau * from the fl6->daddr used to look-up route here. 11003da59bd9SMartin KaFai Lau */ 1101c71099acSThomas Graf 11023da59bd9SMartin KaFai Lau struct rt6_info *uncached_rt; 11033da59bd9SMartin KaFai Lau 1104d52d3997SMartin KaFai Lau dst_use(&rt->dst, jiffies); 1105d52d3997SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 1106d52d3997SMartin KaFai Lau 11073da59bd9SMartin KaFai Lau uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL); 11083da59bd9SMartin KaFai Lau dst_release(&rt->dst); 11093da59bd9SMartin KaFai Lau 11103da59bd9SMartin KaFai Lau if (uncached_rt) 11118d0b94afSMartin KaFai Lau rt6_uncached_list_add(uncached_rt); 11123da59bd9SMartin KaFai Lau else 11133da59bd9SMartin KaFai Lau uncached_rt = net->ipv6.ip6_null_entry; 1114d52d3997SMartin KaFai Lau 11153da59bd9SMartin KaFai Lau dst_hold(&uncached_rt->dst); 11163da59bd9SMartin KaFai Lau return uncached_rt; 11173da59bd9SMartin KaFai Lau 1118d52d3997SMartin KaFai Lau } else { 1119d52d3997SMartin KaFai Lau /* Get a percpu copy */ 1120d52d3997SMartin KaFai Lau 1121d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 1122d52d3997SMartin KaFai Lau 1123d52d3997SMartin KaFai Lau rt->dst.lastuse = jiffies; 1124d52d3997SMartin KaFai Lau rt->dst.__use++; 1125d52d3997SMartin KaFai Lau pcpu_rt = rt6_get_pcpu_route(rt); 1126d52d3997SMartin KaFai Lau 11279c7370a1SMartin KaFai Lau if (pcpu_rt) { 1128d52d3997SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 11299c7370a1SMartin KaFai Lau } else { 11309c7370a1SMartin KaFai Lau /* We have to do the read_unlock first 11319c7370a1SMartin KaFai Lau * because rt6_make_pcpu_route() may trigger 11329c7370a1SMartin KaFai Lau * ip6_dst_gc() which will take the write_lock. 11339c7370a1SMartin KaFai Lau */ 11349c7370a1SMartin KaFai Lau dst_hold(&rt->dst); 11359c7370a1SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 11369c7370a1SMartin KaFai Lau pcpu_rt = rt6_make_pcpu_route(rt); 11379c7370a1SMartin KaFai Lau dst_release(&rt->dst); 11389c7370a1SMartin KaFai Lau } 1139d52d3997SMartin KaFai Lau 1140d52d3997SMartin KaFai Lau return pcpu_rt; 11419c7370a1SMartin KaFai Lau 1142d52d3997SMartin KaFai Lau } 1143c71099acSThomas Graf } 1144c71099acSThomas Graf 11458ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 11464c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 11474acad72dSPavel Emelyanov { 11484c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 11494acad72dSPavel Emelyanov } 11504acad72dSPavel Emelyanov 115172331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 115272331bc0SShmulik Ladkani struct net_device *dev, 115372331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 115472331bc0SShmulik Ladkani { 115572331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 115672331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 115772331bc0SShmulik Ladkani 115872331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 115972331bc0SShmulik Ladkani } 116072331bc0SShmulik Ladkani 1161c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 1162c71099acSThomas Graf { 1163b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 1164c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 1165adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 1166904af04dSJiri Benc struct ip_tunnel_info *tun_info; 11674c9483b2SDavid S. Miller struct flowi6 fl6 = { 11684c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 11694c9483b2SDavid S. Miller .daddr = iph->daddr, 11704c9483b2SDavid S. Miller .saddr = iph->saddr, 11716502ca52SYOSHIFUJI Hideaki / 吉藤英明 .flowlabel = ip6_flowinfo(iph), 11724c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 11734c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 1174c71099acSThomas Graf }; 1175adaa70bbSThomas Graf 1176904af04dSJiri Benc tun_info = skb_tunnel_info(skb); 117746fa062aSJiri Benc if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX)) 1178904af04dSJiri Benc fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id; 117906e9d040SJiri Benc skb_dst_drop(skb); 118072331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 1181c71099acSThomas Graf } 1182c71099acSThomas Graf 11838ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 11844c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 1185c71099acSThomas Graf { 11864c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 1187c71099acSThomas Graf } 1188c71099acSThomas Graf 11899c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk, 11904c9483b2SDavid S. Miller struct flowi6 *fl6) 1191c71099acSThomas Graf { 1192c71099acSThomas Graf int flags = 0; 1193c71099acSThomas Graf 11941fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 11954dc27d1cSDavid McCullough 1196*741a11d9SDavid Ahern if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) || 1197*741a11d9SDavid Ahern fl6->flowi6_oif) 119877d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 1199c71099acSThomas Graf 12004c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 1201adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 12020c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 12030c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 1204adaa70bbSThomas Graf 12054c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 12061da177e4SLinus Torvalds } 12077159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 12081da177e4SLinus Torvalds 12092774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 121014e50e57SDavid S. Miller { 12115c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 121214e50e57SDavid S. Miller struct dst_entry *new = NULL; 121314e50e57SDavid S. Miller 1214f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 121514e50e57SDavid S. Miller if (rt) { 1216d8d1f30bSChangli Gao new = &rt->dst; 121714e50e57SDavid S. Miller 12188104891bSSteffen Klassert memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); 12198104891bSSteffen Klassert 122014e50e57SDavid S. Miller new->__use = 1; 1221352e512cSHerbert Xu new->input = dst_discard; 1222aad88724SEric Dumazet new->output = dst_discard_sk; 122314e50e57SDavid S. Miller 122421efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 122521efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 122621efcfa0SEric Dumazet else 1227defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 122814e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 122914e50e57SDavid S. Miller if (rt->rt6i_idev) 123014e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 123114e50e57SDavid S. Miller 12324e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 12331716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 123414e50e57SDavid S. Miller rt->rt6i_metric = 0; 123514e50e57SDavid S. Miller 123614e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 123714e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 123814e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 123914e50e57SDavid S. Miller #endif 124014e50e57SDavid S. Miller 124114e50e57SDavid S. Miller dst_free(new); 124214e50e57SDavid S. Miller } 124314e50e57SDavid S. Miller 124469ead7afSDavid S. Miller dst_release(dst_orig); 124569ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 124614e50e57SDavid S. Miller } 124714e50e57SDavid S. Miller 12481da177e4SLinus Torvalds /* 12491da177e4SLinus Torvalds * Destination cache support functions 12501da177e4SLinus Torvalds */ 12511da177e4SLinus Torvalds 12524b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt) 12534b32b5adSMartin KaFai Lau { 12544b32b5adSMartin KaFai Lau if (rt->dst.from && 12554b32b5adSMartin KaFai Lau dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from)) 12564b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true); 12574b32b5adSMartin KaFai Lau } 12584b32b5adSMartin KaFai Lau 12593da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie) 12603da59bd9SMartin KaFai Lau { 12613da59bd9SMartin KaFai Lau if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) 12623da59bd9SMartin KaFai Lau return NULL; 12633da59bd9SMartin KaFai Lau 12643da59bd9SMartin KaFai Lau if (rt6_check_expired(rt)) 12653da59bd9SMartin KaFai Lau return NULL; 12663da59bd9SMartin KaFai Lau 12673da59bd9SMartin KaFai Lau return &rt->dst; 12683da59bd9SMartin KaFai Lau } 12693da59bd9SMartin KaFai Lau 12703da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie) 12713da59bd9SMartin KaFai Lau { 12723da59bd9SMartin KaFai Lau if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && 12733da59bd9SMartin KaFai Lau rt6_check((struct rt6_info *)(rt->dst.from), cookie)) 12743da59bd9SMartin KaFai Lau return &rt->dst; 12753da59bd9SMartin KaFai Lau else 12763da59bd9SMartin KaFai Lau return NULL; 12773da59bd9SMartin KaFai Lau } 12783da59bd9SMartin KaFai Lau 12791da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 12801da177e4SLinus Torvalds { 12811da177e4SLinus Torvalds struct rt6_info *rt; 12821da177e4SLinus Torvalds 12831da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 12841da177e4SLinus Torvalds 12856f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 12866f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 12876f3118b5SNicolas Dichtel * into this function always. 12886f3118b5SNicolas Dichtel */ 1289e3bc10bdSHannes Frederic Sowa 12904b32b5adSMartin KaFai Lau rt6_dst_from_metrics_check(rt); 12914b32b5adSMartin KaFai Lau 1292d52d3997SMartin KaFai Lau if ((rt->rt6i_flags & RTF_PCPU) || unlikely(dst->flags & DST_NOCACHE)) 12933da59bd9SMartin KaFai Lau return rt6_dst_from_check(rt, cookie); 12943da59bd9SMartin KaFai Lau else 12953da59bd9SMartin KaFai Lau return rt6_check(rt, cookie); 12961da177e4SLinus Torvalds } 12971da177e4SLinus Torvalds 12981da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 12991da177e4SLinus Torvalds { 13001da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 13011da177e4SLinus Torvalds 13021da177e4SLinus Torvalds if (rt) { 130354c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 130454c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1305e0a1ad73SThomas Graf ip6_del_rt(rt); 130654c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 13071da177e4SLinus Torvalds } 130854c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 130954c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 131054c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 131154c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 131254c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 131354c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 13141da177e4SLinus Torvalds } 13151da177e4SLinus Torvalds 13161da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 13171da177e4SLinus Torvalds { 13181da177e4SLinus Torvalds struct rt6_info *rt; 13191da177e4SLinus Torvalds 13203ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 13211da177e4SLinus Torvalds 1322adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 13231da177e4SLinus Torvalds if (rt) { 13241eb4f758SHannes Frederic Sowa if (rt->rt6i_flags & RTF_CACHE) { 13251eb4f758SHannes Frederic Sowa dst_hold(&rt->dst); 13268e3d5be7SMartin KaFai Lau ip6_del_rt(rt); 13271eb4f758SHannes Frederic Sowa } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) { 13281da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 13291da177e4SLinus Torvalds } 13301da177e4SLinus Torvalds } 13311eb4f758SHannes Frederic Sowa } 13321da177e4SLinus Torvalds 133345e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) 133445e4fd26SMartin KaFai Lau { 133545e4fd26SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 133645e4fd26SMartin KaFai Lau 133745e4fd26SMartin KaFai Lau rt->rt6i_flags |= RTF_MODIFIED; 133845e4fd26SMartin KaFai Lau rt->rt6i_pmtu = mtu; 133945e4fd26SMartin KaFai Lau rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); 134045e4fd26SMartin KaFai Lau } 134145e4fd26SMartin KaFai Lau 134245e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, 134345e4fd26SMartin KaFai Lau const struct ipv6hdr *iph, u32 mtu) 13441da177e4SLinus Torvalds { 13451da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info *)dst; 13461da177e4SLinus Torvalds 134745e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_LOCAL) 134845e4fd26SMartin KaFai Lau return; 134945e4fd26SMartin KaFai Lau 135081aded24SDavid S. Miller dst_confirm(dst); 135145e4fd26SMartin KaFai Lau mtu = max_t(u32, mtu, IPV6_MIN_MTU); 135245e4fd26SMartin KaFai Lau if (mtu >= dst_mtu(dst)) 135345e4fd26SMartin KaFai Lau return; 135481aded24SDavid S. Miller 135545e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_CACHE) { 135645e4fd26SMartin KaFai Lau rt6_do_update_pmtu(rt6, mtu); 135745e4fd26SMartin KaFai Lau } else { 135845e4fd26SMartin KaFai Lau const struct in6_addr *daddr, *saddr; 135945e4fd26SMartin KaFai Lau struct rt6_info *nrt6; 13609d289715SHagen Paul Pfeifer 136145e4fd26SMartin KaFai Lau if (iph) { 136245e4fd26SMartin KaFai Lau daddr = &iph->daddr; 136345e4fd26SMartin KaFai Lau saddr = &iph->saddr; 136445e4fd26SMartin KaFai Lau } else if (sk) { 136545e4fd26SMartin KaFai Lau daddr = &sk->sk_v6_daddr; 136645e4fd26SMartin KaFai Lau saddr = &inet6_sk(sk)->saddr; 136745e4fd26SMartin KaFai Lau } else { 136845e4fd26SMartin KaFai Lau return; 13691da177e4SLinus Torvalds } 137045e4fd26SMartin KaFai Lau nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr); 137145e4fd26SMartin KaFai Lau if (nrt6) { 137245e4fd26SMartin KaFai Lau rt6_do_update_pmtu(nrt6, mtu); 137345e4fd26SMartin KaFai Lau 137445e4fd26SMartin KaFai Lau /* ip6_ins_rt(nrt6) will bump the 137545e4fd26SMartin KaFai Lau * rt6->rt6i_node->fn_sernum 137645e4fd26SMartin KaFai Lau * which will fail the next rt6_check() and 137745e4fd26SMartin KaFai Lau * invalidate the sk->sk_dst_cache. 137845e4fd26SMartin KaFai Lau */ 137945e4fd26SMartin KaFai Lau ip6_ins_rt(nrt6); 138045e4fd26SMartin KaFai Lau } 138145e4fd26SMartin KaFai Lau } 138245e4fd26SMartin KaFai Lau } 138345e4fd26SMartin KaFai Lau 138445e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 138545e4fd26SMartin KaFai Lau struct sk_buff *skb, u32 mtu) 138645e4fd26SMartin KaFai Lau { 138745e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu); 13881da177e4SLinus Torvalds } 13891da177e4SLinus Torvalds 139042ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 139142ae66c8SDavid S. Miller int oif, u32 mark) 139281aded24SDavid S. Miller { 139381aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 139481aded24SDavid S. Miller struct dst_entry *dst; 139581aded24SDavid S. Miller struct flowi6 fl6; 139681aded24SDavid S. Miller 139781aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 139881aded24SDavid S. Miller fl6.flowi6_oif = oif; 13991b3c61dcSLorenzo Colitti fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark); 140081aded24SDavid S. Miller fl6.daddr = iph->daddr; 140181aded24SDavid S. Miller fl6.saddr = iph->saddr; 14026502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 140381aded24SDavid S. Miller 140481aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 140581aded24SDavid S. Miller if (!dst->error) 140645e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu)); 140781aded24SDavid S. Miller dst_release(dst); 140881aded24SDavid S. Miller } 140981aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 141081aded24SDavid S. Miller 141181aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 141281aded24SDavid S. Miller { 141381aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 141481aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 141581aded24SDavid S. Miller } 141681aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 141781aded24SDavid S. Miller 1418b55b76b2SDuan Jiong /* Handle redirects */ 1419b55b76b2SDuan Jiong struct ip6rd_flowi { 1420b55b76b2SDuan Jiong struct flowi6 fl6; 1421b55b76b2SDuan Jiong struct in6_addr gateway; 1422b55b76b2SDuan Jiong }; 1423b55b76b2SDuan Jiong 1424b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net, 1425b55b76b2SDuan Jiong struct fib6_table *table, 1426b55b76b2SDuan Jiong struct flowi6 *fl6, 1427b55b76b2SDuan Jiong int flags) 1428b55b76b2SDuan Jiong { 1429b55b76b2SDuan Jiong struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 1430b55b76b2SDuan Jiong struct rt6_info *rt; 1431b55b76b2SDuan Jiong struct fib6_node *fn; 1432b55b76b2SDuan Jiong 1433b55b76b2SDuan Jiong /* Get the "current" route for this destination and 1434b55b76b2SDuan Jiong * check if the redirect has come from approriate router. 1435b55b76b2SDuan Jiong * 1436b55b76b2SDuan Jiong * RFC 4861 specifies that redirects should only be 1437b55b76b2SDuan Jiong * accepted if they come from the nexthop to the target. 1438b55b76b2SDuan Jiong * Due to the way the routes are chosen, this notion 1439b55b76b2SDuan Jiong * is a bit fuzzy and one might need to check all possible 1440b55b76b2SDuan Jiong * routes. 1441b55b76b2SDuan Jiong */ 1442b55b76b2SDuan Jiong 1443b55b76b2SDuan Jiong read_lock_bh(&table->tb6_lock); 1444b55b76b2SDuan Jiong fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1445b55b76b2SDuan Jiong restart: 1446b55b76b2SDuan Jiong for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1447b55b76b2SDuan Jiong if (rt6_check_expired(rt)) 1448b55b76b2SDuan Jiong continue; 1449b55b76b2SDuan Jiong if (rt->dst.error) 1450b55b76b2SDuan Jiong break; 1451b55b76b2SDuan Jiong if (!(rt->rt6i_flags & RTF_GATEWAY)) 1452b55b76b2SDuan Jiong continue; 1453b55b76b2SDuan Jiong if (fl6->flowi6_oif != rt->dst.dev->ifindex) 1454b55b76b2SDuan Jiong continue; 1455b55b76b2SDuan Jiong if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) 1456b55b76b2SDuan Jiong continue; 1457b55b76b2SDuan Jiong break; 1458b55b76b2SDuan Jiong } 1459b55b76b2SDuan Jiong 1460b55b76b2SDuan Jiong if (!rt) 1461b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1462b55b76b2SDuan Jiong else if (rt->dst.error) { 1463b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1464b0a1ba59SMartin KaFai Lau goto out; 1465b0a1ba59SMartin KaFai Lau } 1466b0a1ba59SMartin KaFai Lau 1467b0a1ba59SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1468a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1469a3c00e46SMartin KaFai Lau if (fn) 1470a3c00e46SMartin KaFai Lau goto restart; 1471b55b76b2SDuan Jiong } 1472a3c00e46SMartin KaFai Lau 1473b0a1ba59SMartin KaFai Lau out: 1474b55b76b2SDuan Jiong dst_hold(&rt->dst); 1475b55b76b2SDuan Jiong 1476b55b76b2SDuan Jiong read_unlock_bh(&table->tb6_lock); 1477b55b76b2SDuan Jiong 1478b55b76b2SDuan Jiong return rt; 1479b55b76b2SDuan Jiong }; 1480b55b76b2SDuan Jiong 1481b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net, 1482b55b76b2SDuan Jiong const struct flowi6 *fl6, 1483b55b76b2SDuan Jiong const struct in6_addr *gateway) 1484b55b76b2SDuan Jiong { 1485b55b76b2SDuan Jiong int flags = RT6_LOOKUP_F_HAS_SADDR; 1486b55b76b2SDuan Jiong struct ip6rd_flowi rdfl; 1487b55b76b2SDuan Jiong 1488b55b76b2SDuan Jiong rdfl.fl6 = *fl6; 1489b55b76b2SDuan Jiong rdfl.gateway = *gateway; 1490b55b76b2SDuan Jiong 1491b55b76b2SDuan Jiong return fib6_rule_lookup(net, &rdfl.fl6, 1492b55b76b2SDuan Jiong flags, __ip6_route_redirect); 1493b55b76b2SDuan Jiong } 1494b55b76b2SDuan Jiong 14953a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 14963a5ad2eeSDavid S. Miller { 14973a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 14983a5ad2eeSDavid S. Miller struct dst_entry *dst; 14993a5ad2eeSDavid S. Miller struct flowi6 fl6; 15003a5ad2eeSDavid S. Miller 15013a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 1502e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 15033a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 15043a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 15053a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 15063a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 15076502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 15083a5ad2eeSDavid S. Miller 1509b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); 15106700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 15113a5ad2eeSDavid S. Miller dst_release(dst); 15123a5ad2eeSDavid S. Miller } 15133a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 15143a5ad2eeSDavid S. Miller 1515c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, 1516c92a59ecSDuan Jiong u32 mark) 1517c92a59ecSDuan Jiong { 1518c92a59ecSDuan Jiong const struct ipv6hdr *iph = ipv6_hdr(skb); 1519c92a59ecSDuan Jiong const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 1520c92a59ecSDuan Jiong struct dst_entry *dst; 1521c92a59ecSDuan Jiong struct flowi6 fl6; 1522c92a59ecSDuan Jiong 1523c92a59ecSDuan Jiong memset(&fl6, 0, sizeof(fl6)); 1524e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 1525c92a59ecSDuan Jiong fl6.flowi6_oif = oif; 1526c92a59ecSDuan Jiong fl6.flowi6_mark = mark; 1527c92a59ecSDuan Jiong fl6.daddr = msg->dest; 1528c92a59ecSDuan Jiong fl6.saddr = iph->daddr; 1529c92a59ecSDuan Jiong 1530b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &iph->saddr); 1531c92a59ecSDuan Jiong rt6_do_redirect(dst, NULL, skb); 1532c92a59ecSDuan Jiong dst_release(dst); 1533c92a59ecSDuan Jiong } 1534c92a59ecSDuan Jiong 15353a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 15363a5ad2eeSDavid S. Miller { 15373a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 15383a5ad2eeSDavid S. Miller } 15393a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 15403a5ad2eeSDavid S. Miller 15410dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 15421da177e4SLinus Torvalds { 15430dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 15440dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 15450dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 15460dbaee3bSDavid S. Miller 15471da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 15481da177e4SLinus Torvalds 15495578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 15505578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 15511da177e4SLinus Torvalds 15521da177e4SLinus Torvalds /* 15531da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 15541da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 15551da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 15561da177e4SLinus Torvalds * rely only on pmtu discovery" 15571da177e4SLinus Torvalds */ 15581da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 15591da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 15601da177e4SLinus Torvalds return mtu; 15611da177e4SLinus Torvalds } 15621da177e4SLinus Torvalds 1563ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1564d33e4553SDavid S. Miller { 15654b32b5adSMartin KaFai Lau const struct rt6_info *rt = (const struct rt6_info *)dst; 15664b32b5adSMartin KaFai Lau unsigned int mtu = rt->rt6i_pmtu; 1567d33e4553SDavid S. Miller struct inet6_dev *idev; 1568618f9bc7SSteffen Klassert 1569618f9bc7SSteffen Klassert if (mtu) 157030f78d8eSEric Dumazet goto out; 1571618f9bc7SSteffen Klassert 15724b32b5adSMartin KaFai Lau mtu = dst_metric_raw(dst, RTAX_MTU); 15734b32b5adSMartin KaFai Lau if (mtu) 15744b32b5adSMartin KaFai Lau goto out; 15754b32b5adSMartin KaFai Lau 1576618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1577d33e4553SDavid S. Miller 1578d33e4553SDavid S. Miller rcu_read_lock(); 1579d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1580d33e4553SDavid S. Miller if (idev) 1581d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1582d33e4553SDavid S. Miller rcu_read_unlock(); 1583d33e4553SDavid S. Miller 158430f78d8eSEric Dumazet out: 158530f78d8eSEric Dumazet return min_t(unsigned int, mtu, IP6_MAX_MTU); 1586d33e4553SDavid S. Miller } 1587d33e4553SDavid S. Miller 15883b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 15893b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 15905d0bbeebSThomas Graf 15913b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 159287a11578SDavid S. Miller struct flowi6 *fl6) 15931da177e4SLinus Torvalds { 159487a11578SDavid S. Miller struct dst_entry *dst; 15951da177e4SLinus Torvalds struct rt6_info *rt; 15961da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1597c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 15981da177e4SLinus Torvalds 159938308473SDavid S. Miller if (unlikely(!idev)) 1600122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 16011da177e4SLinus Torvalds 1602ad706862SMartin KaFai Lau rt = ip6_dst_alloc(net, dev, 0); 160338308473SDavid S. Miller if (unlikely(!rt)) { 16041da177e4SLinus Torvalds in6_dev_put(idev); 160587a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 16061da177e4SLinus Torvalds goto out; 16071da177e4SLinus Torvalds } 16081da177e4SLinus Torvalds 16098e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 16108e2ec639SYan, Zheng rt->dst.output = ip6_output; 1611d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 1612550bab42SJulian Anastasov rt->rt6i_gateway = fl6->daddr; 161387a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 16148e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 16158e2ec639SYan, Zheng rt->rt6i_idev = idev; 161614edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 16171da177e4SLinus Torvalds 16183b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1619d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1620d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 16213b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 16221da177e4SLinus Torvalds 16235578689aSDaniel Lezcano fib6_force_start_gc(net); 16241da177e4SLinus Torvalds 162587a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 162687a11578SDavid S. Miller 16271da177e4SLinus Torvalds out: 162887a11578SDavid S. Miller return dst; 16291da177e4SLinus Torvalds } 16301da177e4SLinus Torvalds 16313d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 16321da177e4SLinus Torvalds { 1633e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 16343d0f24a7SStephen Hemminger int more = 0; 16351da177e4SLinus Torvalds 16363b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 16373b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 16385d0bbeebSThomas Graf 16391da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 16401da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 16411da177e4SLinus Torvalds *pprev = dst->next; 16421da177e4SLinus Torvalds dst_free(dst); 16431da177e4SLinus Torvalds } else { 16441da177e4SLinus Torvalds pprev = &dst->next; 16453d0f24a7SStephen Hemminger ++more; 16461da177e4SLinus Torvalds } 16471da177e4SLinus Torvalds } 16481da177e4SLinus Torvalds 16493b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 16505d0bbeebSThomas Graf 16513d0f24a7SStephen Hemminger return more; 16521da177e4SLinus Torvalds } 16531da177e4SLinus Torvalds 16541e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 16551e493d19SDavid S. Miller void *arg) 16561e493d19SDavid S. Miller { 16571e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 16581e493d19SDavid S. Miller 16591e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 16601e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 16611e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 16621e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 16631e493d19SDavid S. Miller if (func(rt, arg)) { 16641e493d19SDavid S. Miller *pprev = dst->next; 16651e493d19SDavid S. Miller dst_free(dst); 16661e493d19SDavid S. Miller } else { 16671e493d19SDavid S. Miller pprev = &dst->next; 16681e493d19SDavid S. Miller } 16691e493d19SDavid S. Miller } 16701e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 16711e493d19SDavid S. Miller } 16721e493d19SDavid S. Miller 1673569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 16741da177e4SLinus Torvalds { 167586393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 16767019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 16777019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 16787019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 16797019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 16807019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1681fc66f95cSEric Dumazet int entries; 16821da177e4SLinus Torvalds 1683fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 168449a18d86SMichal Kubeček if (time_after(rt_last_gc + rt_min_interval, jiffies) && 1685fc66f95cSEric Dumazet entries <= rt_max_size) 16861da177e4SLinus Torvalds goto out; 16871da177e4SLinus Torvalds 16886891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 168914956643SLi RongQing fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true); 1690fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1691fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 16927019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 16931da177e4SLinus Torvalds out: 16947019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1695fc66f95cSEric Dumazet return entries > rt_max_size; 16961da177e4SLinus Torvalds } 16971da177e4SLinus Torvalds 1698e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc, 1699e715b6d3SFlorian Westphal const struct fib6_config *cfg) 1700e715b6d3SFlorian Westphal { 1701c3a8d947SDaniel Borkmann bool ecn_ca = false; 1702e715b6d3SFlorian Westphal struct nlattr *nla; 1703e715b6d3SFlorian Westphal int remaining; 1704e715b6d3SFlorian Westphal u32 *mp; 1705e715b6d3SFlorian Westphal 170663159f29SIan Morris if (!cfg->fc_mx) 1707e715b6d3SFlorian Westphal return 0; 1708e715b6d3SFlorian Westphal 1709e715b6d3SFlorian Westphal mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 1710e715b6d3SFlorian Westphal if (unlikely(!mp)) 1711e715b6d3SFlorian Westphal return -ENOMEM; 1712e715b6d3SFlorian Westphal 1713e715b6d3SFlorian Westphal nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 1714e715b6d3SFlorian Westphal int type = nla_type(nla); 1715ea697639SDaniel Borkmann u32 val; 1716ea697639SDaniel Borkmann 17171bb14807SDaniel Borkmann if (!type) 17181bb14807SDaniel Borkmann continue; 1719e715b6d3SFlorian Westphal if (unlikely(type > RTAX_MAX)) 1720e715b6d3SFlorian Westphal goto err; 17211bb14807SDaniel Borkmann 1722ea697639SDaniel Borkmann if (type == RTAX_CC_ALGO) { 1723ea697639SDaniel Borkmann char tmp[TCP_CA_NAME_MAX]; 1724e715b6d3SFlorian Westphal 1725ea697639SDaniel Borkmann nla_strlcpy(tmp, nla, sizeof(tmp)); 1726c3a8d947SDaniel Borkmann val = tcp_ca_get_key_by_name(tmp, &ecn_ca); 1727ea697639SDaniel Borkmann if (val == TCP_CA_UNSPEC) 1728ea697639SDaniel Borkmann goto err; 1729ea697639SDaniel Borkmann } else { 1730ea697639SDaniel Borkmann val = nla_get_u32(nla); 1731ea697639SDaniel Borkmann } 1732b8d3e416SDaniel Borkmann if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK)) 1733b8d3e416SDaniel Borkmann goto err; 1734ea697639SDaniel Borkmann 1735ea697639SDaniel Borkmann mp[type - 1] = val; 1736e715b6d3SFlorian Westphal __set_bit(type - 1, mxc->mx_valid); 1737e715b6d3SFlorian Westphal } 1738e715b6d3SFlorian Westphal 1739c3a8d947SDaniel Borkmann if (ecn_ca) { 1740c3a8d947SDaniel Borkmann __set_bit(RTAX_FEATURES - 1, mxc->mx_valid); 1741c3a8d947SDaniel Borkmann mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA; 1742c3a8d947SDaniel Borkmann } 1743e715b6d3SFlorian Westphal 1744c3a8d947SDaniel Borkmann mxc->mx = mp; 1745e715b6d3SFlorian Westphal return 0; 1746e715b6d3SFlorian Westphal err: 1747e715b6d3SFlorian Westphal kfree(mp); 1748e715b6d3SFlorian Westphal return -EINVAL; 1749e715b6d3SFlorian Westphal } 17501da177e4SLinus Torvalds 17516b9ea5a6SRoopa Prabhu int ip6_route_info_create(struct fib6_config *cfg, struct rt6_info **rt_ret) 17521da177e4SLinus Torvalds { 17531da177e4SLinus Torvalds int err; 17545578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 17551da177e4SLinus Torvalds struct rt6_info *rt = NULL; 17561da177e4SLinus Torvalds struct net_device *dev = NULL; 17571da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1758c71099acSThomas Graf struct fib6_table *table; 17591da177e4SLinus Torvalds int addr_type; 17601da177e4SLinus Torvalds 176186872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 17621da177e4SLinus Torvalds return -EINVAL; 17631da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 176486872cb5SThomas Graf if (cfg->fc_src_len) 17651da177e4SLinus Torvalds return -EINVAL; 17661da177e4SLinus Torvalds #endif 176786872cb5SThomas Graf if (cfg->fc_ifindex) { 17681da177e4SLinus Torvalds err = -ENODEV; 17695578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 17701da177e4SLinus Torvalds if (!dev) 17711da177e4SLinus Torvalds goto out; 17721da177e4SLinus Torvalds idev = in6_dev_get(dev); 17731da177e4SLinus Torvalds if (!idev) 17741da177e4SLinus Torvalds goto out; 17751da177e4SLinus Torvalds } 17761da177e4SLinus Torvalds 177786872cb5SThomas Graf if (cfg->fc_metric == 0) 177886872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 17791da177e4SLinus Torvalds 1780c71099acSThomas Graf err = -ENOBUFS; 178138308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1782d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1783d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 178438308473SDavid S. Miller if (!table) { 1785f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1786d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1787d71314b4SMatti Vaittinen } 1788d71314b4SMatti Vaittinen } else { 1789d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1790d71314b4SMatti Vaittinen } 179138308473SDavid S. Miller 179238308473SDavid S. Miller if (!table) 1793c71099acSThomas Graf goto out; 1794c71099acSThomas Graf 1795ad706862SMartin KaFai Lau rt = ip6_dst_alloc(net, NULL, 1796ad706862SMartin KaFai Lau (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT); 17971da177e4SLinus Torvalds 179838308473SDavid S. Miller if (!rt) { 17991da177e4SLinus Torvalds err = -ENOMEM; 18001da177e4SLinus Torvalds goto out; 18011da177e4SLinus Torvalds } 18021da177e4SLinus Torvalds 18031716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 18041716a961SGao feng rt6_set_expires(rt, jiffies + 18051716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 18061716a961SGao feng else 18071716a961SGao feng rt6_clean_expires(rt); 18081da177e4SLinus Torvalds 180986872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 181086872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 181186872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 181286872cb5SThomas Graf 181386872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 18141da177e4SLinus Torvalds 18151da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1816d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1817ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1818ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 18191da177e4SLinus Torvalds else 1820d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 18211da177e4SLinus Torvalds 1822d8d1f30bSChangli Gao rt->dst.output = ip6_output; 18231da177e4SLinus Torvalds 182419e42e45SRoopa Prabhu if (cfg->fc_encap) { 182519e42e45SRoopa Prabhu struct lwtunnel_state *lwtstate; 182619e42e45SRoopa Prabhu 182719e42e45SRoopa Prabhu err = lwtunnel_build_state(dev, cfg->fc_encap_type, 1828127eb7cdSTom Herbert cfg->fc_encap, AF_INET6, cfg, 1829127eb7cdSTom Herbert &lwtstate); 183019e42e45SRoopa Prabhu if (err) 183119e42e45SRoopa Prabhu goto out; 183261adedf3SJiri Benc rt->dst.lwtstate = lwtstate_get(lwtstate); 183361adedf3SJiri Benc if (lwtunnel_output_redirect(rt->dst.lwtstate)) { 183461adedf3SJiri Benc rt->dst.lwtstate->orig_output = rt->dst.output; 183561adedf3SJiri Benc rt->dst.output = lwtunnel_output; 183619e42e45SRoopa Prabhu } 183761adedf3SJiri Benc if (lwtunnel_input_redirect(rt->dst.lwtstate)) { 183861adedf3SJiri Benc rt->dst.lwtstate->orig_input = rt->dst.input; 183961adedf3SJiri Benc rt->dst.input = lwtunnel_input; 184025368623STom Herbert } 184125368623STom Herbert } 184219e42e45SRoopa Prabhu 184386872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 184486872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 1845afc4eef8SMartin KaFai Lau if (rt->rt6i_dst.plen == 128) 184611d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 18471da177e4SLinus Torvalds 18481da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 184986872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 185086872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 18511da177e4SLinus Torvalds #endif 18521da177e4SLinus Torvalds 185386872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 18541da177e4SLinus Torvalds 18551da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 18561da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 18571da177e4SLinus Torvalds */ 185886872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 185938308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 186038308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 186138308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 18621da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 18635578689aSDaniel Lezcano if (dev != net->loopback_dev) { 18641da177e4SLinus Torvalds if (dev) { 18651da177e4SLinus Torvalds dev_put(dev); 18661da177e4SLinus Torvalds in6_dev_put(idev); 18671da177e4SLinus Torvalds } 18685578689aSDaniel Lezcano dev = net->loopback_dev; 18691da177e4SLinus Torvalds dev_hold(dev); 18701da177e4SLinus Torvalds idev = in6_dev_get(dev); 18711da177e4SLinus Torvalds if (!idev) { 18721da177e4SLinus Torvalds err = -ENODEV; 18731da177e4SLinus Torvalds goto out; 18741da177e4SLinus Torvalds } 18751da177e4SLinus Torvalds } 18761da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1877ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1878ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1879ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 1880aad88724SEric Dumazet rt->dst.output = dst_discard_sk; 18817150aedeSKamala R rt->dst.input = dst_discard; 1882ef2c7d7bSNicolas Dichtel break; 1883ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1884ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 18857150aedeSKamala R rt->dst.output = ip6_pkt_prohibit_out; 18867150aedeSKamala R rt->dst.input = ip6_pkt_prohibit; 1887ef2c7d7bSNicolas Dichtel break; 1888b4949ab2SNicolas Dichtel case RTN_THROW: 18890315e382SNikola Forró case RTN_UNREACHABLE: 1890ef2c7d7bSNicolas Dichtel default: 18917150aedeSKamala R rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN 18920315e382SNikola Forró : (cfg->fc_type == RTN_UNREACHABLE) 18930315e382SNikola Forró ? -EHOSTUNREACH : -ENETUNREACH; 18947150aedeSKamala R rt->dst.output = ip6_pkt_discard_out; 18957150aedeSKamala R rt->dst.input = ip6_pkt_discard; 1896ef2c7d7bSNicolas Dichtel break; 1897ef2c7d7bSNicolas Dichtel } 18981da177e4SLinus Torvalds goto install_route; 18991da177e4SLinus Torvalds } 19001da177e4SLinus Torvalds 190186872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1902b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 19031da177e4SLinus Torvalds int gwa_type; 19041da177e4SLinus Torvalds 190586872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 1906330567b7SFlorian Westphal gwa_type = ipv6_addr_type(gw_addr); 190748ed7b26SFlorian Westphal 190848ed7b26SFlorian Westphal /* if gw_addr is local we will fail to detect this in case 190948ed7b26SFlorian Westphal * address is still TENTATIVE (DAD in progress). rt6_lookup() 191048ed7b26SFlorian Westphal * will return already-added prefix route via interface that 191148ed7b26SFlorian Westphal * prefix route was assigned to, which might be non-loopback. 191248ed7b26SFlorian Westphal */ 191348ed7b26SFlorian Westphal err = -EINVAL; 1914330567b7SFlorian Westphal if (ipv6_chk_addr_and_flags(net, gw_addr, 1915330567b7SFlorian Westphal gwa_type & IPV6_ADDR_LINKLOCAL ? 1916330567b7SFlorian Westphal dev : NULL, 0, 0)) 191748ed7b26SFlorian Westphal goto out; 191848ed7b26SFlorian Westphal 19194e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 19201da177e4SLinus Torvalds 19211da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 19221da177e4SLinus Torvalds struct rt6_info *grt; 19231da177e4SLinus Torvalds 19241da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 19251da177e4SLinus Torvalds addresses as nexthop address. 19261da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 19271da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 19281da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 19291da177e4SLinus Torvalds some exceptions. --ANK 19301da177e4SLinus Torvalds */ 19311da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 19321da177e4SLinus Torvalds goto out; 19331da177e4SLinus Torvalds 19345578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 19351da177e4SLinus Torvalds 19361da177e4SLinus Torvalds err = -EHOSTUNREACH; 193738308473SDavid S. Miller if (!grt) 19381da177e4SLinus Torvalds goto out; 19391da177e4SLinus Torvalds if (dev) { 1940d1918542SDavid S. Miller if (dev != grt->dst.dev) { 194194e187c0SAmerigo Wang ip6_rt_put(grt); 19421da177e4SLinus Torvalds goto out; 19431da177e4SLinus Torvalds } 19441da177e4SLinus Torvalds } else { 1945d1918542SDavid S. Miller dev = grt->dst.dev; 19461da177e4SLinus Torvalds idev = grt->rt6i_idev; 19471da177e4SLinus Torvalds dev_hold(dev); 19481da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 19491da177e4SLinus Torvalds } 19501da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 19511da177e4SLinus Torvalds err = 0; 195294e187c0SAmerigo Wang ip6_rt_put(grt); 19531da177e4SLinus Torvalds 19541da177e4SLinus Torvalds if (err) 19551da177e4SLinus Torvalds goto out; 19561da177e4SLinus Torvalds } 19571da177e4SLinus Torvalds err = -EINVAL; 195838308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 19591da177e4SLinus Torvalds goto out; 19601da177e4SLinus Torvalds } 19611da177e4SLinus Torvalds 19621da177e4SLinus Torvalds err = -ENODEV; 196338308473SDavid S. Miller if (!dev) 19641da177e4SLinus Torvalds goto out; 19651da177e4SLinus Torvalds 1966c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1967c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1968c3968a85SDaniel Walter err = -EINVAL; 1969c3968a85SDaniel Walter goto out; 1970c3968a85SDaniel Walter } 19714e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1972c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1973c3968a85SDaniel Walter } else 1974c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1975c3968a85SDaniel Walter 197686872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 19771da177e4SLinus Torvalds 19781da177e4SLinus Torvalds install_route: 1979d8d1f30bSChangli Gao rt->dst.dev = dev; 19801da177e4SLinus Torvalds rt->rt6i_idev = idev; 1981c71099acSThomas Graf rt->rt6i_table = table; 198263152fc0SDaniel Lezcano 1983c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 198463152fc0SDaniel Lezcano 19856b9ea5a6SRoopa Prabhu *rt_ret = rt; 19861da177e4SLinus Torvalds 19876b9ea5a6SRoopa Prabhu return 0; 19881da177e4SLinus Torvalds out: 19891da177e4SLinus Torvalds if (dev) 19901da177e4SLinus Torvalds dev_put(dev); 19911da177e4SLinus Torvalds if (idev) 19921da177e4SLinus Torvalds in6_dev_put(idev); 19931da177e4SLinus Torvalds if (rt) 1994d8d1f30bSChangli Gao dst_free(&rt->dst); 19956b9ea5a6SRoopa Prabhu 19966b9ea5a6SRoopa Prabhu *rt_ret = NULL; 19976b9ea5a6SRoopa Prabhu 19986b9ea5a6SRoopa Prabhu return err; 19996b9ea5a6SRoopa Prabhu } 20006b9ea5a6SRoopa Prabhu 20016b9ea5a6SRoopa Prabhu int ip6_route_add(struct fib6_config *cfg) 20026b9ea5a6SRoopa Prabhu { 20036b9ea5a6SRoopa Prabhu struct mx6_config mxc = { .mx = NULL, }; 20046b9ea5a6SRoopa Prabhu struct rt6_info *rt = NULL; 20056b9ea5a6SRoopa Prabhu int err; 20066b9ea5a6SRoopa Prabhu 20076b9ea5a6SRoopa Prabhu err = ip6_route_info_create(cfg, &rt); 20086b9ea5a6SRoopa Prabhu if (err) 20096b9ea5a6SRoopa Prabhu goto out; 20106b9ea5a6SRoopa Prabhu 20116b9ea5a6SRoopa Prabhu err = ip6_convert_metrics(&mxc, cfg); 20126b9ea5a6SRoopa Prabhu if (err) 20136b9ea5a6SRoopa Prabhu goto out; 20146b9ea5a6SRoopa Prabhu 20156b9ea5a6SRoopa Prabhu err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); 20166b9ea5a6SRoopa Prabhu 20176b9ea5a6SRoopa Prabhu kfree(mxc.mx); 20186b9ea5a6SRoopa Prabhu 20196b9ea5a6SRoopa Prabhu return err; 20206b9ea5a6SRoopa Prabhu out: 20216b9ea5a6SRoopa Prabhu if (rt) 20226b9ea5a6SRoopa Prabhu dst_free(&rt->dst); 20236b9ea5a6SRoopa Prabhu 20241da177e4SLinus Torvalds return err; 20251da177e4SLinus Torvalds } 20261da177e4SLinus Torvalds 202786872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 20281da177e4SLinus Torvalds { 20291da177e4SLinus Torvalds int err; 2030c71099acSThomas Graf struct fib6_table *table; 2031d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 20321da177e4SLinus Torvalds 20338e3d5be7SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry || 20348e3d5be7SMartin KaFai Lau rt->dst.flags & DST_NOCACHE) { 20356825a26cSGao feng err = -ENOENT; 20366825a26cSGao feng goto out; 20376825a26cSGao feng } 20386c813a72SPatrick McHardy 2039c71099acSThomas Graf table = rt->rt6i_table; 2040c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 204186872cb5SThomas Graf err = fib6_del(rt, info); 2042c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 20431da177e4SLinus Torvalds 20446825a26cSGao feng out: 204594e187c0SAmerigo Wang ip6_rt_put(rt); 20461da177e4SLinus Torvalds return err; 20471da177e4SLinus Torvalds } 20481da177e4SLinus Torvalds 2049e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 2050e0a1ad73SThomas Graf { 20514d1169c1SDenis V. Lunev struct nl_info info = { 2052d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 20534d1169c1SDenis V. Lunev }; 2054528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 2055e0a1ad73SThomas Graf } 2056e0a1ad73SThomas Graf 205786872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 20581da177e4SLinus Torvalds { 2059c71099acSThomas Graf struct fib6_table *table; 20601da177e4SLinus Torvalds struct fib6_node *fn; 20611da177e4SLinus Torvalds struct rt6_info *rt; 20621da177e4SLinus Torvalds int err = -ESRCH; 20631da177e4SLinus Torvalds 20645578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 206538308473SDavid S. Miller if (!table) 2066c71099acSThomas Graf return err; 20671da177e4SLinus Torvalds 2068c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2069c71099acSThomas Graf 2070c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 207186872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 207286872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 20731da177e4SLinus Torvalds 20741da177e4SLinus Torvalds if (fn) { 2075d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 20761f56a01fSMartin KaFai Lau if ((rt->rt6i_flags & RTF_CACHE) && 20771f56a01fSMartin KaFai Lau !(cfg->fc_flags & RTF_CACHE)) 20781f56a01fSMartin KaFai Lau continue; 207986872cb5SThomas Graf if (cfg->fc_ifindex && 2080d1918542SDavid S. Miller (!rt->dst.dev || 2081d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 20821da177e4SLinus Torvalds continue; 208386872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 208486872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 20851da177e4SLinus Torvalds continue; 208686872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 20871da177e4SLinus Torvalds continue; 2088d8d1f30bSChangli Gao dst_hold(&rt->dst); 2089c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 20901da177e4SLinus Torvalds 209186872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 20921da177e4SLinus Torvalds } 20931da177e4SLinus Torvalds } 2094c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 20951da177e4SLinus Torvalds 20961da177e4SLinus Torvalds return err; 20971da177e4SLinus Torvalds } 20981da177e4SLinus Torvalds 20996700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 2100a6279458SYOSHIFUJI Hideaki { 2101e8599ff4SDavid S. Miller struct net *net = dev_net(skb->dev); 2102a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 2103e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 2104e8599ff4SDavid S. Miller struct ndisc_options ndopts; 2105e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 2106e8599ff4SDavid S. Miller struct neighbour *neigh; 210771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 struct rd_msg *msg; 21086e157b6aSDavid S. Miller int optlen, on_link; 21096e157b6aSDavid S. Miller u8 *lladdr; 2110e8599ff4SDavid S. Miller 211129a3cad5SSimon Horman optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 211271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 optlen -= sizeof(*msg); 2113e8599ff4SDavid S. Miller 2114e8599ff4SDavid S. Miller if (optlen < 0) { 21156e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 2116e8599ff4SDavid S. Miller return; 2117e8599ff4SDavid S. Miller } 2118e8599ff4SDavid S. Miller 211971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 msg = (struct rd_msg *)icmp6_hdr(skb); 2120e8599ff4SDavid S. Miller 212171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_is_multicast(&msg->dest)) { 21226e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 2123e8599ff4SDavid S. Miller return; 2124e8599ff4SDavid S. Miller } 2125e8599ff4SDavid S. Miller 21266e157b6aSDavid S. Miller on_link = 0; 212771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_equal(&msg->dest, &msg->target)) { 2128e8599ff4SDavid S. Miller on_link = 1; 212971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 } else if (ipv6_addr_type(&msg->target) != 2130e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 21316e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 2132e8599ff4SDavid S. Miller return; 2133e8599ff4SDavid S. Miller } 2134e8599ff4SDavid S. Miller 2135e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 2136e8599ff4SDavid S. Miller if (!in6_dev) 2137e8599ff4SDavid S. Miller return; 2138e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 2139e8599ff4SDavid S. Miller return; 2140e8599ff4SDavid S. Miller 2141e8599ff4SDavid S. Miller /* RFC2461 8.1: 2142e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 2143e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 2144e8599ff4SDavid S. Miller */ 2145e8599ff4SDavid S. Miller 214671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) { 2147e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 2148e8599ff4SDavid S. Miller return; 2149e8599ff4SDavid S. Miller } 21506e157b6aSDavid S. Miller 21516e157b6aSDavid S. Miller lladdr = NULL; 2152e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 2153e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 2154e8599ff4SDavid S. Miller skb->dev); 2155e8599ff4SDavid S. Miller if (!lladdr) { 2156e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 2157e8599ff4SDavid S. Miller return; 2158e8599ff4SDavid S. Miller } 2159e8599ff4SDavid S. Miller } 2160e8599ff4SDavid S. Miller 21616e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 21626e157b6aSDavid S. Miller if (rt == net->ipv6.ip6_null_entry) { 21636e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 21646e157b6aSDavid S. Miller return; 21656e157b6aSDavid S. Miller } 21666e157b6aSDavid S. Miller 21676e157b6aSDavid S. Miller /* Redirect received -> path was valid. 21686e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 21696e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 21706e157b6aSDavid S. Miller */ 21716e157b6aSDavid S. Miller dst_confirm(&rt->dst); 21726e157b6aSDavid S. Miller 217371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 2174e8599ff4SDavid S. Miller if (!neigh) 2175e8599ff4SDavid S. Miller return; 2176e8599ff4SDavid S. Miller 21771da177e4SLinus Torvalds /* 21781da177e4SLinus Torvalds * We have finally decided to accept it. 21791da177e4SLinus Torvalds */ 21801da177e4SLinus Torvalds 21811da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 21821da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 21831da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 21841da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 21851da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 21861da177e4SLinus Torvalds ); 21871da177e4SLinus Torvalds 218883a09abdSMartin KaFai Lau nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL); 218938308473SDavid S. Miller if (!nrt) 21901da177e4SLinus Torvalds goto out; 21911da177e4SLinus Torvalds 21921da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 21931da177e4SLinus Torvalds if (on_link) 21941da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 21951da177e4SLinus Torvalds 21964e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 21971da177e4SLinus Torvalds 219840e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 21991da177e4SLinus Torvalds goto out; 22001da177e4SLinus Torvalds 2201d8d1f30bSChangli Gao netevent.old = &rt->dst; 2202d8d1f30bSChangli Gao netevent.new = &nrt->dst; 220371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 netevent.daddr = &msg->dest; 220460592833SYOSHIFUJI Hideaki / 吉藤英明 netevent.neigh = neigh; 22058d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 22068d71740cSTom Tucker 22071da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 22086e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 2209e0a1ad73SThomas Graf ip6_del_rt(rt); 22101da177e4SLinus Torvalds } 22111da177e4SLinus Torvalds 22121da177e4SLinus Torvalds out: 2213e8599ff4SDavid S. Miller neigh_release(neigh); 22146e157b6aSDavid S. Miller } 22156e157b6aSDavid S. Miller 22161da177e4SLinus Torvalds /* 22171da177e4SLinus Torvalds * Misc support functions 22181da177e4SLinus Torvalds */ 22191da177e4SLinus Torvalds 22204b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) 22214b32b5adSMartin KaFai Lau { 22224b32b5adSMartin KaFai Lau BUG_ON(from->dst.from); 22234b32b5adSMartin KaFai Lau 22244b32b5adSMartin KaFai Lau rt->rt6i_flags &= ~RTF_EXPIRES; 22254b32b5adSMartin KaFai Lau dst_hold(&from->dst); 22264b32b5adSMartin KaFai Lau rt->dst.from = &from->dst; 22274b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true); 22284b32b5adSMartin KaFai Lau } 22294b32b5adSMartin KaFai Lau 223083a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort) 22311da177e4SLinus Torvalds { 2232d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 2233d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 223483a09abdSMartin KaFai Lau rt->rt6i_dst = ort->rt6i_dst; 2235d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 22361da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 22371da177e4SLinus Torvalds if (rt->rt6i_idev) 22381da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 2239d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 22404e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 22411716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 22421716a961SGao feng rt6_set_from(rt, ort); 224383a09abdSMartin KaFai Lau rt->rt6i_metric = ort->rt6i_metric; 22441da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 224583a09abdSMartin KaFai Lau rt->rt6i_src = ort->rt6i_src; 22461da177e4SLinus Torvalds #endif 224783a09abdSMartin KaFai Lau rt->rt6i_prefsrc = ort->rt6i_prefsrc; 2248c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 224961adedf3SJiri Benc rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate); 22501da177e4SLinus Torvalds } 22511da177e4SLinus Torvalds 225270ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 2253efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 2254b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2255b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 225670ceb4f5SYOSHIFUJI Hideaki { 225770ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 225870ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 2259c71099acSThomas Graf struct fib6_table *table; 226070ceb4f5SYOSHIFUJI Hideaki 2261efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 226238308473SDavid S. Miller if (!table) 2263c71099acSThomas Graf return NULL; 2264c71099acSThomas Graf 22655744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2266c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0); 226770ceb4f5SYOSHIFUJI Hideaki if (!fn) 226870ceb4f5SYOSHIFUJI Hideaki goto out; 226970ceb4f5SYOSHIFUJI Hideaki 2270d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 2271d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 227270ceb4f5SYOSHIFUJI Hideaki continue; 227370ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 227470ceb4f5SYOSHIFUJI Hideaki continue; 227570ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 227670ceb4f5SYOSHIFUJI Hideaki continue; 2277d8d1f30bSChangli Gao dst_hold(&rt->dst); 227870ceb4f5SYOSHIFUJI Hideaki break; 227970ceb4f5SYOSHIFUJI Hideaki } 228070ceb4f5SYOSHIFUJI Hideaki out: 22815744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 228270ceb4f5SYOSHIFUJI Hideaki return rt; 228370ceb4f5SYOSHIFUJI Hideaki } 228470ceb4f5SYOSHIFUJI Hideaki 2285efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 2286b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2287b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 228895c96174SEric Dumazet unsigned int pref) 228970ceb4f5SYOSHIFUJI Hideaki { 229086872cb5SThomas Graf struct fib6_config cfg = { 229186872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 2292238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 229386872cb5SThomas Graf .fc_ifindex = ifindex, 229486872cb5SThomas Graf .fc_dst_len = prefixlen, 229586872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 229686872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 229715e47304SEric W. Biederman .fc_nlinfo.portid = 0, 2298efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 2299efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 230086872cb5SThomas Graf }; 230170ceb4f5SYOSHIFUJI Hideaki 23024e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 23034e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 230486872cb5SThomas Graf 2305e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 2306e317da96SYOSHIFUJI Hideaki if (!prefixlen) 230786872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 230870ceb4f5SYOSHIFUJI Hideaki 230986872cb5SThomas Graf ip6_route_add(&cfg); 231070ceb4f5SYOSHIFUJI Hideaki 2311efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 231270ceb4f5SYOSHIFUJI Hideaki } 231370ceb4f5SYOSHIFUJI Hideaki #endif 231470ceb4f5SYOSHIFUJI Hideaki 2315b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 23161da177e4SLinus Torvalds { 23171da177e4SLinus Torvalds struct rt6_info *rt; 2318c71099acSThomas Graf struct fib6_table *table; 23191da177e4SLinus Torvalds 2320c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 232138308473SDavid S. Miller if (!table) 2322c71099acSThomas Graf return NULL; 23231da177e4SLinus Torvalds 23245744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2325d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 2326d1918542SDavid S. Miller if (dev == rt->dst.dev && 2327045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 23281da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 23291da177e4SLinus Torvalds break; 23301da177e4SLinus Torvalds } 23311da177e4SLinus Torvalds if (rt) 2332d8d1f30bSChangli Gao dst_hold(&rt->dst); 23335744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 23341da177e4SLinus Torvalds return rt; 23351da177e4SLinus Torvalds } 23361da177e4SLinus Torvalds 2337b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 2338ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 2339ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 23401da177e4SLinus Torvalds { 234186872cb5SThomas Graf struct fib6_config cfg = { 234286872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 2343238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 234486872cb5SThomas Graf .fc_ifindex = dev->ifindex, 234586872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 234686872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 234715e47304SEric W. Biederman .fc_nlinfo.portid = 0, 23485578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 2349c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 235086872cb5SThomas Graf }; 23511da177e4SLinus Torvalds 23524e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 23531da177e4SLinus Torvalds 235486872cb5SThomas Graf ip6_route_add(&cfg); 23551da177e4SLinus Torvalds 23561da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 23571da177e4SLinus Torvalds } 23581da177e4SLinus Torvalds 23597b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 23601da177e4SLinus Torvalds { 23611da177e4SLinus Torvalds struct rt6_info *rt; 2362c71099acSThomas Graf struct fib6_table *table; 2363c71099acSThomas Graf 2364c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 23657b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 236638308473SDavid S. Miller if (!table) 2367c71099acSThomas Graf return; 23681da177e4SLinus Torvalds 23691da177e4SLinus Torvalds restart: 2370c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2371d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 23723e8b0ac3SLorenzo Colitti if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 23733e8b0ac3SLorenzo Colitti (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { 2374d8d1f30bSChangli Gao dst_hold(&rt->dst); 2375c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2376e0a1ad73SThomas Graf ip6_del_rt(rt); 23771da177e4SLinus Torvalds goto restart; 23781da177e4SLinus Torvalds } 23791da177e4SLinus Torvalds } 2380c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 23811da177e4SLinus Torvalds } 23821da177e4SLinus Torvalds 23835578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 23845578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 238586872cb5SThomas Graf struct fib6_config *cfg) 238686872cb5SThomas Graf { 238786872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 238886872cb5SThomas Graf 238986872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 239086872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 239186872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 239286872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 239386872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 239486872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 239586872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 239686872cb5SThomas Graf 23975578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 2398f1243c2dSBenjamin Thery 23994e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 24004e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 24014e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 240286872cb5SThomas Graf } 240386872cb5SThomas Graf 24045578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 24051da177e4SLinus Torvalds { 240686872cb5SThomas Graf struct fib6_config cfg; 24071da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 24081da177e4SLinus Torvalds int err; 24091da177e4SLinus Torvalds 24101da177e4SLinus Torvalds switch (cmd) { 24111da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 24121da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 2413af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 24141da177e4SLinus Torvalds return -EPERM; 24151da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 24161da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 24171da177e4SLinus Torvalds if (err) 24181da177e4SLinus Torvalds return -EFAULT; 24191da177e4SLinus Torvalds 24205578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 242186872cb5SThomas Graf 24221da177e4SLinus Torvalds rtnl_lock(); 24231da177e4SLinus Torvalds switch (cmd) { 24241da177e4SLinus Torvalds case SIOCADDRT: 242586872cb5SThomas Graf err = ip6_route_add(&cfg); 24261da177e4SLinus Torvalds break; 24271da177e4SLinus Torvalds case SIOCDELRT: 242886872cb5SThomas Graf err = ip6_route_del(&cfg); 24291da177e4SLinus Torvalds break; 24301da177e4SLinus Torvalds default: 24311da177e4SLinus Torvalds err = -EINVAL; 24321da177e4SLinus Torvalds } 24331da177e4SLinus Torvalds rtnl_unlock(); 24341da177e4SLinus Torvalds 24351da177e4SLinus Torvalds return err; 24363ff50b79SStephen Hemminger } 24371da177e4SLinus Torvalds 24381da177e4SLinus Torvalds return -EINVAL; 24391da177e4SLinus Torvalds } 24401da177e4SLinus Torvalds 24411da177e4SLinus Torvalds /* 24421da177e4SLinus Torvalds * Drop the packet on the floor 24431da177e4SLinus Torvalds */ 24441da177e4SLinus Torvalds 2445d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 24461da177e4SLinus Torvalds { 2447612f09e8SYOSHIFUJI Hideaki int type; 2448adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2449612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2450612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 24510660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 245245bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 24533bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 24543bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2455612f09e8SYOSHIFUJI Hideaki break; 2456612f09e8SYOSHIFUJI Hideaki } 2457612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2458612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 24593bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 24603bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2461612f09e8SYOSHIFUJI Hideaki break; 2462612f09e8SYOSHIFUJI Hideaki } 24633ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 24641da177e4SLinus Torvalds kfree_skb(skb); 24651da177e4SLinus Torvalds return 0; 24661da177e4SLinus Torvalds } 24671da177e4SLinus Torvalds 24689ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 24699ce8ade0SThomas Graf { 2470612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 24719ce8ade0SThomas Graf } 24729ce8ade0SThomas Graf 2473aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb) 24741da177e4SLinus Torvalds { 2475adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2476612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 24771da177e4SLinus Torvalds } 24781da177e4SLinus Torvalds 24799ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 24809ce8ade0SThomas Graf { 2481612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 24829ce8ade0SThomas Graf } 24839ce8ade0SThomas Graf 2484aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb) 24859ce8ade0SThomas Graf { 2486adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2487612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 24889ce8ade0SThomas Graf } 24899ce8ade0SThomas Graf 24901da177e4SLinus Torvalds /* 24911da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 24921da177e4SLinus Torvalds */ 24931da177e4SLinus Torvalds 24941da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 24951da177e4SLinus Torvalds const struct in6_addr *addr, 24968f031519SDavid S. Miller bool anycast) 24971da177e4SLinus Torvalds { 2498c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 2499a3300ef4SHannes Frederic Sowa struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 2500ad706862SMartin KaFai Lau DST_NOCOUNT); 2501a3300ef4SHannes Frederic Sowa if (!rt) 25021da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 25031da177e4SLinus Torvalds 25041da177e4SLinus Torvalds in6_dev_hold(idev); 25051da177e4SLinus Torvalds 250611d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2507d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2508d8d1f30bSChangli Gao rt->dst.output = ip6_output; 25091da177e4SLinus Torvalds rt->rt6i_idev = idev; 25101da177e4SLinus Torvalds 25111da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 251258c4fb86SYOSHIFUJI Hideaki if (anycast) 251358c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 251458c4fb86SYOSHIFUJI Hideaki else 25151da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 25161da177e4SLinus Torvalds 2517550bab42SJulian Anastasov rt->rt6i_gateway = *addr; 25184e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 25191da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 25205578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 25218e3d5be7SMartin KaFai Lau rt->dst.flags |= DST_NOCACHE; 25221da177e4SLinus Torvalds 2523d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 25241da177e4SLinus Torvalds 25251da177e4SLinus Torvalds return rt; 25261da177e4SLinus Torvalds } 25271da177e4SLinus Torvalds 2528c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2529c3968a85SDaniel Walter struct rt6_info *rt, 2530b71d1d42SEric Dumazet const struct in6_addr *daddr, 2531c3968a85SDaniel Walter unsigned int prefs, 2532c3968a85SDaniel Walter struct in6_addr *saddr) 2533c3968a85SDaniel Walter { 2534e16e888bSMarkus Stenberg struct inet6_dev *idev = 2535e16e888bSMarkus Stenberg rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL; 2536c3968a85SDaniel Walter int err = 0; 2537e16e888bSMarkus Stenberg if (rt && rt->rt6i_prefsrc.plen) 25384e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2539c3968a85SDaniel Walter else 2540c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2541c3968a85SDaniel Walter daddr, prefs, saddr); 2542c3968a85SDaniel Walter return err; 2543c3968a85SDaniel Walter } 2544c3968a85SDaniel Walter 2545c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2546c3968a85SDaniel Walter struct arg_dev_net_ip { 2547c3968a85SDaniel Walter struct net_device *dev; 2548c3968a85SDaniel Walter struct net *net; 2549c3968a85SDaniel Walter struct in6_addr *addr; 2550c3968a85SDaniel Walter }; 2551c3968a85SDaniel Walter 2552c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2553c3968a85SDaniel Walter { 2554c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2555c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2556c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2557c3968a85SDaniel Walter 2558d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2559c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2560c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2561c3968a85SDaniel Walter /* remove prefsrc entry */ 2562c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2563c3968a85SDaniel Walter } 2564c3968a85SDaniel Walter return 0; 2565c3968a85SDaniel Walter } 2566c3968a85SDaniel Walter 2567c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2568c3968a85SDaniel Walter { 2569c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2570c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2571c3968a85SDaniel Walter .dev = ifp->idev->dev, 2572c3968a85SDaniel Walter .net = net, 2573c3968a85SDaniel Walter .addr = &ifp->addr, 2574c3968a85SDaniel Walter }; 25750c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 2576c3968a85SDaniel Walter } 2577c3968a85SDaniel Walter 2578be7a010dSDuan Jiong #define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) 2579be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 2580be7a010dSDuan Jiong 2581be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */ 2582be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg) 2583be7a010dSDuan Jiong { 2584be7a010dSDuan Jiong struct in6_addr *gateway = (struct in6_addr *)arg; 2585be7a010dSDuan Jiong 2586be7a010dSDuan Jiong if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) || 2587be7a010dSDuan Jiong ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) && 2588be7a010dSDuan Jiong ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { 2589be7a010dSDuan Jiong return -1; 2590be7a010dSDuan Jiong } 2591be7a010dSDuan Jiong return 0; 2592be7a010dSDuan Jiong } 2593be7a010dSDuan Jiong 2594be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 2595be7a010dSDuan Jiong { 2596be7a010dSDuan Jiong fib6_clean_all(net, fib6_clean_tohost, gateway); 2597be7a010dSDuan Jiong } 2598be7a010dSDuan Jiong 25998ed67789SDaniel Lezcano struct arg_dev_net { 26008ed67789SDaniel Lezcano struct net_device *dev; 26018ed67789SDaniel Lezcano struct net *net; 26028ed67789SDaniel Lezcano }; 26038ed67789SDaniel Lezcano 26041da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 26051da177e4SLinus Torvalds { 2606bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2607bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 26088ed67789SDaniel Lezcano 2609d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2610c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 26111da177e4SLinus Torvalds return -1; 2612c159d30cSDavid S. Miller 26131da177e4SLinus Torvalds return 0; 26141da177e4SLinus Torvalds } 26151da177e4SLinus Torvalds 2616f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 26171da177e4SLinus Torvalds { 26188ed67789SDaniel Lezcano struct arg_dev_net adn = { 26198ed67789SDaniel Lezcano .dev = dev, 26208ed67789SDaniel Lezcano .net = net, 26218ed67789SDaniel Lezcano }; 26228ed67789SDaniel Lezcano 26230c3584d5SLi RongQing fib6_clean_all(net, fib6_ifdown, &adn); 26241e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 26258d0b94afSMartin KaFai Lau rt6_uncached_list_flush_dev(net, dev); 26261da177e4SLinus Torvalds } 26271da177e4SLinus Torvalds 262895c96174SEric Dumazet struct rt6_mtu_change_arg { 26291da177e4SLinus Torvalds struct net_device *dev; 263095c96174SEric Dumazet unsigned int mtu; 26311da177e4SLinus Torvalds }; 26321da177e4SLinus Torvalds 26331da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 26341da177e4SLinus Torvalds { 26351da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 26361da177e4SLinus Torvalds struct inet6_dev *idev; 26371da177e4SLinus Torvalds 26381da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 26391da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 26401da177e4SLinus Torvalds We still use this lock to block changes 26411da177e4SLinus Torvalds caused by addrconf/ndisc. 26421da177e4SLinus Torvalds */ 26431da177e4SLinus Torvalds 26441da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 264538308473SDavid S. Miller if (!idev) 26461da177e4SLinus Torvalds return 0; 26471da177e4SLinus Torvalds 26481da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 26491da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 26501da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 26511da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 26521da177e4SLinus Torvalds */ 26531da177e4SLinus Torvalds /* 26541da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 26551da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 26561da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 26571da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 26581da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 26591da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 26601da177e4SLinus Torvalds PMTU discouvery. 26611da177e4SLinus Torvalds */ 2662d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 26634b32b5adSMartin KaFai Lau !dst_metric_locked(&rt->dst, RTAX_MTU)) { 26644b32b5adSMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) { 26654b32b5adSMartin KaFai Lau /* For RTF_CACHE with rt6i_pmtu == 0 26664b32b5adSMartin KaFai Lau * (i.e. a redirected route), 26674b32b5adSMartin KaFai Lau * the metrics of its rt->dst.from has already 26684b32b5adSMartin KaFai Lau * been updated. 26694b32b5adSMartin KaFai Lau */ 26704b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu) 26714b32b5adSMartin KaFai Lau rt->rt6i_pmtu = arg->mtu; 26724b32b5adSMartin KaFai Lau } else if (dst_mtu(&rt->dst) >= arg->mtu || 2673d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 26744b32b5adSMartin KaFai Lau dst_mtu(&rt->dst) == idev->cnf.mtu6)) { 2675defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2676566cfd8fSSimon Arlott } 26774b32b5adSMartin KaFai Lau } 26781da177e4SLinus Torvalds return 0; 26791da177e4SLinus Torvalds } 26801da177e4SLinus Torvalds 268195c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 26821da177e4SLinus Torvalds { 2683c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2684c71099acSThomas Graf .dev = dev, 2685c71099acSThomas Graf .mtu = mtu, 2686c71099acSThomas Graf }; 26871da177e4SLinus Torvalds 26880c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 26891da177e4SLinus Torvalds } 26901da177e4SLinus Torvalds 2691ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 26925176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 269386872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2694ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 269586872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 269686872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 269751ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2698c78ba6d6SLubomir Rintel [RTA_PREF] = { .type = NLA_U8 }, 269919e42e45SRoopa Prabhu [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, 270019e42e45SRoopa Prabhu [RTA_ENCAP] = { .type = NLA_NESTED }, 270186872cb5SThomas Graf }; 270286872cb5SThomas Graf 270386872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 270486872cb5SThomas Graf struct fib6_config *cfg) 27051da177e4SLinus Torvalds { 270686872cb5SThomas Graf struct rtmsg *rtm; 270786872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 2708c78ba6d6SLubomir Rintel unsigned int pref; 270986872cb5SThomas Graf int err; 27101da177e4SLinus Torvalds 271186872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 271286872cb5SThomas Graf if (err < 0) 271386872cb5SThomas Graf goto errout; 27141da177e4SLinus Torvalds 271586872cb5SThomas Graf err = -EINVAL; 271686872cb5SThomas Graf rtm = nlmsg_data(nlh); 271786872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 271886872cb5SThomas Graf 271986872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 272086872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 272186872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 272286872cb5SThomas Graf cfg->fc_flags = RTF_UP; 272386872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2724ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 272586872cb5SThomas Graf 2726ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2727ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2728b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2729b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 273086872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 273186872cb5SThomas Graf 2732ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2733ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2734ab79ad14SMaciej Żenczykowski 27351f56a01fSMartin KaFai Lau if (rtm->rtm_flags & RTM_F_CLONED) 27361f56a01fSMartin KaFai Lau cfg->fc_flags |= RTF_CACHE; 27371f56a01fSMartin KaFai Lau 273815e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 273986872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 27403b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 274186872cb5SThomas Graf 274286872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 274367b61f6cSJiri Benc cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 274486872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 27451da177e4SLinus Torvalds } 274686872cb5SThomas Graf 274786872cb5SThomas Graf if (tb[RTA_DST]) { 274886872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 274986872cb5SThomas Graf 275086872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 275186872cb5SThomas Graf goto errout; 275286872cb5SThomas Graf 275386872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 27541da177e4SLinus Torvalds } 275586872cb5SThomas Graf 275686872cb5SThomas Graf if (tb[RTA_SRC]) { 275786872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 275886872cb5SThomas Graf 275986872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 276086872cb5SThomas Graf goto errout; 276186872cb5SThomas Graf 276286872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 27631da177e4SLinus Torvalds } 276486872cb5SThomas Graf 2765c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 276667b61f6cSJiri Benc cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 2767c3968a85SDaniel Walter 276886872cb5SThomas Graf if (tb[RTA_OIF]) 276986872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 277086872cb5SThomas Graf 277186872cb5SThomas Graf if (tb[RTA_PRIORITY]) 277286872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 277386872cb5SThomas Graf 277486872cb5SThomas Graf if (tb[RTA_METRICS]) { 277586872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 277686872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 27771da177e4SLinus Torvalds } 277886872cb5SThomas Graf 277986872cb5SThomas Graf if (tb[RTA_TABLE]) 278086872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 278186872cb5SThomas Graf 278251ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 278351ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 278451ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 278551ebd318SNicolas Dichtel } 278651ebd318SNicolas Dichtel 2787c78ba6d6SLubomir Rintel if (tb[RTA_PREF]) { 2788c78ba6d6SLubomir Rintel pref = nla_get_u8(tb[RTA_PREF]); 2789c78ba6d6SLubomir Rintel if (pref != ICMPV6_ROUTER_PREF_LOW && 2790c78ba6d6SLubomir Rintel pref != ICMPV6_ROUTER_PREF_HIGH) 2791c78ba6d6SLubomir Rintel pref = ICMPV6_ROUTER_PREF_MEDIUM; 2792c78ba6d6SLubomir Rintel cfg->fc_flags |= RTF_PREF(pref); 2793c78ba6d6SLubomir Rintel } 2794c78ba6d6SLubomir Rintel 279519e42e45SRoopa Prabhu if (tb[RTA_ENCAP]) 279619e42e45SRoopa Prabhu cfg->fc_encap = tb[RTA_ENCAP]; 279719e42e45SRoopa Prabhu 279819e42e45SRoopa Prabhu if (tb[RTA_ENCAP_TYPE]) 279919e42e45SRoopa Prabhu cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]); 280019e42e45SRoopa Prabhu 280186872cb5SThomas Graf err = 0; 280286872cb5SThomas Graf errout: 280386872cb5SThomas Graf return err; 28041da177e4SLinus Torvalds } 28051da177e4SLinus Torvalds 28066b9ea5a6SRoopa Prabhu struct rt6_nh { 28076b9ea5a6SRoopa Prabhu struct rt6_info *rt6_info; 28086b9ea5a6SRoopa Prabhu struct fib6_config r_cfg; 28096b9ea5a6SRoopa Prabhu struct mx6_config mxc; 28106b9ea5a6SRoopa Prabhu struct list_head next; 28116b9ea5a6SRoopa Prabhu }; 28126b9ea5a6SRoopa Prabhu 28136b9ea5a6SRoopa Prabhu static void ip6_print_replace_route_err(struct list_head *rt6_nh_list) 28146b9ea5a6SRoopa Prabhu { 28156b9ea5a6SRoopa Prabhu struct rt6_nh *nh; 28166b9ea5a6SRoopa Prabhu 28176b9ea5a6SRoopa Prabhu list_for_each_entry(nh, rt6_nh_list, next) { 28186b9ea5a6SRoopa Prabhu pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6 nexthop %pI6 ifi %d\n", 28196b9ea5a6SRoopa Prabhu &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway, 28206b9ea5a6SRoopa Prabhu nh->r_cfg.fc_ifindex); 28216b9ea5a6SRoopa Prabhu } 28226b9ea5a6SRoopa Prabhu } 28236b9ea5a6SRoopa Prabhu 28246b9ea5a6SRoopa Prabhu static int ip6_route_info_append(struct list_head *rt6_nh_list, 28256b9ea5a6SRoopa Prabhu struct rt6_info *rt, struct fib6_config *r_cfg) 28266b9ea5a6SRoopa Prabhu { 28276b9ea5a6SRoopa Prabhu struct rt6_nh *nh; 28286b9ea5a6SRoopa Prabhu struct rt6_info *rtnh; 28296b9ea5a6SRoopa Prabhu int err = -EEXIST; 28306b9ea5a6SRoopa Prabhu 28316b9ea5a6SRoopa Prabhu list_for_each_entry(nh, rt6_nh_list, next) { 28326b9ea5a6SRoopa Prabhu /* check if rt6_info already exists */ 28336b9ea5a6SRoopa Prabhu rtnh = nh->rt6_info; 28346b9ea5a6SRoopa Prabhu 28356b9ea5a6SRoopa Prabhu if (rtnh->dst.dev == rt->dst.dev && 28366b9ea5a6SRoopa Prabhu rtnh->rt6i_idev == rt->rt6i_idev && 28376b9ea5a6SRoopa Prabhu ipv6_addr_equal(&rtnh->rt6i_gateway, 28386b9ea5a6SRoopa Prabhu &rt->rt6i_gateway)) 28396b9ea5a6SRoopa Prabhu return err; 28406b9ea5a6SRoopa Prabhu } 28416b9ea5a6SRoopa Prabhu 28426b9ea5a6SRoopa Prabhu nh = kzalloc(sizeof(*nh), GFP_KERNEL); 28436b9ea5a6SRoopa Prabhu if (!nh) 28446b9ea5a6SRoopa Prabhu return -ENOMEM; 28456b9ea5a6SRoopa Prabhu nh->rt6_info = rt; 28466b9ea5a6SRoopa Prabhu err = ip6_convert_metrics(&nh->mxc, r_cfg); 28476b9ea5a6SRoopa Prabhu if (err) { 28486b9ea5a6SRoopa Prabhu kfree(nh); 28496b9ea5a6SRoopa Prabhu return err; 28506b9ea5a6SRoopa Prabhu } 28516b9ea5a6SRoopa Prabhu memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg)); 28526b9ea5a6SRoopa Prabhu list_add_tail(&nh->next, rt6_nh_list); 28536b9ea5a6SRoopa Prabhu 28546b9ea5a6SRoopa Prabhu return 0; 28556b9ea5a6SRoopa Prabhu } 28566b9ea5a6SRoopa Prabhu 28576b9ea5a6SRoopa Prabhu static int ip6_route_multipath_add(struct fib6_config *cfg) 285851ebd318SNicolas Dichtel { 285951ebd318SNicolas Dichtel struct fib6_config r_cfg; 286051ebd318SNicolas Dichtel struct rtnexthop *rtnh; 28616b9ea5a6SRoopa Prabhu struct rt6_info *rt; 28626b9ea5a6SRoopa Prabhu struct rt6_nh *err_nh; 28636b9ea5a6SRoopa Prabhu struct rt6_nh *nh, *nh_safe; 286451ebd318SNicolas Dichtel int remaining; 286551ebd318SNicolas Dichtel int attrlen; 28666b9ea5a6SRoopa Prabhu int err = 1; 28676b9ea5a6SRoopa Prabhu int nhn = 0; 28686b9ea5a6SRoopa Prabhu int replace = (cfg->fc_nlinfo.nlh && 28696b9ea5a6SRoopa Prabhu (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); 28706b9ea5a6SRoopa Prabhu LIST_HEAD(rt6_nh_list); 287151ebd318SNicolas Dichtel 287235f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len; 287351ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 287451ebd318SNicolas Dichtel 28756b9ea5a6SRoopa Prabhu /* Parse a Multipath Entry and build a list (rt6_nh_list) of 28766b9ea5a6SRoopa Prabhu * rt6_info structs per nexthop 28776b9ea5a6SRoopa Prabhu */ 287851ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 287951ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 288051ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 288151ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 288251ebd318SNicolas Dichtel 288351ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 288451ebd318SNicolas Dichtel if (attrlen > 0) { 288551ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 288651ebd318SNicolas Dichtel 288751ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 288851ebd318SNicolas Dichtel if (nla) { 288967b61f6cSJiri Benc r_cfg.fc_gateway = nla_get_in6_addr(nla); 289051ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 289151ebd318SNicolas Dichtel } 289219e42e45SRoopa Prabhu r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 289319e42e45SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 289419e42e45SRoopa Prabhu if (nla) 289519e42e45SRoopa Prabhu r_cfg.fc_encap_type = nla_get_u16(nla); 289651ebd318SNicolas Dichtel } 28976b9ea5a6SRoopa Prabhu 28986b9ea5a6SRoopa Prabhu err = ip6_route_info_create(&r_cfg, &rt); 28996b9ea5a6SRoopa Prabhu if (err) 29006b9ea5a6SRoopa Prabhu goto cleanup; 29016b9ea5a6SRoopa Prabhu 29026b9ea5a6SRoopa Prabhu err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg); 290351ebd318SNicolas Dichtel if (err) { 29046b9ea5a6SRoopa Prabhu dst_free(&rt->dst); 29056b9ea5a6SRoopa Prabhu goto cleanup; 290651ebd318SNicolas Dichtel } 29076b9ea5a6SRoopa Prabhu 29086b9ea5a6SRoopa Prabhu rtnh = rtnh_next(rtnh, &remaining); 290951ebd318SNicolas Dichtel } 29106b9ea5a6SRoopa Prabhu 29116b9ea5a6SRoopa Prabhu err_nh = NULL; 29126b9ea5a6SRoopa Prabhu list_for_each_entry(nh, &rt6_nh_list, next) { 29136b9ea5a6SRoopa Prabhu err = __ip6_ins_rt(nh->rt6_info, &cfg->fc_nlinfo, &nh->mxc); 29146b9ea5a6SRoopa Prabhu /* nh->rt6_info is used or freed at this point, reset to NULL*/ 29156b9ea5a6SRoopa Prabhu nh->rt6_info = NULL; 29166b9ea5a6SRoopa Prabhu if (err) { 29176b9ea5a6SRoopa Prabhu if (replace && nhn) 29186b9ea5a6SRoopa Prabhu ip6_print_replace_route_err(&rt6_nh_list); 29196b9ea5a6SRoopa Prabhu err_nh = nh; 29206b9ea5a6SRoopa Prabhu goto add_errout; 29216b9ea5a6SRoopa Prabhu } 29226b9ea5a6SRoopa Prabhu 29231a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 292427596472SMichal Kubeček * these flags after the first nexthop: if there is a collision, 292527596472SMichal Kubeček * we have already failed to add the first nexthop: 292627596472SMichal Kubeček * fib6_add_rt2node() has rejected it; when replacing, old 292727596472SMichal Kubeček * nexthops have been replaced by first new, the rest should 292827596472SMichal Kubeček * be added to it. 29291a72418bSNicolas Dichtel */ 293027596472SMichal Kubeček cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | 293127596472SMichal Kubeček NLM_F_REPLACE); 29326b9ea5a6SRoopa Prabhu nhn++; 29336b9ea5a6SRoopa Prabhu } 29346b9ea5a6SRoopa Prabhu 29356b9ea5a6SRoopa Prabhu goto cleanup; 29366b9ea5a6SRoopa Prabhu 29376b9ea5a6SRoopa Prabhu add_errout: 29386b9ea5a6SRoopa Prabhu /* Delete routes that were already added */ 29396b9ea5a6SRoopa Prabhu list_for_each_entry(nh, &rt6_nh_list, next) { 29406b9ea5a6SRoopa Prabhu if (err_nh == nh) 29416b9ea5a6SRoopa Prabhu break; 29426b9ea5a6SRoopa Prabhu ip6_route_del(&nh->r_cfg); 29436b9ea5a6SRoopa Prabhu } 29446b9ea5a6SRoopa Prabhu 29456b9ea5a6SRoopa Prabhu cleanup: 29466b9ea5a6SRoopa Prabhu list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) { 29476b9ea5a6SRoopa Prabhu if (nh->rt6_info) 29486b9ea5a6SRoopa Prabhu dst_free(&nh->rt6_info->dst); 29496b9ea5a6SRoopa Prabhu kfree(nh->mxc.mx); 29506b9ea5a6SRoopa Prabhu list_del(&nh->next); 29516b9ea5a6SRoopa Prabhu kfree(nh); 29526b9ea5a6SRoopa Prabhu } 29536b9ea5a6SRoopa Prabhu 29546b9ea5a6SRoopa Prabhu return err; 29556b9ea5a6SRoopa Prabhu } 29566b9ea5a6SRoopa Prabhu 29576b9ea5a6SRoopa Prabhu static int ip6_route_multipath_del(struct fib6_config *cfg) 29586b9ea5a6SRoopa Prabhu { 29596b9ea5a6SRoopa Prabhu struct fib6_config r_cfg; 29606b9ea5a6SRoopa Prabhu struct rtnexthop *rtnh; 29616b9ea5a6SRoopa Prabhu int remaining; 29626b9ea5a6SRoopa Prabhu int attrlen; 29636b9ea5a6SRoopa Prabhu int err = 1, last_err = 0; 29646b9ea5a6SRoopa Prabhu 29656b9ea5a6SRoopa Prabhu remaining = cfg->fc_mp_len; 29666b9ea5a6SRoopa Prabhu rtnh = (struct rtnexthop *)cfg->fc_mp; 29676b9ea5a6SRoopa Prabhu 29686b9ea5a6SRoopa Prabhu /* Parse a Multipath Entry */ 29696b9ea5a6SRoopa Prabhu while (rtnh_ok(rtnh, remaining)) { 29706b9ea5a6SRoopa Prabhu memcpy(&r_cfg, cfg, sizeof(*cfg)); 29716b9ea5a6SRoopa Prabhu if (rtnh->rtnh_ifindex) 29726b9ea5a6SRoopa Prabhu r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 29736b9ea5a6SRoopa Prabhu 29746b9ea5a6SRoopa Prabhu attrlen = rtnh_attrlen(rtnh); 29756b9ea5a6SRoopa Prabhu if (attrlen > 0) { 29766b9ea5a6SRoopa Prabhu struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 29776b9ea5a6SRoopa Prabhu 29786b9ea5a6SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_GATEWAY); 29796b9ea5a6SRoopa Prabhu if (nla) { 29806b9ea5a6SRoopa Prabhu nla_memcpy(&r_cfg.fc_gateway, nla, 16); 29816b9ea5a6SRoopa Prabhu r_cfg.fc_flags |= RTF_GATEWAY; 29826b9ea5a6SRoopa Prabhu } 29836b9ea5a6SRoopa Prabhu } 29846b9ea5a6SRoopa Prabhu err = ip6_route_del(&r_cfg); 29856b9ea5a6SRoopa Prabhu if (err) 29866b9ea5a6SRoopa Prabhu last_err = err; 29876b9ea5a6SRoopa Prabhu 298851ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 298951ebd318SNicolas Dichtel } 299051ebd318SNicolas Dichtel 299151ebd318SNicolas Dichtel return last_err; 299251ebd318SNicolas Dichtel } 299351ebd318SNicolas Dichtel 2994661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) 29951da177e4SLinus Torvalds { 299686872cb5SThomas Graf struct fib6_config cfg; 299786872cb5SThomas Graf int err; 29981da177e4SLinus Torvalds 299986872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 300086872cb5SThomas Graf if (err < 0) 300186872cb5SThomas Graf return err; 300286872cb5SThomas Graf 300351ebd318SNicolas Dichtel if (cfg.fc_mp) 30046b9ea5a6SRoopa Prabhu return ip6_route_multipath_del(&cfg); 300551ebd318SNicolas Dichtel else 300686872cb5SThomas Graf return ip6_route_del(&cfg); 30071da177e4SLinus Torvalds } 30081da177e4SLinus Torvalds 3009661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) 30101da177e4SLinus Torvalds { 301186872cb5SThomas Graf struct fib6_config cfg; 301286872cb5SThomas Graf int err; 30131da177e4SLinus Torvalds 301486872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 301586872cb5SThomas Graf if (err < 0) 301686872cb5SThomas Graf return err; 301786872cb5SThomas Graf 301851ebd318SNicolas Dichtel if (cfg.fc_mp) 30196b9ea5a6SRoopa Prabhu return ip6_route_multipath_add(&cfg); 302051ebd318SNicolas Dichtel else 302186872cb5SThomas Graf return ip6_route_add(&cfg); 30221da177e4SLinus Torvalds } 30231da177e4SLinus Torvalds 302419e42e45SRoopa Prabhu static inline size_t rt6_nlmsg_size(struct rt6_info *rt) 3025339bf98fSThomas Graf { 3026339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 3027339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 3028339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 3029339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 3030339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 3031339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 3032339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 3033339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 3034339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 30356a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 3036ea697639SDaniel Borkmann + nla_total_size(sizeof(struct rta_cacheinfo)) 3037c78ba6d6SLubomir Rintel + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 303819e42e45SRoopa Prabhu + nla_total_size(1) /* RTA_PREF */ 303961adedf3SJiri Benc + lwtunnel_get_encap_size(rt->dst.lwtstate); 3040339bf98fSThomas Graf } 3041339bf98fSThomas Graf 3042191cd582SBrian Haley static int rt6_fill_node(struct net *net, 3043191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 30440d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 304515e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 30467bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 30471da177e4SLinus Torvalds { 30484b32b5adSMartin KaFai Lau u32 metrics[RTAX_MAX]; 30491da177e4SLinus Torvalds struct rtmsg *rtm; 30501da177e4SLinus Torvalds struct nlmsghdr *nlh; 3051e3703b3dSThomas Graf long expires; 30529e762a4aSPatrick McHardy u32 table; 30531da177e4SLinus Torvalds 30541da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 30551da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 30561da177e4SLinus Torvalds /* success since this is not a prefix route */ 30571da177e4SLinus Torvalds return 1; 30581da177e4SLinus Torvalds } 30591da177e4SLinus Torvalds } 30601da177e4SLinus Torvalds 306115e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 306238308473SDavid S. Miller if (!nlh) 306326932566SPatrick McHardy return -EMSGSIZE; 30642d7202bfSThomas Graf 30652d7202bfSThomas Graf rtm = nlmsg_data(nlh); 30661da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 30671da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 30681da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 30691da177e4SLinus Torvalds rtm->rtm_tos = 0; 3070c71099acSThomas Graf if (rt->rt6i_table) 30719e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 3072c71099acSThomas Graf else 30739e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 30749e762a4aSPatrick McHardy rtm->rtm_table = table; 3075c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 3076c78679e8SDavid S. Miller goto nla_put_failure; 3077ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 3078ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 3079ef2c7d7bSNicolas Dichtel case -EINVAL: 3080ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 3081ef2c7d7bSNicolas Dichtel break; 3082ef2c7d7bSNicolas Dichtel case -EACCES: 3083ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 3084ef2c7d7bSNicolas Dichtel break; 3085b4949ab2SNicolas Dichtel case -EAGAIN: 3086b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 3087b4949ab2SNicolas Dichtel break; 3088ef2c7d7bSNicolas Dichtel default: 30891da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 3090ef2c7d7bSNicolas Dichtel break; 3091ef2c7d7bSNicolas Dichtel } 3092ef2c7d7bSNicolas Dichtel } 3093ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 3094ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 3095d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 30961da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 30971da177e4SLinus Torvalds else 30981da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 30991da177e4SLinus Torvalds rtm->rtm_flags = 0; 310035103d11SAndy Gospodarek if (!netif_carrier_ok(rt->dst.dev)) { 3101cea45e20SAndy Gospodarek rtm->rtm_flags |= RTNH_F_LINKDOWN; 310235103d11SAndy Gospodarek if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown) 310335103d11SAndy Gospodarek rtm->rtm_flags |= RTNH_F_DEAD; 310435103d11SAndy Gospodarek } 31051da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 31061da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 31071da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 31081da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 3109f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 3110f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 31111da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 3112f0396f60SDenis Ovsienko else 3113f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 3114f0396f60SDenis Ovsienko } 31151da177e4SLinus Torvalds 31161da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 31171da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 31181da177e4SLinus Torvalds 31191da177e4SLinus Torvalds if (dst) { 3120930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, dst)) 3121c78679e8SDavid S. Miller goto nla_put_failure; 31221da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 31231da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 3124930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr)) 3125c78679e8SDavid S. Miller goto nla_put_failure; 31261da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 31271da177e4SLinus Torvalds if (src) { 3128930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_SRC, src)) 3129c78679e8SDavid S. Miller goto nla_put_failure; 31301da177e4SLinus Torvalds rtm->rtm_src_len = 128; 3131c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 3132930345eaSJiri Benc nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr)) 3133c78679e8SDavid S. Miller goto nla_put_failure; 31341da177e4SLinus Torvalds #endif 31357bc570c8SYOSHIFUJI Hideaki if (iif) { 31367bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 31377bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 31388229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 31397bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 31407bc570c8SYOSHIFUJI Hideaki if (!nowait) { 31417bc570c8SYOSHIFUJI Hideaki if (err == 0) 31427bc570c8SYOSHIFUJI Hideaki return 0; 31437bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 31447bc570c8SYOSHIFUJI Hideaki } else { 31457bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 31467bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 31477bc570c8SYOSHIFUJI Hideaki } 31487bc570c8SYOSHIFUJI Hideaki } 31497bc570c8SYOSHIFUJI Hideaki } else 31507bc570c8SYOSHIFUJI Hideaki #endif 3151c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 3152c78679e8SDavid S. Miller goto nla_put_failure; 31537bc570c8SYOSHIFUJI Hideaki } else if (dst) { 31541da177e4SLinus Torvalds struct in6_addr saddr_buf; 3155c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 3156930345eaSJiri Benc nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 3157c78679e8SDavid S. Miller goto nla_put_failure; 3158c3968a85SDaniel Walter } 3159c3968a85SDaniel Walter 3160c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 3161c3968a85SDaniel Walter struct in6_addr saddr_buf; 31624e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 3163930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 3164c78679e8SDavid S. Miller goto nla_put_failure; 31651da177e4SLinus Torvalds } 31662d7202bfSThomas Graf 31674b32b5adSMartin KaFai Lau memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); 31684b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu) 31694b32b5adSMartin KaFai Lau metrics[RTAX_MTU - 1] = rt->rt6i_pmtu; 31704b32b5adSMartin KaFai Lau if (rtnetlink_put_metrics(skb, metrics) < 0) 31712d7202bfSThomas Graf goto nla_put_failure; 31722d7202bfSThomas Graf 3173dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_GATEWAY) { 3174930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0) 317594f826b8SEric Dumazet goto nla_put_failure; 317694f826b8SEric Dumazet } 31772d7202bfSThomas Graf 3178c78679e8SDavid S. Miller if (rt->dst.dev && 3179c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 3180c78679e8SDavid S. Miller goto nla_put_failure; 3181c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 3182c78679e8SDavid S. Miller goto nla_put_failure; 31838253947eSLi Wei 31848253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 318569cdf8f9SYOSHIFUJI Hideaki 318687a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 3187e3703b3dSThomas Graf goto nla_put_failure; 31881da177e4SLinus Torvalds 3189c78ba6d6SLubomir Rintel if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) 3190c78ba6d6SLubomir Rintel goto nla_put_failure; 3191c78ba6d6SLubomir Rintel 319261adedf3SJiri Benc lwtunnel_fill_encap(skb, rt->dst.lwtstate); 319319e42e45SRoopa Prabhu 3194053c095aSJohannes Berg nlmsg_end(skb, nlh); 3195053c095aSJohannes Berg return 0; 31962d7202bfSThomas Graf 31972d7202bfSThomas Graf nla_put_failure: 319826932566SPatrick McHardy nlmsg_cancel(skb, nlh); 319926932566SPatrick McHardy return -EMSGSIZE; 32001da177e4SLinus Torvalds } 32011da177e4SLinus Torvalds 32021b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 32031da177e4SLinus Torvalds { 32041da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 32051da177e4SLinus Torvalds int prefix; 32061da177e4SLinus Torvalds 32072d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 32082d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 32091da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 32101da177e4SLinus Torvalds } else 32111da177e4SLinus Torvalds prefix = 0; 32121da177e4SLinus Torvalds 3213191cd582SBrian Haley return rt6_fill_node(arg->net, 3214191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 321515e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 32167bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 32171da177e4SLinus Torvalds } 32181da177e4SLinus Torvalds 3219661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) 32201da177e4SLinus Torvalds { 32213b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 3222ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 32231da177e4SLinus Torvalds struct rt6_info *rt; 3224ab364a6fSThomas Graf struct sk_buff *skb; 3225ab364a6fSThomas Graf struct rtmsg *rtm; 32264c9483b2SDavid S. Miller struct flowi6 fl6; 322772331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 3228ab364a6fSThomas Graf 3229ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 3230ab364a6fSThomas Graf if (err < 0) 3231ab364a6fSThomas Graf goto errout; 3232ab364a6fSThomas Graf 3233ab364a6fSThomas Graf err = -EINVAL; 32344c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 3235ab364a6fSThomas Graf 3236ab364a6fSThomas Graf if (tb[RTA_SRC]) { 3237ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 3238ab364a6fSThomas Graf goto errout; 3239ab364a6fSThomas Graf 32404e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 3241ab364a6fSThomas Graf } 3242ab364a6fSThomas Graf 3243ab364a6fSThomas Graf if (tb[RTA_DST]) { 3244ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 3245ab364a6fSThomas Graf goto errout; 3246ab364a6fSThomas Graf 32474e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 3248ab364a6fSThomas Graf } 3249ab364a6fSThomas Graf 3250ab364a6fSThomas Graf if (tb[RTA_IIF]) 3251ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 3252ab364a6fSThomas Graf 3253ab364a6fSThomas Graf if (tb[RTA_OIF]) 325472331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 3255ab364a6fSThomas Graf 32562e47b291SLorenzo Colitti if (tb[RTA_MARK]) 32572e47b291SLorenzo Colitti fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 32582e47b291SLorenzo Colitti 3259ab364a6fSThomas Graf if (iif) { 3260ab364a6fSThomas Graf struct net_device *dev; 326172331bc0SShmulik Ladkani int flags = 0; 326272331bc0SShmulik Ladkani 32635578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 3264ab364a6fSThomas Graf if (!dev) { 3265ab364a6fSThomas Graf err = -ENODEV; 3266ab364a6fSThomas Graf goto errout; 3267ab364a6fSThomas Graf } 326872331bc0SShmulik Ladkani 326972331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 327072331bc0SShmulik Ladkani 327172331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 327272331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 327372331bc0SShmulik Ladkani 327472331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 327572331bc0SShmulik Ladkani flags); 327672331bc0SShmulik Ladkani } else { 327772331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 327872331bc0SShmulik Ladkani 327972331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 3280ab364a6fSThomas Graf } 32811da177e4SLinus Torvalds 32821da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 328338308473SDavid S. Miller if (!skb) { 328494e187c0SAmerigo Wang ip6_rt_put(rt); 3285ab364a6fSThomas Graf err = -ENOBUFS; 3286ab364a6fSThomas Graf goto errout; 3287ab364a6fSThomas Graf } 32881da177e4SLinus Torvalds 32891da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 32901da177e4SLinus Torvalds through good chunk of routing engine. 32911da177e4SLinus Torvalds */ 3292459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 32931da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 32941da177e4SLinus Torvalds 3295d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 32961da177e4SLinus Torvalds 32974c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 329815e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 32997bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 33001da177e4SLinus Torvalds if (err < 0) { 3301ab364a6fSThomas Graf kfree_skb(skb); 3302ab364a6fSThomas Graf goto errout; 33031da177e4SLinus Torvalds } 33041da177e4SLinus Torvalds 330515e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 3306ab364a6fSThomas Graf errout: 33071da177e4SLinus Torvalds return err; 33081da177e4SLinus Torvalds } 33091da177e4SLinus Torvalds 331037a1d361SRoopa Prabhu void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info, 331137a1d361SRoopa Prabhu unsigned int nlm_flags) 33121da177e4SLinus Torvalds { 33131da177e4SLinus Torvalds struct sk_buff *skb; 33145578689aSDaniel Lezcano struct net *net = info->nl_net; 3315528c4cebSDenis V. Lunev u32 seq; 3316528c4cebSDenis V. Lunev int err; 33170d51aa80SJamal Hadi Salim 3318528c4cebSDenis V. Lunev err = -ENOBUFS; 331938308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 332086872cb5SThomas Graf 332119e42e45SRoopa Prabhu skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 332238308473SDavid S. Miller if (!skb) 332321713ebcSThomas Graf goto errout; 33241da177e4SLinus Torvalds 3325191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 332637a1d361SRoopa Prabhu event, info->portid, seq, 0, 0, nlm_flags); 332726932566SPatrick McHardy if (err < 0) { 332826932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 332926932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 333026932566SPatrick McHardy kfree_skb(skb); 333126932566SPatrick McHardy goto errout; 333226932566SPatrick McHardy } 333315e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 33345578689aSDaniel Lezcano info->nlh, gfp_any()); 33351ce85fe4SPablo Neira Ayuso return; 333621713ebcSThomas Graf errout: 333721713ebcSThomas Graf if (err < 0) 33385578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 33391da177e4SLinus Torvalds } 33401da177e4SLinus Torvalds 33418ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 3342351638e7SJiri Pirko unsigned long event, void *ptr) 33438ed67789SDaniel Lezcano { 3344351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 3345c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 33468ed67789SDaniel Lezcano 33478ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 3348d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 33498ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 33508ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3351d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 33528ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 3353d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 33548ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 33558ed67789SDaniel Lezcano #endif 33568ed67789SDaniel Lezcano } 33578ed67789SDaniel Lezcano 33588ed67789SDaniel Lezcano return NOTIFY_OK; 33598ed67789SDaniel Lezcano } 33608ed67789SDaniel Lezcano 33611da177e4SLinus Torvalds /* 33621da177e4SLinus Torvalds * /proc 33631da177e4SLinus Torvalds */ 33641da177e4SLinus Torvalds 33651da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 33661da177e4SLinus Torvalds 336733120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 336833120b30SAlexey Dobriyan .owner = THIS_MODULE, 336933120b30SAlexey Dobriyan .open = ipv6_route_open, 337033120b30SAlexey Dobriyan .read = seq_read, 337133120b30SAlexey Dobriyan .llseek = seq_lseek, 33728d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 337333120b30SAlexey Dobriyan }; 337433120b30SAlexey Dobriyan 33751da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 33761da177e4SLinus Torvalds { 337769ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 33781da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 337969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 338069ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 338169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 338269ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 338369ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 3384fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 338569ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 33861da177e4SLinus Torvalds 33871da177e4SLinus Torvalds return 0; 33881da177e4SLinus Torvalds } 33891da177e4SLinus Torvalds 33901da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 33911da177e4SLinus Torvalds { 3392de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 339369ddb805SDaniel Lezcano } 339469ddb805SDaniel Lezcano 33959a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 33961da177e4SLinus Torvalds .owner = THIS_MODULE, 33971da177e4SLinus Torvalds .open = rt6_stats_seq_open, 33981da177e4SLinus Torvalds .read = seq_read, 33991da177e4SLinus Torvalds .llseek = seq_lseek, 3400b6fcbdb4SPavel Emelyanov .release = single_release_net, 34011da177e4SLinus Torvalds }; 34021da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 34031da177e4SLinus Torvalds 34041da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 34051da177e4SLinus Torvalds 34061da177e4SLinus Torvalds static 3407fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 34081da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 34091da177e4SLinus Torvalds { 3410c486da34SLucian Adrian Grijincu struct net *net; 3411c486da34SLucian Adrian Grijincu int delay; 3412c486da34SLucian Adrian Grijincu if (!write) 3413c486da34SLucian Adrian Grijincu return -EINVAL; 3414c486da34SLucian Adrian Grijincu 3415c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 3416c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 34178d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 34182ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 34191da177e4SLinus Torvalds return 0; 34201da177e4SLinus Torvalds } 34211da177e4SLinus Torvalds 3422fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 34231da177e4SLinus Torvalds { 34241da177e4SLinus Torvalds .procname = "flush", 34254990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 34261da177e4SLinus Torvalds .maxlen = sizeof(int), 342789c8b3a1SDave Jones .mode = 0200, 34286d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 34291da177e4SLinus Torvalds }, 34301da177e4SLinus Torvalds { 34311da177e4SLinus Torvalds .procname = "gc_thresh", 34329a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 34331da177e4SLinus Torvalds .maxlen = sizeof(int), 34341da177e4SLinus Torvalds .mode = 0644, 34356d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 34361da177e4SLinus Torvalds }, 34371da177e4SLinus Torvalds { 34381da177e4SLinus Torvalds .procname = "max_size", 34394990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 34401da177e4SLinus Torvalds .maxlen = sizeof(int), 34411da177e4SLinus Torvalds .mode = 0644, 34426d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 34431da177e4SLinus Torvalds }, 34441da177e4SLinus Torvalds { 34451da177e4SLinus Torvalds .procname = "gc_min_interval", 34464990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 34471da177e4SLinus Torvalds .maxlen = sizeof(int), 34481da177e4SLinus Torvalds .mode = 0644, 34496d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 34501da177e4SLinus Torvalds }, 34511da177e4SLinus Torvalds { 34521da177e4SLinus Torvalds .procname = "gc_timeout", 34534990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 34541da177e4SLinus Torvalds .maxlen = sizeof(int), 34551da177e4SLinus Torvalds .mode = 0644, 34566d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 34571da177e4SLinus Torvalds }, 34581da177e4SLinus Torvalds { 34591da177e4SLinus Torvalds .procname = "gc_interval", 34604990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 34611da177e4SLinus Torvalds .maxlen = sizeof(int), 34621da177e4SLinus Torvalds .mode = 0644, 34636d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 34641da177e4SLinus Torvalds }, 34651da177e4SLinus Torvalds { 34661da177e4SLinus Torvalds .procname = "gc_elasticity", 34674990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 34681da177e4SLinus Torvalds .maxlen = sizeof(int), 34691da177e4SLinus Torvalds .mode = 0644, 3470f3d3f616SMin Zhang .proc_handler = proc_dointvec, 34711da177e4SLinus Torvalds }, 34721da177e4SLinus Torvalds { 34731da177e4SLinus Torvalds .procname = "mtu_expires", 34744990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 34751da177e4SLinus Torvalds .maxlen = sizeof(int), 34761da177e4SLinus Torvalds .mode = 0644, 34776d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 34781da177e4SLinus Torvalds }, 34791da177e4SLinus Torvalds { 34801da177e4SLinus Torvalds .procname = "min_adv_mss", 34814990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 34821da177e4SLinus Torvalds .maxlen = sizeof(int), 34831da177e4SLinus Torvalds .mode = 0644, 3484f3d3f616SMin Zhang .proc_handler = proc_dointvec, 34851da177e4SLinus Torvalds }, 34861da177e4SLinus Torvalds { 34871da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 34884990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 34891da177e4SLinus Torvalds .maxlen = sizeof(int), 34901da177e4SLinus Torvalds .mode = 0644, 34916d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 34921da177e4SLinus Torvalds }, 3493f8572d8fSEric W. Biederman { } 34941da177e4SLinus Torvalds }; 34951da177e4SLinus Torvalds 34962c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 3497760f2d01SDaniel Lezcano { 3498760f2d01SDaniel Lezcano struct ctl_table *table; 3499760f2d01SDaniel Lezcano 3500760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 3501760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 3502760f2d01SDaniel Lezcano GFP_KERNEL); 35035ee09105SYOSHIFUJI Hideaki 35045ee09105SYOSHIFUJI Hideaki if (table) { 35055ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 3506c486da34SLucian Adrian Grijincu table[0].extra1 = net; 350786393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 35085ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 35095ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 35105ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 35115ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 35125ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 35135ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 35145ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 35159c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 3516464dc801SEric W. Biederman 3517464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 3518464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 3519464dc801SEric W. Biederman table[0].procname = NULL; 35205ee09105SYOSHIFUJI Hideaki } 35215ee09105SYOSHIFUJI Hideaki 3522760f2d01SDaniel Lezcano return table; 3523760f2d01SDaniel Lezcano } 35241da177e4SLinus Torvalds #endif 35251da177e4SLinus Torvalds 35262c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 3527cdb18761SDaniel Lezcano { 3528633d424bSPavel Emelyanov int ret = -ENOMEM; 35298ed67789SDaniel Lezcano 353086393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 353186393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 3532f2fc6a54SBenjamin Thery 3533fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 3534fc66f95cSEric Dumazet goto out_ip6_dst_ops; 3535fc66f95cSEric Dumazet 35368ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 35378ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 35388ed67789SDaniel Lezcano GFP_KERNEL); 35398ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 3540fc66f95cSEric Dumazet goto out_ip6_dst_entries; 3541d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 35428ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 3543d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 354462fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 354562fa8a84SDavid S. Miller ip6_template_metrics, true); 35468ed67789SDaniel Lezcano 35478ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 35488ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 35498ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 35508ed67789SDaniel Lezcano GFP_KERNEL); 355168fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 355268fffc67SPeter Zijlstra goto out_ip6_null_entry; 3553d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 35548ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 3555d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 355662fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 355762fa8a84SDavid S. Miller ip6_template_metrics, true); 35588ed67789SDaniel Lezcano 35598ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 35608ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 35618ed67789SDaniel Lezcano GFP_KERNEL); 356268fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 356368fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 3564d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 35658ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 3566d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 356762fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 356862fa8a84SDavid S. Miller ip6_template_metrics, true); 35698ed67789SDaniel Lezcano #endif 35708ed67789SDaniel Lezcano 3571b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 3572b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 3573b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 3574b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 3575b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 3576b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 3577b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 3578b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 3579b339a47cSPeter Zijlstra 35806891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 35816891a346SBenjamin Thery 35828ed67789SDaniel Lezcano ret = 0; 35838ed67789SDaniel Lezcano out: 35848ed67789SDaniel Lezcano return ret; 3585f2fc6a54SBenjamin Thery 358668fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 358768fffc67SPeter Zijlstra out_ip6_prohibit_entry: 358868fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 358968fffc67SPeter Zijlstra out_ip6_null_entry: 359068fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 359168fffc67SPeter Zijlstra #endif 3592fc66f95cSEric Dumazet out_ip6_dst_entries: 3593fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3594f2fc6a54SBenjamin Thery out_ip6_dst_ops: 3595f2fc6a54SBenjamin Thery goto out; 3596cdb18761SDaniel Lezcano } 3597cdb18761SDaniel Lezcano 35982c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 3599cdb18761SDaniel Lezcano { 36008ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 36018ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 36028ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 36038ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 36048ed67789SDaniel Lezcano #endif 360541bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3606cdb18761SDaniel Lezcano } 3607cdb18761SDaniel Lezcano 3608d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 3609d189634eSThomas Graf { 3610d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3611d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 3612d4beaa66SGao feng proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops); 3613d189634eSThomas Graf #endif 3614d189634eSThomas Graf return 0; 3615d189634eSThomas Graf } 3616d189634eSThomas Graf 3617d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 3618d189634eSThomas Graf { 3619d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3620ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 3621ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 3622d189634eSThomas Graf #endif 3623d189634eSThomas Graf } 3624d189634eSThomas Graf 3625cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 3626cdb18761SDaniel Lezcano .init = ip6_route_net_init, 3627cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 3628cdb18761SDaniel Lezcano }; 3629cdb18761SDaniel Lezcano 3630c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 3631c3426b47SDavid S. Miller { 3632c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 3633c3426b47SDavid S. Miller 3634c3426b47SDavid S. Miller if (!bp) 3635c3426b47SDavid S. Miller return -ENOMEM; 3636c3426b47SDavid S. Miller inet_peer_base_init(bp); 3637c3426b47SDavid S. Miller net->ipv6.peers = bp; 3638c3426b47SDavid S. Miller return 0; 3639c3426b47SDavid S. Miller } 3640c3426b47SDavid S. Miller 3641c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 3642c3426b47SDavid S. Miller { 3643c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 3644c3426b47SDavid S. Miller 3645c3426b47SDavid S. Miller net->ipv6.peers = NULL; 364656a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3647c3426b47SDavid S. Miller kfree(bp); 3648c3426b47SDavid S. Miller } 3649c3426b47SDavid S. Miller 36502b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3651c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3652c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3653c3426b47SDavid S. Miller }; 3654c3426b47SDavid S. Miller 3655d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3656d189634eSThomas Graf .init = ip6_route_net_init_late, 3657d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3658d189634eSThomas Graf }; 3659d189634eSThomas Graf 36608ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 36618ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 36628ed67789SDaniel Lezcano .priority = 0, 36638ed67789SDaniel Lezcano }; 36648ed67789SDaniel Lezcano 3665433d49c3SDaniel Lezcano int __init ip6_route_init(void) 36661da177e4SLinus Torvalds { 3667433d49c3SDaniel Lezcano int ret; 36688d0b94afSMartin KaFai Lau int cpu; 3669433d49c3SDaniel Lezcano 36709a7ec3a9SDaniel Lezcano ret = -ENOMEM; 36719a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 36729a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 36739a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 36749a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3675c19a28e1SFernando Carrijo goto out; 367614e50e57SDavid S. Miller 3677fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 36788ed67789SDaniel Lezcano if (ret) 3679bdb3289fSDaniel Lezcano goto out_kmem_cache; 3680bdb3289fSDaniel Lezcano 3681c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3682c3426b47SDavid S. Miller if (ret) 3683e8803b6cSDavid S. Miller goto out_dst_entries; 36842a0c451aSThomas Graf 36857e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 36867e52b33bSDavid S. Miller if (ret) 36877e52b33bSDavid S. Miller goto out_register_inetpeer; 3688c3426b47SDavid S. Miller 36895dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 36905dc121e9SArnaud Ebalard 36918ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 36928ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 36938ed67789SDaniel Lezcano * manually for init_net */ 3694d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 36958ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3696bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3697d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 36988ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3699d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 37008ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3701bdb3289fSDaniel Lezcano #endif 3702e8803b6cSDavid S. Miller ret = fib6_init(); 3703433d49c3SDaniel Lezcano if (ret) 37048ed67789SDaniel Lezcano goto out_register_subsys; 3705433d49c3SDaniel Lezcano 3706433d49c3SDaniel Lezcano ret = xfrm6_init(); 3707433d49c3SDaniel Lezcano if (ret) 3708e8803b6cSDavid S. Miller goto out_fib6_init; 3709c35b7e72SDaniel Lezcano 3710433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3711433d49c3SDaniel Lezcano if (ret) 3712433d49c3SDaniel Lezcano goto xfrm6_init; 37137e5449c2SDaniel Lezcano 3714d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3715d189634eSThomas Graf if (ret) 3716d189634eSThomas Graf goto fib6_rules_init; 3717d189634eSThomas Graf 3718433d49c3SDaniel Lezcano ret = -ENOBUFS; 3719c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3720c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3721c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3722d189634eSThomas Graf goto out_register_late_subsys; 3723433d49c3SDaniel Lezcano 37248ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3725cdb18761SDaniel Lezcano if (ret) 3726d189634eSThomas Graf goto out_register_late_subsys; 37278ed67789SDaniel Lezcano 37288d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 37298d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 37308d0b94afSMartin KaFai Lau 37318d0b94afSMartin KaFai Lau INIT_LIST_HEAD(&ul->head); 37328d0b94afSMartin KaFai Lau spin_lock_init(&ul->lock); 37338d0b94afSMartin KaFai Lau } 37348d0b94afSMartin KaFai Lau 3735433d49c3SDaniel Lezcano out: 3736433d49c3SDaniel Lezcano return ret; 3737433d49c3SDaniel Lezcano 3738d189634eSThomas Graf out_register_late_subsys: 3739d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3740433d49c3SDaniel Lezcano fib6_rules_init: 3741433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3742433d49c3SDaniel Lezcano xfrm6_init: 3743433d49c3SDaniel Lezcano xfrm6_fini(); 37442a0c451aSThomas Graf out_fib6_init: 37452a0c451aSThomas Graf fib6_gc_cleanup(); 37468ed67789SDaniel Lezcano out_register_subsys: 37478ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 37487e52b33bSDavid S. Miller out_register_inetpeer: 37497e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3750fc66f95cSEric Dumazet out_dst_entries: 3751fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3752433d49c3SDaniel Lezcano out_kmem_cache: 3753f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3754433d49c3SDaniel Lezcano goto out; 37551da177e4SLinus Torvalds } 37561da177e4SLinus Torvalds 37571da177e4SLinus Torvalds void ip6_route_cleanup(void) 37581da177e4SLinus Torvalds { 37598ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3760d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3761101367c2SThomas Graf fib6_rules_cleanup(); 37621da177e4SLinus Torvalds xfrm6_fini(); 37631da177e4SLinus Torvalds fib6_gc_cleanup(); 3764c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 37658ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 376641bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3767f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 37681da177e4SLinus Torvalds } 3769