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 { 424644d0e65STom Herbert return get_hash_from_flowi6(fl6) % candidate_count; 42551ebd318SNicolas Dichtel } 42651ebd318SNicolas Dichtel 42751ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match, 42852bd4c0cSNicolas Dichtel struct flowi6 *fl6, int oif, 42952bd4c0cSNicolas Dichtel int strict) 43051ebd318SNicolas Dichtel { 43151ebd318SNicolas Dichtel struct rt6_info *sibling, *next_sibling; 43251ebd318SNicolas Dichtel int route_choosen; 43351ebd318SNicolas Dichtel 43451ebd318SNicolas Dichtel route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6); 43551ebd318SNicolas Dichtel /* Don't change the route, if route_choosen == 0 43651ebd318SNicolas Dichtel * (siblings does not include ourself) 43751ebd318SNicolas Dichtel */ 43851ebd318SNicolas Dichtel if (route_choosen) 43951ebd318SNicolas Dichtel list_for_each_entry_safe(sibling, next_sibling, 44051ebd318SNicolas Dichtel &match->rt6i_siblings, rt6i_siblings) { 44151ebd318SNicolas Dichtel route_choosen--; 44251ebd318SNicolas Dichtel if (route_choosen == 0) { 44352bd4c0cSNicolas Dichtel if (rt6_score_route(sibling, oif, strict) < 0) 44452bd4c0cSNicolas Dichtel break; 44551ebd318SNicolas Dichtel match = sibling; 44651ebd318SNicolas Dichtel break; 44751ebd318SNicolas Dichtel } 44851ebd318SNicolas Dichtel } 44951ebd318SNicolas Dichtel return match; 45051ebd318SNicolas Dichtel } 45151ebd318SNicolas Dichtel 4521da177e4SLinus Torvalds /* 453c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 4541da177e4SLinus Torvalds */ 4551da177e4SLinus Torvalds 4568ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 4578ed67789SDaniel Lezcano struct rt6_info *rt, 458b71d1d42SEric Dumazet const struct in6_addr *saddr, 4591da177e4SLinus Torvalds int oif, 460d420895eSYOSHIFUJI Hideaki int flags) 4611da177e4SLinus Torvalds { 4621da177e4SLinus Torvalds struct rt6_info *local = NULL; 4631da177e4SLinus Torvalds struct rt6_info *sprt; 4641da177e4SLinus Torvalds 465dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 466dd3abc4eSYOSHIFUJI Hideaki goto out; 467dd3abc4eSYOSHIFUJI Hideaki 468d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 469d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 470dd3abc4eSYOSHIFUJI Hideaki 471dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4721da177e4SLinus Torvalds if (dev->ifindex == oif) 4731da177e4SLinus Torvalds return sprt; 4741da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 47538308473SDavid S. Miller if (!sprt->rt6i_idev || 4761da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 477*17fb0b2bSDavid Ahern if (flags & RT6_LOOKUP_F_IFACE) 4781da177e4SLinus Torvalds continue; 479*17fb0b2bSDavid Ahern if (local && 480*17fb0b2bSDavid Ahern local->rt6i_idev->dev->ifindex == oif) 4811da177e4SLinus Torvalds continue; 4821da177e4SLinus Torvalds } 4831da177e4SLinus Torvalds local = sprt; 4841da177e4SLinus Torvalds } 485dd3abc4eSYOSHIFUJI Hideaki } else { 486dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 487dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 488dd3abc4eSYOSHIFUJI Hideaki return sprt; 489dd3abc4eSYOSHIFUJI Hideaki } 4901da177e4SLinus Torvalds } 4911da177e4SLinus Torvalds 492dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4931da177e4SLinus Torvalds if (local) 4941da177e4SLinus Torvalds return local; 4951da177e4SLinus Torvalds 496d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 4978ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 4981da177e4SLinus Torvalds } 499dd3abc4eSYOSHIFUJI Hideaki out: 5001da177e4SLinus Torvalds return rt; 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds 50327097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 504c2f17e82SHannes Frederic Sowa struct __rt6_probe_work { 505c2f17e82SHannes Frederic Sowa struct work_struct work; 506c2f17e82SHannes Frederic Sowa struct in6_addr target; 507c2f17e82SHannes Frederic Sowa struct net_device *dev; 508c2f17e82SHannes Frederic Sowa }; 509c2f17e82SHannes Frederic Sowa 510c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w) 511c2f17e82SHannes Frederic Sowa { 512c2f17e82SHannes Frederic Sowa struct in6_addr mcaddr; 513c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work = 514c2f17e82SHannes Frederic Sowa container_of(w, struct __rt6_probe_work, work); 515c2f17e82SHannes Frederic Sowa 516c2f17e82SHannes Frederic Sowa addrconf_addr_solict_mult(&work->target, &mcaddr); 51738cf595bSJiri Benc ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, NULL); 518c2f17e82SHannes Frederic Sowa dev_put(work->dev); 519662f5533SMichael Büsch kfree(work); 520c2f17e82SHannes Frederic Sowa } 521c2f17e82SHannes Frederic Sowa 52227097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 52327097255SYOSHIFUJI Hideaki { 524990edb42SMartin KaFai Lau struct __rt6_probe_work *work; 525f2c31e32SEric Dumazet struct neighbour *neigh; 52627097255SYOSHIFUJI Hideaki /* 52727097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 52827097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 52927097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 53027097255SYOSHIFUJI Hideaki * 53127097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 53227097255SYOSHIFUJI Hideaki * to no more than one per minute. 53327097255SYOSHIFUJI Hideaki */ 5342152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!rt || !(rt->rt6i_flags & RTF_GATEWAY)) 535fdd6681dSAmerigo Wang return; 5362152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 5372152caeaSYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 5382152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 5398d6c31bfSMartin KaFai Lau if (neigh->nud_state & NUD_VALID) 5408d6c31bfSMartin KaFai Lau goto out; 5418d6c31bfSMartin KaFai Lau 542990edb42SMartin KaFai Lau work = NULL; 5432152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_lock(&neigh->lock); 544990edb42SMartin KaFai Lau if (!(neigh->nud_state & NUD_VALID) && 545990edb42SMartin KaFai Lau time_after(jiffies, 546990edb42SMartin KaFai Lau neigh->updated + 547990edb42SMartin KaFai Lau rt->rt6i_idev->cnf.rtr_probe_interval)) { 548c2f17e82SHannes Frederic Sowa work = kmalloc(sizeof(*work), GFP_ATOMIC); 549990edb42SMartin KaFai Lau if (work) 5507e980569SJiri Benc __neigh_set_probe_once(neigh); 551990edb42SMartin KaFai Lau } 552c2f17e82SHannes Frederic Sowa write_unlock(&neigh->lock); 553990edb42SMartin KaFai Lau } else { 554990edb42SMartin KaFai Lau work = kmalloc(sizeof(*work), GFP_ATOMIC); 555990edb42SMartin KaFai Lau } 556c2f17e82SHannes Frederic Sowa 557c2f17e82SHannes Frederic Sowa if (work) { 558c2f17e82SHannes Frederic Sowa INIT_WORK(&work->work, rt6_probe_deferred); 559c2f17e82SHannes Frederic Sowa work->target = rt->rt6i_gateway; 560c2f17e82SHannes Frederic Sowa dev_hold(rt->dst.dev); 561c2f17e82SHannes Frederic Sowa work->dev = rt->dst.dev; 562c2f17e82SHannes Frederic Sowa schedule_work(&work->work); 563c2f17e82SHannes Frederic Sowa } 564990edb42SMartin KaFai Lau 5658d6c31bfSMartin KaFai Lau out: 5662152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 567f2c31e32SEric Dumazet } 56827097255SYOSHIFUJI Hideaki #else 56927097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 57027097255SYOSHIFUJI Hideaki { 57127097255SYOSHIFUJI Hideaki } 57227097255SYOSHIFUJI Hideaki #endif 57327097255SYOSHIFUJI Hideaki 5741da177e4SLinus Torvalds /* 575554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 5761da177e4SLinus Torvalds */ 577b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 5781da177e4SLinus Torvalds { 579d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 580161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 581554cfb7eSYOSHIFUJI Hideaki return 2; 582161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 583161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 584161980f4SDavid S. Miller return 1; 585554cfb7eSYOSHIFUJI Hideaki return 0; 5861da177e4SLinus Torvalds } 5871da177e4SLinus Torvalds 588afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt) 5891da177e4SLinus Torvalds { 590f2c31e32SEric Dumazet struct neighbour *neigh; 591afc154e9SHannes Frederic Sowa enum rt6_nud_state ret = RT6_NUD_FAIL_HARD; 592f2c31e32SEric Dumazet 5934d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 5944d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 595afc154e9SHannes Frederic Sowa return RT6_NUD_SUCCEED; 596145a3621SYOSHIFUJI Hideaki / 吉藤英明 597145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 598145a3621SYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 599145a3621SYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 600145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_lock(&neigh->lock); 601554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 602afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 603398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 604a5a81f0bSPaul Marks else if (!(neigh->nud_state & NUD_FAILED)) 605afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 6067e980569SJiri Benc else 6077e980569SJiri Benc ret = RT6_NUD_FAIL_PROBE; 608398bcbebSYOSHIFUJI Hideaki #endif 609145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_unlock(&neigh->lock); 610afc154e9SHannes Frederic Sowa } else { 611afc154e9SHannes Frederic Sowa ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 6127e980569SJiri Benc RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; 613a5a81f0bSPaul Marks } 614145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 615145a3621SYOSHIFUJI Hideaki / 吉藤英明 616a5a81f0bSPaul Marks return ret; 6171da177e4SLinus Torvalds } 6181da177e4SLinus Torvalds 619554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 620554cfb7eSYOSHIFUJI Hideaki int strict) 621554cfb7eSYOSHIFUJI Hideaki { 622a5a81f0bSPaul Marks int m; 6234d0c5911SYOSHIFUJI Hideaki 6244d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 62577d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 626afc154e9SHannes Frederic Sowa return RT6_NUD_FAIL_HARD; 627ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 628ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 629ebacaaa0SYOSHIFUJI Hideaki #endif 630afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) { 631afc154e9SHannes Frederic Sowa int n = rt6_check_neigh(rt); 632afc154e9SHannes Frederic Sowa if (n < 0) 633afc154e9SHannes Frederic Sowa return n; 634afc154e9SHannes Frederic Sowa } 635554cfb7eSYOSHIFUJI Hideaki return m; 636554cfb7eSYOSHIFUJI Hideaki } 637554cfb7eSYOSHIFUJI Hideaki 638f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 639afc154e9SHannes Frederic Sowa int *mpri, struct rt6_info *match, 640afc154e9SHannes Frederic Sowa bool *do_rr) 641554cfb7eSYOSHIFUJI Hideaki { 642554cfb7eSYOSHIFUJI Hideaki int m; 643afc154e9SHannes Frederic Sowa bool match_do_rr = false; 64435103d11SAndy Gospodarek struct inet6_dev *idev = rt->rt6i_idev; 64535103d11SAndy Gospodarek struct net_device *dev = rt->dst.dev; 64635103d11SAndy Gospodarek 64735103d11SAndy Gospodarek if (dev && !netif_carrier_ok(dev) && 64835103d11SAndy Gospodarek idev->cnf.ignore_routes_with_linkdown) 64935103d11SAndy Gospodarek goto out; 650554cfb7eSYOSHIFUJI Hideaki 651554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 652f11e6659SDavid S. Miller goto out; 653554cfb7eSYOSHIFUJI Hideaki 654554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 6557e980569SJiri Benc if (m == RT6_NUD_FAIL_DO_RR) { 656afc154e9SHannes Frederic Sowa match_do_rr = true; 657afc154e9SHannes Frederic Sowa m = 0; /* lowest valid score */ 6587e980569SJiri Benc } else if (m == RT6_NUD_FAIL_HARD) { 659f11e6659SDavid S. Miller goto out; 6601da177e4SLinus Torvalds } 661f11e6659SDavid S. Miller 662afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) 663afc154e9SHannes Frederic Sowa rt6_probe(rt); 664afc154e9SHannes Frederic Sowa 6657e980569SJiri Benc /* note that m can be RT6_NUD_FAIL_PROBE at this point */ 666afc154e9SHannes Frederic Sowa if (m > *mpri) { 667afc154e9SHannes Frederic Sowa *do_rr = match_do_rr; 668afc154e9SHannes Frederic Sowa *mpri = m; 669afc154e9SHannes Frederic Sowa match = rt; 670afc154e9SHannes Frederic Sowa } 671f11e6659SDavid S. Miller out: 672f11e6659SDavid S. Miller return match; 6731da177e4SLinus Torvalds } 6741da177e4SLinus Torvalds 675f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 676f11e6659SDavid S. Miller struct rt6_info *rr_head, 677afc154e9SHannes Frederic Sowa u32 metric, int oif, int strict, 678afc154e9SHannes Frederic Sowa bool *do_rr) 679f11e6659SDavid S. Miller { 6809fbdcfafSSteffen Klassert struct rt6_info *rt, *match, *cont; 681f11e6659SDavid S. Miller int mpri = -1; 682f11e6659SDavid S. Miller 683f11e6659SDavid S. Miller match = NULL; 6849fbdcfafSSteffen Klassert cont = NULL; 6859fbdcfafSSteffen Klassert for (rt = rr_head; rt; rt = rt->dst.rt6_next) { 6869fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 6879fbdcfafSSteffen Klassert cont = rt; 6889fbdcfafSSteffen Klassert break; 6899fbdcfafSSteffen Klassert } 6909fbdcfafSSteffen Klassert 691afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 6929fbdcfafSSteffen Klassert } 6939fbdcfafSSteffen Klassert 6949fbdcfafSSteffen Klassert for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) { 6959fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 6969fbdcfafSSteffen Klassert cont = rt; 6979fbdcfafSSteffen Klassert break; 6989fbdcfafSSteffen Klassert } 6999fbdcfafSSteffen Klassert 7009fbdcfafSSteffen Klassert match = find_match(rt, oif, strict, &mpri, match, do_rr); 7019fbdcfafSSteffen Klassert } 7029fbdcfafSSteffen Klassert 7039fbdcfafSSteffen Klassert if (match || !cont) 7049fbdcfafSSteffen Klassert return match; 7059fbdcfafSSteffen Klassert 7069fbdcfafSSteffen Klassert for (rt = cont; rt; rt = rt->dst.rt6_next) 707afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 708f11e6659SDavid S. Miller 709f11e6659SDavid S. Miller return match; 710f11e6659SDavid S. Miller } 711f11e6659SDavid S. Miller 712f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 713f11e6659SDavid S. Miller { 714f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 7158ed67789SDaniel Lezcano struct net *net; 716afc154e9SHannes Frederic Sowa bool do_rr = false; 717f11e6659SDavid S. Miller 718f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 719f11e6659SDavid S. Miller if (!rt0) 720f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 721f11e6659SDavid S. Miller 722afc154e9SHannes Frederic Sowa match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict, 723afc154e9SHannes Frederic Sowa &do_rr); 724f11e6659SDavid S. Miller 725afc154e9SHannes Frederic Sowa if (do_rr) { 726d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 727f11e6659SDavid S. Miller 728554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 729f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 730f11e6659SDavid S. Miller next = fn->leaf; 731f11e6659SDavid S. Miller 732f11e6659SDavid S. Miller if (next != rt0) 733f11e6659SDavid S. Miller fn->rr_ptr = next; 734554cfb7eSYOSHIFUJI Hideaki } 735554cfb7eSYOSHIFUJI Hideaki 736d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 737a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 7381da177e4SLinus Torvalds } 7391da177e4SLinus Torvalds 7408b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt) 7418b9df265SMartin KaFai Lau { 7428b9df265SMartin KaFai Lau return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY)); 7438b9df265SMartin KaFai Lau } 7448b9df265SMartin KaFai Lau 74570ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 74670ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 747b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 74870ceb4f5SYOSHIFUJI Hideaki { 749c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 75070ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 75170ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 75270ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 7534bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 75470ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 75570ceb4f5SYOSHIFUJI Hideaki 75670ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 75770ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 75870ceb4f5SYOSHIFUJI Hideaki } 75970ceb4f5SYOSHIFUJI Hideaki 76070ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 76170ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 76270ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 76370ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 76470ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 76570ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 76670ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 76770ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 76870ceb4f5SYOSHIFUJI Hideaki } 76970ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 77070ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 77170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 77270ceb4f5SYOSHIFUJI Hideaki } 77370ceb4f5SYOSHIFUJI Hideaki } 77470ceb4f5SYOSHIFUJI Hideaki 77570ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 77670ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 7773933fc95SJens Rosenboom return -EINVAL; 77870ceb4f5SYOSHIFUJI Hideaki 7794bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 78070ceb4f5SYOSHIFUJI Hideaki 78170ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 78270ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 78370ceb4f5SYOSHIFUJI Hideaki else { 78470ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 78570ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 78670ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 78770ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 78870ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 78970ceb4f5SYOSHIFUJI Hideaki } 79070ceb4f5SYOSHIFUJI Hideaki 791f104a567SDuan Jiong if (rinfo->prefix_len == 0) 792f104a567SDuan Jiong rt = rt6_get_dflt_router(gwaddr, dev); 793f104a567SDuan Jiong else 794f104a567SDuan Jiong rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, 795f104a567SDuan Jiong gwaddr, dev->ifindex); 79670ceb4f5SYOSHIFUJI Hideaki 79770ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 798e0a1ad73SThomas Graf ip6_del_rt(rt); 79970ceb4f5SYOSHIFUJI Hideaki rt = NULL; 80070ceb4f5SYOSHIFUJI Hideaki } 80170ceb4f5SYOSHIFUJI Hideaki 80270ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 803efa2cea0SDaniel Lezcano rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 80470ceb4f5SYOSHIFUJI Hideaki pref); 80570ceb4f5SYOSHIFUJI Hideaki else if (rt) 80670ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 80770ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 80870ceb4f5SYOSHIFUJI Hideaki 80970ceb4f5SYOSHIFUJI Hideaki if (rt) { 8101716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 8111716a961SGao feng rt6_clean_expires(rt); 8121716a961SGao feng else 8131716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 8141716a961SGao feng 81594e187c0SAmerigo Wang ip6_rt_put(rt); 81670ceb4f5SYOSHIFUJI Hideaki } 81770ceb4f5SYOSHIFUJI Hideaki return 0; 81870ceb4f5SYOSHIFUJI Hideaki } 81970ceb4f5SYOSHIFUJI Hideaki #endif 82070ceb4f5SYOSHIFUJI Hideaki 821a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn, 822a3c00e46SMartin KaFai Lau struct in6_addr *saddr) 823a3c00e46SMartin KaFai Lau { 824a3c00e46SMartin KaFai Lau struct fib6_node *pn; 825a3c00e46SMartin KaFai Lau while (1) { 826a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_TL_ROOT) 827a3c00e46SMartin KaFai Lau return NULL; 828a3c00e46SMartin KaFai Lau pn = fn->parent; 829a3c00e46SMartin KaFai Lau if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) 830a3c00e46SMartin KaFai Lau fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); 831a3c00e46SMartin KaFai Lau else 832a3c00e46SMartin KaFai Lau fn = pn; 833a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_RTINFO) 834a3c00e46SMartin KaFai Lau return fn; 835a3c00e46SMartin KaFai Lau } 836a3c00e46SMartin KaFai Lau } 837c71099acSThomas Graf 8388ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 8398ed67789SDaniel Lezcano struct fib6_table *table, 8404c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 8411da177e4SLinus Torvalds { 8421da177e4SLinus Torvalds struct fib6_node *fn; 8431da177e4SLinus Torvalds struct rt6_info *rt; 8441da177e4SLinus Torvalds 845c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 8464c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 847c71099acSThomas Graf restart: 848c71099acSThomas Graf rt = fn->leaf; 8494c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 85051ebd318SNicolas Dichtel if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) 85152bd4c0cSNicolas Dichtel rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags); 852a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 853a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 854a3c00e46SMartin KaFai Lau if (fn) 855a3c00e46SMartin KaFai Lau goto restart; 856a3c00e46SMartin KaFai Lau } 857d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 858c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 8591da177e4SLinus Torvalds return rt; 860c71099acSThomas Graf 861c71099acSThomas Graf } 862c71099acSThomas Graf 863ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, 864ea6e574eSFlorian Westphal int flags) 865ea6e574eSFlorian Westphal { 866ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 867ea6e574eSFlorian Westphal } 868ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 869ea6e574eSFlorian Westphal 8709acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 8719acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 872c71099acSThomas Graf { 8734c9483b2SDavid S. Miller struct flowi6 fl6 = { 8744c9483b2SDavid S. Miller .flowi6_oif = oif, 8754c9483b2SDavid S. Miller .daddr = *daddr, 876c71099acSThomas Graf }; 877c71099acSThomas Graf struct dst_entry *dst; 87877d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 879c71099acSThomas Graf 880adaa70bbSThomas Graf if (saddr) { 8814c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 882adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 883adaa70bbSThomas Graf } 884adaa70bbSThomas Graf 8854c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 886c71099acSThomas Graf if (dst->error == 0) 887c71099acSThomas Graf return (struct rt6_info *) dst; 888c71099acSThomas Graf 889c71099acSThomas Graf dst_release(dst); 890c71099acSThomas Graf 8911da177e4SLinus Torvalds return NULL; 8921da177e4SLinus Torvalds } 8937159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 8947159039aSYOSHIFUJI Hideaki 895c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 8961da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 8971da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 8981da177e4SLinus Torvalds be destroyed. 8991da177e4SLinus Torvalds */ 9001da177e4SLinus Torvalds 901e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, 902e715b6d3SFlorian Westphal struct mx6_config *mxc) 9031da177e4SLinus Torvalds { 9041da177e4SLinus Torvalds int err; 905c71099acSThomas Graf struct fib6_table *table; 9061da177e4SLinus Torvalds 907c71099acSThomas Graf table = rt->rt6i_table; 908c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 909e715b6d3SFlorian Westphal err = fib6_add(&table->tb6_root, rt, info, mxc); 910c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 9111da177e4SLinus Torvalds 9121da177e4SLinus Torvalds return err; 9131da177e4SLinus Torvalds } 9141da177e4SLinus Torvalds 91540e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 91640e22e8fSThomas Graf { 917e715b6d3SFlorian Westphal struct nl_info info = { .nl_net = dev_net(rt->dst.dev), }; 918e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 919e715b6d3SFlorian Westphal 920e715b6d3SFlorian Westphal return __ip6_ins_rt(rt, &info, &mxc); 92140e22e8fSThomas Graf } 92240e22e8fSThomas Graf 9238b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, 92421efcfa0SEric Dumazet const struct in6_addr *daddr, 925b71d1d42SEric Dumazet const struct in6_addr *saddr) 9261da177e4SLinus Torvalds { 9271da177e4SLinus Torvalds struct rt6_info *rt; 9281da177e4SLinus Torvalds 9291da177e4SLinus Torvalds /* 9301da177e4SLinus Torvalds * Clone the route. 9311da177e4SLinus Torvalds */ 9321da177e4SLinus Torvalds 933d52d3997SMartin KaFai Lau if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)) 93483a09abdSMartin KaFai Lau ort = (struct rt6_info *)ort->dst.from; 9351da177e4SLinus Torvalds 936ad706862SMartin KaFai Lau rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0); 93783a09abdSMartin KaFai Lau 93883a09abdSMartin KaFai Lau if (!rt) 93983a09abdSMartin KaFai Lau return NULL; 94083a09abdSMartin KaFai Lau 94183a09abdSMartin KaFai Lau ip6_rt_copy_init(rt, ort); 9428b9df265SMartin KaFai Lau rt->rt6i_flags |= RTF_CACHE; 94383a09abdSMartin KaFai Lau rt->rt6i_metric = 0; 94483a09abdSMartin KaFai Lau rt->dst.flags |= DST_HOST; 94583a09abdSMartin KaFai Lau rt->rt6i_dst.addr = *daddr; 94683a09abdSMartin KaFai Lau rt->rt6i_dst.plen = 128; 9478b9df265SMartin KaFai Lau 9488b9df265SMartin KaFai Lau if (!rt6_is_gw_or_nonexthop(ort)) { 949bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 95021efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 95158c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 9521da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 9531da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 9544e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 9551da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 9561da177e4SLinus Torvalds } 9571da177e4SLinus Torvalds #endif 95895a9a5baSYOSHIFUJI Hideaki } 95995a9a5baSYOSHIFUJI Hideaki 960299d9939SYOSHIFUJI Hideaki return rt; 961299d9939SYOSHIFUJI Hideaki } 962299d9939SYOSHIFUJI Hideaki 963d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt) 964d52d3997SMartin KaFai Lau { 965d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 966d52d3997SMartin KaFai Lau 967d52d3997SMartin KaFai Lau pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev), 968ad706862SMartin KaFai Lau rt->dst.dev, rt->dst.flags); 969d52d3997SMartin KaFai Lau 970d52d3997SMartin KaFai Lau if (!pcpu_rt) 971d52d3997SMartin KaFai Lau return NULL; 972d52d3997SMartin KaFai Lau ip6_rt_copy_init(pcpu_rt, rt); 973d52d3997SMartin KaFai Lau pcpu_rt->rt6i_protocol = rt->rt6i_protocol; 974d52d3997SMartin KaFai Lau pcpu_rt->rt6i_flags |= RTF_PCPU; 975d52d3997SMartin KaFai Lau return pcpu_rt; 976d52d3997SMartin KaFai Lau } 977d52d3997SMartin KaFai Lau 978d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */ 979d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt) 980d52d3997SMartin KaFai Lau { 981a73e4195SMartin KaFai Lau struct rt6_info *pcpu_rt, **p; 982d52d3997SMartin KaFai Lau 983d52d3997SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 984d52d3997SMartin KaFai Lau pcpu_rt = *p; 985d52d3997SMartin KaFai Lau 986a73e4195SMartin KaFai Lau if (pcpu_rt) { 987a73e4195SMartin KaFai Lau dst_hold(&pcpu_rt->dst); 988a73e4195SMartin KaFai Lau rt6_dst_from_metrics_check(pcpu_rt); 989a73e4195SMartin KaFai Lau } 990a73e4195SMartin KaFai Lau return pcpu_rt; 991a73e4195SMartin KaFai Lau } 992a73e4195SMartin KaFai Lau 993a73e4195SMartin KaFai Lau static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt) 994a73e4195SMartin KaFai Lau { 9959c7370a1SMartin KaFai Lau struct fib6_table *table = rt->rt6i_table; 996a73e4195SMartin KaFai Lau struct rt6_info *pcpu_rt, *prev, **p; 997d52d3997SMartin KaFai Lau 998d52d3997SMartin KaFai Lau pcpu_rt = ip6_rt_pcpu_alloc(rt); 999d52d3997SMartin KaFai Lau if (!pcpu_rt) { 1000d52d3997SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 1001d52d3997SMartin KaFai Lau 10029c7370a1SMartin KaFai Lau dst_hold(&net->ipv6.ip6_null_entry->dst); 10039c7370a1SMartin KaFai Lau return net->ipv6.ip6_null_entry; 1004d52d3997SMartin KaFai Lau } 1005d52d3997SMartin KaFai Lau 10069c7370a1SMartin KaFai Lau read_lock_bh(&table->tb6_lock); 10079c7370a1SMartin KaFai Lau if (rt->rt6i_pcpu) { 1008a73e4195SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 1009d52d3997SMartin KaFai Lau prev = cmpxchg(p, NULL, pcpu_rt); 1010d52d3997SMartin KaFai Lau if (prev) { 1011d52d3997SMartin KaFai Lau /* If someone did it before us, return prev instead */ 1012d52d3997SMartin KaFai Lau dst_destroy(&pcpu_rt->dst); 1013d52d3997SMartin KaFai Lau pcpu_rt = prev; 1014d52d3997SMartin KaFai Lau } 10159c7370a1SMartin KaFai Lau } else { 10169c7370a1SMartin KaFai Lau /* rt has been removed from the fib6 tree 10179c7370a1SMartin KaFai Lau * before we have a chance to acquire the read_lock. 10189c7370a1SMartin KaFai Lau * In this case, don't brother to create a pcpu rt 10199c7370a1SMartin KaFai Lau * since rt is going away anyway. The next 10209c7370a1SMartin KaFai Lau * dst_check() will trigger a re-lookup. 10219c7370a1SMartin KaFai Lau */ 10229c7370a1SMartin KaFai Lau dst_destroy(&pcpu_rt->dst); 10239c7370a1SMartin KaFai Lau pcpu_rt = rt; 10249c7370a1SMartin KaFai Lau } 1025d52d3997SMartin KaFai Lau dst_hold(&pcpu_rt->dst); 1026d52d3997SMartin KaFai Lau rt6_dst_from_metrics_check(pcpu_rt); 10279c7370a1SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 1028d52d3997SMartin KaFai Lau return pcpu_rt; 1029d52d3997SMartin KaFai Lau } 1030d52d3997SMartin KaFai Lau 10318ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, 10324c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 10331da177e4SLinus Torvalds { 1034367efcb9SMartin KaFai Lau struct fib6_node *fn, *saved_fn; 103545e4fd26SMartin KaFai Lau struct rt6_info *rt; 1036c71099acSThomas Graf int strict = 0; 10371da177e4SLinus Torvalds 103877d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 1039367efcb9SMartin KaFai Lau if (net->ipv6.devconf_all->forwarding == 0) 1040367efcb9SMartin KaFai Lau strict |= RT6_LOOKUP_F_REACHABLE; 10411da177e4SLinus Torvalds 1042c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 10431da177e4SLinus Torvalds 10444c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1045367efcb9SMartin KaFai Lau saved_fn = fn; 10461da177e4SLinus Torvalds 1047a3c00e46SMartin KaFai Lau redo_rt6_select: 1048367efcb9SMartin KaFai Lau rt = rt6_select(fn, oif, strict); 104952bd4c0cSNicolas Dichtel if (rt->rt6i_nsiblings) 1050367efcb9SMartin KaFai Lau rt = rt6_multipath_select(rt, fl6, oif, strict); 1051a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1052a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1053a3c00e46SMartin KaFai Lau if (fn) 1054a3c00e46SMartin KaFai Lau goto redo_rt6_select; 1055367efcb9SMartin KaFai Lau else if (strict & RT6_LOOKUP_F_REACHABLE) { 1056367efcb9SMartin KaFai Lau /* also consider unreachable route */ 1057367efcb9SMartin KaFai Lau strict &= ~RT6_LOOKUP_F_REACHABLE; 1058367efcb9SMartin KaFai Lau fn = saved_fn; 1059367efcb9SMartin KaFai Lau goto redo_rt6_select; 1060367efcb9SMartin KaFai Lau } 1061a3c00e46SMartin KaFai Lau } 1062a3c00e46SMartin KaFai Lau 1063d52d3997SMartin KaFai Lau 1064d52d3997SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) { 10653da59bd9SMartin KaFai Lau dst_use(&rt->dst, jiffies); 1066c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 10671da177e4SLinus Torvalds 1068d52d3997SMartin KaFai Lau rt6_dst_from_metrics_check(rt); 1069d52d3997SMartin KaFai Lau return rt; 10703da59bd9SMartin KaFai Lau } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && 10713da59bd9SMartin KaFai Lau !(rt->rt6i_flags & RTF_GATEWAY))) { 10723da59bd9SMartin KaFai Lau /* Create a RTF_CACHE clone which will not be 10733da59bd9SMartin KaFai Lau * owned by the fib6 tree. It is for the special case where 10743da59bd9SMartin KaFai Lau * the daddr in the skb during the neighbor look-up is different 10753da59bd9SMartin KaFai Lau * from the fl6->daddr used to look-up route here. 10763da59bd9SMartin KaFai Lau */ 1077c71099acSThomas Graf 10783da59bd9SMartin KaFai Lau struct rt6_info *uncached_rt; 10793da59bd9SMartin KaFai Lau 1080d52d3997SMartin KaFai Lau dst_use(&rt->dst, jiffies); 1081d52d3997SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 1082d52d3997SMartin KaFai Lau 10833da59bd9SMartin KaFai Lau uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL); 10843da59bd9SMartin KaFai Lau dst_release(&rt->dst); 10853da59bd9SMartin KaFai Lau 10863da59bd9SMartin KaFai Lau if (uncached_rt) 10878d0b94afSMartin KaFai Lau rt6_uncached_list_add(uncached_rt); 10883da59bd9SMartin KaFai Lau else 10893da59bd9SMartin KaFai Lau uncached_rt = net->ipv6.ip6_null_entry; 1090d52d3997SMartin KaFai Lau 10913da59bd9SMartin KaFai Lau dst_hold(&uncached_rt->dst); 10923da59bd9SMartin KaFai Lau return uncached_rt; 10933da59bd9SMartin KaFai Lau 1094d52d3997SMartin KaFai Lau } else { 1095d52d3997SMartin KaFai Lau /* Get a percpu copy */ 1096d52d3997SMartin KaFai Lau 1097d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 1098d52d3997SMartin KaFai Lau 1099d52d3997SMartin KaFai Lau rt->dst.lastuse = jiffies; 1100d52d3997SMartin KaFai Lau rt->dst.__use++; 1101d52d3997SMartin KaFai Lau pcpu_rt = rt6_get_pcpu_route(rt); 1102d52d3997SMartin KaFai Lau 11039c7370a1SMartin KaFai Lau if (pcpu_rt) { 1104d52d3997SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 11059c7370a1SMartin KaFai Lau } else { 11069c7370a1SMartin KaFai Lau /* We have to do the read_unlock first 11079c7370a1SMartin KaFai Lau * because rt6_make_pcpu_route() may trigger 11089c7370a1SMartin KaFai Lau * ip6_dst_gc() which will take the write_lock. 11099c7370a1SMartin KaFai Lau */ 11109c7370a1SMartin KaFai Lau dst_hold(&rt->dst); 11119c7370a1SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 11129c7370a1SMartin KaFai Lau pcpu_rt = rt6_make_pcpu_route(rt); 11139c7370a1SMartin KaFai Lau dst_release(&rt->dst); 11149c7370a1SMartin KaFai Lau } 1115d52d3997SMartin KaFai Lau 1116d52d3997SMartin KaFai Lau return pcpu_rt; 11179c7370a1SMartin KaFai Lau 1118d52d3997SMartin KaFai Lau } 1119c71099acSThomas Graf } 1120c71099acSThomas Graf 11218ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 11224c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 11234acad72dSPavel Emelyanov { 11244c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 11254acad72dSPavel Emelyanov } 11264acad72dSPavel Emelyanov 112772331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 112872331bc0SShmulik Ladkani struct net_device *dev, 112972331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 113072331bc0SShmulik Ladkani { 113172331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 113272331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 113372331bc0SShmulik Ladkani 113472331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 113572331bc0SShmulik Ladkani } 113672331bc0SShmulik Ladkani 1137c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 1138c71099acSThomas Graf { 1139b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 1140c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 1141adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 1142904af04dSJiri Benc struct ip_tunnel_info *tun_info; 11434c9483b2SDavid S. Miller struct flowi6 fl6 = { 11444c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 11454c9483b2SDavid S. Miller .daddr = iph->daddr, 11464c9483b2SDavid S. Miller .saddr = iph->saddr, 11476502ca52SYOSHIFUJI Hideaki / 吉藤英明 .flowlabel = ip6_flowinfo(iph), 11484c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 11494c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 1150c71099acSThomas Graf }; 1151adaa70bbSThomas Graf 1152904af04dSJiri Benc tun_info = skb_tunnel_info(skb); 115346fa062aSJiri Benc if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX)) 1154904af04dSJiri Benc fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id; 115506e9d040SJiri Benc skb_dst_drop(skb); 115672331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 1157c71099acSThomas Graf } 1158c71099acSThomas Graf 11598ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 11604c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 1161c71099acSThomas Graf { 11624c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 1163c71099acSThomas Graf } 1164c71099acSThomas Graf 11659c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk, 11664c9483b2SDavid S. Miller struct flowi6 *fl6) 1167c71099acSThomas Graf { 1168c71099acSThomas Graf int flags = 0; 1169c71099acSThomas Graf 11701fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 11714dc27d1cSDavid McCullough 11724c9483b2SDavid S. Miller if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) 117377d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 1174c71099acSThomas Graf 11754c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 1176adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 11770c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 11780c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 1179adaa70bbSThomas Graf 11804c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 11811da177e4SLinus Torvalds } 11827159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 11831da177e4SLinus Torvalds 11842774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 118514e50e57SDavid S. Miller { 11865c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 118714e50e57SDavid S. Miller struct dst_entry *new = NULL; 118814e50e57SDavid S. Miller 1189f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 119014e50e57SDavid S. Miller if (rt) { 1191d8d1f30bSChangli Gao new = &rt->dst; 119214e50e57SDavid S. Miller 11938104891bSSteffen Klassert memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); 11948104891bSSteffen Klassert 119514e50e57SDavid S. Miller new->__use = 1; 1196352e512cSHerbert Xu new->input = dst_discard; 1197aad88724SEric Dumazet new->output = dst_discard_sk; 119814e50e57SDavid S. Miller 119921efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 120021efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 120121efcfa0SEric Dumazet else 1202defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 120314e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 120414e50e57SDavid S. Miller if (rt->rt6i_idev) 120514e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 120614e50e57SDavid S. Miller 12074e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 12081716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 120914e50e57SDavid S. Miller rt->rt6i_metric = 0; 121014e50e57SDavid S. Miller 121114e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 121214e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 121314e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 121414e50e57SDavid S. Miller #endif 121514e50e57SDavid S. Miller 121614e50e57SDavid S. Miller dst_free(new); 121714e50e57SDavid S. Miller } 121814e50e57SDavid S. Miller 121969ead7afSDavid S. Miller dst_release(dst_orig); 122069ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 122114e50e57SDavid S. Miller } 122214e50e57SDavid S. Miller 12231da177e4SLinus Torvalds /* 12241da177e4SLinus Torvalds * Destination cache support functions 12251da177e4SLinus Torvalds */ 12261da177e4SLinus Torvalds 12274b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt) 12284b32b5adSMartin KaFai Lau { 12294b32b5adSMartin KaFai Lau if (rt->dst.from && 12304b32b5adSMartin KaFai Lau dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from)) 12314b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true); 12324b32b5adSMartin KaFai Lau } 12334b32b5adSMartin KaFai Lau 12343da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie) 12353da59bd9SMartin KaFai Lau { 12363da59bd9SMartin KaFai Lau if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) 12373da59bd9SMartin KaFai Lau return NULL; 12383da59bd9SMartin KaFai Lau 12393da59bd9SMartin KaFai Lau if (rt6_check_expired(rt)) 12403da59bd9SMartin KaFai Lau return NULL; 12413da59bd9SMartin KaFai Lau 12423da59bd9SMartin KaFai Lau return &rt->dst; 12433da59bd9SMartin KaFai Lau } 12443da59bd9SMartin KaFai Lau 12453da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie) 12463da59bd9SMartin KaFai Lau { 12473da59bd9SMartin KaFai Lau if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && 12483da59bd9SMartin KaFai Lau rt6_check((struct rt6_info *)(rt->dst.from), cookie)) 12493da59bd9SMartin KaFai Lau return &rt->dst; 12503da59bd9SMartin KaFai Lau else 12513da59bd9SMartin KaFai Lau return NULL; 12523da59bd9SMartin KaFai Lau } 12533da59bd9SMartin KaFai Lau 12541da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 12551da177e4SLinus Torvalds { 12561da177e4SLinus Torvalds struct rt6_info *rt; 12571da177e4SLinus Torvalds 12581da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 12591da177e4SLinus Torvalds 12606f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 12616f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 12626f3118b5SNicolas Dichtel * into this function always. 12636f3118b5SNicolas Dichtel */ 1264e3bc10bdSHannes Frederic Sowa 12654b32b5adSMartin KaFai Lau rt6_dst_from_metrics_check(rt); 12664b32b5adSMartin KaFai Lau 1267d52d3997SMartin KaFai Lau if ((rt->rt6i_flags & RTF_PCPU) || unlikely(dst->flags & DST_NOCACHE)) 12683da59bd9SMartin KaFai Lau return rt6_dst_from_check(rt, cookie); 12693da59bd9SMartin KaFai Lau else 12703da59bd9SMartin KaFai Lau return rt6_check(rt, cookie); 12711da177e4SLinus Torvalds } 12721da177e4SLinus Torvalds 12731da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 12741da177e4SLinus Torvalds { 12751da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 12761da177e4SLinus Torvalds 12771da177e4SLinus Torvalds if (rt) { 127854c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 127954c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1280e0a1ad73SThomas Graf ip6_del_rt(rt); 128154c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 12821da177e4SLinus Torvalds } 128354c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 128454c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 128554c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 128654c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 128754c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 128854c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 12891da177e4SLinus Torvalds } 12901da177e4SLinus Torvalds 12911da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 12921da177e4SLinus Torvalds { 12931da177e4SLinus Torvalds struct rt6_info *rt; 12941da177e4SLinus Torvalds 12953ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 12961da177e4SLinus Torvalds 1297adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 12981da177e4SLinus Torvalds if (rt) { 12991eb4f758SHannes Frederic Sowa if (rt->rt6i_flags & RTF_CACHE) { 13001eb4f758SHannes Frederic Sowa dst_hold(&rt->dst); 13018e3d5be7SMartin KaFai Lau ip6_del_rt(rt); 13021eb4f758SHannes Frederic Sowa } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) { 13031da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 13041da177e4SLinus Torvalds } 13051da177e4SLinus Torvalds } 13061eb4f758SHannes Frederic Sowa } 13071da177e4SLinus Torvalds 130845e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) 130945e4fd26SMartin KaFai Lau { 131045e4fd26SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 131145e4fd26SMartin KaFai Lau 131245e4fd26SMartin KaFai Lau rt->rt6i_flags |= RTF_MODIFIED; 131345e4fd26SMartin KaFai Lau rt->rt6i_pmtu = mtu; 131445e4fd26SMartin KaFai Lau rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); 131545e4fd26SMartin KaFai Lau } 131645e4fd26SMartin KaFai Lau 131745e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, 131845e4fd26SMartin KaFai Lau const struct ipv6hdr *iph, u32 mtu) 13191da177e4SLinus Torvalds { 13201da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info *)dst; 13211da177e4SLinus Torvalds 132245e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_LOCAL) 132345e4fd26SMartin KaFai Lau return; 132445e4fd26SMartin KaFai Lau 132581aded24SDavid S. Miller dst_confirm(dst); 132645e4fd26SMartin KaFai Lau mtu = max_t(u32, mtu, IPV6_MIN_MTU); 132745e4fd26SMartin KaFai Lau if (mtu >= dst_mtu(dst)) 132845e4fd26SMartin KaFai Lau return; 132981aded24SDavid S. Miller 133045e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_CACHE) { 133145e4fd26SMartin KaFai Lau rt6_do_update_pmtu(rt6, mtu); 133245e4fd26SMartin KaFai Lau } else { 133345e4fd26SMartin KaFai Lau const struct in6_addr *daddr, *saddr; 133445e4fd26SMartin KaFai Lau struct rt6_info *nrt6; 13359d289715SHagen Paul Pfeifer 133645e4fd26SMartin KaFai Lau if (iph) { 133745e4fd26SMartin KaFai Lau daddr = &iph->daddr; 133845e4fd26SMartin KaFai Lau saddr = &iph->saddr; 133945e4fd26SMartin KaFai Lau } else if (sk) { 134045e4fd26SMartin KaFai Lau daddr = &sk->sk_v6_daddr; 134145e4fd26SMartin KaFai Lau saddr = &inet6_sk(sk)->saddr; 134245e4fd26SMartin KaFai Lau } else { 134345e4fd26SMartin KaFai Lau return; 13441da177e4SLinus Torvalds } 134545e4fd26SMartin KaFai Lau nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr); 134645e4fd26SMartin KaFai Lau if (nrt6) { 134745e4fd26SMartin KaFai Lau rt6_do_update_pmtu(nrt6, mtu); 134845e4fd26SMartin KaFai Lau 134945e4fd26SMartin KaFai Lau /* ip6_ins_rt(nrt6) will bump the 135045e4fd26SMartin KaFai Lau * rt6->rt6i_node->fn_sernum 135145e4fd26SMartin KaFai Lau * which will fail the next rt6_check() and 135245e4fd26SMartin KaFai Lau * invalidate the sk->sk_dst_cache. 135345e4fd26SMartin KaFai Lau */ 135445e4fd26SMartin KaFai Lau ip6_ins_rt(nrt6); 135545e4fd26SMartin KaFai Lau } 135645e4fd26SMartin KaFai Lau } 135745e4fd26SMartin KaFai Lau } 135845e4fd26SMartin KaFai Lau 135945e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 136045e4fd26SMartin KaFai Lau struct sk_buff *skb, u32 mtu) 136145e4fd26SMartin KaFai Lau { 136245e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu); 13631da177e4SLinus Torvalds } 13641da177e4SLinus Torvalds 136542ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 136642ae66c8SDavid S. Miller int oif, u32 mark) 136781aded24SDavid S. Miller { 136881aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 136981aded24SDavid S. Miller struct dst_entry *dst; 137081aded24SDavid S. Miller struct flowi6 fl6; 137181aded24SDavid S. Miller 137281aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 137381aded24SDavid S. Miller fl6.flowi6_oif = oif; 13741b3c61dcSLorenzo Colitti fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark); 137581aded24SDavid S. Miller fl6.daddr = iph->daddr; 137681aded24SDavid S. Miller fl6.saddr = iph->saddr; 13776502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 137881aded24SDavid S. Miller 137981aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 138081aded24SDavid S. Miller if (!dst->error) 138145e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu)); 138281aded24SDavid S. Miller dst_release(dst); 138381aded24SDavid S. Miller } 138481aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 138581aded24SDavid S. Miller 138681aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 138781aded24SDavid S. Miller { 138881aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 138981aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 139081aded24SDavid S. Miller } 139181aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 139281aded24SDavid S. Miller 1393b55b76b2SDuan Jiong /* Handle redirects */ 1394b55b76b2SDuan Jiong struct ip6rd_flowi { 1395b55b76b2SDuan Jiong struct flowi6 fl6; 1396b55b76b2SDuan Jiong struct in6_addr gateway; 1397b55b76b2SDuan Jiong }; 1398b55b76b2SDuan Jiong 1399b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net, 1400b55b76b2SDuan Jiong struct fib6_table *table, 1401b55b76b2SDuan Jiong struct flowi6 *fl6, 1402b55b76b2SDuan Jiong int flags) 1403b55b76b2SDuan Jiong { 1404b55b76b2SDuan Jiong struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 1405b55b76b2SDuan Jiong struct rt6_info *rt; 1406b55b76b2SDuan Jiong struct fib6_node *fn; 1407b55b76b2SDuan Jiong 1408b55b76b2SDuan Jiong /* Get the "current" route for this destination and 1409b55b76b2SDuan Jiong * check if the redirect has come from approriate router. 1410b55b76b2SDuan Jiong * 1411b55b76b2SDuan Jiong * RFC 4861 specifies that redirects should only be 1412b55b76b2SDuan Jiong * accepted if they come from the nexthop to the target. 1413b55b76b2SDuan Jiong * Due to the way the routes are chosen, this notion 1414b55b76b2SDuan Jiong * is a bit fuzzy and one might need to check all possible 1415b55b76b2SDuan Jiong * routes. 1416b55b76b2SDuan Jiong */ 1417b55b76b2SDuan Jiong 1418b55b76b2SDuan Jiong read_lock_bh(&table->tb6_lock); 1419b55b76b2SDuan Jiong fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1420b55b76b2SDuan Jiong restart: 1421b55b76b2SDuan Jiong for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1422b55b76b2SDuan Jiong if (rt6_check_expired(rt)) 1423b55b76b2SDuan Jiong continue; 1424b55b76b2SDuan Jiong if (rt->dst.error) 1425b55b76b2SDuan Jiong break; 1426b55b76b2SDuan Jiong if (!(rt->rt6i_flags & RTF_GATEWAY)) 1427b55b76b2SDuan Jiong continue; 1428b55b76b2SDuan Jiong if (fl6->flowi6_oif != rt->dst.dev->ifindex) 1429b55b76b2SDuan Jiong continue; 1430b55b76b2SDuan Jiong if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) 1431b55b76b2SDuan Jiong continue; 1432b55b76b2SDuan Jiong break; 1433b55b76b2SDuan Jiong } 1434b55b76b2SDuan Jiong 1435b55b76b2SDuan Jiong if (!rt) 1436b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1437b55b76b2SDuan Jiong else if (rt->dst.error) { 1438b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1439b0a1ba59SMartin KaFai Lau goto out; 1440b0a1ba59SMartin KaFai Lau } 1441b0a1ba59SMartin KaFai Lau 1442b0a1ba59SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1443a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1444a3c00e46SMartin KaFai Lau if (fn) 1445a3c00e46SMartin KaFai Lau goto restart; 1446b55b76b2SDuan Jiong } 1447a3c00e46SMartin KaFai Lau 1448b0a1ba59SMartin KaFai Lau out: 1449b55b76b2SDuan Jiong dst_hold(&rt->dst); 1450b55b76b2SDuan Jiong 1451b55b76b2SDuan Jiong read_unlock_bh(&table->tb6_lock); 1452b55b76b2SDuan Jiong 1453b55b76b2SDuan Jiong return rt; 1454b55b76b2SDuan Jiong }; 1455b55b76b2SDuan Jiong 1456b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net, 1457b55b76b2SDuan Jiong const struct flowi6 *fl6, 1458b55b76b2SDuan Jiong const struct in6_addr *gateway) 1459b55b76b2SDuan Jiong { 1460b55b76b2SDuan Jiong int flags = RT6_LOOKUP_F_HAS_SADDR; 1461b55b76b2SDuan Jiong struct ip6rd_flowi rdfl; 1462b55b76b2SDuan Jiong 1463b55b76b2SDuan Jiong rdfl.fl6 = *fl6; 1464b55b76b2SDuan Jiong rdfl.gateway = *gateway; 1465b55b76b2SDuan Jiong 1466b55b76b2SDuan Jiong return fib6_rule_lookup(net, &rdfl.fl6, 1467b55b76b2SDuan Jiong flags, __ip6_route_redirect); 1468b55b76b2SDuan Jiong } 1469b55b76b2SDuan Jiong 14703a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 14713a5ad2eeSDavid S. Miller { 14723a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 14733a5ad2eeSDavid S. Miller struct dst_entry *dst; 14743a5ad2eeSDavid S. Miller struct flowi6 fl6; 14753a5ad2eeSDavid S. Miller 14763a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 1477e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 14783a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 14793a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 14803a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 14813a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 14826502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 14833a5ad2eeSDavid S. Miller 1484b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); 14856700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 14863a5ad2eeSDavid S. Miller dst_release(dst); 14873a5ad2eeSDavid S. Miller } 14883a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 14893a5ad2eeSDavid S. Miller 1490c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, 1491c92a59ecSDuan Jiong u32 mark) 1492c92a59ecSDuan Jiong { 1493c92a59ecSDuan Jiong const struct ipv6hdr *iph = ipv6_hdr(skb); 1494c92a59ecSDuan Jiong const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 1495c92a59ecSDuan Jiong struct dst_entry *dst; 1496c92a59ecSDuan Jiong struct flowi6 fl6; 1497c92a59ecSDuan Jiong 1498c92a59ecSDuan Jiong memset(&fl6, 0, sizeof(fl6)); 1499e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 1500c92a59ecSDuan Jiong fl6.flowi6_oif = oif; 1501c92a59ecSDuan Jiong fl6.flowi6_mark = mark; 1502c92a59ecSDuan Jiong fl6.daddr = msg->dest; 1503c92a59ecSDuan Jiong fl6.saddr = iph->daddr; 1504c92a59ecSDuan Jiong 1505b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &iph->saddr); 1506c92a59ecSDuan Jiong rt6_do_redirect(dst, NULL, skb); 1507c92a59ecSDuan Jiong dst_release(dst); 1508c92a59ecSDuan Jiong } 1509c92a59ecSDuan Jiong 15103a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 15113a5ad2eeSDavid S. Miller { 15123a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 15133a5ad2eeSDavid S. Miller } 15143a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 15153a5ad2eeSDavid S. Miller 15160dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 15171da177e4SLinus Torvalds { 15180dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 15190dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 15200dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 15210dbaee3bSDavid S. Miller 15221da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 15231da177e4SLinus Torvalds 15245578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 15255578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 15261da177e4SLinus Torvalds 15271da177e4SLinus Torvalds /* 15281da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 15291da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 15301da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 15311da177e4SLinus Torvalds * rely only on pmtu discovery" 15321da177e4SLinus Torvalds */ 15331da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 15341da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 15351da177e4SLinus Torvalds return mtu; 15361da177e4SLinus Torvalds } 15371da177e4SLinus Torvalds 1538ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1539d33e4553SDavid S. Miller { 15404b32b5adSMartin KaFai Lau const struct rt6_info *rt = (const struct rt6_info *)dst; 15414b32b5adSMartin KaFai Lau unsigned int mtu = rt->rt6i_pmtu; 1542d33e4553SDavid S. Miller struct inet6_dev *idev; 1543618f9bc7SSteffen Klassert 1544618f9bc7SSteffen Klassert if (mtu) 154530f78d8eSEric Dumazet goto out; 1546618f9bc7SSteffen Klassert 15474b32b5adSMartin KaFai Lau mtu = dst_metric_raw(dst, RTAX_MTU); 15484b32b5adSMartin KaFai Lau if (mtu) 15494b32b5adSMartin KaFai Lau goto out; 15504b32b5adSMartin KaFai Lau 1551618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1552d33e4553SDavid S. Miller 1553d33e4553SDavid S. Miller rcu_read_lock(); 1554d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1555d33e4553SDavid S. Miller if (idev) 1556d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1557d33e4553SDavid S. Miller rcu_read_unlock(); 1558d33e4553SDavid S. Miller 155930f78d8eSEric Dumazet out: 156030f78d8eSEric Dumazet return min_t(unsigned int, mtu, IP6_MAX_MTU); 1561d33e4553SDavid S. Miller } 1562d33e4553SDavid S. Miller 15633b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 15643b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 15655d0bbeebSThomas Graf 15663b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 156787a11578SDavid S. Miller struct flowi6 *fl6) 15681da177e4SLinus Torvalds { 156987a11578SDavid S. Miller struct dst_entry *dst; 15701da177e4SLinus Torvalds struct rt6_info *rt; 15711da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1572c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 15731da177e4SLinus Torvalds 157438308473SDavid S. Miller if (unlikely(!idev)) 1575122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 15761da177e4SLinus Torvalds 1577ad706862SMartin KaFai Lau rt = ip6_dst_alloc(net, dev, 0); 157838308473SDavid S. Miller if (unlikely(!rt)) { 15791da177e4SLinus Torvalds in6_dev_put(idev); 158087a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 15811da177e4SLinus Torvalds goto out; 15821da177e4SLinus Torvalds } 15831da177e4SLinus Torvalds 15848e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 15858e2ec639SYan, Zheng rt->dst.output = ip6_output; 1586d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 1587550bab42SJulian Anastasov rt->rt6i_gateway = fl6->daddr; 158887a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 15898e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 15908e2ec639SYan, Zheng rt->rt6i_idev = idev; 159114edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 15921da177e4SLinus Torvalds 15933b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1594d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1595d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 15963b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 15971da177e4SLinus Torvalds 15985578689aSDaniel Lezcano fib6_force_start_gc(net); 15991da177e4SLinus Torvalds 160087a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 160187a11578SDavid S. Miller 16021da177e4SLinus Torvalds out: 160387a11578SDavid S. Miller return dst; 16041da177e4SLinus Torvalds } 16051da177e4SLinus Torvalds 16063d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 16071da177e4SLinus Torvalds { 1608e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 16093d0f24a7SStephen Hemminger int more = 0; 16101da177e4SLinus Torvalds 16113b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 16123b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 16135d0bbeebSThomas Graf 16141da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 16151da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 16161da177e4SLinus Torvalds *pprev = dst->next; 16171da177e4SLinus Torvalds dst_free(dst); 16181da177e4SLinus Torvalds } else { 16191da177e4SLinus Torvalds pprev = &dst->next; 16203d0f24a7SStephen Hemminger ++more; 16211da177e4SLinus Torvalds } 16221da177e4SLinus Torvalds } 16231da177e4SLinus Torvalds 16243b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 16255d0bbeebSThomas Graf 16263d0f24a7SStephen Hemminger return more; 16271da177e4SLinus Torvalds } 16281da177e4SLinus Torvalds 16291e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 16301e493d19SDavid S. Miller void *arg) 16311e493d19SDavid S. Miller { 16321e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 16331e493d19SDavid S. Miller 16341e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 16351e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 16361e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 16371e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 16381e493d19SDavid S. Miller if (func(rt, arg)) { 16391e493d19SDavid S. Miller *pprev = dst->next; 16401e493d19SDavid S. Miller dst_free(dst); 16411e493d19SDavid S. Miller } else { 16421e493d19SDavid S. Miller pprev = &dst->next; 16431e493d19SDavid S. Miller } 16441e493d19SDavid S. Miller } 16451e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 16461e493d19SDavid S. Miller } 16471e493d19SDavid S. Miller 1648569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 16491da177e4SLinus Torvalds { 165086393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 16517019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 16527019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 16537019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 16547019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 16557019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1656fc66f95cSEric Dumazet int entries; 16571da177e4SLinus Torvalds 1658fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 165949a18d86SMichal Kubeček if (time_after(rt_last_gc + rt_min_interval, jiffies) && 1660fc66f95cSEric Dumazet entries <= rt_max_size) 16611da177e4SLinus Torvalds goto out; 16621da177e4SLinus Torvalds 16636891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 166414956643SLi RongQing fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true); 1665fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1666fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 16677019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 16681da177e4SLinus Torvalds out: 16697019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1670fc66f95cSEric Dumazet return entries > rt_max_size; 16711da177e4SLinus Torvalds } 16721da177e4SLinus Torvalds 1673e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc, 1674e715b6d3SFlorian Westphal const struct fib6_config *cfg) 1675e715b6d3SFlorian Westphal { 1676c3a8d947SDaniel Borkmann bool ecn_ca = false; 1677e715b6d3SFlorian Westphal struct nlattr *nla; 1678e715b6d3SFlorian Westphal int remaining; 1679e715b6d3SFlorian Westphal u32 *mp; 1680e715b6d3SFlorian Westphal 168163159f29SIan Morris if (!cfg->fc_mx) 1682e715b6d3SFlorian Westphal return 0; 1683e715b6d3SFlorian Westphal 1684e715b6d3SFlorian Westphal mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 1685e715b6d3SFlorian Westphal if (unlikely(!mp)) 1686e715b6d3SFlorian Westphal return -ENOMEM; 1687e715b6d3SFlorian Westphal 1688e715b6d3SFlorian Westphal nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 1689e715b6d3SFlorian Westphal int type = nla_type(nla); 1690ea697639SDaniel Borkmann u32 val; 1691ea697639SDaniel Borkmann 16921bb14807SDaniel Borkmann if (!type) 16931bb14807SDaniel Borkmann continue; 1694e715b6d3SFlorian Westphal if (unlikely(type > RTAX_MAX)) 1695e715b6d3SFlorian Westphal goto err; 16961bb14807SDaniel Borkmann 1697ea697639SDaniel Borkmann if (type == RTAX_CC_ALGO) { 1698ea697639SDaniel Borkmann char tmp[TCP_CA_NAME_MAX]; 1699e715b6d3SFlorian Westphal 1700ea697639SDaniel Borkmann nla_strlcpy(tmp, nla, sizeof(tmp)); 1701c3a8d947SDaniel Borkmann val = tcp_ca_get_key_by_name(tmp, &ecn_ca); 1702ea697639SDaniel Borkmann if (val == TCP_CA_UNSPEC) 1703ea697639SDaniel Borkmann goto err; 1704ea697639SDaniel Borkmann } else { 1705ea697639SDaniel Borkmann val = nla_get_u32(nla); 1706ea697639SDaniel Borkmann } 1707b8d3e416SDaniel Borkmann if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK)) 1708b8d3e416SDaniel Borkmann goto err; 1709ea697639SDaniel Borkmann 1710ea697639SDaniel Borkmann mp[type - 1] = val; 1711e715b6d3SFlorian Westphal __set_bit(type - 1, mxc->mx_valid); 1712e715b6d3SFlorian Westphal } 1713e715b6d3SFlorian Westphal 1714c3a8d947SDaniel Borkmann if (ecn_ca) { 1715c3a8d947SDaniel Borkmann __set_bit(RTAX_FEATURES - 1, mxc->mx_valid); 1716c3a8d947SDaniel Borkmann mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA; 1717c3a8d947SDaniel Borkmann } 1718e715b6d3SFlorian Westphal 1719c3a8d947SDaniel Borkmann mxc->mx = mp; 1720e715b6d3SFlorian Westphal return 0; 1721e715b6d3SFlorian Westphal err: 1722e715b6d3SFlorian Westphal kfree(mp); 1723e715b6d3SFlorian Westphal return -EINVAL; 1724e715b6d3SFlorian Westphal } 17251da177e4SLinus Torvalds 17266b9ea5a6SRoopa Prabhu int ip6_route_info_create(struct fib6_config *cfg, struct rt6_info **rt_ret) 17271da177e4SLinus Torvalds { 17281da177e4SLinus Torvalds int err; 17295578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 17301da177e4SLinus Torvalds struct rt6_info *rt = NULL; 17311da177e4SLinus Torvalds struct net_device *dev = NULL; 17321da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1733c71099acSThomas Graf struct fib6_table *table; 17341da177e4SLinus Torvalds int addr_type; 17351da177e4SLinus Torvalds 173686872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 17371da177e4SLinus Torvalds return -EINVAL; 17381da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 173986872cb5SThomas Graf if (cfg->fc_src_len) 17401da177e4SLinus Torvalds return -EINVAL; 17411da177e4SLinus Torvalds #endif 174286872cb5SThomas Graf if (cfg->fc_ifindex) { 17431da177e4SLinus Torvalds err = -ENODEV; 17445578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 17451da177e4SLinus Torvalds if (!dev) 17461da177e4SLinus Torvalds goto out; 17471da177e4SLinus Torvalds idev = in6_dev_get(dev); 17481da177e4SLinus Torvalds if (!idev) 17491da177e4SLinus Torvalds goto out; 17501da177e4SLinus Torvalds } 17511da177e4SLinus Torvalds 175286872cb5SThomas Graf if (cfg->fc_metric == 0) 175386872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 17541da177e4SLinus Torvalds 1755c71099acSThomas Graf err = -ENOBUFS; 175638308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1757d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1758d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 175938308473SDavid S. Miller if (!table) { 1760f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1761d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1762d71314b4SMatti Vaittinen } 1763d71314b4SMatti Vaittinen } else { 1764d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1765d71314b4SMatti Vaittinen } 176638308473SDavid S. Miller 176738308473SDavid S. Miller if (!table) 1768c71099acSThomas Graf goto out; 1769c71099acSThomas Graf 1770ad706862SMartin KaFai Lau rt = ip6_dst_alloc(net, NULL, 1771ad706862SMartin KaFai Lau (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT); 17721da177e4SLinus Torvalds 177338308473SDavid S. Miller if (!rt) { 17741da177e4SLinus Torvalds err = -ENOMEM; 17751da177e4SLinus Torvalds goto out; 17761da177e4SLinus Torvalds } 17771da177e4SLinus Torvalds 17781716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 17791716a961SGao feng rt6_set_expires(rt, jiffies + 17801716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 17811716a961SGao feng else 17821716a961SGao feng rt6_clean_expires(rt); 17831da177e4SLinus Torvalds 178486872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 178586872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 178686872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 178786872cb5SThomas Graf 178886872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 17891da177e4SLinus Torvalds 17901da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1791d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1792ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1793ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 17941da177e4SLinus Torvalds else 1795d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 17961da177e4SLinus Torvalds 1797d8d1f30bSChangli Gao rt->dst.output = ip6_output; 17981da177e4SLinus Torvalds 179919e42e45SRoopa Prabhu if (cfg->fc_encap) { 180019e42e45SRoopa Prabhu struct lwtunnel_state *lwtstate; 180119e42e45SRoopa Prabhu 180219e42e45SRoopa Prabhu err = lwtunnel_build_state(dev, cfg->fc_encap_type, 1803127eb7cdSTom Herbert cfg->fc_encap, AF_INET6, cfg, 1804127eb7cdSTom Herbert &lwtstate); 180519e42e45SRoopa Prabhu if (err) 180619e42e45SRoopa Prabhu goto out; 180761adedf3SJiri Benc rt->dst.lwtstate = lwtstate_get(lwtstate); 180861adedf3SJiri Benc if (lwtunnel_output_redirect(rt->dst.lwtstate)) { 180961adedf3SJiri Benc rt->dst.lwtstate->orig_output = rt->dst.output; 181061adedf3SJiri Benc rt->dst.output = lwtunnel_output; 181119e42e45SRoopa Prabhu } 181261adedf3SJiri Benc if (lwtunnel_input_redirect(rt->dst.lwtstate)) { 181361adedf3SJiri Benc rt->dst.lwtstate->orig_input = rt->dst.input; 181461adedf3SJiri Benc rt->dst.input = lwtunnel_input; 181525368623STom Herbert } 181625368623STom Herbert } 181719e42e45SRoopa Prabhu 181886872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 181986872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 1820afc4eef8SMartin KaFai Lau if (rt->rt6i_dst.plen == 128) 182111d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 18221da177e4SLinus Torvalds 18231da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 182486872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 182586872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 18261da177e4SLinus Torvalds #endif 18271da177e4SLinus Torvalds 182886872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 18291da177e4SLinus Torvalds 18301da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 18311da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 18321da177e4SLinus Torvalds */ 183386872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 183438308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 183538308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 183638308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 18371da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 18385578689aSDaniel Lezcano if (dev != net->loopback_dev) { 18391da177e4SLinus Torvalds if (dev) { 18401da177e4SLinus Torvalds dev_put(dev); 18411da177e4SLinus Torvalds in6_dev_put(idev); 18421da177e4SLinus Torvalds } 18435578689aSDaniel Lezcano dev = net->loopback_dev; 18441da177e4SLinus Torvalds dev_hold(dev); 18451da177e4SLinus Torvalds idev = in6_dev_get(dev); 18461da177e4SLinus Torvalds if (!idev) { 18471da177e4SLinus Torvalds err = -ENODEV; 18481da177e4SLinus Torvalds goto out; 18491da177e4SLinus Torvalds } 18501da177e4SLinus Torvalds } 18511da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1852ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1853ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1854ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 1855aad88724SEric Dumazet rt->dst.output = dst_discard_sk; 18567150aedeSKamala R rt->dst.input = dst_discard; 1857ef2c7d7bSNicolas Dichtel break; 1858ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1859ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 18607150aedeSKamala R rt->dst.output = ip6_pkt_prohibit_out; 18617150aedeSKamala R rt->dst.input = ip6_pkt_prohibit; 1862ef2c7d7bSNicolas Dichtel break; 1863b4949ab2SNicolas Dichtel case RTN_THROW: 18640315e382SNikola Forró case RTN_UNREACHABLE: 1865ef2c7d7bSNicolas Dichtel default: 18667150aedeSKamala R rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN 18670315e382SNikola Forró : (cfg->fc_type == RTN_UNREACHABLE) 18680315e382SNikola Forró ? -EHOSTUNREACH : -ENETUNREACH; 18697150aedeSKamala R rt->dst.output = ip6_pkt_discard_out; 18707150aedeSKamala R rt->dst.input = ip6_pkt_discard; 1871ef2c7d7bSNicolas Dichtel break; 1872ef2c7d7bSNicolas Dichtel } 18731da177e4SLinus Torvalds goto install_route; 18741da177e4SLinus Torvalds } 18751da177e4SLinus Torvalds 187686872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1877b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 18781da177e4SLinus Torvalds int gwa_type; 18791da177e4SLinus Torvalds 188086872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 1881330567b7SFlorian Westphal gwa_type = ipv6_addr_type(gw_addr); 188248ed7b26SFlorian Westphal 188348ed7b26SFlorian Westphal /* if gw_addr is local we will fail to detect this in case 188448ed7b26SFlorian Westphal * address is still TENTATIVE (DAD in progress). rt6_lookup() 188548ed7b26SFlorian Westphal * will return already-added prefix route via interface that 188648ed7b26SFlorian Westphal * prefix route was assigned to, which might be non-loopback. 188748ed7b26SFlorian Westphal */ 188848ed7b26SFlorian Westphal err = -EINVAL; 1889330567b7SFlorian Westphal if (ipv6_chk_addr_and_flags(net, gw_addr, 1890330567b7SFlorian Westphal gwa_type & IPV6_ADDR_LINKLOCAL ? 1891330567b7SFlorian Westphal dev : NULL, 0, 0)) 189248ed7b26SFlorian Westphal goto out; 189348ed7b26SFlorian Westphal 18944e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 18951da177e4SLinus Torvalds 18961da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 18971da177e4SLinus Torvalds struct rt6_info *grt; 18981da177e4SLinus Torvalds 18991da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 19001da177e4SLinus Torvalds addresses as nexthop address. 19011da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 19021da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 19031da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 19041da177e4SLinus Torvalds some exceptions. --ANK 19051da177e4SLinus Torvalds */ 19061da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 19071da177e4SLinus Torvalds goto out; 19081da177e4SLinus Torvalds 19095578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 19101da177e4SLinus Torvalds 19111da177e4SLinus Torvalds err = -EHOSTUNREACH; 191238308473SDavid S. Miller if (!grt) 19131da177e4SLinus Torvalds goto out; 19141da177e4SLinus Torvalds if (dev) { 1915d1918542SDavid S. Miller if (dev != grt->dst.dev) { 191694e187c0SAmerigo Wang ip6_rt_put(grt); 19171da177e4SLinus Torvalds goto out; 19181da177e4SLinus Torvalds } 19191da177e4SLinus Torvalds } else { 1920d1918542SDavid S. Miller dev = grt->dst.dev; 19211da177e4SLinus Torvalds idev = grt->rt6i_idev; 19221da177e4SLinus Torvalds dev_hold(dev); 19231da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 19241da177e4SLinus Torvalds } 19251da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 19261da177e4SLinus Torvalds err = 0; 192794e187c0SAmerigo Wang ip6_rt_put(grt); 19281da177e4SLinus Torvalds 19291da177e4SLinus Torvalds if (err) 19301da177e4SLinus Torvalds goto out; 19311da177e4SLinus Torvalds } 19321da177e4SLinus Torvalds err = -EINVAL; 193338308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 19341da177e4SLinus Torvalds goto out; 19351da177e4SLinus Torvalds } 19361da177e4SLinus Torvalds 19371da177e4SLinus Torvalds err = -ENODEV; 193838308473SDavid S. Miller if (!dev) 19391da177e4SLinus Torvalds goto out; 19401da177e4SLinus Torvalds 1941c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1942c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1943c3968a85SDaniel Walter err = -EINVAL; 1944c3968a85SDaniel Walter goto out; 1945c3968a85SDaniel Walter } 19464e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1947c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1948c3968a85SDaniel Walter } else 1949c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1950c3968a85SDaniel Walter 195186872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 19521da177e4SLinus Torvalds 19531da177e4SLinus Torvalds install_route: 1954d8d1f30bSChangli Gao rt->dst.dev = dev; 19551da177e4SLinus Torvalds rt->rt6i_idev = idev; 1956c71099acSThomas Graf rt->rt6i_table = table; 195763152fc0SDaniel Lezcano 1958c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 195963152fc0SDaniel Lezcano 19606b9ea5a6SRoopa Prabhu *rt_ret = rt; 19611da177e4SLinus Torvalds 19626b9ea5a6SRoopa Prabhu return 0; 19631da177e4SLinus Torvalds out: 19641da177e4SLinus Torvalds if (dev) 19651da177e4SLinus Torvalds dev_put(dev); 19661da177e4SLinus Torvalds if (idev) 19671da177e4SLinus Torvalds in6_dev_put(idev); 19681da177e4SLinus Torvalds if (rt) 1969d8d1f30bSChangli Gao dst_free(&rt->dst); 19706b9ea5a6SRoopa Prabhu 19716b9ea5a6SRoopa Prabhu *rt_ret = NULL; 19726b9ea5a6SRoopa Prabhu 19736b9ea5a6SRoopa Prabhu return err; 19746b9ea5a6SRoopa Prabhu } 19756b9ea5a6SRoopa Prabhu 19766b9ea5a6SRoopa Prabhu int ip6_route_add(struct fib6_config *cfg) 19776b9ea5a6SRoopa Prabhu { 19786b9ea5a6SRoopa Prabhu struct mx6_config mxc = { .mx = NULL, }; 19796b9ea5a6SRoopa Prabhu struct rt6_info *rt = NULL; 19806b9ea5a6SRoopa Prabhu int err; 19816b9ea5a6SRoopa Prabhu 19826b9ea5a6SRoopa Prabhu err = ip6_route_info_create(cfg, &rt); 19836b9ea5a6SRoopa Prabhu if (err) 19846b9ea5a6SRoopa Prabhu goto out; 19856b9ea5a6SRoopa Prabhu 19866b9ea5a6SRoopa Prabhu err = ip6_convert_metrics(&mxc, cfg); 19876b9ea5a6SRoopa Prabhu if (err) 19886b9ea5a6SRoopa Prabhu goto out; 19896b9ea5a6SRoopa Prabhu 19906b9ea5a6SRoopa Prabhu err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); 19916b9ea5a6SRoopa Prabhu 19926b9ea5a6SRoopa Prabhu kfree(mxc.mx); 19936b9ea5a6SRoopa Prabhu 19946b9ea5a6SRoopa Prabhu return err; 19956b9ea5a6SRoopa Prabhu out: 19966b9ea5a6SRoopa Prabhu if (rt) 19976b9ea5a6SRoopa Prabhu dst_free(&rt->dst); 19986b9ea5a6SRoopa Prabhu 19991da177e4SLinus Torvalds return err; 20001da177e4SLinus Torvalds } 20011da177e4SLinus Torvalds 200286872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 20031da177e4SLinus Torvalds { 20041da177e4SLinus Torvalds int err; 2005c71099acSThomas Graf struct fib6_table *table; 2006d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 20071da177e4SLinus Torvalds 20088e3d5be7SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry || 20098e3d5be7SMartin KaFai Lau rt->dst.flags & DST_NOCACHE) { 20106825a26cSGao feng err = -ENOENT; 20116825a26cSGao feng goto out; 20126825a26cSGao feng } 20136c813a72SPatrick McHardy 2014c71099acSThomas Graf table = rt->rt6i_table; 2015c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 201686872cb5SThomas Graf err = fib6_del(rt, info); 2017c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 20181da177e4SLinus Torvalds 20196825a26cSGao feng out: 202094e187c0SAmerigo Wang ip6_rt_put(rt); 20211da177e4SLinus Torvalds return err; 20221da177e4SLinus Torvalds } 20231da177e4SLinus Torvalds 2024e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 2025e0a1ad73SThomas Graf { 20264d1169c1SDenis V. Lunev struct nl_info info = { 2027d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 20284d1169c1SDenis V. Lunev }; 2029528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 2030e0a1ad73SThomas Graf } 2031e0a1ad73SThomas Graf 203286872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 20331da177e4SLinus Torvalds { 2034c71099acSThomas Graf struct fib6_table *table; 20351da177e4SLinus Torvalds struct fib6_node *fn; 20361da177e4SLinus Torvalds struct rt6_info *rt; 20371da177e4SLinus Torvalds int err = -ESRCH; 20381da177e4SLinus Torvalds 20395578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 204038308473SDavid S. Miller if (!table) 2041c71099acSThomas Graf return err; 20421da177e4SLinus Torvalds 2043c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2044c71099acSThomas Graf 2045c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 204686872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 204786872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 20481da177e4SLinus Torvalds 20491da177e4SLinus Torvalds if (fn) { 2050d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 20511f56a01fSMartin KaFai Lau if ((rt->rt6i_flags & RTF_CACHE) && 20521f56a01fSMartin KaFai Lau !(cfg->fc_flags & RTF_CACHE)) 20531f56a01fSMartin KaFai Lau continue; 205486872cb5SThomas Graf if (cfg->fc_ifindex && 2055d1918542SDavid S. Miller (!rt->dst.dev || 2056d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 20571da177e4SLinus Torvalds continue; 205886872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 205986872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 20601da177e4SLinus Torvalds continue; 206186872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 20621da177e4SLinus Torvalds continue; 2063d8d1f30bSChangli Gao dst_hold(&rt->dst); 2064c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 20651da177e4SLinus Torvalds 206686872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 20671da177e4SLinus Torvalds } 20681da177e4SLinus Torvalds } 2069c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 20701da177e4SLinus Torvalds 20711da177e4SLinus Torvalds return err; 20721da177e4SLinus Torvalds } 20731da177e4SLinus Torvalds 20746700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 2075a6279458SYOSHIFUJI Hideaki { 2076e8599ff4SDavid S. Miller struct net *net = dev_net(skb->dev); 2077a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 2078e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 2079e8599ff4SDavid S. Miller struct ndisc_options ndopts; 2080e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 2081e8599ff4SDavid S. Miller struct neighbour *neigh; 208271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 struct rd_msg *msg; 20836e157b6aSDavid S. Miller int optlen, on_link; 20846e157b6aSDavid S. Miller u8 *lladdr; 2085e8599ff4SDavid S. Miller 208629a3cad5SSimon Horman optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 208771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 optlen -= sizeof(*msg); 2088e8599ff4SDavid S. Miller 2089e8599ff4SDavid S. Miller if (optlen < 0) { 20906e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 2091e8599ff4SDavid S. Miller return; 2092e8599ff4SDavid S. Miller } 2093e8599ff4SDavid S. Miller 209471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 msg = (struct rd_msg *)icmp6_hdr(skb); 2095e8599ff4SDavid S. Miller 209671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_is_multicast(&msg->dest)) { 20976e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 2098e8599ff4SDavid S. Miller return; 2099e8599ff4SDavid S. Miller } 2100e8599ff4SDavid S. Miller 21016e157b6aSDavid S. Miller on_link = 0; 210271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_equal(&msg->dest, &msg->target)) { 2103e8599ff4SDavid S. Miller on_link = 1; 210471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 } else if (ipv6_addr_type(&msg->target) != 2105e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 21066e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 2107e8599ff4SDavid S. Miller return; 2108e8599ff4SDavid S. Miller } 2109e8599ff4SDavid S. Miller 2110e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 2111e8599ff4SDavid S. Miller if (!in6_dev) 2112e8599ff4SDavid S. Miller return; 2113e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 2114e8599ff4SDavid S. Miller return; 2115e8599ff4SDavid S. Miller 2116e8599ff4SDavid S. Miller /* RFC2461 8.1: 2117e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 2118e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 2119e8599ff4SDavid S. Miller */ 2120e8599ff4SDavid S. Miller 212171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) { 2122e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 2123e8599ff4SDavid S. Miller return; 2124e8599ff4SDavid S. Miller } 21256e157b6aSDavid S. Miller 21266e157b6aSDavid S. Miller lladdr = NULL; 2127e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 2128e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 2129e8599ff4SDavid S. Miller skb->dev); 2130e8599ff4SDavid S. Miller if (!lladdr) { 2131e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 2132e8599ff4SDavid S. Miller return; 2133e8599ff4SDavid S. Miller } 2134e8599ff4SDavid S. Miller } 2135e8599ff4SDavid S. Miller 21366e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 21376e157b6aSDavid S. Miller if (rt == net->ipv6.ip6_null_entry) { 21386e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 21396e157b6aSDavid S. Miller return; 21406e157b6aSDavid S. Miller } 21416e157b6aSDavid S. Miller 21426e157b6aSDavid S. Miller /* Redirect received -> path was valid. 21436e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 21446e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 21456e157b6aSDavid S. Miller */ 21466e157b6aSDavid S. Miller dst_confirm(&rt->dst); 21476e157b6aSDavid S. Miller 214871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 2149e8599ff4SDavid S. Miller if (!neigh) 2150e8599ff4SDavid S. Miller return; 2151e8599ff4SDavid S. Miller 21521da177e4SLinus Torvalds /* 21531da177e4SLinus Torvalds * We have finally decided to accept it. 21541da177e4SLinus Torvalds */ 21551da177e4SLinus Torvalds 21561da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 21571da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 21581da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 21591da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 21601da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 21611da177e4SLinus Torvalds ); 21621da177e4SLinus Torvalds 216383a09abdSMartin KaFai Lau nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL); 216438308473SDavid S. Miller if (!nrt) 21651da177e4SLinus Torvalds goto out; 21661da177e4SLinus Torvalds 21671da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 21681da177e4SLinus Torvalds if (on_link) 21691da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 21701da177e4SLinus Torvalds 21714e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 21721da177e4SLinus Torvalds 217340e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 21741da177e4SLinus Torvalds goto out; 21751da177e4SLinus Torvalds 2176d8d1f30bSChangli Gao netevent.old = &rt->dst; 2177d8d1f30bSChangli Gao netevent.new = &nrt->dst; 217871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 netevent.daddr = &msg->dest; 217960592833SYOSHIFUJI Hideaki / 吉藤英明 netevent.neigh = neigh; 21808d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 21818d71740cSTom Tucker 21821da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 21836e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 2184e0a1ad73SThomas Graf ip6_del_rt(rt); 21851da177e4SLinus Torvalds } 21861da177e4SLinus Torvalds 21871da177e4SLinus Torvalds out: 2188e8599ff4SDavid S. Miller neigh_release(neigh); 21896e157b6aSDavid S. Miller } 21906e157b6aSDavid S. Miller 21911da177e4SLinus Torvalds /* 21921da177e4SLinus Torvalds * Misc support functions 21931da177e4SLinus Torvalds */ 21941da177e4SLinus Torvalds 21954b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) 21964b32b5adSMartin KaFai Lau { 21974b32b5adSMartin KaFai Lau BUG_ON(from->dst.from); 21984b32b5adSMartin KaFai Lau 21994b32b5adSMartin KaFai Lau rt->rt6i_flags &= ~RTF_EXPIRES; 22004b32b5adSMartin KaFai Lau dst_hold(&from->dst); 22014b32b5adSMartin KaFai Lau rt->dst.from = &from->dst; 22024b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true); 22034b32b5adSMartin KaFai Lau } 22044b32b5adSMartin KaFai Lau 220583a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort) 22061da177e4SLinus Torvalds { 2207d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 2208d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 220983a09abdSMartin KaFai Lau rt->rt6i_dst = ort->rt6i_dst; 2210d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 22111da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 22121da177e4SLinus Torvalds if (rt->rt6i_idev) 22131da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 2214d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 22154e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 22161716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 22171716a961SGao feng rt6_set_from(rt, ort); 221883a09abdSMartin KaFai Lau rt->rt6i_metric = ort->rt6i_metric; 22191da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 222083a09abdSMartin KaFai Lau rt->rt6i_src = ort->rt6i_src; 22211da177e4SLinus Torvalds #endif 222283a09abdSMartin KaFai Lau rt->rt6i_prefsrc = ort->rt6i_prefsrc; 2223c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 222461adedf3SJiri Benc rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate); 22251da177e4SLinus Torvalds } 22261da177e4SLinus Torvalds 222770ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 2228efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 2229b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2230b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 223170ceb4f5SYOSHIFUJI Hideaki { 223270ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 223370ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 2234c71099acSThomas Graf struct fib6_table *table; 223570ceb4f5SYOSHIFUJI Hideaki 2236efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 223738308473SDavid S. Miller if (!table) 2238c71099acSThomas Graf return NULL; 2239c71099acSThomas Graf 22405744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2241c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0); 224270ceb4f5SYOSHIFUJI Hideaki if (!fn) 224370ceb4f5SYOSHIFUJI Hideaki goto out; 224470ceb4f5SYOSHIFUJI Hideaki 2245d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 2246d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 224770ceb4f5SYOSHIFUJI Hideaki continue; 224870ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 224970ceb4f5SYOSHIFUJI Hideaki continue; 225070ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 225170ceb4f5SYOSHIFUJI Hideaki continue; 2252d8d1f30bSChangli Gao dst_hold(&rt->dst); 225370ceb4f5SYOSHIFUJI Hideaki break; 225470ceb4f5SYOSHIFUJI Hideaki } 225570ceb4f5SYOSHIFUJI Hideaki out: 22565744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 225770ceb4f5SYOSHIFUJI Hideaki return rt; 225870ceb4f5SYOSHIFUJI Hideaki } 225970ceb4f5SYOSHIFUJI Hideaki 2260efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 2261b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2262b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 226395c96174SEric Dumazet unsigned int pref) 226470ceb4f5SYOSHIFUJI Hideaki { 226586872cb5SThomas Graf struct fib6_config cfg = { 226686872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 2267238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 226886872cb5SThomas Graf .fc_ifindex = ifindex, 226986872cb5SThomas Graf .fc_dst_len = prefixlen, 227086872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 227186872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 227215e47304SEric W. Biederman .fc_nlinfo.portid = 0, 2273efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 2274efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 227586872cb5SThomas Graf }; 227670ceb4f5SYOSHIFUJI Hideaki 22774e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 22784e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 227986872cb5SThomas Graf 2280e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 2281e317da96SYOSHIFUJI Hideaki if (!prefixlen) 228286872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 228370ceb4f5SYOSHIFUJI Hideaki 228486872cb5SThomas Graf ip6_route_add(&cfg); 228570ceb4f5SYOSHIFUJI Hideaki 2286efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 228770ceb4f5SYOSHIFUJI Hideaki } 228870ceb4f5SYOSHIFUJI Hideaki #endif 228970ceb4f5SYOSHIFUJI Hideaki 2290b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 22911da177e4SLinus Torvalds { 22921da177e4SLinus Torvalds struct rt6_info *rt; 2293c71099acSThomas Graf struct fib6_table *table; 22941da177e4SLinus Torvalds 2295c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 229638308473SDavid S. Miller if (!table) 2297c71099acSThomas Graf return NULL; 22981da177e4SLinus Torvalds 22995744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2300d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 2301d1918542SDavid S. Miller if (dev == rt->dst.dev && 2302045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 23031da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 23041da177e4SLinus Torvalds break; 23051da177e4SLinus Torvalds } 23061da177e4SLinus Torvalds if (rt) 2307d8d1f30bSChangli Gao dst_hold(&rt->dst); 23085744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 23091da177e4SLinus Torvalds return rt; 23101da177e4SLinus Torvalds } 23111da177e4SLinus Torvalds 2312b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 2313ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 2314ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 23151da177e4SLinus Torvalds { 231686872cb5SThomas Graf struct fib6_config cfg = { 231786872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 2318238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 231986872cb5SThomas Graf .fc_ifindex = dev->ifindex, 232086872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 232186872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 232215e47304SEric W. Biederman .fc_nlinfo.portid = 0, 23235578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 2324c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 232586872cb5SThomas Graf }; 23261da177e4SLinus Torvalds 23274e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 23281da177e4SLinus Torvalds 232986872cb5SThomas Graf ip6_route_add(&cfg); 23301da177e4SLinus Torvalds 23311da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 23321da177e4SLinus Torvalds } 23331da177e4SLinus Torvalds 23347b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 23351da177e4SLinus Torvalds { 23361da177e4SLinus Torvalds struct rt6_info *rt; 2337c71099acSThomas Graf struct fib6_table *table; 2338c71099acSThomas Graf 2339c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 23407b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 234138308473SDavid S. Miller if (!table) 2342c71099acSThomas Graf return; 23431da177e4SLinus Torvalds 23441da177e4SLinus Torvalds restart: 2345c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2346d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 23473e8b0ac3SLorenzo Colitti if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 23483e8b0ac3SLorenzo Colitti (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { 2349d8d1f30bSChangli Gao dst_hold(&rt->dst); 2350c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2351e0a1ad73SThomas Graf ip6_del_rt(rt); 23521da177e4SLinus Torvalds goto restart; 23531da177e4SLinus Torvalds } 23541da177e4SLinus Torvalds } 2355c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 23561da177e4SLinus Torvalds } 23571da177e4SLinus Torvalds 23585578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 23595578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 236086872cb5SThomas Graf struct fib6_config *cfg) 236186872cb5SThomas Graf { 236286872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 236386872cb5SThomas Graf 236486872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 236586872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 236686872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 236786872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 236886872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 236986872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 237086872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 237186872cb5SThomas Graf 23725578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 2373f1243c2dSBenjamin Thery 23744e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 23754e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 23764e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 237786872cb5SThomas Graf } 237886872cb5SThomas Graf 23795578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 23801da177e4SLinus Torvalds { 238186872cb5SThomas Graf struct fib6_config cfg; 23821da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 23831da177e4SLinus Torvalds int err; 23841da177e4SLinus Torvalds 23851da177e4SLinus Torvalds switch (cmd) { 23861da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 23871da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 2388af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 23891da177e4SLinus Torvalds return -EPERM; 23901da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 23911da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 23921da177e4SLinus Torvalds if (err) 23931da177e4SLinus Torvalds return -EFAULT; 23941da177e4SLinus Torvalds 23955578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 239686872cb5SThomas Graf 23971da177e4SLinus Torvalds rtnl_lock(); 23981da177e4SLinus Torvalds switch (cmd) { 23991da177e4SLinus Torvalds case SIOCADDRT: 240086872cb5SThomas Graf err = ip6_route_add(&cfg); 24011da177e4SLinus Torvalds break; 24021da177e4SLinus Torvalds case SIOCDELRT: 240386872cb5SThomas Graf err = ip6_route_del(&cfg); 24041da177e4SLinus Torvalds break; 24051da177e4SLinus Torvalds default: 24061da177e4SLinus Torvalds err = -EINVAL; 24071da177e4SLinus Torvalds } 24081da177e4SLinus Torvalds rtnl_unlock(); 24091da177e4SLinus Torvalds 24101da177e4SLinus Torvalds return err; 24113ff50b79SStephen Hemminger } 24121da177e4SLinus Torvalds 24131da177e4SLinus Torvalds return -EINVAL; 24141da177e4SLinus Torvalds } 24151da177e4SLinus Torvalds 24161da177e4SLinus Torvalds /* 24171da177e4SLinus Torvalds * Drop the packet on the floor 24181da177e4SLinus Torvalds */ 24191da177e4SLinus Torvalds 2420d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 24211da177e4SLinus Torvalds { 2422612f09e8SYOSHIFUJI Hideaki int type; 2423adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2424612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2425612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 24260660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 242745bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 24283bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 24293bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2430612f09e8SYOSHIFUJI Hideaki break; 2431612f09e8SYOSHIFUJI Hideaki } 2432612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2433612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 24343bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 24353bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2436612f09e8SYOSHIFUJI Hideaki break; 2437612f09e8SYOSHIFUJI Hideaki } 24383ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 24391da177e4SLinus Torvalds kfree_skb(skb); 24401da177e4SLinus Torvalds return 0; 24411da177e4SLinus Torvalds } 24421da177e4SLinus Torvalds 24439ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 24449ce8ade0SThomas Graf { 2445612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 24469ce8ade0SThomas Graf } 24479ce8ade0SThomas Graf 2448aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb) 24491da177e4SLinus Torvalds { 2450adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2451612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 24521da177e4SLinus Torvalds } 24531da177e4SLinus Torvalds 24549ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 24559ce8ade0SThomas Graf { 2456612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 24579ce8ade0SThomas Graf } 24589ce8ade0SThomas Graf 2459aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb) 24609ce8ade0SThomas Graf { 2461adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2462612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 24639ce8ade0SThomas Graf } 24649ce8ade0SThomas Graf 24651da177e4SLinus Torvalds /* 24661da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 24671da177e4SLinus Torvalds */ 24681da177e4SLinus Torvalds 24691da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 24701da177e4SLinus Torvalds const struct in6_addr *addr, 24718f031519SDavid S. Miller bool anycast) 24721da177e4SLinus Torvalds { 2473c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 2474a3300ef4SHannes Frederic Sowa struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 2475ad706862SMartin KaFai Lau DST_NOCOUNT); 2476a3300ef4SHannes Frederic Sowa if (!rt) 24771da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 24781da177e4SLinus Torvalds 24791da177e4SLinus Torvalds in6_dev_hold(idev); 24801da177e4SLinus Torvalds 248111d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2482d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2483d8d1f30bSChangli Gao rt->dst.output = ip6_output; 24841da177e4SLinus Torvalds rt->rt6i_idev = idev; 24851da177e4SLinus Torvalds 24861da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 248758c4fb86SYOSHIFUJI Hideaki if (anycast) 248858c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 248958c4fb86SYOSHIFUJI Hideaki else 24901da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 24911da177e4SLinus Torvalds 2492550bab42SJulian Anastasov rt->rt6i_gateway = *addr; 24934e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 24941da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 24955578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 24968e3d5be7SMartin KaFai Lau rt->dst.flags |= DST_NOCACHE; 24971da177e4SLinus Torvalds 2498d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 24991da177e4SLinus Torvalds 25001da177e4SLinus Torvalds return rt; 25011da177e4SLinus Torvalds } 25021da177e4SLinus Torvalds 2503c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2504c3968a85SDaniel Walter struct rt6_info *rt, 2505b71d1d42SEric Dumazet const struct in6_addr *daddr, 2506c3968a85SDaniel Walter unsigned int prefs, 2507c3968a85SDaniel Walter struct in6_addr *saddr) 2508c3968a85SDaniel Walter { 2509e16e888bSMarkus Stenberg struct inet6_dev *idev = 2510e16e888bSMarkus Stenberg rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL; 2511c3968a85SDaniel Walter int err = 0; 2512e16e888bSMarkus Stenberg if (rt && rt->rt6i_prefsrc.plen) 25134e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2514c3968a85SDaniel Walter else 2515c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2516c3968a85SDaniel Walter daddr, prefs, saddr); 2517c3968a85SDaniel Walter return err; 2518c3968a85SDaniel Walter } 2519c3968a85SDaniel Walter 2520c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2521c3968a85SDaniel Walter struct arg_dev_net_ip { 2522c3968a85SDaniel Walter struct net_device *dev; 2523c3968a85SDaniel Walter struct net *net; 2524c3968a85SDaniel Walter struct in6_addr *addr; 2525c3968a85SDaniel Walter }; 2526c3968a85SDaniel Walter 2527c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2528c3968a85SDaniel Walter { 2529c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2530c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2531c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2532c3968a85SDaniel Walter 2533d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2534c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2535c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2536c3968a85SDaniel Walter /* remove prefsrc entry */ 2537c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2538c3968a85SDaniel Walter } 2539c3968a85SDaniel Walter return 0; 2540c3968a85SDaniel Walter } 2541c3968a85SDaniel Walter 2542c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2543c3968a85SDaniel Walter { 2544c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2545c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2546c3968a85SDaniel Walter .dev = ifp->idev->dev, 2547c3968a85SDaniel Walter .net = net, 2548c3968a85SDaniel Walter .addr = &ifp->addr, 2549c3968a85SDaniel Walter }; 25500c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 2551c3968a85SDaniel Walter } 2552c3968a85SDaniel Walter 2553be7a010dSDuan Jiong #define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) 2554be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 2555be7a010dSDuan Jiong 2556be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */ 2557be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg) 2558be7a010dSDuan Jiong { 2559be7a010dSDuan Jiong struct in6_addr *gateway = (struct in6_addr *)arg; 2560be7a010dSDuan Jiong 2561be7a010dSDuan Jiong if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) || 2562be7a010dSDuan Jiong ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) && 2563be7a010dSDuan Jiong ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { 2564be7a010dSDuan Jiong return -1; 2565be7a010dSDuan Jiong } 2566be7a010dSDuan Jiong return 0; 2567be7a010dSDuan Jiong } 2568be7a010dSDuan Jiong 2569be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 2570be7a010dSDuan Jiong { 2571be7a010dSDuan Jiong fib6_clean_all(net, fib6_clean_tohost, gateway); 2572be7a010dSDuan Jiong } 2573be7a010dSDuan Jiong 25748ed67789SDaniel Lezcano struct arg_dev_net { 25758ed67789SDaniel Lezcano struct net_device *dev; 25768ed67789SDaniel Lezcano struct net *net; 25778ed67789SDaniel Lezcano }; 25788ed67789SDaniel Lezcano 25791da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 25801da177e4SLinus Torvalds { 2581bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2582bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 25838ed67789SDaniel Lezcano 2584d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2585c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 25861da177e4SLinus Torvalds return -1; 2587c159d30cSDavid S. Miller 25881da177e4SLinus Torvalds return 0; 25891da177e4SLinus Torvalds } 25901da177e4SLinus Torvalds 2591f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 25921da177e4SLinus Torvalds { 25938ed67789SDaniel Lezcano struct arg_dev_net adn = { 25948ed67789SDaniel Lezcano .dev = dev, 25958ed67789SDaniel Lezcano .net = net, 25968ed67789SDaniel Lezcano }; 25978ed67789SDaniel Lezcano 25980c3584d5SLi RongQing fib6_clean_all(net, fib6_ifdown, &adn); 25991e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 26008d0b94afSMartin KaFai Lau rt6_uncached_list_flush_dev(net, dev); 26011da177e4SLinus Torvalds } 26021da177e4SLinus Torvalds 260395c96174SEric Dumazet struct rt6_mtu_change_arg { 26041da177e4SLinus Torvalds struct net_device *dev; 260595c96174SEric Dumazet unsigned int mtu; 26061da177e4SLinus Torvalds }; 26071da177e4SLinus Torvalds 26081da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 26091da177e4SLinus Torvalds { 26101da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 26111da177e4SLinus Torvalds struct inet6_dev *idev; 26121da177e4SLinus Torvalds 26131da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 26141da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 26151da177e4SLinus Torvalds We still use this lock to block changes 26161da177e4SLinus Torvalds caused by addrconf/ndisc. 26171da177e4SLinus Torvalds */ 26181da177e4SLinus Torvalds 26191da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 262038308473SDavid S. Miller if (!idev) 26211da177e4SLinus Torvalds return 0; 26221da177e4SLinus Torvalds 26231da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 26241da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 26251da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 26261da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 26271da177e4SLinus Torvalds */ 26281da177e4SLinus Torvalds /* 26291da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 26301da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 26311da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 26321da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 26331da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 26341da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 26351da177e4SLinus Torvalds PMTU discouvery. 26361da177e4SLinus Torvalds */ 2637d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 26384b32b5adSMartin KaFai Lau !dst_metric_locked(&rt->dst, RTAX_MTU)) { 26394b32b5adSMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) { 26404b32b5adSMartin KaFai Lau /* For RTF_CACHE with rt6i_pmtu == 0 26414b32b5adSMartin KaFai Lau * (i.e. a redirected route), 26424b32b5adSMartin KaFai Lau * the metrics of its rt->dst.from has already 26434b32b5adSMartin KaFai Lau * been updated. 26444b32b5adSMartin KaFai Lau */ 26454b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu) 26464b32b5adSMartin KaFai Lau rt->rt6i_pmtu = arg->mtu; 26474b32b5adSMartin KaFai Lau } else if (dst_mtu(&rt->dst) >= arg->mtu || 2648d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 26494b32b5adSMartin KaFai Lau dst_mtu(&rt->dst) == idev->cnf.mtu6)) { 2650defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2651566cfd8fSSimon Arlott } 26524b32b5adSMartin KaFai Lau } 26531da177e4SLinus Torvalds return 0; 26541da177e4SLinus Torvalds } 26551da177e4SLinus Torvalds 265695c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 26571da177e4SLinus Torvalds { 2658c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2659c71099acSThomas Graf .dev = dev, 2660c71099acSThomas Graf .mtu = mtu, 2661c71099acSThomas Graf }; 26621da177e4SLinus Torvalds 26630c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 26641da177e4SLinus Torvalds } 26651da177e4SLinus Torvalds 2666ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 26675176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 266886872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2669ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 267086872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 267186872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 267251ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2673c78ba6d6SLubomir Rintel [RTA_PREF] = { .type = NLA_U8 }, 267419e42e45SRoopa Prabhu [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, 267519e42e45SRoopa Prabhu [RTA_ENCAP] = { .type = NLA_NESTED }, 267686872cb5SThomas Graf }; 267786872cb5SThomas Graf 267886872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 267986872cb5SThomas Graf struct fib6_config *cfg) 26801da177e4SLinus Torvalds { 268186872cb5SThomas Graf struct rtmsg *rtm; 268286872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 2683c78ba6d6SLubomir Rintel unsigned int pref; 268486872cb5SThomas Graf int err; 26851da177e4SLinus Torvalds 268686872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 268786872cb5SThomas Graf if (err < 0) 268886872cb5SThomas Graf goto errout; 26891da177e4SLinus Torvalds 269086872cb5SThomas Graf err = -EINVAL; 269186872cb5SThomas Graf rtm = nlmsg_data(nlh); 269286872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 269386872cb5SThomas Graf 269486872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 269586872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 269686872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 269786872cb5SThomas Graf cfg->fc_flags = RTF_UP; 269886872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2699ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 270086872cb5SThomas Graf 2701ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2702ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2703b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2704b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 270586872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 270686872cb5SThomas Graf 2707ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2708ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2709ab79ad14SMaciej Żenczykowski 27101f56a01fSMartin KaFai Lau if (rtm->rtm_flags & RTM_F_CLONED) 27111f56a01fSMartin KaFai Lau cfg->fc_flags |= RTF_CACHE; 27121f56a01fSMartin KaFai Lau 271315e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 271486872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 27153b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 271686872cb5SThomas Graf 271786872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 271867b61f6cSJiri Benc cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 271986872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 27201da177e4SLinus Torvalds } 272186872cb5SThomas Graf 272286872cb5SThomas Graf if (tb[RTA_DST]) { 272386872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 272486872cb5SThomas Graf 272586872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 272686872cb5SThomas Graf goto errout; 272786872cb5SThomas Graf 272886872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 27291da177e4SLinus Torvalds } 273086872cb5SThomas Graf 273186872cb5SThomas Graf if (tb[RTA_SRC]) { 273286872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 273386872cb5SThomas Graf 273486872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 273586872cb5SThomas Graf goto errout; 273686872cb5SThomas Graf 273786872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 27381da177e4SLinus Torvalds } 273986872cb5SThomas Graf 2740c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 274167b61f6cSJiri Benc cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 2742c3968a85SDaniel Walter 274386872cb5SThomas Graf if (tb[RTA_OIF]) 274486872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 274586872cb5SThomas Graf 274686872cb5SThomas Graf if (tb[RTA_PRIORITY]) 274786872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 274886872cb5SThomas Graf 274986872cb5SThomas Graf if (tb[RTA_METRICS]) { 275086872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 275186872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 27521da177e4SLinus Torvalds } 275386872cb5SThomas Graf 275486872cb5SThomas Graf if (tb[RTA_TABLE]) 275586872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 275686872cb5SThomas Graf 275751ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 275851ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 275951ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 276051ebd318SNicolas Dichtel } 276151ebd318SNicolas Dichtel 2762c78ba6d6SLubomir Rintel if (tb[RTA_PREF]) { 2763c78ba6d6SLubomir Rintel pref = nla_get_u8(tb[RTA_PREF]); 2764c78ba6d6SLubomir Rintel if (pref != ICMPV6_ROUTER_PREF_LOW && 2765c78ba6d6SLubomir Rintel pref != ICMPV6_ROUTER_PREF_HIGH) 2766c78ba6d6SLubomir Rintel pref = ICMPV6_ROUTER_PREF_MEDIUM; 2767c78ba6d6SLubomir Rintel cfg->fc_flags |= RTF_PREF(pref); 2768c78ba6d6SLubomir Rintel } 2769c78ba6d6SLubomir Rintel 277019e42e45SRoopa Prabhu if (tb[RTA_ENCAP]) 277119e42e45SRoopa Prabhu cfg->fc_encap = tb[RTA_ENCAP]; 277219e42e45SRoopa Prabhu 277319e42e45SRoopa Prabhu if (tb[RTA_ENCAP_TYPE]) 277419e42e45SRoopa Prabhu cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]); 277519e42e45SRoopa Prabhu 277686872cb5SThomas Graf err = 0; 277786872cb5SThomas Graf errout: 277886872cb5SThomas Graf return err; 27791da177e4SLinus Torvalds } 27801da177e4SLinus Torvalds 27816b9ea5a6SRoopa Prabhu struct rt6_nh { 27826b9ea5a6SRoopa Prabhu struct rt6_info *rt6_info; 27836b9ea5a6SRoopa Prabhu struct fib6_config r_cfg; 27846b9ea5a6SRoopa Prabhu struct mx6_config mxc; 27856b9ea5a6SRoopa Prabhu struct list_head next; 27866b9ea5a6SRoopa Prabhu }; 27876b9ea5a6SRoopa Prabhu 27886b9ea5a6SRoopa Prabhu static void ip6_print_replace_route_err(struct list_head *rt6_nh_list) 27896b9ea5a6SRoopa Prabhu { 27906b9ea5a6SRoopa Prabhu struct rt6_nh *nh; 27916b9ea5a6SRoopa Prabhu 27926b9ea5a6SRoopa Prabhu list_for_each_entry(nh, rt6_nh_list, next) { 27936b9ea5a6SRoopa Prabhu pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6 nexthop %pI6 ifi %d\n", 27946b9ea5a6SRoopa Prabhu &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway, 27956b9ea5a6SRoopa Prabhu nh->r_cfg.fc_ifindex); 27966b9ea5a6SRoopa Prabhu } 27976b9ea5a6SRoopa Prabhu } 27986b9ea5a6SRoopa Prabhu 27996b9ea5a6SRoopa Prabhu static int ip6_route_info_append(struct list_head *rt6_nh_list, 28006b9ea5a6SRoopa Prabhu struct rt6_info *rt, struct fib6_config *r_cfg) 28016b9ea5a6SRoopa Prabhu { 28026b9ea5a6SRoopa Prabhu struct rt6_nh *nh; 28036b9ea5a6SRoopa Prabhu struct rt6_info *rtnh; 28046b9ea5a6SRoopa Prabhu int err = -EEXIST; 28056b9ea5a6SRoopa Prabhu 28066b9ea5a6SRoopa Prabhu list_for_each_entry(nh, rt6_nh_list, next) { 28076b9ea5a6SRoopa Prabhu /* check if rt6_info already exists */ 28086b9ea5a6SRoopa Prabhu rtnh = nh->rt6_info; 28096b9ea5a6SRoopa Prabhu 28106b9ea5a6SRoopa Prabhu if (rtnh->dst.dev == rt->dst.dev && 28116b9ea5a6SRoopa Prabhu rtnh->rt6i_idev == rt->rt6i_idev && 28126b9ea5a6SRoopa Prabhu ipv6_addr_equal(&rtnh->rt6i_gateway, 28136b9ea5a6SRoopa Prabhu &rt->rt6i_gateway)) 28146b9ea5a6SRoopa Prabhu return err; 28156b9ea5a6SRoopa Prabhu } 28166b9ea5a6SRoopa Prabhu 28176b9ea5a6SRoopa Prabhu nh = kzalloc(sizeof(*nh), GFP_KERNEL); 28186b9ea5a6SRoopa Prabhu if (!nh) 28196b9ea5a6SRoopa Prabhu return -ENOMEM; 28206b9ea5a6SRoopa Prabhu nh->rt6_info = rt; 28216b9ea5a6SRoopa Prabhu err = ip6_convert_metrics(&nh->mxc, r_cfg); 28226b9ea5a6SRoopa Prabhu if (err) { 28236b9ea5a6SRoopa Prabhu kfree(nh); 28246b9ea5a6SRoopa Prabhu return err; 28256b9ea5a6SRoopa Prabhu } 28266b9ea5a6SRoopa Prabhu memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg)); 28276b9ea5a6SRoopa Prabhu list_add_tail(&nh->next, rt6_nh_list); 28286b9ea5a6SRoopa Prabhu 28296b9ea5a6SRoopa Prabhu return 0; 28306b9ea5a6SRoopa Prabhu } 28316b9ea5a6SRoopa Prabhu 28326b9ea5a6SRoopa Prabhu static int ip6_route_multipath_add(struct fib6_config *cfg) 283351ebd318SNicolas Dichtel { 283451ebd318SNicolas Dichtel struct fib6_config r_cfg; 283551ebd318SNicolas Dichtel struct rtnexthop *rtnh; 28366b9ea5a6SRoopa Prabhu struct rt6_info *rt; 28376b9ea5a6SRoopa Prabhu struct rt6_nh *err_nh; 28386b9ea5a6SRoopa Prabhu struct rt6_nh *nh, *nh_safe; 283951ebd318SNicolas Dichtel int remaining; 284051ebd318SNicolas Dichtel int attrlen; 28416b9ea5a6SRoopa Prabhu int err = 1; 28426b9ea5a6SRoopa Prabhu int nhn = 0; 28436b9ea5a6SRoopa Prabhu int replace = (cfg->fc_nlinfo.nlh && 28446b9ea5a6SRoopa Prabhu (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); 28456b9ea5a6SRoopa Prabhu LIST_HEAD(rt6_nh_list); 284651ebd318SNicolas Dichtel 284735f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len; 284851ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 284951ebd318SNicolas Dichtel 28506b9ea5a6SRoopa Prabhu /* Parse a Multipath Entry and build a list (rt6_nh_list) of 28516b9ea5a6SRoopa Prabhu * rt6_info structs per nexthop 28526b9ea5a6SRoopa Prabhu */ 285351ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 285451ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 285551ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 285651ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 285751ebd318SNicolas Dichtel 285851ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 285951ebd318SNicolas Dichtel if (attrlen > 0) { 286051ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 286151ebd318SNicolas Dichtel 286251ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 286351ebd318SNicolas Dichtel if (nla) { 286467b61f6cSJiri Benc r_cfg.fc_gateway = nla_get_in6_addr(nla); 286551ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 286651ebd318SNicolas Dichtel } 286719e42e45SRoopa Prabhu r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 286819e42e45SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 286919e42e45SRoopa Prabhu if (nla) 287019e42e45SRoopa Prabhu r_cfg.fc_encap_type = nla_get_u16(nla); 287151ebd318SNicolas Dichtel } 28726b9ea5a6SRoopa Prabhu 28736b9ea5a6SRoopa Prabhu err = ip6_route_info_create(&r_cfg, &rt); 28746b9ea5a6SRoopa Prabhu if (err) 28756b9ea5a6SRoopa Prabhu goto cleanup; 28766b9ea5a6SRoopa Prabhu 28776b9ea5a6SRoopa Prabhu err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg); 287851ebd318SNicolas Dichtel if (err) { 28796b9ea5a6SRoopa Prabhu dst_free(&rt->dst); 28806b9ea5a6SRoopa Prabhu goto cleanup; 288151ebd318SNicolas Dichtel } 28826b9ea5a6SRoopa Prabhu 28836b9ea5a6SRoopa Prabhu rtnh = rtnh_next(rtnh, &remaining); 288451ebd318SNicolas Dichtel } 28856b9ea5a6SRoopa Prabhu 28866b9ea5a6SRoopa Prabhu err_nh = NULL; 28876b9ea5a6SRoopa Prabhu list_for_each_entry(nh, &rt6_nh_list, next) { 28886b9ea5a6SRoopa Prabhu err = __ip6_ins_rt(nh->rt6_info, &cfg->fc_nlinfo, &nh->mxc); 28896b9ea5a6SRoopa Prabhu /* nh->rt6_info is used or freed at this point, reset to NULL*/ 28906b9ea5a6SRoopa Prabhu nh->rt6_info = NULL; 28916b9ea5a6SRoopa Prabhu if (err) { 28926b9ea5a6SRoopa Prabhu if (replace && nhn) 28936b9ea5a6SRoopa Prabhu ip6_print_replace_route_err(&rt6_nh_list); 28946b9ea5a6SRoopa Prabhu err_nh = nh; 28956b9ea5a6SRoopa Prabhu goto add_errout; 28966b9ea5a6SRoopa Prabhu } 28976b9ea5a6SRoopa Prabhu 28981a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 289927596472SMichal Kubeček * these flags after the first nexthop: if there is a collision, 290027596472SMichal Kubeček * we have already failed to add the first nexthop: 290127596472SMichal Kubeček * fib6_add_rt2node() has rejected it; when replacing, old 290227596472SMichal Kubeček * nexthops have been replaced by first new, the rest should 290327596472SMichal Kubeček * be added to it. 29041a72418bSNicolas Dichtel */ 290527596472SMichal Kubeček cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | 290627596472SMichal Kubeček NLM_F_REPLACE); 29076b9ea5a6SRoopa Prabhu nhn++; 29086b9ea5a6SRoopa Prabhu } 29096b9ea5a6SRoopa Prabhu 29106b9ea5a6SRoopa Prabhu goto cleanup; 29116b9ea5a6SRoopa Prabhu 29126b9ea5a6SRoopa Prabhu add_errout: 29136b9ea5a6SRoopa Prabhu /* Delete routes that were already added */ 29146b9ea5a6SRoopa Prabhu list_for_each_entry(nh, &rt6_nh_list, next) { 29156b9ea5a6SRoopa Prabhu if (err_nh == nh) 29166b9ea5a6SRoopa Prabhu break; 29176b9ea5a6SRoopa Prabhu ip6_route_del(&nh->r_cfg); 29186b9ea5a6SRoopa Prabhu } 29196b9ea5a6SRoopa Prabhu 29206b9ea5a6SRoopa Prabhu cleanup: 29216b9ea5a6SRoopa Prabhu list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) { 29226b9ea5a6SRoopa Prabhu if (nh->rt6_info) 29236b9ea5a6SRoopa Prabhu dst_free(&nh->rt6_info->dst); 29246b9ea5a6SRoopa Prabhu kfree(nh->mxc.mx); 29256b9ea5a6SRoopa Prabhu list_del(&nh->next); 29266b9ea5a6SRoopa Prabhu kfree(nh); 29276b9ea5a6SRoopa Prabhu } 29286b9ea5a6SRoopa Prabhu 29296b9ea5a6SRoopa Prabhu return err; 29306b9ea5a6SRoopa Prabhu } 29316b9ea5a6SRoopa Prabhu 29326b9ea5a6SRoopa Prabhu static int ip6_route_multipath_del(struct fib6_config *cfg) 29336b9ea5a6SRoopa Prabhu { 29346b9ea5a6SRoopa Prabhu struct fib6_config r_cfg; 29356b9ea5a6SRoopa Prabhu struct rtnexthop *rtnh; 29366b9ea5a6SRoopa Prabhu int remaining; 29376b9ea5a6SRoopa Prabhu int attrlen; 29386b9ea5a6SRoopa Prabhu int err = 1, last_err = 0; 29396b9ea5a6SRoopa Prabhu 29406b9ea5a6SRoopa Prabhu remaining = cfg->fc_mp_len; 29416b9ea5a6SRoopa Prabhu rtnh = (struct rtnexthop *)cfg->fc_mp; 29426b9ea5a6SRoopa Prabhu 29436b9ea5a6SRoopa Prabhu /* Parse a Multipath Entry */ 29446b9ea5a6SRoopa Prabhu while (rtnh_ok(rtnh, remaining)) { 29456b9ea5a6SRoopa Prabhu memcpy(&r_cfg, cfg, sizeof(*cfg)); 29466b9ea5a6SRoopa Prabhu if (rtnh->rtnh_ifindex) 29476b9ea5a6SRoopa Prabhu r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 29486b9ea5a6SRoopa Prabhu 29496b9ea5a6SRoopa Prabhu attrlen = rtnh_attrlen(rtnh); 29506b9ea5a6SRoopa Prabhu if (attrlen > 0) { 29516b9ea5a6SRoopa Prabhu struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 29526b9ea5a6SRoopa Prabhu 29536b9ea5a6SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_GATEWAY); 29546b9ea5a6SRoopa Prabhu if (nla) { 29556b9ea5a6SRoopa Prabhu nla_memcpy(&r_cfg.fc_gateway, nla, 16); 29566b9ea5a6SRoopa Prabhu r_cfg.fc_flags |= RTF_GATEWAY; 29576b9ea5a6SRoopa Prabhu } 29586b9ea5a6SRoopa Prabhu } 29596b9ea5a6SRoopa Prabhu err = ip6_route_del(&r_cfg); 29606b9ea5a6SRoopa Prabhu if (err) 29616b9ea5a6SRoopa Prabhu last_err = err; 29626b9ea5a6SRoopa Prabhu 296351ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 296451ebd318SNicolas Dichtel } 296551ebd318SNicolas Dichtel 296651ebd318SNicolas Dichtel return last_err; 296751ebd318SNicolas Dichtel } 296851ebd318SNicolas Dichtel 2969661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) 29701da177e4SLinus Torvalds { 297186872cb5SThomas Graf struct fib6_config cfg; 297286872cb5SThomas Graf int err; 29731da177e4SLinus Torvalds 297486872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 297586872cb5SThomas Graf if (err < 0) 297686872cb5SThomas Graf return err; 297786872cb5SThomas Graf 297851ebd318SNicolas Dichtel if (cfg.fc_mp) 29796b9ea5a6SRoopa Prabhu return ip6_route_multipath_del(&cfg); 298051ebd318SNicolas Dichtel else 298186872cb5SThomas Graf return ip6_route_del(&cfg); 29821da177e4SLinus Torvalds } 29831da177e4SLinus Torvalds 2984661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) 29851da177e4SLinus Torvalds { 298686872cb5SThomas Graf struct fib6_config cfg; 298786872cb5SThomas Graf int err; 29881da177e4SLinus Torvalds 298986872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 299086872cb5SThomas Graf if (err < 0) 299186872cb5SThomas Graf return err; 299286872cb5SThomas Graf 299351ebd318SNicolas Dichtel if (cfg.fc_mp) 29946b9ea5a6SRoopa Prabhu return ip6_route_multipath_add(&cfg); 299551ebd318SNicolas Dichtel else 299686872cb5SThomas Graf return ip6_route_add(&cfg); 29971da177e4SLinus Torvalds } 29981da177e4SLinus Torvalds 299919e42e45SRoopa Prabhu static inline size_t rt6_nlmsg_size(struct rt6_info *rt) 3000339bf98fSThomas Graf { 3001339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 3002339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 3003339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 3004339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 3005339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 3006339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 3007339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 3008339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 3009339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 30106a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 3011ea697639SDaniel Borkmann + nla_total_size(sizeof(struct rta_cacheinfo)) 3012c78ba6d6SLubomir Rintel + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 301319e42e45SRoopa Prabhu + nla_total_size(1) /* RTA_PREF */ 301461adedf3SJiri Benc + lwtunnel_get_encap_size(rt->dst.lwtstate); 3015339bf98fSThomas Graf } 3016339bf98fSThomas Graf 3017191cd582SBrian Haley static int rt6_fill_node(struct net *net, 3018191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 30190d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 302015e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 30217bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 30221da177e4SLinus Torvalds { 30234b32b5adSMartin KaFai Lau u32 metrics[RTAX_MAX]; 30241da177e4SLinus Torvalds struct rtmsg *rtm; 30251da177e4SLinus Torvalds struct nlmsghdr *nlh; 3026e3703b3dSThomas Graf long expires; 30279e762a4aSPatrick McHardy u32 table; 30281da177e4SLinus Torvalds 30291da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 30301da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 30311da177e4SLinus Torvalds /* success since this is not a prefix route */ 30321da177e4SLinus Torvalds return 1; 30331da177e4SLinus Torvalds } 30341da177e4SLinus Torvalds } 30351da177e4SLinus Torvalds 303615e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 303738308473SDavid S. Miller if (!nlh) 303826932566SPatrick McHardy return -EMSGSIZE; 30392d7202bfSThomas Graf 30402d7202bfSThomas Graf rtm = nlmsg_data(nlh); 30411da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 30421da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 30431da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 30441da177e4SLinus Torvalds rtm->rtm_tos = 0; 3045c71099acSThomas Graf if (rt->rt6i_table) 30469e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 3047c71099acSThomas Graf else 30489e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 30499e762a4aSPatrick McHardy rtm->rtm_table = table; 3050c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 3051c78679e8SDavid S. Miller goto nla_put_failure; 3052ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 3053ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 3054ef2c7d7bSNicolas Dichtel case -EINVAL: 3055ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 3056ef2c7d7bSNicolas Dichtel break; 3057ef2c7d7bSNicolas Dichtel case -EACCES: 3058ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 3059ef2c7d7bSNicolas Dichtel break; 3060b4949ab2SNicolas Dichtel case -EAGAIN: 3061b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 3062b4949ab2SNicolas Dichtel break; 3063ef2c7d7bSNicolas Dichtel default: 30641da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 3065ef2c7d7bSNicolas Dichtel break; 3066ef2c7d7bSNicolas Dichtel } 3067ef2c7d7bSNicolas Dichtel } 3068ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 3069ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 3070d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 30711da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 30721da177e4SLinus Torvalds else 30731da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 30741da177e4SLinus Torvalds rtm->rtm_flags = 0; 307535103d11SAndy Gospodarek if (!netif_carrier_ok(rt->dst.dev)) { 3076cea45e20SAndy Gospodarek rtm->rtm_flags |= RTNH_F_LINKDOWN; 307735103d11SAndy Gospodarek if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown) 307835103d11SAndy Gospodarek rtm->rtm_flags |= RTNH_F_DEAD; 307935103d11SAndy Gospodarek } 30801da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 30811da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 30821da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 30831da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 3084f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 3085f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 30861da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 3087f0396f60SDenis Ovsienko else 3088f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 3089f0396f60SDenis Ovsienko } 30901da177e4SLinus Torvalds 30911da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 30921da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 30931da177e4SLinus Torvalds 30941da177e4SLinus Torvalds if (dst) { 3095930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, dst)) 3096c78679e8SDavid S. Miller goto nla_put_failure; 30971da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 30981da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 3099930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr)) 3100c78679e8SDavid S. Miller goto nla_put_failure; 31011da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 31021da177e4SLinus Torvalds if (src) { 3103930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_SRC, src)) 3104c78679e8SDavid S. Miller goto nla_put_failure; 31051da177e4SLinus Torvalds rtm->rtm_src_len = 128; 3106c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 3107930345eaSJiri Benc nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr)) 3108c78679e8SDavid S. Miller goto nla_put_failure; 31091da177e4SLinus Torvalds #endif 31107bc570c8SYOSHIFUJI Hideaki if (iif) { 31117bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 31127bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 31138229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 31147bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 31157bc570c8SYOSHIFUJI Hideaki if (!nowait) { 31167bc570c8SYOSHIFUJI Hideaki if (err == 0) 31177bc570c8SYOSHIFUJI Hideaki return 0; 31187bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 31197bc570c8SYOSHIFUJI Hideaki } else { 31207bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 31217bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 31227bc570c8SYOSHIFUJI Hideaki } 31237bc570c8SYOSHIFUJI Hideaki } 31247bc570c8SYOSHIFUJI Hideaki } else 31257bc570c8SYOSHIFUJI Hideaki #endif 3126c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 3127c78679e8SDavid S. Miller goto nla_put_failure; 31287bc570c8SYOSHIFUJI Hideaki } else if (dst) { 31291da177e4SLinus Torvalds struct in6_addr saddr_buf; 3130c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 3131930345eaSJiri Benc nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 3132c78679e8SDavid S. Miller goto nla_put_failure; 3133c3968a85SDaniel Walter } 3134c3968a85SDaniel Walter 3135c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 3136c3968a85SDaniel Walter struct in6_addr saddr_buf; 31374e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 3138930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 3139c78679e8SDavid S. Miller goto nla_put_failure; 31401da177e4SLinus Torvalds } 31412d7202bfSThomas Graf 31424b32b5adSMartin KaFai Lau memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); 31434b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu) 31444b32b5adSMartin KaFai Lau metrics[RTAX_MTU - 1] = rt->rt6i_pmtu; 31454b32b5adSMartin KaFai Lau if (rtnetlink_put_metrics(skb, metrics) < 0) 31462d7202bfSThomas Graf goto nla_put_failure; 31472d7202bfSThomas Graf 3148dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_GATEWAY) { 3149930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0) 315094f826b8SEric Dumazet goto nla_put_failure; 315194f826b8SEric Dumazet } 31522d7202bfSThomas Graf 3153c78679e8SDavid S. Miller if (rt->dst.dev && 3154c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 3155c78679e8SDavid S. Miller goto nla_put_failure; 3156c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 3157c78679e8SDavid S. Miller goto nla_put_failure; 31588253947eSLi Wei 31598253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 316069cdf8f9SYOSHIFUJI Hideaki 316187a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 3162e3703b3dSThomas Graf goto nla_put_failure; 31631da177e4SLinus Torvalds 3164c78ba6d6SLubomir Rintel if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) 3165c78ba6d6SLubomir Rintel goto nla_put_failure; 3166c78ba6d6SLubomir Rintel 316761adedf3SJiri Benc lwtunnel_fill_encap(skb, rt->dst.lwtstate); 316819e42e45SRoopa Prabhu 3169053c095aSJohannes Berg nlmsg_end(skb, nlh); 3170053c095aSJohannes Berg return 0; 31712d7202bfSThomas Graf 31722d7202bfSThomas Graf nla_put_failure: 317326932566SPatrick McHardy nlmsg_cancel(skb, nlh); 317426932566SPatrick McHardy return -EMSGSIZE; 31751da177e4SLinus Torvalds } 31761da177e4SLinus Torvalds 31771b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 31781da177e4SLinus Torvalds { 31791da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 31801da177e4SLinus Torvalds int prefix; 31811da177e4SLinus Torvalds 31822d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 31832d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 31841da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 31851da177e4SLinus Torvalds } else 31861da177e4SLinus Torvalds prefix = 0; 31871da177e4SLinus Torvalds 3188191cd582SBrian Haley return rt6_fill_node(arg->net, 3189191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 319015e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 31917bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 31921da177e4SLinus Torvalds } 31931da177e4SLinus Torvalds 3194661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) 31951da177e4SLinus Torvalds { 31963b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 3197ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 31981da177e4SLinus Torvalds struct rt6_info *rt; 3199ab364a6fSThomas Graf struct sk_buff *skb; 3200ab364a6fSThomas Graf struct rtmsg *rtm; 32014c9483b2SDavid S. Miller struct flowi6 fl6; 320272331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 3203ab364a6fSThomas Graf 3204ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 3205ab364a6fSThomas Graf if (err < 0) 3206ab364a6fSThomas Graf goto errout; 3207ab364a6fSThomas Graf 3208ab364a6fSThomas Graf err = -EINVAL; 32094c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 3210ab364a6fSThomas Graf 3211ab364a6fSThomas Graf if (tb[RTA_SRC]) { 3212ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 3213ab364a6fSThomas Graf goto errout; 3214ab364a6fSThomas Graf 32154e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 3216ab364a6fSThomas Graf } 3217ab364a6fSThomas Graf 3218ab364a6fSThomas Graf if (tb[RTA_DST]) { 3219ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 3220ab364a6fSThomas Graf goto errout; 3221ab364a6fSThomas Graf 32224e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 3223ab364a6fSThomas Graf } 3224ab364a6fSThomas Graf 3225ab364a6fSThomas Graf if (tb[RTA_IIF]) 3226ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 3227ab364a6fSThomas Graf 3228ab364a6fSThomas Graf if (tb[RTA_OIF]) 322972331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 3230ab364a6fSThomas Graf 32312e47b291SLorenzo Colitti if (tb[RTA_MARK]) 32322e47b291SLorenzo Colitti fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 32332e47b291SLorenzo Colitti 3234ab364a6fSThomas Graf if (iif) { 3235ab364a6fSThomas Graf struct net_device *dev; 323672331bc0SShmulik Ladkani int flags = 0; 323772331bc0SShmulik Ladkani 32385578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 3239ab364a6fSThomas Graf if (!dev) { 3240ab364a6fSThomas Graf err = -ENODEV; 3241ab364a6fSThomas Graf goto errout; 3242ab364a6fSThomas Graf } 324372331bc0SShmulik Ladkani 324472331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 324572331bc0SShmulik Ladkani 324672331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 324772331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 324872331bc0SShmulik Ladkani 324972331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 325072331bc0SShmulik Ladkani flags); 325172331bc0SShmulik Ladkani } else { 325272331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 325372331bc0SShmulik Ladkani 325472331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 3255ab364a6fSThomas Graf } 32561da177e4SLinus Torvalds 32571da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 325838308473SDavid S. Miller if (!skb) { 325994e187c0SAmerigo Wang ip6_rt_put(rt); 3260ab364a6fSThomas Graf err = -ENOBUFS; 3261ab364a6fSThomas Graf goto errout; 3262ab364a6fSThomas Graf } 32631da177e4SLinus Torvalds 32641da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 32651da177e4SLinus Torvalds through good chunk of routing engine. 32661da177e4SLinus Torvalds */ 3267459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 32681da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 32691da177e4SLinus Torvalds 3270d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 32711da177e4SLinus Torvalds 32724c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 327315e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 32747bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 32751da177e4SLinus Torvalds if (err < 0) { 3276ab364a6fSThomas Graf kfree_skb(skb); 3277ab364a6fSThomas Graf goto errout; 32781da177e4SLinus Torvalds } 32791da177e4SLinus Torvalds 328015e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 3281ab364a6fSThomas Graf errout: 32821da177e4SLinus Torvalds return err; 32831da177e4SLinus Torvalds } 32841da177e4SLinus Torvalds 328537a1d361SRoopa Prabhu void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info, 328637a1d361SRoopa Prabhu unsigned int nlm_flags) 32871da177e4SLinus Torvalds { 32881da177e4SLinus Torvalds struct sk_buff *skb; 32895578689aSDaniel Lezcano struct net *net = info->nl_net; 3290528c4cebSDenis V. Lunev u32 seq; 3291528c4cebSDenis V. Lunev int err; 32920d51aa80SJamal Hadi Salim 3293528c4cebSDenis V. Lunev err = -ENOBUFS; 329438308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 329586872cb5SThomas Graf 329619e42e45SRoopa Prabhu skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 329738308473SDavid S. Miller if (!skb) 329821713ebcSThomas Graf goto errout; 32991da177e4SLinus Torvalds 3300191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 330137a1d361SRoopa Prabhu event, info->portid, seq, 0, 0, nlm_flags); 330226932566SPatrick McHardy if (err < 0) { 330326932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 330426932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 330526932566SPatrick McHardy kfree_skb(skb); 330626932566SPatrick McHardy goto errout; 330726932566SPatrick McHardy } 330815e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 33095578689aSDaniel Lezcano info->nlh, gfp_any()); 33101ce85fe4SPablo Neira Ayuso return; 331121713ebcSThomas Graf errout: 331221713ebcSThomas Graf if (err < 0) 33135578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 33141da177e4SLinus Torvalds } 33151da177e4SLinus Torvalds 33168ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 3317351638e7SJiri Pirko unsigned long event, void *ptr) 33188ed67789SDaniel Lezcano { 3319351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 3320c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 33218ed67789SDaniel Lezcano 33228ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 3323d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 33248ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 33258ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3326d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 33278ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 3328d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 33298ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 33308ed67789SDaniel Lezcano #endif 33318ed67789SDaniel Lezcano } 33328ed67789SDaniel Lezcano 33338ed67789SDaniel Lezcano return NOTIFY_OK; 33348ed67789SDaniel Lezcano } 33358ed67789SDaniel Lezcano 33361da177e4SLinus Torvalds /* 33371da177e4SLinus Torvalds * /proc 33381da177e4SLinus Torvalds */ 33391da177e4SLinus Torvalds 33401da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 33411da177e4SLinus Torvalds 334233120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 334333120b30SAlexey Dobriyan .owner = THIS_MODULE, 334433120b30SAlexey Dobriyan .open = ipv6_route_open, 334533120b30SAlexey Dobriyan .read = seq_read, 334633120b30SAlexey Dobriyan .llseek = seq_lseek, 33478d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 334833120b30SAlexey Dobriyan }; 334933120b30SAlexey Dobriyan 33501da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 33511da177e4SLinus Torvalds { 335269ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 33531da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 335469ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 335569ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 335669ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 335769ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 335869ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 3359fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 336069ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 33611da177e4SLinus Torvalds 33621da177e4SLinus Torvalds return 0; 33631da177e4SLinus Torvalds } 33641da177e4SLinus Torvalds 33651da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 33661da177e4SLinus Torvalds { 3367de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 336869ddb805SDaniel Lezcano } 336969ddb805SDaniel Lezcano 33709a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 33711da177e4SLinus Torvalds .owner = THIS_MODULE, 33721da177e4SLinus Torvalds .open = rt6_stats_seq_open, 33731da177e4SLinus Torvalds .read = seq_read, 33741da177e4SLinus Torvalds .llseek = seq_lseek, 3375b6fcbdb4SPavel Emelyanov .release = single_release_net, 33761da177e4SLinus Torvalds }; 33771da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 33781da177e4SLinus Torvalds 33791da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 33801da177e4SLinus Torvalds 33811da177e4SLinus Torvalds static 3382fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 33831da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 33841da177e4SLinus Torvalds { 3385c486da34SLucian Adrian Grijincu struct net *net; 3386c486da34SLucian Adrian Grijincu int delay; 3387c486da34SLucian Adrian Grijincu if (!write) 3388c486da34SLucian Adrian Grijincu return -EINVAL; 3389c486da34SLucian Adrian Grijincu 3390c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 3391c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 33928d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 33932ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 33941da177e4SLinus Torvalds return 0; 33951da177e4SLinus Torvalds } 33961da177e4SLinus Torvalds 3397fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 33981da177e4SLinus Torvalds { 33991da177e4SLinus Torvalds .procname = "flush", 34004990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 34011da177e4SLinus Torvalds .maxlen = sizeof(int), 340289c8b3a1SDave Jones .mode = 0200, 34036d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 34041da177e4SLinus Torvalds }, 34051da177e4SLinus Torvalds { 34061da177e4SLinus Torvalds .procname = "gc_thresh", 34079a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 34081da177e4SLinus Torvalds .maxlen = sizeof(int), 34091da177e4SLinus Torvalds .mode = 0644, 34106d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 34111da177e4SLinus Torvalds }, 34121da177e4SLinus Torvalds { 34131da177e4SLinus Torvalds .procname = "max_size", 34144990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 34151da177e4SLinus Torvalds .maxlen = sizeof(int), 34161da177e4SLinus Torvalds .mode = 0644, 34176d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 34181da177e4SLinus Torvalds }, 34191da177e4SLinus Torvalds { 34201da177e4SLinus Torvalds .procname = "gc_min_interval", 34214990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 34221da177e4SLinus Torvalds .maxlen = sizeof(int), 34231da177e4SLinus Torvalds .mode = 0644, 34246d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 34251da177e4SLinus Torvalds }, 34261da177e4SLinus Torvalds { 34271da177e4SLinus Torvalds .procname = "gc_timeout", 34284990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 34291da177e4SLinus Torvalds .maxlen = sizeof(int), 34301da177e4SLinus Torvalds .mode = 0644, 34316d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 34321da177e4SLinus Torvalds }, 34331da177e4SLinus Torvalds { 34341da177e4SLinus Torvalds .procname = "gc_interval", 34354990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 34361da177e4SLinus Torvalds .maxlen = sizeof(int), 34371da177e4SLinus Torvalds .mode = 0644, 34386d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 34391da177e4SLinus Torvalds }, 34401da177e4SLinus Torvalds { 34411da177e4SLinus Torvalds .procname = "gc_elasticity", 34424990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 34431da177e4SLinus Torvalds .maxlen = sizeof(int), 34441da177e4SLinus Torvalds .mode = 0644, 3445f3d3f616SMin Zhang .proc_handler = proc_dointvec, 34461da177e4SLinus Torvalds }, 34471da177e4SLinus Torvalds { 34481da177e4SLinus Torvalds .procname = "mtu_expires", 34494990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 34501da177e4SLinus Torvalds .maxlen = sizeof(int), 34511da177e4SLinus Torvalds .mode = 0644, 34526d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 34531da177e4SLinus Torvalds }, 34541da177e4SLinus Torvalds { 34551da177e4SLinus Torvalds .procname = "min_adv_mss", 34564990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 34571da177e4SLinus Torvalds .maxlen = sizeof(int), 34581da177e4SLinus Torvalds .mode = 0644, 3459f3d3f616SMin Zhang .proc_handler = proc_dointvec, 34601da177e4SLinus Torvalds }, 34611da177e4SLinus Torvalds { 34621da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 34634990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 34641da177e4SLinus Torvalds .maxlen = sizeof(int), 34651da177e4SLinus Torvalds .mode = 0644, 34666d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 34671da177e4SLinus Torvalds }, 3468f8572d8fSEric W. Biederman { } 34691da177e4SLinus Torvalds }; 34701da177e4SLinus Torvalds 34712c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 3472760f2d01SDaniel Lezcano { 3473760f2d01SDaniel Lezcano struct ctl_table *table; 3474760f2d01SDaniel Lezcano 3475760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 3476760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 3477760f2d01SDaniel Lezcano GFP_KERNEL); 34785ee09105SYOSHIFUJI Hideaki 34795ee09105SYOSHIFUJI Hideaki if (table) { 34805ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 3481c486da34SLucian Adrian Grijincu table[0].extra1 = net; 348286393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 34835ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 34845ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 34855ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 34865ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 34875ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 34885ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 34895ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 34909c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 3491464dc801SEric W. Biederman 3492464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 3493464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 3494464dc801SEric W. Biederman table[0].procname = NULL; 34955ee09105SYOSHIFUJI Hideaki } 34965ee09105SYOSHIFUJI Hideaki 3497760f2d01SDaniel Lezcano return table; 3498760f2d01SDaniel Lezcano } 34991da177e4SLinus Torvalds #endif 35001da177e4SLinus Torvalds 35012c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 3502cdb18761SDaniel Lezcano { 3503633d424bSPavel Emelyanov int ret = -ENOMEM; 35048ed67789SDaniel Lezcano 350586393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 350686393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 3507f2fc6a54SBenjamin Thery 3508fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 3509fc66f95cSEric Dumazet goto out_ip6_dst_ops; 3510fc66f95cSEric Dumazet 35118ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 35128ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 35138ed67789SDaniel Lezcano GFP_KERNEL); 35148ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 3515fc66f95cSEric Dumazet goto out_ip6_dst_entries; 3516d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 35178ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 3518d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 351962fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 352062fa8a84SDavid S. Miller ip6_template_metrics, true); 35218ed67789SDaniel Lezcano 35228ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 35238ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 35248ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 35258ed67789SDaniel Lezcano GFP_KERNEL); 352668fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 352768fffc67SPeter Zijlstra goto out_ip6_null_entry; 3528d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 35298ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 3530d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 353162fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 353262fa8a84SDavid S. Miller ip6_template_metrics, true); 35338ed67789SDaniel Lezcano 35348ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 35358ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 35368ed67789SDaniel Lezcano GFP_KERNEL); 353768fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 353868fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 3539d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 35408ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 3541d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 354262fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 354362fa8a84SDavid S. Miller ip6_template_metrics, true); 35448ed67789SDaniel Lezcano #endif 35458ed67789SDaniel Lezcano 3546b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 3547b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 3548b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 3549b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 3550b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 3551b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 3552b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 3553b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 3554b339a47cSPeter Zijlstra 35556891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 35566891a346SBenjamin Thery 35578ed67789SDaniel Lezcano ret = 0; 35588ed67789SDaniel Lezcano out: 35598ed67789SDaniel Lezcano return ret; 3560f2fc6a54SBenjamin Thery 356168fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 356268fffc67SPeter Zijlstra out_ip6_prohibit_entry: 356368fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 356468fffc67SPeter Zijlstra out_ip6_null_entry: 356568fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 356668fffc67SPeter Zijlstra #endif 3567fc66f95cSEric Dumazet out_ip6_dst_entries: 3568fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3569f2fc6a54SBenjamin Thery out_ip6_dst_ops: 3570f2fc6a54SBenjamin Thery goto out; 3571cdb18761SDaniel Lezcano } 3572cdb18761SDaniel Lezcano 35732c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 3574cdb18761SDaniel Lezcano { 35758ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 35768ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 35778ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 35788ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 35798ed67789SDaniel Lezcano #endif 358041bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3581cdb18761SDaniel Lezcano } 3582cdb18761SDaniel Lezcano 3583d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 3584d189634eSThomas Graf { 3585d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3586d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 3587d4beaa66SGao feng proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops); 3588d189634eSThomas Graf #endif 3589d189634eSThomas Graf return 0; 3590d189634eSThomas Graf } 3591d189634eSThomas Graf 3592d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 3593d189634eSThomas Graf { 3594d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3595ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 3596ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 3597d189634eSThomas Graf #endif 3598d189634eSThomas Graf } 3599d189634eSThomas Graf 3600cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 3601cdb18761SDaniel Lezcano .init = ip6_route_net_init, 3602cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 3603cdb18761SDaniel Lezcano }; 3604cdb18761SDaniel Lezcano 3605c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 3606c3426b47SDavid S. Miller { 3607c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 3608c3426b47SDavid S. Miller 3609c3426b47SDavid S. Miller if (!bp) 3610c3426b47SDavid S. Miller return -ENOMEM; 3611c3426b47SDavid S. Miller inet_peer_base_init(bp); 3612c3426b47SDavid S. Miller net->ipv6.peers = bp; 3613c3426b47SDavid S. Miller return 0; 3614c3426b47SDavid S. Miller } 3615c3426b47SDavid S. Miller 3616c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 3617c3426b47SDavid S. Miller { 3618c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 3619c3426b47SDavid S. Miller 3620c3426b47SDavid S. Miller net->ipv6.peers = NULL; 362156a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3622c3426b47SDavid S. Miller kfree(bp); 3623c3426b47SDavid S. Miller } 3624c3426b47SDavid S. Miller 36252b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3626c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3627c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3628c3426b47SDavid S. Miller }; 3629c3426b47SDavid S. Miller 3630d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3631d189634eSThomas Graf .init = ip6_route_net_init_late, 3632d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3633d189634eSThomas Graf }; 3634d189634eSThomas Graf 36358ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 36368ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 36378ed67789SDaniel Lezcano .priority = 0, 36388ed67789SDaniel Lezcano }; 36398ed67789SDaniel Lezcano 3640433d49c3SDaniel Lezcano int __init ip6_route_init(void) 36411da177e4SLinus Torvalds { 3642433d49c3SDaniel Lezcano int ret; 36438d0b94afSMartin KaFai Lau int cpu; 3644433d49c3SDaniel Lezcano 36459a7ec3a9SDaniel Lezcano ret = -ENOMEM; 36469a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 36479a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 36489a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 36499a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3650c19a28e1SFernando Carrijo goto out; 365114e50e57SDavid S. Miller 3652fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 36538ed67789SDaniel Lezcano if (ret) 3654bdb3289fSDaniel Lezcano goto out_kmem_cache; 3655bdb3289fSDaniel Lezcano 3656c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3657c3426b47SDavid S. Miller if (ret) 3658e8803b6cSDavid S. Miller goto out_dst_entries; 36592a0c451aSThomas Graf 36607e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 36617e52b33bSDavid S. Miller if (ret) 36627e52b33bSDavid S. Miller goto out_register_inetpeer; 3663c3426b47SDavid S. Miller 36645dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 36655dc121e9SArnaud Ebalard 36668ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 36678ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 36688ed67789SDaniel Lezcano * manually for init_net */ 3669d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 36708ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3671bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3672d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 36738ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3674d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 36758ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3676bdb3289fSDaniel Lezcano #endif 3677e8803b6cSDavid S. Miller ret = fib6_init(); 3678433d49c3SDaniel Lezcano if (ret) 36798ed67789SDaniel Lezcano goto out_register_subsys; 3680433d49c3SDaniel Lezcano 3681433d49c3SDaniel Lezcano ret = xfrm6_init(); 3682433d49c3SDaniel Lezcano if (ret) 3683e8803b6cSDavid S. Miller goto out_fib6_init; 3684c35b7e72SDaniel Lezcano 3685433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3686433d49c3SDaniel Lezcano if (ret) 3687433d49c3SDaniel Lezcano goto xfrm6_init; 36887e5449c2SDaniel Lezcano 3689d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3690d189634eSThomas Graf if (ret) 3691d189634eSThomas Graf goto fib6_rules_init; 3692d189634eSThomas Graf 3693433d49c3SDaniel Lezcano ret = -ENOBUFS; 3694c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3695c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3696c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3697d189634eSThomas Graf goto out_register_late_subsys; 3698433d49c3SDaniel Lezcano 36998ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3700cdb18761SDaniel Lezcano if (ret) 3701d189634eSThomas Graf goto out_register_late_subsys; 37028ed67789SDaniel Lezcano 37038d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 37048d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 37058d0b94afSMartin KaFai Lau 37068d0b94afSMartin KaFai Lau INIT_LIST_HEAD(&ul->head); 37078d0b94afSMartin KaFai Lau spin_lock_init(&ul->lock); 37088d0b94afSMartin KaFai Lau } 37098d0b94afSMartin KaFai Lau 3710433d49c3SDaniel Lezcano out: 3711433d49c3SDaniel Lezcano return ret; 3712433d49c3SDaniel Lezcano 3713d189634eSThomas Graf out_register_late_subsys: 3714d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3715433d49c3SDaniel Lezcano fib6_rules_init: 3716433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3717433d49c3SDaniel Lezcano xfrm6_init: 3718433d49c3SDaniel Lezcano xfrm6_fini(); 37192a0c451aSThomas Graf out_fib6_init: 37202a0c451aSThomas Graf fib6_gc_cleanup(); 37218ed67789SDaniel Lezcano out_register_subsys: 37228ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 37237e52b33bSDavid S. Miller out_register_inetpeer: 37247e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3725fc66f95cSEric Dumazet out_dst_entries: 3726fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3727433d49c3SDaniel Lezcano out_kmem_cache: 3728f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3729433d49c3SDaniel Lezcano goto out; 37301da177e4SLinus Torvalds } 37311da177e4SLinus Torvalds 37321da177e4SLinus Torvalds void ip6_route_cleanup(void) 37331da177e4SLinus Torvalds { 37348ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3735d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3736101367c2SThomas Graf fib6_rules_cleanup(); 37371da177e4SLinus Torvalds xfrm6_fini(); 37381da177e4SLinus Torvalds fib6_gc_cleanup(); 3739c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 37408ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 374141bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3742f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 37431da177e4SLinus Torvalds } 3744