11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Linux INET6 implementation 31da177e4SLinus Torvalds * FIB front-end. 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Authors: 61da177e4SLinus Torvalds * Pedro Roque <roque@di.fc.ul.pt> 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 91da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 101da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 111da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds /* Changes: 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * YOSHIFUJI Hideaki @USAGI 171da177e4SLinus Torvalds * reworked default router selection. 181da177e4SLinus Torvalds * - respect outgoing interface 191da177e4SLinus Torvalds * - select from (probably) reachable routers (i.e. 201da177e4SLinus Torvalds * routers in REACHABLE, STALE, DELAY or PROBE states). 211da177e4SLinus Torvalds * - always select the same router if it is (probably) 221da177e4SLinus Torvalds * reachable. otherwise, round-robin the list. 23c0bece9fSYOSHIFUJI Hideaki * Ville Nuorvala 24c0bece9fSYOSHIFUJI Hideaki * Fixed routing subtrees. 251da177e4SLinus Torvalds */ 261da177e4SLinus Torvalds 27f3213831SJoe Perches #define pr_fmt(fmt) "IPv6: " fmt 28f3213831SJoe Perches 294fc268d2SRandy Dunlap #include <linux/capability.h> 301da177e4SLinus Torvalds #include <linux/errno.h> 31bc3b2d7fSPaul Gortmaker #include <linux/export.h> 321da177e4SLinus Torvalds #include <linux/types.h> 331da177e4SLinus Torvalds #include <linux/times.h> 341da177e4SLinus Torvalds #include <linux/socket.h> 351da177e4SLinus Torvalds #include <linux/sockios.h> 361da177e4SLinus Torvalds #include <linux/net.h> 371da177e4SLinus Torvalds #include <linux/route.h> 381da177e4SLinus Torvalds #include <linux/netdevice.h> 391da177e4SLinus Torvalds #include <linux/in6.h> 407bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h> 411da177e4SLinus Torvalds #include <linux/init.h> 421da177e4SLinus Torvalds #include <linux/if_arp.h> 431da177e4SLinus Torvalds #include <linux/proc_fs.h> 441da177e4SLinus Torvalds #include <linux/seq_file.h> 455b7c931dSDaniel Lezcano #include <linux/nsproxy.h> 465a0e3ad6STejun Heo #include <linux/slab.h> 47457c4cbcSEric W. Biederman #include <net/net_namespace.h> 481da177e4SLinus Torvalds #include <net/snmp.h> 491da177e4SLinus Torvalds #include <net/ipv6.h> 501da177e4SLinus Torvalds #include <net/ip6_fib.h> 511da177e4SLinus Torvalds #include <net/ip6_route.h> 521da177e4SLinus Torvalds #include <net/ndisc.h> 531da177e4SLinus Torvalds #include <net/addrconf.h> 541da177e4SLinus Torvalds #include <net/tcp.h> 551da177e4SLinus Torvalds #include <linux/rtnetlink.h> 561da177e4SLinus Torvalds #include <net/dst.h> 57904af04dSJiri Benc #include <net/dst_metadata.h> 581da177e4SLinus Torvalds #include <net/xfrm.h> 598d71740cSTom Tucker #include <net/netevent.h> 6021713ebcSThomas Graf #include <net/netlink.h> 6151ebd318SNicolas Dichtel #include <net/nexthop.h> 6219e42e45SRoopa Prabhu #include <net/lwtunnel.h> 63904af04dSJiri Benc #include <net/ip_tunnels.h> 64ca254490SDavid Ahern #include <net/l3mdev.h> 65b811580dSDavid Ahern #include <trace/events/fib6.h> 661da177e4SLinus Torvalds 677c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 701da177e4SLinus Torvalds #include <linux/sysctl.h> 711da177e4SLinus Torvalds #endif 721da177e4SLinus Torvalds 73afc154e9SHannes Frederic Sowa enum rt6_nud_state { 747e980569SJiri Benc RT6_NUD_FAIL_HARD = -3, 757e980569SJiri Benc RT6_NUD_FAIL_PROBE = -2, 767e980569SJiri Benc RT6_NUD_FAIL_DO_RR = -1, 77afc154e9SHannes Frederic Sowa RT6_NUD_SUCCEED = 1 78afc154e9SHannes Frederic Sowa }; 79afc154e9SHannes Frederic Sowa 8083a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort); 811da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 820dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst); 83ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst); 841da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *); 851da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *); 861da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *, 871da177e4SLinus Torvalds struct net_device *dev, int how); 88569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops); 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds static int ip6_pkt_discard(struct sk_buff *skb); 91ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); 927150aedeSKamala R static int ip6_pkt_prohibit(struct sk_buff *skb); 93ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb); 941da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb); 956700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 966700c270SDavid S. Miller struct sk_buff *skb, u32 mtu); 976700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, 986700c270SDavid S. Miller struct sk_buff *skb); 994b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt); 10052bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict); 1011da177e4SLinus Torvalds 10270ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 103efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 104b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 105830218c1SDavid Ahern const struct in6_addr *gwaddr, 106830218c1SDavid Ahern struct net_device *dev, 10795c96174SEric Dumazet unsigned int pref); 108efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 109b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 110830218c1SDavid Ahern const struct in6_addr *gwaddr, 111830218c1SDavid Ahern struct net_device *dev); 11270ceb4f5SYOSHIFUJI Hideaki #endif 11370ceb4f5SYOSHIFUJI Hideaki 1148d0b94afSMartin KaFai Lau struct uncached_list { 1158d0b94afSMartin KaFai Lau spinlock_t lock; 1168d0b94afSMartin KaFai Lau struct list_head head; 1178d0b94afSMartin KaFai Lau }; 1188d0b94afSMartin KaFai Lau 1198d0b94afSMartin KaFai Lau static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt6_uncached_list); 1208d0b94afSMartin KaFai Lau 1218d0b94afSMartin KaFai Lau static void rt6_uncached_list_add(struct rt6_info *rt) 1228d0b94afSMartin KaFai Lau { 1238d0b94afSMartin KaFai Lau struct uncached_list *ul = raw_cpu_ptr(&rt6_uncached_list); 1248d0b94afSMartin KaFai Lau 1258d0b94afSMartin KaFai Lau rt->dst.flags |= DST_NOCACHE; 1268d0b94afSMartin KaFai Lau rt->rt6i_uncached_list = ul; 1278d0b94afSMartin KaFai Lau 1288d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1298d0b94afSMartin KaFai Lau list_add_tail(&rt->rt6i_uncached, &ul->head); 1308d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1318d0b94afSMartin KaFai Lau } 1328d0b94afSMartin KaFai Lau 1338d0b94afSMartin KaFai Lau static void rt6_uncached_list_del(struct rt6_info *rt) 1348d0b94afSMartin KaFai Lau { 1358d0b94afSMartin KaFai Lau if (!list_empty(&rt->rt6i_uncached)) { 1368d0b94afSMartin KaFai Lau struct uncached_list *ul = rt->rt6i_uncached_list; 1378d0b94afSMartin KaFai Lau 1388d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1398d0b94afSMartin KaFai Lau list_del(&rt->rt6i_uncached); 1408d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1418d0b94afSMartin KaFai Lau } 1428d0b94afSMartin KaFai Lau } 1438d0b94afSMartin KaFai Lau 1448d0b94afSMartin KaFai Lau static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) 1458d0b94afSMartin KaFai Lau { 1468d0b94afSMartin KaFai Lau struct net_device *loopback_dev = net->loopback_dev; 1478d0b94afSMartin KaFai Lau int cpu; 1488d0b94afSMartin KaFai Lau 149e332bc67SEric W. Biederman if (dev == loopback_dev) 150e332bc67SEric W. Biederman return; 151e332bc67SEric W. Biederman 1528d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 1538d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 1548d0b94afSMartin KaFai Lau struct rt6_info *rt; 1558d0b94afSMartin KaFai Lau 1568d0b94afSMartin KaFai Lau spin_lock_bh(&ul->lock); 1578d0b94afSMartin KaFai Lau list_for_each_entry(rt, &ul->head, rt6i_uncached) { 1588d0b94afSMartin KaFai Lau struct inet6_dev *rt_idev = rt->rt6i_idev; 1598d0b94afSMartin KaFai Lau struct net_device *rt_dev = rt->dst.dev; 1608d0b94afSMartin KaFai Lau 161e332bc67SEric W. Biederman if (rt_idev->dev == dev) { 1628d0b94afSMartin KaFai Lau rt->rt6i_idev = in6_dev_get(loopback_dev); 1638d0b94afSMartin KaFai Lau in6_dev_put(rt_idev); 1648d0b94afSMartin KaFai Lau } 1658d0b94afSMartin KaFai Lau 166e332bc67SEric W. Biederman if (rt_dev == dev) { 1678d0b94afSMartin KaFai Lau rt->dst.dev = loopback_dev; 1688d0b94afSMartin KaFai Lau dev_hold(rt->dst.dev); 1698d0b94afSMartin KaFai Lau dev_put(rt_dev); 1708d0b94afSMartin KaFai Lau } 1718d0b94afSMartin KaFai Lau } 1728d0b94afSMartin KaFai Lau spin_unlock_bh(&ul->lock); 1738d0b94afSMartin KaFai Lau } 1748d0b94afSMartin KaFai Lau } 1758d0b94afSMartin KaFai Lau 176d52d3997SMartin KaFai Lau static u32 *rt6_pcpu_cow_metrics(struct rt6_info *rt) 177d52d3997SMartin KaFai Lau { 178d52d3997SMartin KaFai Lau return dst_metrics_write_ptr(rt->dst.from); 179d52d3997SMartin KaFai Lau } 180d52d3997SMartin KaFai Lau 18106582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) 18206582540SDavid S. Miller { 18306582540SDavid S. Miller struct rt6_info *rt = (struct rt6_info *)dst; 18406582540SDavid S. Miller 185d52d3997SMartin KaFai Lau if (rt->rt6i_flags & RTF_PCPU) 186d52d3997SMartin KaFai Lau return rt6_pcpu_cow_metrics(rt); 187d52d3997SMartin KaFai Lau else if (rt->rt6i_flags & RTF_CACHE) 1884b32b5adSMartin KaFai Lau return NULL; 1894b32b5adSMartin KaFai Lau else 1903b471175SMartin KaFai Lau return dst_cow_metrics_generic(dst, old); 19106582540SDavid S. Miller } 19206582540SDavid S. Miller 193f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, 194f894cbf8SDavid S. Miller struct sk_buff *skb, 195f894cbf8SDavid S. Miller const void *daddr) 19639232973SDavid S. Miller { 19739232973SDavid S. Miller struct in6_addr *p = &rt->rt6i_gateway; 19839232973SDavid S. Miller 199a7563f34SDavid S. Miller if (!ipv6_addr_any(p)) 20039232973SDavid S. Miller return (const void *) p; 201f894cbf8SDavid S. Miller else if (skb) 202f894cbf8SDavid S. Miller return &ipv6_hdr(skb)->daddr; 20339232973SDavid S. Miller return daddr; 20439232973SDavid S. Miller } 20539232973SDavid S. Miller 206f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, 207f894cbf8SDavid S. Miller struct sk_buff *skb, 208f894cbf8SDavid S. Miller const void *daddr) 209d3aaeb38SDavid S. Miller { 21039232973SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 21139232973SDavid S. Miller struct neighbour *n; 21239232973SDavid S. Miller 213f894cbf8SDavid S. Miller daddr = choose_neigh_daddr(rt, skb, daddr); 2148e022ee6SYOSHIFUJI Hideaki / 吉藤英明 n = __ipv6_neigh_lookup(dst->dev, daddr); 215f83c7790SDavid S. Miller if (n) 216f83c7790SDavid S. Miller return n; 217f83c7790SDavid S. Miller return neigh_create(&nd_tbl, daddr, dst->dev); 218f83c7790SDavid S. Miller } 219f83c7790SDavid S. Miller 2209a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 2211da177e4SLinus Torvalds .family = AF_INET6, 2221da177e4SLinus Torvalds .gc = ip6_dst_gc, 2231da177e4SLinus Torvalds .gc_thresh = 1024, 2241da177e4SLinus Torvalds .check = ip6_dst_check, 2250dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 226ebb762f2SSteffen Klassert .mtu = ip6_mtu, 22706582540SDavid S. Miller .cow_metrics = ipv6_cow_metrics, 2281da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 2291da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 2301da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 2311da177e4SLinus Torvalds .link_failure = ip6_link_failure, 2321da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 2336e157b6aSDavid S. Miller .redirect = rt6_do_redirect, 2349f8955ccSEric W. Biederman .local_out = __ip6_local_out, 235d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 2361da177e4SLinus Torvalds }; 2371da177e4SLinus Torvalds 238ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 239ec831ea7SRoland Dreier { 240618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 241618f9bc7SSteffen Klassert 242618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 243ec831ea7SRoland Dreier } 244ec831ea7SRoland Dreier 2456700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, 2466700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 24714e50e57SDavid S. Miller { 24814e50e57SDavid S. Miller } 24914e50e57SDavid S. Miller 2506700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, 2516700c270SDavid S. Miller struct sk_buff *skb) 252b587ee3bSDavid S. Miller { 253b587ee3bSDavid S. Miller } 254b587ee3bSDavid S. Miller 25514e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 25614e50e57SDavid S. Miller .family = AF_INET6, 25714e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 25814e50e57SDavid S. Miller .check = ip6_dst_check, 259ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 260214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 26114e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 262b587ee3bSDavid S. Miller .redirect = ip6_rt_blackhole_redirect, 2630a1f5962SMartin KaFai Lau .cow_metrics = dst_cow_metrics_generic, 264d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 26514e50e57SDavid S. Miller }; 26614e50e57SDavid S. Miller 26762fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 26814edd87dSLi RongQing [RTAX_HOPLIMIT - 1] = 0, 26962fa8a84SDavid S. Miller }; 27062fa8a84SDavid S. Miller 271fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = { 2721da177e4SLinus Torvalds .dst = { 2731da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 2741da177e4SLinus Torvalds .__use = 1, 2752c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 2761da177e4SLinus Torvalds .error = -ENETUNREACH, 2771da177e4SLinus Torvalds .input = ip6_pkt_discard, 2781da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 2791da177e4SLinus Torvalds }, 2801da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2814f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 2821da177e4SLinus Torvalds .rt6i_metric = ~(u32) 0, 2831da177e4SLinus Torvalds .rt6i_ref = ATOMIC_INIT(1), 2841da177e4SLinus Torvalds }; 2851da177e4SLinus Torvalds 286101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 287101367c2SThomas Graf 288fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = { 289101367c2SThomas Graf .dst = { 290101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 291101367c2SThomas Graf .__use = 1, 2922c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 293101367c2SThomas Graf .error = -EACCES, 2949ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 2959ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 296101367c2SThomas Graf }, 297101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2984f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 299101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 300101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 301101367c2SThomas Graf }; 302101367c2SThomas Graf 303fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = { 304101367c2SThomas Graf .dst = { 305101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 306101367c2SThomas Graf .__use = 1, 3072c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 308101367c2SThomas Graf .error = -EINVAL, 309352e512cSHerbert Xu .input = dst_discard, 310ede2059dSEric W. Biederman .output = dst_discard_out, 311101367c2SThomas Graf }, 312101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 3134f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 314101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 315101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 316101367c2SThomas Graf }; 317101367c2SThomas Graf 318101367c2SThomas Graf #endif 319101367c2SThomas Graf 320ebfa45f0SMartin KaFai Lau static void rt6_info_init(struct rt6_info *rt) 321ebfa45f0SMartin KaFai Lau { 322ebfa45f0SMartin KaFai Lau struct dst_entry *dst = &rt->dst; 323ebfa45f0SMartin KaFai Lau 324ebfa45f0SMartin KaFai Lau memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 325ebfa45f0SMartin KaFai Lau INIT_LIST_HEAD(&rt->rt6i_siblings); 326ebfa45f0SMartin KaFai Lau INIT_LIST_HEAD(&rt->rt6i_uncached); 327ebfa45f0SMartin KaFai Lau } 328ebfa45f0SMartin KaFai Lau 3291da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 330d52d3997SMartin KaFai Lau static struct rt6_info *__ip6_dst_alloc(struct net *net, 331957c665fSDavid S. Miller struct net_device *dev, 332ad706862SMartin KaFai Lau int flags) 3331da177e4SLinus Torvalds { 33497bab73fSDavid S. Miller struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 3356f3118b5SNicolas Dichtel 0, DST_OBSOLETE_FORCE_CHK, flags); 336cf911662SDavid S. Miller 337ebfa45f0SMartin KaFai Lau if (rt) 338ebfa45f0SMartin KaFai Lau rt6_info_init(rt); 3398104891bSSteffen Klassert 340cf911662SDavid S. Miller return rt; 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds 3439ab179d8SDavid Ahern struct rt6_info *ip6_dst_alloc(struct net *net, 344d52d3997SMartin KaFai Lau struct net_device *dev, 345ad706862SMartin KaFai Lau int flags) 346d52d3997SMartin KaFai Lau { 347ad706862SMartin KaFai Lau struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags); 348d52d3997SMartin KaFai Lau 349d52d3997SMartin KaFai Lau if (rt) { 350d52d3997SMartin KaFai Lau rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC); 351d52d3997SMartin KaFai Lau if (rt->rt6i_pcpu) { 352d52d3997SMartin KaFai Lau int cpu; 353d52d3997SMartin KaFai Lau 354d52d3997SMartin KaFai Lau for_each_possible_cpu(cpu) { 355d52d3997SMartin KaFai Lau struct rt6_info **p; 356d52d3997SMartin KaFai Lau 357d52d3997SMartin KaFai Lau p = per_cpu_ptr(rt->rt6i_pcpu, cpu); 358d52d3997SMartin KaFai Lau /* no one shares rt */ 359d52d3997SMartin KaFai Lau *p = NULL; 360d52d3997SMartin KaFai Lau } 361d52d3997SMartin KaFai Lau } else { 362d52d3997SMartin KaFai Lau dst_destroy((struct dst_entry *)rt); 363d52d3997SMartin KaFai Lau return NULL; 364d52d3997SMartin KaFai Lau } 365d52d3997SMartin KaFai Lau } 366d52d3997SMartin KaFai Lau 367d52d3997SMartin KaFai Lau return rt; 368d52d3997SMartin KaFai Lau } 3699ab179d8SDavid Ahern EXPORT_SYMBOL(ip6_dst_alloc); 370d52d3997SMartin KaFai Lau 3711da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 3721da177e4SLinus Torvalds { 3731da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 374ecd98837SYOSHIFUJI Hideaki / 吉藤英明 struct dst_entry *from = dst->from; 3758d0b94afSMartin KaFai Lau struct inet6_dev *idev; 3761da177e4SLinus Torvalds 3778e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 378d52d3997SMartin KaFai Lau free_percpu(rt->rt6i_pcpu); 3798d0b94afSMartin KaFai Lau rt6_uncached_list_del(rt); 3808d0b94afSMartin KaFai Lau 3818d0b94afSMartin KaFai Lau idev = rt->rt6i_idev; 38238308473SDavid S. Miller if (idev) { 3831da177e4SLinus Torvalds rt->rt6i_idev = NULL; 3841da177e4SLinus Torvalds in6_dev_put(idev); 3851da177e4SLinus Torvalds } 3861716a961SGao feng 387ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst->from = NULL; 388ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst_release(from); 389b3419363SDavid S. Miller } 390b3419363SDavid S. Miller 3911da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 3921da177e4SLinus Torvalds int how) 3931da177e4SLinus Torvalds { 3941da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3951da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3965a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 397c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 3981da177e4SLinus Torvalds 39997cac082SDavid S. Miller if (dev != loopback_dev) { 40097cac082SDavid S. Miller if (idev && idev->dev == dev) { 4015a3e55d6SDenis V. Lunev struct inet6_dev *loopback_idev = 4025a3e55d6SDenis V. Lunev in6_dev_get(loopback_dev); 40338308473SDavid S. Miller if (loopback_idev) { 4041da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 4051da177e4SLinus Torvalds in6_dev_put(idev); 4061da177e4SLinus Torvalds } 4071da177e4SLinus Torvalds } 40897cac082SDavid S. Miller } 4091da177e4SLinus Torvalds } 4101da177e4SLinus Torvalds 4115973fb1eSMartin KaFai Lau static bool __rt6_check_expired(const struct rt6_info *rt) 4125973fb1eSMartin KaFai Lau { 4135973fb1eSMartin KaFai Lau if (rt->rt6i_flags & RTF_EXPIRES) 4145973fb1eSMartin KaFai Lau return time_after(jiffies, rt->dst.expires); 4155973fb1eSMartin KaFai Lau else 4165973fb1eSMartin KaFai Lau return false; 4175973fb1eSMartin KaFai Lau } 4185973fb1eSMartin KaFai Lau 419a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt) 4201da177e4SLinus Torvalds { 4211716a961SGao feng if (rt->rt6i_flags & RTF_EXPIRES) { 4221716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 423a50feda5SEric Dumazet return true; 4241716a961SGao feng } else if (rt->dst.from) { 4253fd91fb3SLi RongQing return rt6_check_expired((struct rt6_info *) rt->dst.from); 4261716a961SGao feng } 427a50feda5SEric Dumazet return false; 4281da177e4SLinus Torvalds } 4291da177e4SLinus Torvalds 43051ebd318SNicolas Dichtel /* Multipath route selection: 43151ebd318SNicolas Dichtel * Hash based function using packet header and flowlabel. 43251ebd318SNicolas Dichtel * Adapted from fib_info_hashfn() 43351ebd318SNicolas Dichtel */ 43451ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count, 43551ebd318SNicolas Dichtel const struct flowi6 *fl6) 43651ebd318SNicolas Dichtel { 437644d0e65STom Herbert return get_hash_from_flowi6(fl6) % candidate_count; 43851ebd318SNicolas Dichtel } 43951ebd318SNicolas Dichtel 44051ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match, 44152bd4c0cSNicolas Dichtel struct flowi6 *fl6, int oif, 44252bd4c0cSNicolas Dichtel int strict) 44351ebd318SNicolas Dichtel { 44451ebd318SNicolas Dichtel struct rt6_info *sibling, *next_sibling; 44551ebd318SNicolas Dichtel int route_choosen; 44651ebd318SNicolas Dichtel 44751ebd318SNicolas Dichtel route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6); 44851ebd318SNicolas Dichtel /* Don't change the route, if route_choosen == 0 44951ebd318SNicolas Dichtel * (siblings does not include ourself) 45051ebd318SNicolas Dichtel */ 45151ebd318SNicolas Dichtel if (route_choosen) 45251ebd318SNicolas Dichtel list_for_each_entry_safe(sibling, next_sibling, 45351ebd318SNicolas Dichtel &match->rt6i_siblings, rt6i_siblings) { 45451ebd318SNicolas Dichtel route_choosen--; 45551ebd318SNicolas Dichtel if (route_choosen == 0) { 45652bd4c0cSNicolas Dichtel if (rt6_score_route(sibling, oif, strict) < 0) 45752bd4c0cSNicolas Dichtel break; 45851ebd318SNicolas Dichtel match = sibling; 45951ebd318SNicolas Dichtel break; 46051ebd318SNicolas Dichtel } 46151ebd318SNicolas Dichtel } 46251ebd318SNicolas Dichtel return match; 46351ebd318SNicolas Dichtel } 46451ebd318SNicolas Dichtel 4651da177e4SLinus Torvalds /* 466c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 4671da177e4SLinus Torvalds */ 4681da177e4SLinus Torvalds 4698ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 4708ed67789SDaniel Lezcano struct rt6_info *rt, 471b71d1d42SEric Dumazet const struct in6_addr *saddr, 4721da177e4SLinus Torvalds int oif, 473d420895eSYOSHIFUJI Hideaki int flags) 4741da177e4SLinus Torvalds { 4751da177e4SLinus Torvalds struct rt6_info *local = NULL; 4761da177e4SLinus Torvalds struct rt6_info *sprt; 4771da177e4SLinus Torvalds 478dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 479dd3abc4eSYOSHIFUJI Hideaki goto out; 480dd3abc4eSYOSHIFUJI Hideaki 481d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 482d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 483dd3abc4eSYOSHIFUJI Hideaki 484dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4851da177e4SLinus Torvalds if (dev->ifindex == oif) 4861da177e4SLinus Torvalds return sprt; 4871da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 48838308473SDavid S. Miller if (!sprt->rt6i_idev || 4891da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 49017fb0b2bSDavid Ahern if (flags & RT6_LOOKUP_F_IFACE) 4911da177e4SLinus Torvalds continue; 49217fb0b2bSDavid Ahern if (local && 49317fb0b2bSDavid Ahern local->rt6i_idev->dev->ifindex == oif) 4941da177e4SLinus Torvalds continue; 4951da177e4SLinus Torvalds } 4961da177e4SLinus Torvalds local = sprt; 4971da177e4SLinus Torvalds } 498dd3abc4eSYOSHIFUJI Hideaki } else { 499dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 500dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 501dd3abc4eSYOSHIFUJI Hideaki return sprt; 502dd3abc4eSYOSHIFUJI Hideaki } 5031da177e4SLinus Torvalds } 5041da177e4SLinus Torvalds 505dd3abc4eSYOSHIFUJI Hideaki if (oif) { 5061da177e4SLinus Torvalds if (local) 5071da177e4SLinus Torvalds return local; 5081da177e4SLinus Torvalds 509d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 5108ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 5111da177e4SLinus Torvalds } 512dd3abc4eSYOSHIFUJI Hideaki out: 5131da177e4SLinus Torvalds return rt; 5141da177e4SLinus Torvalds } 5151da177e4SLinus Torvalds 51627097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 517c2f17e82SHannes Frederic Sowa struct __rt6_probe_work { 518c2f17e82SHannes Frederic Sowa struct work_struct work; 519c2f17e82SHannes Frederic Sowa struct in6_addr target; 520c2f17e82SHannes Frederic Sowa struct net_device *dev; 521c2f17e82SHannes Frederic Sowa }; 522c2f17e82SHannes Frederic Sowa 523c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w) 524c2f17e82SHannes Frederic Sowa { 525c2f17e82SHannes Frederic Sowa struct in6_addr mcaddr; 526c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work = 527c2f17e82SHannes Frederic Sowa container_of(w, struct __rt6_probe_work, work); 528c2f17e82SHannes Frederic Sowa 529c2f17e82SHannes Frederic Sowa addrconf_addr_solict_mult(&work->target, &mcaddr); 530adc176c5SErik Nordmark ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0); 531c2f17e82SHannes Frederic Sowa dev_put(work->dev); 532662f5533SMichael Büsch kfree(work); 533c2f17e82SHannes Frederic Sowa } 534c2f17e82SHannes Frederic Sowa 53527097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 53627097255SYOSHIFUJI Hideaki { 537990edb42SMartin KaFai Lau struct __rt6_probe_work *work; 538f2c31e32SEric Dumazet struct neighbour *neigh; 53927097255SYOSHIFUJI Hideaki /* 54027097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 54127097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 54227097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 54327097255SYOSHIFUJI Hideaki * 54427097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 54527097255SYOSHIFUJI Hideaki * to no more than one per minute. 54627097255SYOSHIFUJI Hideaki */ 5472152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!rt || !(rt->rt6i_flags & RTF_GATEWAY)) 548fdd6681dSAmerigo Wang return; 5492152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 5502152caeaSYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 5512152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 5528d6c31bfSMartin KaFai Lau if (neigh->nud_state & NUD_VALID) 5538d6c31bfSMartin KaFai Lau goto out; 5548d6c31bfSMartin KaFai Lau 555990edb42SMartin KaFai Lau work = NULL; 5562152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_lock(&neigh->lock); 557990edb42SMartin KaFai Lau if (!(neigh->nud_state & NUD_VALID) && 558990edb42SMartin KaFai Lau time_after(jiffies, 559990edb42SMartin KaFai Lau neigh->updated + 560990edb42SMartin KaFai Lau rt->rt6i_idev->cnf.rtr_probe_interval)) { 561c2f17e82SHannes Frederic Sowa work = kmalloc(sizeof(*work), GFP_ATOMIC); 562990edb42SMartin KaFai Lau if (work) 5637e980569SJiri Benc __neigh_set_probe_once(neigh); 564990edb42SMartin KaFai Lau } 565c2f17e82SHannes Frederic Sowa write_unlock(&neigh->lock); 566990edb42SMartin KaFai Lau } else { 567990edb42SMartin KaFai Lau work = kmalloc(sizeof(*work), GFP_ATOMIC); 568990edb42SMartin KaFai Lau } 569c2f17e82SHannes Frederic Sowa 570c2f17e82SHannes Frederic Sowa if (work) { 571c2f17e82SHannes Frederic Sowa INIT_WORK(&work->work, rt6_probe_deferred); 572c2f17e82SHannes Frederic Sowa work->target = rt->rt6i_gateway; 573c2f17e82SHannes Frederic Sowa dev_hold(rt->dst.dev); 574c2f17e82SHannes Frederic Sowa work->dev = rt->dst.dev; 575c2f17e82SHannes Frederic Sowa schedule_work(&work->work); 576c2f17e82SHannes Frederic Sowa } 577990edb42SMartin KaFai Lau 5788d6c31bfSMartin KaFai Lau out: 5792152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 580f2c31e32SEric Dumazet } 58127097255SYOSHIFUJI Hideaki #else 58227097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 58327097255SYOSHIFUJI Hideaki { 58427097255SYOSHIFUJI Hideaki } 58527097255SYOSHIFUJI Hideaki #endif 58627097255SYOSHIFUJI Hideaki 5871da177e4SLinus Torvalds /* 588554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 5891da177e4SLinus Torvalds */ 590b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 5911da177e4SLinus Torvalds { 592d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 593161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 594554cfb7eSYOSHIFUJI Hideaki return 2; 595161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 596161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 597161980f4SDavid S. Miller return 1; 598554cfb7eSYOSHIFUJI Hideaki return 0; 5991da177e4SLinus Torvalds } 6001da177e4SLinus Torvalds 601afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt) 6021da177e4SLinus Torvalds { 603f2c31e32SEric Dumazet struct neighbour *neigh; 604afc154e9SHannes Frederic Sowa enum rt6_nud_state ret = RT6_NUD_FAIL_HARD; 605f2c31e32SEric Dumazet 6064d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 6074d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 608afc154e9SHannes Frederic Sowa return RT6_NUD_SUCCEED; 609145a3621SYOSHIFUJI Hideaki / 吉藤英明 610145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 611145a3621SYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 612145a3621SYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 613145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_lock(&neigh->lock); 614554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 615afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 616398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 617a5a81f0bSPaul Marks else if (!(neigh->nud_state & NUD_FAILED)) 618afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 6197e980569SJiri Benc else 6207e980569SJiri Benc ret = RT6_NUD_FAIL_PROBE; 621398bcbebSYOSHIFUJI Hideaki #endif 622145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_unlock(&neigh->lock); 623afc154e9SHannes Frederic Sowa } else { 624afc154e9SHannes Frederic Sowa ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 6257e980569SJiri Benc RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; 626a5a81f0bSPaul Marks } 627145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 628145a3621SYOSHIFUJI Hideaki / 吉藤英明 629a5a81f0bSPaul Marks return ret; 6301da177e4SLinus Torvalds } 6311da177e4SLinus Torvalds 632554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 633554cfb7eSYOSHIFUJI Hideaki int strict) 634554cfb7eSYOSHIFUJI Hideaki { 635a5a81f0bSPaul Marks int m; 6364d0c5911SYOSHIFUJI Hideaki 6374d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 63877d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 639afc154e9SHannes Frederic Sowa return RT6_NUD_FAIL_HARD; 640ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 641ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 642ebacaaa0SYOSHIFUJI Hideaki #endif 643afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) { 644afc154e9SHannes Frederic Sowa int n = rt6_check_neigh(rt); 645afc154e9SHannes Frederic Sowa if (n < 0) 646afc154e9SHannes Frederic Sowa return n; 647afc154e9SHannes Frederic Sowa } 648554cfb7eSYOSHIFUJI Hideaki return m; 649554cfb7eSYOSHIFUJI Hideaki } 650554cfb7eSYOSHIFUJI Hideaki 651f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 652afc154e9SHannes Frederic Sowa int *mpri, struct rt6_info *match, 653afc154e9SHannes Frederic Sowa bool *do_rr) 654554cfb7eSYOSHIFUJI Hideaki { 655554cfb7eSYOSHIFUJI Hideaki int m; 656afc154e9SHannes Frederic Sowa bool match_do_rr = false; 65735103d11SAndy Gospodarek struct inet6_dev *idev = rt->rt6i_idev; 65835103d11SAndy Gospodarek struct net_device *dev = rt->dst.dev; 65935103d11SAndy Gospodarek 66035103d11SAndy Gospodarek if (dev && !netif_carrier_ok(dev) && 661d5d32e4bSDavid Ahern idev->cnf.ignore_routes_with_linkdown && 662d5d32e4bSDavid Ahern !(strict & RT6_LOOKUP_F_IGNORE_LINKSTATE)) 66335103d11SAndy Gospodarek goto out; 664554cfb7eSYOSHIFUJI Hideaki 665554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 666f11e6659SDavid S. Miller goto out; 667554cfb7eSYOSHIFUJI Hideaki 668554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 6697e980569SJiri Benc if (m == RT6_NUD_FAIL_DO_RR) { 670afc154e9SHannes Frederic Sowa match_do_rr = true; 671afc154e9SHannes Frederic Sowa m = 0; /* lowest valid score */ 6727e980569SJiri Benc } else if (m == RT6_NUD_FAIL_HARD) { 673f11e6659SDavid S. Miller goto out; 6741da177e4SLinus Torvalds } 675f11e6659SDavid S. Miller 676afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) 677afc154e9SHannes Frederic Sowa rt6_probe(rt); 678afc154e9SHannes Frederic Sowa 6797e980569SJiri Benc /* note that m can be RT6_NUD_FAIL_PROBE at this point */ 680afc154e9SHannes Frederic Sowa if (m > *mpri) { 681afc154e9SHannes Frederic Sowa *do_rr = match_do_rr; 682afc154e9SHannes Frederic Sowa *mpri = m; 683afc154e9SHannes Frederic Sowa match = rt; 684afc154e9SHannes Frederic Sowa } 685f11e6659SDavid S. Miller out: 686f11e6659SDavid S. Miller return match; 6871da177e4SLinus Torvalds } 6881da177e4SLinus Torvalds 689f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 690f11e6659SDavid S. Miller struct rt6_info *rr_head, 691afc154e9SHannes Frederic Sowa u32 metric, int oif, int strict, 692afc154e9SHannes Frederic Sowa bool *do_rr) 693f11e6659SDavid S. Miller { 6949fbdcfafSSteffen Klassert struct rt6_info *rt, *match, *cont; 695f11e6659SDavid S. Miller int mpri = -1; 696f11e6659SDavid S. Miller 697f11e6659SDavid S. Miller match = NULL; 6989fbdcfafSSteffen Klassert cont = NULL; 6999fbdcfafSSteffen Klassert for (rt = rr_head; rt; rt = rt->dst.rt6_next) { 7009fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 7019fbdcfafSSteffen Klassert cont = rt; 7029fbdcfafSSteffen Klassert break; 7039fbdcfafSSteffen Klassert } 7049fbdcfafSSteffen Klassert 705afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 7069fbdcfafSSteffen Klassert } 7079fbdcfafSSteffen Klassert 7089fbdcfafSSteffen Klassert for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) { 7099fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 7109fbdcfafSSteffen Klassert cont = rt; 7119fbdcfafSSteffen Klassert break; 7129fbdcfafSSteffen Klassert } 7139fbdcfafSSteffen Klassert 7149fbdcfafSSteffen Klassert match = find_match(rt, oif, strict, &mpri, match, do_rr); 7159fbdcfafSSteffen Klassert } 7169fbdcfafSSteffen Klassert 7179fbdcfafSSteffen Klassert if (match || !cont) 7189fbdcfafSSteffen Klassert return match; 7199fbdcfafSSteffen Klassert 7209fbdcfafSSteffen Klassert for (rt = cont; rt; rt = rt->dst.rt6_next) 721afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 722f11e6659SDavid S. Miller 723f11e6659SDavid S. Miller return match; 724f11e6659SDavid S. Miller } 725f11e6659SDavid S. Miller 726f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 727f11e6659SDavid S. Miller { 728f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 7298ed67789SDaniel Lezcano struct net *net; 730afc154e9SHannes Frederic Sowa bool do_rr = false; 731f11e6659SDavid S. Miller 732f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 733f11e6659SDavid S. Miller if (!rt0) 734f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 735f11e6659SDavid S. Miller 736afc154e9SHannes Frederic Sowa match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict, 737afc154e9SHannes Frederic Sowa &do_rr); 738f11e6659SDavid S. Miller 739afc154e9SHannes Frederic Sowa if (do_rr) { 740d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 741f11e6659SDavid S. Miller 742554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 743f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 744f11e6659SDavid S. Miller next = fn->leaf; 745f11e6659SDavid S. Miller 746f11e6659SDavid S. Miller if (next != rt0) 747f11e6659SDavid S. Miller fn->rr_ptr = next; 748554cfb7eSYOSHIFUJI Hideaki } 749554cfb7eSYOSHIFUJI Hideaki 750d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 751a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 7521da177e4SLinus Torvalds } 7531da177e4SLinus Torvalds 7548b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt) 7558b9df265SMartin KaFai Lau { 7568b9df265SMartin KaFai Lau return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY)); 7578b9df265SMartin KaFai Lau } 7588b9df265SMartin KaFai Lau 75970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 76070ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 761b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 76270ceb4f5SYOSHIFUJI Hideaki { 763c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 76470ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 76570ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 76670ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 7674bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 76870ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 76970ceb4f5SYOSHIFUJI Hideaki 77070ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 77170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 77270ceb4f5SYOSHIFUJI Hideaki } 77370ceb4f5SYOSHIFUJI Hideaki 77470ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 77570ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 77670ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 77770ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 77870ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 77970ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 78070ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 78170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 78270ceb4f5SYOSHIFUJI Hideaki } 78370ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 78470ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 78570ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 78670ceb4f5SYOSHIFUJI Hideaki } 78770ceb4f5SYOSHIFUJI Hideaki } 78870ceb4f5SYOSHIFUJI Hideaki 78970ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 79070ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 7913933fc95SJens Rosenboom return -EINVAL; 79270ceb4f5SYOSHIFUJI Hideaki 7934bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 79470ceb4f5SYOSHIFUJI Hideaki 79570ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 79670ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 79770ceb4f5SYOSHIFUJI Hideaki else { 79870ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 79970ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 80070ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 80170ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 80270ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 80370ceb4f5SYOSHIFUJI Hideaki } 80470ceb4f5SYOSHIFUJI Hideaki 805f104a567SDuan Jiong if (rinfo->prefix_len == 0) 806f104a567SDuan Jiong rt = rt6_get_dflt_router(gwaddr, dev); 807f104a567SDuan Jiong else 808f104a567SDuan Jiong rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, 809830218c1SDavid Ahern gwaddr, dev); 81070ceb4f5SYOSHIFUJI Hideaki 81170ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 812e0a1ad73SThomas Graf ip6_del_rt(rt); 81370ceb4f5SYOSHIFUJI Hideaki rt = NULL; 81470ceb4f5SYOSHIFUJI Hideaki } 81570ceb4f5SYOSHIFUJI Hideaki 81670ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 817830218c1SDavid Ahern rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, 818830218c1SDavid Ahern dev, pref); 81970ceb4f5SYOSHIFUJI Hideaki else if (rt) 82070ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 82170ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 82270ceb4f5SYOSHIFUJI Hideaki 82370ceb4f5SYOSHIFUJI Hideaki if (rt) { 8241716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 8251716a961SGao feng rt6_clean_expires(rt); 8261716a961SGao feng else 8271716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 8281716a961SGao feng 82994e187c0SAmerigo Wang ip6_rt_put(rt); 83070ceb4f5SYOSHIFUJI Hideaki } 83170ceb4f5SYOSHIFUJI Hideaki return 0; 83270ceb4f5SYOSHIFUJI Hideaki } 83370ceb4f5SYOSHIFUJI Hideaki #endif 83470ceb4f5SYOSHIFUJI Hideaki 835a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn, 836a3c00e46SMartin KaFai Lau struct in6_addr *saddr) 837a3c00e46SMartin KaFai Lau { 838a3c00e46SMartin KaFai Lau struct fib6_node *pn; 839a3c00e46SMartin KaFai Lau while (1) { 840a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_TL_ROOT) 841a3c00e46SMartin KaFai Lau return NULL; 842a3c00e46SMartin KaFai Lau pn = fn->parent; 843a3c00e46SMartin KaFai Lau if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) 844a3c00e46SMartin KaFai Lau fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); 845a3c00e46SMartin KaFai Lau else 846a3c00e46SMartin KaFai Lau fn = pn; 847a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_RTINFO) 848a3c00e46SMartin KaFai Lau return fn; 849a3c00e46SMartin KaFai Lau } 850a3c00e46SMartin KaFai Lau } 851c71099acSThomas Graf 8528ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 8538ed67789SDaniel Lezcano struct fib6_table *table, 8544c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 8551da177e4SLinus Torvalds { 8561da177e4SLinus Torvalds struct fib6_node *fn; 8571da177e4SLinus Torvalds struct rt6_info *rt; 8581da177e4SLinus Torvalds 859c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 8604c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 861c71099acSThomas Graf restart: 862c71099acSThomas Graf rt = fn->leaf; 8634c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 86451ebd318SNicolas Dichtel if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) 86552bd4c0cSNicolas Dichtel rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags); 866a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 867a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 868a3c00e46SMartin KaFai Lau if (fn) 869a3c00e46SMartin KaFai Lau goto restart; 870a3c00e46SMartin KaFai Lau } 871d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 872c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 873b811580dSDavid Ahern 874b811580dSDavid Ahern trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); 875b811580dSDavid Ahern 8761da177e4SLinus Torvalds return rt; 877c71099acSThomas Graf 878c71099acSThomas Graf } 879c71099acSThomas Graf 880ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, 881ea6e574eSFlorian Westphal int flags) 882ea6e574eSFlorian Westphal { 883ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 884ea6e574eSFlorian Westphal } 885ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 886ea6e574eSFlorian Westphal 8879acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 8889acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 889c71099acSThomas Graf { 8904c9483b2SDavid S. Miller struct flowi6 fl6 = { 8914c9483b2SDavid S. Miller .flowi6_oif = oif, 8924c9483b2SDavid S. Miller .daddr = *daddr, 893c71099acSThomas Graf }; 894c71099acSThomas Graf struct dst_entry *dst; 89577d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 896c71099acSThomas Graf 897adaa70bbSThomas Graf if (saddr) { 8984c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 899adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 900adaa70bbSThomas Graf } 901adaa70bbSThomas Graf 9024c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 903c71099acSThomas Graf if (dst->error == 0) 904c71099acSThomas Graf return (struct rt6_info *) dst; 905c71099acSThomas Graf 906c71099acSThomas Graf dst_release(dst); 907c71099acSThomas Graf 9081da177e4SLinus Torvalds return NULL; 9091da177e4SLinus Torvalds } 9107159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 9117159039aSYOSHIFUJI Hideaki 912c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 9131da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 9141da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 9151da177e4SLinus Torvalds be destroyed. 9161da177e4SLinus Torvalds */ 9171da177e4SLinus Torvalds 918e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, 919e715b6d3SFlorian Westphal struct mx6_config *mxc) 9201da177e4SLinus Torvalds { 9211da177e4SLinus Torvalds int err; 922c71099acSThomas Graf struct fib6_table *table; 9231da177e4SLinus Torvalds 924c71099acSThomas Graf table = rt->rt6i_table; 925c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 926e715b6d3SFlorian Westphal err = fib6_add(&table->tb6_root, rt, info, mxc); 927c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 9281da177e4SLinus Torvalds 9291da177e4SLinus Torvalds return err; 9301da177e4SLinus Torvalds } 9311da177e4SLinus Torvalds 93240e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 93340e22e8fSThomas Graf { 934e715b6d3SFlorian Westphal struct nl_info info = { .nl_net = dev_net(rt->dst.dev), }; 935e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 936e715b6d3SFlorian Westphal 937e715b6d3SFlorian Westphal return __ip6_ins_rt(rt, &info, &mxc); 93840e22e8fSThomas Graf } 93940e22e8fSThomas Graf 9408b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, 94121efcfa0SEric Dumazet const struct in6_addr *daddr, 942b71d1d42SEric Dumazet const struct in6_addr *saddr) 9431da177e4SLinus Torvalds { 9441da177e4SLinus Torvalds struct rt6_info *rt; 9451da177e4SLinus Torvalds 9461da177e4SLinus Torvalds /* 9471da177e4SLinus Torvalds * Clone the route. 9481da177e4SLinus Torvalds */ 9491da177e4SLinus Torvalds 950d52d3997SMartin KaFai Lau if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)) 95183a09abdSMartin KaFai Lau ort = (struct rt6_info *)ort->dst.from; 9521da177e4SLinus Torvalds 953ad706862SMartin KaFai Lau rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0); 95483a09abdSMartin KaFai Lau 95583a09abdSMartin KaFai Lau if (!rt) 95683a09abdSMartin KaFai Lau return NULL; 95783a09abdSMartin KaFai Lau 95883a09abdSMartin KaFai Lau ip6_rt_copy_init(rt, ort); 9598b9df265SMartin KaFai Lau rt->rt6i_flags |= RTF_CACHE; 96083a09abdSMartin KaFai Lau rt->rt6i_metric = 0; 96183a09abdSMartin KaFai Lau rt->dst.flags |= DST_HOST; 96283a09abdSMartin KaFai Lau rt->rt6i_dst.addr = *daddr; 96383a09abdSMartin KaFai Lau rt->rt6i_dst.plen = 128; 9648b9df265SMartin KaFai Lau 9658b9df265SMartin KaFai Lau if (!rt6_is_gw_or_nonexthop(ort)) { 966bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 96721efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 96858c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 9691da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 9701da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 9714e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 9721da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 9731da177e4SLinus Torvalds } 9741da177e4SLinus Torvalds #endif 97595a9a5baSYOSHIFUJI Hideaki } 97695a9a5baSYOSHIFUJI Hideaki 977299d9939SYOSHIFUJI Hideaki return rt; 978299d9939SYOSHIFUJI Hideaki } 979299d9939SYOSHIFUJI Hideaki 980d52d3997SMartin KaFai Lau static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt) 981d52d3997SMartin KaFai Lau { 982d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 983d52d3997SMartin KaFai Lau 984d52d3997SMartin KaFai Lau pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev), 985ad706862SMartin KaFai Lau rt->dst.dev, rt->dst.flags); 986d52d3997SMartin KaFai Lau 987d52d3997SMartin KaFai Lau if (!pcpu_rt) 988d52d3997SMartin KaFai Lau return NULL; 989d52d3997SMartin KaFai Lau ip6_rt_copy_init(pcpu_rt, rt); 990d52d3997SMartin KaFai Lau pcpu_rt->rt6i_protocol = rt->rt6i_protocol; 991d52d3997SMartin KaFai Lau pcpu_rt->rt6i_flags |= RTF_PCPU; 992d52d3997SMartin KaFai Lau return pcpu_rt; 993d52d3997SMartin KaFai Lau } 994d52d3997SMartin KaFai Lau 995d52d3997SMartin KaFai Lau /* It should be called with read_lock_bh(&tb6_lock) acquired */ 996d52d3997SMartin KaFai Lau static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt) 997d52d3997SMartin KaFai Lau { 998a73e4195SMartin KaFai Lau struct rt6_info *pcpu_rt, **p; 999d52d3997SMartin KaFai Lau 1000d52d3997SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 1001d52d3997SMartin KaFai Lau pcpu_rt = *p; 1002d52d3997SMartin KaFai Lau 1003a73e4195SMartin KaFai Lau if (pcpu_rt) { 1004a73e4195SMartin KaFai Lau dst_hold(&pcpu_rt->dst); 1005a73e4195SMartin KaFai Lau rt6_dst_from_metrics_check(pcpu_rt); 1006a73e4195SMartin KaFai Lau } 1007a73e4195SMartin KaFai Lau return pcpu_rt; 1008a73e4195SMartin KaFai Lau } 1009a73e4195SMartin KaFai Lau 1010a73e4195SMartin KaFai Lau static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt) 1011a73e4195SMartin KaFai Lau { 10129c7370a1SMartin KaFai Lau struct fib6_table *table = rt->rt6i_table; 1013a73e4195SMartin KaFai Lau struct rt6_info *pcpu_rt, *prev, **p; 1014d52d3997SMartin KaFai Lau 1015d52d3997SMartin KaFai Lau pcpu_rt = ip6_rt_pcpu_alloc(rt); 1016d52d3997SMartin KaFai Lau if (!pcpu_rt) { 1017d52d3997SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 1018d52d3997SMartin KaFai Lau 10199c7370a1SMartin KaFai Lau dst_hold(&net->ipv6.ip6_null_entry->dst); 10209c7370a1SMartin KaFai Lau return net->ipv6.ip6_null_entry; 1021d52d3997SMartin KaFai Lau } 1022d52d3997SMartin KaFai Lau 10239c7370a1SMartin KaFai Lau read_lock_bh(&table->tb6_lock); 10249c7370a1SMartin KaFai Lau if (rt->rt6i_pcpu) { 1025a73e4195SMartin KaFai Lau p = this_cpu_ptr(rt->rt6i_pcpu); 1026d52d3997SMartin KaFai Lau prev = cmpxchg(p, NULL, pcpu_rt); 1027d52d3997SMartin KaFai Lau if (prev) { 1028d52d3997SMartin KaFai Lau /* If someone did it before us, return prev instead */ 1029d52d3997SMartin KaFai Lau dst_destroy(&pcpu_rt->dst); 1030d52d3997SMartin KaFai Lau pcpu_rt = prev; 1031d52d3997SMartin KaFai Lau } 10329c7370a1SMartin KaFai Lau } else { 10339c7370a1SMartin KaFai Lau /* rt has been removed from the fib6 tree 10349c7370a1SMartin KaFai Lau * before we have a chance to acquire the read_lock. 10359c7370a1SMartin KaFai Lau * In this case, don't brother to create a pcpu rt 10369c7370a1SMartin KaFai Lau * since rt is going away anyway. The next 10379c7370a1SMartin KaFai Lau * dst_check() will trigger a re-lookup. 10389c7370a1SMartin KaFai Lau */ 10399c7370a1SMartin KaFai Lau dst_destroy(&pcpu_rt->dst); 10409c7370a1SMartin KaFai Lau pcpu_rt = rt; 10419c7370a1SMartin KaFai Lau } 1042d52d3997SMartin KaFai Lau dst_hold(&pcpu_rt->dst); 1043d52d3997SMartin KaFai Lau rt6_dst_from_metrics_check(pcpu_rt); 10449c7370a1SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 1045d52d3997SMartin KaFai Lau return pcpu_rt; 1046d52d3997SMartin KaFai Lau } 1047d52d3997SMartin KaFai Lau 10489ff74384SDavid Ahern struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, 10499ff74384SDavid Ahern int oif, struct flowi6 *fl6, int flags) 10501da177e4SLinus Torvalds { 1051367efcb9SMartin KaFai Lau struct fib6_node *fn, *saved_fn; 105245e4fd26SMartin KaFai Lau struct rt6_info *rt; 1053c71099acSThomas Graf int strict = 0; 10541da177e4SLinus Torvalds 105577d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 1056d5d32e4bSDavid Ahern strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE; 1057367efcb9SMartin KaFai Lau if (net->ipv6.devconf_all->forwarding == 0) 1058367efcb9SMartin KaFai Lau strict |= RT6_LOOKUP_F_REACHABLE; 10591da177e4SLinus Torvalds 1060c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 10611da177e4SLinus Torvalds 10624c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1063367efcb9SMartin KaFai Lau saved_fn = fn; 10641da177e4SLinus Torvalds 1065ca254490SDavid Ahern if (fl6->flowi6_flags & FLOWI_FLAG_SKIP_NH_OIF) 1066ca254490SDavid Ahern oif = 0; 1067ca254490SDavid Ahern 1068a3c00e46SMartin KaFai Lau redo_rt6_select: 1069367efcb9SMartin KaFai Lau rt = rt6_select(fn, oif, strict); 107052bd4c0cSNicolas Dichtel if (rt->rt6i_nsiblings) 1071367efcb9SMartin KaFai Lau rt = rt6_multipath_select(rt, fl6, oif, strict); 1072a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1073a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1074a3c00e46SMartin KaFai Lau if (fn) 1075a3c00e46SMartin KaFai Lau goto redo_rt6_select; 1076367efcb9SMartin KaFai Lau else if (strict & RT6_LOOKUP_F_REACHABLE) { 1077367efcb9SMartin KaFai Lau /* also consider unreachable route */ 1078367efcb9SMartin KaFai Lau strict &= ~RT6_LOOKUP_F_REACHABLE; 1079367efcb9SMartin KaFai Lau fn = saved_fn; 1080367efcb9SMartin KaFai Lau goto redo_rt6_select; 1081367efcb9SMartin KaFai Lau } 1082a3c00e46SMartin KaFai Lau } 1083a3c00e46SMartin KaFai Lau 1084d52d3997SMartin KaFai Lau 1085d52d3997SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) { 10863da59bd9SMartin KaFai Lau dst_use(&rt->dst, jiffies); 1087c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 10881da177e4SLinus Torvalds 1089d52d3997SMartin KaFai Lau rt6_dst_from_metrics_check(rt); 1090b811580dSDavid Ahern 1091b811580dSDavid Ahern trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); 1092d52d3997SMartin KaFai Lau return rt; 10933da59bd9SMartin KaFai Lau } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && 10943da59bd9SMartin KaFai Lau !(rt->rt6i_flags & RTF_GATEWAY))) { 10953da59bd9SMartin KaFai Lau /* Create a RTF_CACHE clone which will not be 10963da59bd9SMartin KaFai Lau * owned by the fib6 tree. It is for the special case where 10973da59bd9SMartin KaFai Lau * the daddr in the skb during the neighbor look-up is different 10983da59bd9SMartin KaFai Lau * from the fl6->daddr used to look-up route here. 10993da59bd9SMartin KaFai Lau */ 1100c71099acSThomas Graf 11013da59bd9SMartin KaFai Lau struct rt6_info *uncached_rt; 11023da59bd9SMartin KaFai Lau 1103d52d3997SMartin KaFai Lau dst_use(&rt->dst, jiffies); 1104d52d3997SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 1105d52d3997SMartin KaFai Lau 11063da59bd9SMartin KaFai Lau uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL); 11073da59bd9SMartin KaFai Lau dst_release(&rt->dst); 11083da59bd9SMartin KaFai Lau 11093da59bd9SMartin KaFai Lau if (uncached_rt) 11108d0b94afSMartin KaFai Lau rt6_uncached_list_add(uncached_rt); 11113da59bd9SMartin KaFai Lau else 11123da59bd9SMartin KaFai Lau uncached_rt = net->ipv6.ip6_null_entry; 1113d52d3997SMartin KaFai Lau 11143da59bd9SMartin KaFai Lau dst_hold(&uncached_rt->dst); 1115b811580dSDavid Ahern 1116b811580dSDavid Ahern trace_fib6_table_lookup(net, uncached_rt, table->tb6_id, fl6); 11173da59bd9SMartin KaFai Lau return uncached_rt; 11183da59bd9SMartin KaFai Lau 1119d52d3997SMartin KaFai Lau } else { 1120d52d3997SMartin KaFai Lau /* Get a percpu copy */ 1121d52d3997SMartin KaFai Lau 1122d52d3997SMartin KaFai Lau struct rt6_info *pcpu_rt; 1123d52d3997SMartin KaFai Lau 1124d52d3997SMartin KaFai Lau rt->dst.lastuse = jiffies; 1125d52d3997SMartin KaFai Lau rt->dst.__use++; 1126d52d3997SMartin KaFai Lau pcpu_rt = rt6_get_pcpu_route(rt); 1127d52d3997SMartin KaFai Lau 11289c7370a1SMartin KaFai Lau if (pcpu_rt) { 1129d52d3997SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 11309c7370a1SMartin KaFai Lau } else { 11319c7370a1SMartin KaFai Lau /* We have to do the read_unlock first 11329c7370a1SMartin KaFai Lau * because rt6_make_pcpu_route() may trigger 11339c7370a1SMartin KaFai Lau * ip6_dst_gc() which will take the write_lock. 11349c7370a1SMartin KaFai Lau */ 11359c7370a1SMartin KaFai Lau dst_hold(&rt->dst); 11369c7370a1SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 11379c7370a1SMartin KaFai Lau pcpu_rt = rt6_make_pcpu_route(rt); 11389c7370a1SMartin KaFai Lau dst_release(&rt->dst); 11399c7370a1SMartin KaFai Lau } 1140d52d3997SMartin KaFai Lau 1141b811580dSDavid Ahern trace_fib6_table_lookup(net, pcpu_rt, table->tb6_id, fl6); 1142d52d3997SMartin KaFai Lau return pcpu_rt; 11439c7370a1SMartin KaFai Lau 1144d52d3997SMartin KaFai Lau } 1145c71099acSThomas Graf } 11469ff74384SDavid Ahern EXPORT_SYMBOL_GPL(ip6_pol_route); 1147c71099acSThomas Graf 11488ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 11494c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 11504acad72dSPavel Emelyanov { 11514c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 11524acad72dSPavel Emelyanov } 11534acad72dSPavel Emelyanov 1154d409b847SMahesh Bandewar struct dst_entry *ip6_route_input_lookup(struct net *net, 115572331bc0SShmulik Ladkani struct net_device *dev, 115672331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 115772331bc0SShmulik Ladkani { 115872331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 115972331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 116072331bc0SShmulik Ladkani 116172331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 116272331bc0SShmulik Ladkani } 1163d409b847SMahesh Bandewar EXPORT_SYMBOL_GPL(ip6_route_input_lookup); 116472331bc0SShmulik Ladkani 1165c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 1166c71099acSThomas Graf { 1167b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 1168c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 1169adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 1170904af04dSJiri Benc struct ip_tunnel_info *tun_info; 11714c9483b2SDavid S. Miller struct flowi6 fl6 = { 1172e0d56fddSDavid Ahern .flowi6_iif = skb->dev->ifindex, 11734c9483b2SDavid S. Miller .daddr = iph->daddr, 11744c9483b2SDavid S. Miller .saddr = iph->saddr, 11756502ca52SYOSHIFUJI Hideaki / 吉藤英明 .flowlabel = ip6_flowinfo(iph), 11764c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 11774c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 1178c71099acSThomas Graf }; 1179adaa70bbSThomas Graf 1180904af04dSJiri Benc tun_info = skb_tunnel_info(skb); 118146fa062aSJiri Benc if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX)) 1182904af04dSJiri Benc fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id; 118306e9d040SJiri Benc skb_dst_drop(skb); 118472331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 1185c71099acSThomas Graf } 1186c71099acSThomas Graf 11878ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 11884c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 1189c71099acSThomas Graf { 11904c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 1191c71099acSThomas Graf } 1192c71099acSThomas Graf 11936f21c96aSPaolo Abeni struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk, 11946f21c96aSPaolo Abeni struct flowi6 *fl6, int flags) 1195c71099acSThomas Graf { 1196d46a9d67SDavid Ahern bool any_src; 1197c71099acSThomas Graf 11984c1feac5SDavid Ahern if (rt6_need_strict(&fl6->daddr)) { 11994c1feac5SDavid Ahern struct dst_entry *dst; 12004c1feac5SDavid Ahern 12014c1feac5SDavid Ahern dst = l3mdev_link_scope_lookup(net, fl6); 1202ca254490SDavid Ahern if (dst) 1203ca254490SDavid Ahern return dst; 12044c1feac5SDavid Ahern } 1205ca254490SDavid Ahern 12061fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 12074dc27d1cSDavid McCullough 1208d46a9d67SDavid Ahern any_src = ipv6_addr_any(&fl6->saddr); 1209741a11d9SDavid Ahern if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) || 1210d46a9d67SDavid Ahern (fl6->flowi6_oif && any_src)) 121177d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 1212c71099acSThomas Graf 1213d46a9d67SDavid Ahern if (!any_src) 1214adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 12150c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 12160c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 1217adaa70bbSThomas Graf 12184c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 12191da177e4SLinus Torvalds } 12206f21c96aSPaolo Abeni EXPORT_SYMBOL_GPL(ip6_route_output_flags); 12211da177e4SLinus Torvalds 12222774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 122314e50e57SDavid S. Miller { 12245c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 122514e50e57SDavid S. Miller struct dst_entry *new = NULL; 122614e50e57SDavid S. Miller 1227f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 122814e50e57SDavid S. Miller if (rt) { 12290a1f5962SMartin KaFai Lau rt6_info_init(rt); 12300a1f5962SMartin KaFai Lau 1231d8d1f30bSChangli Gao new = &rt->dst; 123214e50e57SDavid S. Miller new->__use = 1; 1233352e512cSHerbert Xu new->input = dst_discard; 1234ede2059dSEric W. Biederman new->output = dst_discard_out; 123514e50e57SDavid S. Miller 1236defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 123714e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 123814e50e57SDavid S. Miller if (rt->rt6i_idev) 123914e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 124014e50e57SDavid S. Miller 12414e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 12420a1f5962SMartin KaFai Lau rt->rt6i_flags = ort->rt6i_flags & ~RTF_PCPU; 124314e50e57SDavid S. Miller rt->rt6i_metric = 0; 124414e50e57SDavid S. Miller 124514e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 124614e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 124714e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 124814e50e57SDavid S. Miller #endif 124914e50e57SDavid S. Miller 125014e50e57SDavid S. Miller dst_free(new); 125114e50e57SDavid S. Miller } 125214e50e57SDavid S. Miller 125369ead7afSDavid S. Miller dst_release(dst_orig); 125469ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 125514e50e57SDavid S. Miller } 125614e50e57SDavid S. Miller 12571da177e4SLinus Torvalds /* 12581da177e4SLinus Torvalds * Destination cache support functions 12591da177e4SLinus Torvalds */ 12601da177e4SLinus Torvalds 12614b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt) 12624b32b5adSMartin KaFai Lau { 12634b32b5adSMartin KaFai Lau if (rt->dst.from && 12644b32b5adSMartin KaFai Lau dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from)) 12654b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true); 12664b32b5adSMartin KaFai Lau } 12674b32b5adSMartin KaFai Lau 12683da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie) 12693da59bd9SMartin KaFai Lau { 12703da59bd9SMartin KaFai Lau if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) 12713da59bd9SMartin KaFai Lau return NULL; 12723da59bd9SMartin KaFai Lau 12733da59bd9SMartin KaFai Lau if (rt6_check_expired(rt)) 12743da59bd9SMartin KaFai Lau return NULL; 12753da59bd9SMartin KaFai Lau 12763da59bd9SMartin KaFai Lau return &rt->dst; 12773da59bd9SMartin KaFai Lau } 12783da59bd9SMartin KaFai Lau 12793da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie) 12803da59bd9SMartin KaFai Lau { 12815973fb1eSMartin KaFai Lau if (!__rt6_check_expired(rt) && 12825973fb1eSMartin KaFai Lau rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && 12833da59bd9SMartin KaFai Lau rt6_check((struct rt6_info *)(rt->dst.from), cookie)) 12843da59bd9SMartin KaFai Lau return &rt->dst; 12853da59bd9SMartin KaFai Lau else 12863da59bd9SMartin KaFai Lau return NULL; 12873da59bd9SMartin KaFai Lau } 12883da59bd9SMartin KaFai Lau 12891da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 12901da177e4SLinus Torvalds { 12911da177e4SLinus Torvalds struct rt6_info *rt; 12921da177e4SLinus Torvalds 12931da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 12941da177e4SLinus Torvalds 12956f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 12966f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 12976f3118b5SNicolas Dichtel * into this function always. 12986f3118b5SNicolas Dichtel */ 1299e3bc10bdSHannes Frederic Sowa 13004b32b5adSMartin KaFai Lau rt6_dst_from_metrics_check(rt); 13014b32b5adSMartin KaFai Lau 130202bcf4e0SMartin KaFai Lau if (rt->rt6i_flags & RTF_PCPU || 130302bcf4e0SMartin KaFai Lau (unlikely(dst->flags & DST_NOCACHE) && rt->dst.from)) 13043da59bd9SMartin KaFai Lau return rt6_dst_from_check(rt, cookie); 13053da59bd9SMartin KaFai Lau else 13063da59bd9SMartin KaFai Lau return rt6_check(rt, cookie); 13071da177e4SLinus Torvalds } 13081da177e4SLinus Torvalds 13091da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 13101da177e4SLinus Torvalds { 13111da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 13121da177e4SLinus Torvalds 13131da177e4SLinus Torvalds if (rt) { 131454c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 131554c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1316e0a1ad73SThomas Graf ip6_del_rt(rt); 131754c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 13181da177e4SLinus Torvalds } 131954c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 132054c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 132154c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 132254c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 132354c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 132454c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 13251da177e4SLinus Torvalds } 13261da177e4SLinus Torvalds 13271da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 13281da177e4SLinus Torvalds { 13291da177e4SLinus Torvalds struct rt6_info *rt; 13301da177e4SLinus Torvalds 13313ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 13321da177e4SLinus Torvalds 1333adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 13341da177e4SLinus Torvalds if (rt) { 13351eb4f758SHannes Frederic Sowa if (rt->rt6i_flags & RTF_CACHE) { 13361eb4f758SHannes Frederic Sowa dst_hold(&rt->dst); 13378e3d5be7SMartin KaFai Lau ip6_del_rt(rt); 13381eb4f758SHannes Frederic Sowa } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) { 13391da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 13401da177e4SLinus Torvalds } 13411da177e4SLinus Torvalds } 13421eb4f758SHannes Frederic Sowa } 13431da177e4SLinus Torvalds 134445e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) 134545e4fd26SMartin KaFai Lau { 134645e4fd26SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 134745e4fd26SMartin KaFai Lau 134845e4fd26SMartin KaFai Lau rt->rt6i_flags |= RTF_MODIFIED; 134945e4fd26SMartin KaFai Lau rt->rt6i_pmtu = mtu; 135045e4fd26SMartin KaFai Lau rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); 135145e4fd26SMartin KaFai Lau } 135245e4fd26SMartin KaFai Lau 13530d3f6d29SMartin KaFai Lau static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt) 13540d3f6d29SMartin KaFai Lau { 13550d3f6d29SMartin KaFai Lau return !(rt->rt6i_flags & RTF_CACHE) && 13560d3f6d29SMartin KaFai Lau (rt->rt6i_flags & RTF_PCPU || rt->rt6i_node); 13570d3f6d29SMartin KaFai Lau } 13580d3f6d29SMartin KaFai Lau 135945e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, 136045e4fd26SMartin KaFai Lau const struct ipv6hdr *iph, u32 mtu) 13611da177e4SLinus Torvalds { 13621da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info *)dst; 13631da177e4SLinus Torvalds 136445e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_LOCAL) 136545e4fd26SMartin KaFai Lau return; 136645e4fd26SMartin KaFai Lau 136719bda36cSXin Long if (dst_metric_locked(dst, RTAX_MTU)) 136819bda36cSXin Long return; 136919bda36cSXin Long 137081aded24SDavid S. Miller dst_confirm(dst); 137145e4fd26SMartin KaFai Lau mtu = max_t(u32, mtu, IPV6_MIN_MTU); 137245e4fd26SMartin KaFai Lau if (mtu >= dst_mtu(dst)) 137345e4fd26SMartin KaFai Lau return; 137481aded24SDavid S. Miller 13750d3f6d29SMartin KaFai Lau if (!rt6_cache_allowed_for_pmtu(rt6)) { 137645e4fd26SMartin KaFai Lau rt6_do_update_pmtu(rt6, mtu); 137745e4fd26SMartin KaFai Lau } else { 137845e4fd26SMartin KaFai Lau const struct in6_addr *daddr, *saddr; 137945e4fd26SMartin KaFai Lau struct rt6_info *nrt6; 13809d289715SHagen Paul Pfeifer 138145e4fd26SMartin KaFai Lau if (iph) { 138245e4fd26SMartin KaFai Lau daddr = &iph->daddr; 138345e4fd26SMartin KaFai Lau saddr = &iph->saddr; 138445e4fd26SMartin KaFai Lau } else if (sk) { 138545e4fd26SMartin KaFai Lau daddr = &sk->sk_v6_daddr; 138645e4fd26SMartin KaFai Lau saddr = &inet6_sk(sk)->saddr; 138745e4fd26SMartin KaFai Lau } else { 138845e4fd26SMartin KaFai Lau return; 13891da177e4SLinus Torvalds } 139045e4fd26SMartin KaFai Lau nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr); 139145e4fd26SMartin KaFai Lau if (nrt6) { 139245e4fd26SMartin KaFai Lau rt6_do_update_pmtu(nrt6, mtu); 139345e4fd26SMartin KaFai Lau 139445e4fd26SMartin KaFai Lau /* ip6_ins_rt(nrt6) will bump the 139545e4fd26SMartin KaFai Lau * rt6->rt6i_node->fn_sernum 139645e4fd26SMartin KaFai Lau * which will fail the next rt6_check() and 139745e4fd26SMartin KaFai Lau * invalidate the sk->sk_dst_cache. 139845e4fd26SMartin KaFai Lau */ 139945e4fd26SMartin KaFai Lau ip6_ins_rt(nrt6); 140045e4fd26SMartin KaFai Lau } 140145e4fd26SMartin KaFai Lau } 140245e4fd26SMartin KaFai Lau } 140345e4fd26SMartin KaFai Lau 140445e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 140545e4fd26SMartin KaFai Lau struct sk_buff *skb, u32 mtu) 140645e4fd26SMartin KaFai Lau { 140745e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu); 14081da177e4SLinus Torvalds } 14091da177e4SLinus Torvalds 141042ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 1411e2d118a1SLorenzo Colitti int oif, u32 mark, kuid_t uid) 141281aded24SDavid S. Miller { 141381aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 141481aded24SDavid S. Miller struct dst_entry *dst; 141581aded24SDavid S. Miller struct flowi6 fl6; 141681aded24SDavid S. Miller 141781aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 141881aded24SDavid S. Miller fl6.flowi6_oif = oif; 14191b3c61dcSLorenzo Colitti fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark); 142081aded24SDavid S. Miller fl6.daddr = iph->daddr; 142181aded24SDavid S. Miller fl6.saddr = iph->saddr; 14226502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 1423e2d118a1SLorenzo Colitti fl6.flowi6_uid = uid; 142481aded24SDavid S. Miller 142581aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 142681aded24SDavid S. Miller if (!dst->error) 142745e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu)); 142881aded24SDavid S. Miller dst_release(dst); 142981aded24SDavid S. Miller } 143081aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 143181aded24SDavid S. Miller 143281aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 143381aded24SDavid S. Miller { 143433c162a9SMartin KaFai Lau struct dst_entry *dst; 143533c162a9SMartin KaFai Lau 143681aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 1437e2d118a1SLorenzo Colitti sk->sk_bound_dev_if, sk->sk_mark, sk->sk_uid); 143833c162a9SMartin KaFai Lau 143933c162a9SMartin KaFai Lau dst = __sk_dst_get(sk); 144033c162a9SMartin KaFai Lau if (!dst || !dst->obsolete || 144133c162a9SMartin KaFai Lau dst->ops->check(dst, inet6_sk(sk)->dst_cookie)) 144233c162a9SMartin KaFai Lau return; 144333c162a9SMartin KaFai Lau 144433c162a9SMartin KaFai Lau bh_lock_sock(sk); 144533c162a9SMartin KaFai Lau if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 144633c162a9SMartin KaFai Lau ip6_datagram_dst_update(sk, false); 144733c162a9SMartin KaFai Lau bh_unlock_sock(sk); 144881aded24SDavid S. Miller } 144981aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 145081aded24SDavid S. Miller 1451b55b76b2SDuan Jiong /* Handle redirects */ 1452b55b76b2SDuan Jiong struct ip6rd_flowi { 1453b55b76b2SDuan Jiong struct flowi6 fl6; 1454b55b76b2SDuan Jiong struct in6_addr gateway; 1455b55b76b2SDuan Jiong }; 1456b55b76b2SDuan Jiong 1457b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net, 1458b55b76b2SDuan Jiong struct fib6_table *table, 1459b55b76b2SDuan Jiong struct flowi6 *fl6, 1460b55b76b2SDuan Jiong int flags) 1461b55b76b2SDuan Jiong { 1462b55b76b2SDuan Jiong struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 1463b55b76b2SDuan Jiong struct rt6_info *rt; 1464b55b76b2SDuan Jiong struct fib6_node *fn; 1465b55b76b2SDuan Jiong 1466b55b76b2SDuan Jiong /* Get the "current" route for this destination and 146767c408cfSAlexander Alemayhu * check if the redirect has come from appropriate router. 1468b55b76b2SDuan Jiong * 1469b55b76b2SDuan Jiong * RFC 4861 specifies that redirects should only be 1470b55b76b2SDuan Jiong * accepted if they come from the nexthop to the target. 1471b55b76b2SDuan Jiong * Due to the way the routes are chosen, this notion 1472b55b76b2SDuan Jiong * is a bit fuzzy and one might need to check all possible 1473b55b76b2SDuan Jiong * routes. 1474b55b76b2SDuan Jiong */ 1475b55b76b2SDuan Jiong 1476b55b76b2SDuan Jiong read_lock_bh(&table->tb6_lock); 1477b55b76b2SDuan Jiong fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1478b55b76b2SDuan Jiong restart: 1479b55b76b2SDuan Jiong for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1480b55b76b2SDuan Jiong if (rt6_check_expired(rt)) 1481b55b76b2SDuan Jiong continue; 1482b55b76b2SDuan Jiong if (rt->dst.error) 1483b55b76b2SDuan Jiong break; 1484b55b76b2SDuan Jiong if (!(rt->rt6i_flags & RTF_GATEWAY)) 1485b55b76b2SDuan Jiong continue; 1486b55b76b2SDuan Jiong if (fl6->flowi6_oif != rt->dst.dev->ifindex) 1487b55b76b2SDuan Jiong continue; 1488b55b76b2SDuan Jiong if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) 1489b55b76b2SDuan Jiong continue; 1490b55b76b2SDuan Jiong break; 1491b55b76b2SDuan Jiong } 1492b55b76b2SDuan Jiong 1493b55b76b2SDuan Jiong if (!rt) 1494b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1495b55b76b2SDuan Jiong else if (rt->dst.error) { 1496b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1497b0a1ba59SMartin KaFai Lau goto out; 1498b0a1ba59SMartin KaFai Lau } 1499b0a1ba59SMartin KaFai Lau 1500b0a1ba59SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1501a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1502a3c00e46SMartin KaFai Lau if (fn) 1503a3c00e46SMartin KaFai Lau goto restart; 1504b55b76b2SDuan Jiong } 1505a3c00e46SMartin KaFai Lau 1506b0a1ba59SMartin KaFai Lau out: 1507b55b76b2SDuan Jiong dst_hold(&rt->dst); 1508b55b76b2SDuan Jiong 1509b55b76b2SDuan Jiong read_unlock_bh(&table->tb6_lock); 1510b55b76b2SDuan Jiong 1511b811580dSDavid Ahern trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); 1512b55b76b2SDuan Jiong return rt; 1513b55b76b2SDuan Jiong }; 1514b55b76b2SDuan Jiong 1515b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net, 1516b55b76b2SDuan Jiong const struct flowi6 *fl6, 1517b55b76b2SDuan Jiong const struct in6_addr *gateway) 1518b55b76b2SDuan Jiong { 1519b55b76b2SDuan Jiong int flags = RT6_LOOKUP_F_HAS_SADDR; 1520b55b76b2SDuan Jiong struct ip6rd_flowi rdfl; 1521b55b76b2SDuan Jiong 1522b55b76b2SDuan Jiong rdfl.fl6 = *fl6; 1523b55b76b2SDuan Jiong rdfl.gateway = *gateway; 1524b55b76b2SDuan Jiong 1525b55b76b2SDuan Jiong return fib6_rule_lookup(net, &rdfl.fl6, 1526b55b76b2SDuan Jiong flags, __ip6_route_redirect); 1527b55b76b2SDuan Jiong } 1528b55b76b2SDuan Jiong 1529e2d118a1SLorenzo Colitti void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark, 1530e2d118a1SLorenzo Colitti kuid_t uid) 15313a5ad2eeSDavid S. Miller { 15323a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 15333a5ad2eeSDavid S. Miller struct dst_entry *dst; 15343a5ad2eeSDavid S. Miller struct flowi6 fl6; 15353a5ad2eeSDavid S. Miller 15363a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 1537e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 15383a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 15393a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 15403a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 15413a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 15426502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 1543e2d118a1SLorenzo Colitti fl6.flowi6_uid = uid; 15443a5ad2eeSDavid S. Miller 1545b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); 15466700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 15473a5ad2eeSDavid S. Miller dst_release(dst); 15483a5ad2eeSDavid S. Miller } 15493a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 15503a5ad2eeSDavid S. Miller 1551c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, 1552c92a59ecSDuan Jiong u32 mark) 1553c92a59ecSDuan Jiong { 1554c92a59ecSDuan Jiong const struct ipv6hdr *iph = ipv6_hdr(skb); 1555c92a59ecSDuan Jiong const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 1556c92a59ecSDuan Jiong struct dst_entry *dst; 1557c92a59ecSDuan Jiong struct flowi6 fl6; 1558c92a59ecSDuan Jiong 1559c92a59ecSDuan Jiong memset(&fl6, 0, sizeof(fl6)); 1560e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 1561c92a59ecSDuan Jiong fl6.flowi6_oif = oif; 1562c92a59ecSDuan Jiong fl6.flowi6_mark = mark; 1563c92a59ecSDuan Jiong fl6.daddr = msg->dest; 1564c92a59ecSDuan Jiong fl6.saddr = iph->daddr; 1565e2d118a1SLorenzo Colitti fl6.flowi6_uid = sock_net_uid(net, NULL); 1566c92a59ecSDuan Jiong 1567b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &iph->saddr); 1568c92a59ecSDuan Jiong rt6_do_redirect(dst, NULL, skb); 1569c92a59ecSDuan Jiong dst_release(dst); 1570c92a59ecSDuan Jiong } 1571c92a59ecSDuan Jiong 15723a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 15733a5ad2eeSDavid S. Miller { 1574e2d118a1SLorenzo Colitti ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark, 1575e2d118a1SLorenzo Colitti sk->sk_uid); 15763a5ad2eeSDavid S. Miller } 15773a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 15783a5ad2eeSDavid S. Miller 15790dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 15801da177e4SLinus Torvalds { 15810dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 15820dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 15830dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 15840dbaee3bSDavid S. Miller 15851da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 15861da177e4SLinus Torvalds 15875578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 15885578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 15891da177e4SLinus Torvalds 15901da177e4SLinus Torvalds /* 15911da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 15921da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 15931da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 15941da177e4SLinus Torvalds * rely only on pmtu discovery" 15951da177e4SLinus Torvalds */ 15961da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 15971da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 15981da177e4SLinus Torvalds return mtu; 15991da177e4SLinus Torvalds } 16001da177e4SLinus Torvalds 1601ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1602d33e4553SDavid S. Miller { 16034b32b5adSMartin KaFai Lau const struct rt6_info *rt = (const struct rt6_info *)dst; 16044b32b5adSMartin KaFai Lau unsigned int mtu = rt->rt6i_pmtu; 1605d33e4553SDavid S. Miller struct inet6_dev *idev; 1606618f9bc7SSteffen Klassert 1607618f9bc7SSteffen Klassert if (mtu) 160830f78d8eSEric Dumazet goto out; 1609618f9bc7SSteffen Klassert 16104b32b5adSMartin KaFai Lau mtu = dst_metric_raw(dst, RTAX_MTU); 16114b32b5adSMartin KaFai Lau if (mtu) 16124b32b5adSMartin KaFai Lau goto out; 16134b32b5adSMartin KaFai Lau 1614618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1615d33e4553SDavid S. Miller 1616d33e4553SDavid S. Miller rcu_read_lock(); 1617d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1618d33e4553SDavid S. Miller if (idev) 1619d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1620d33e4553SDavid S. Miller rcu_read_unlock(); 1621d33e4553SDavid S. Miller 162230f78d8eSEric Dumazet out: 162314972cbdSRoopa Prabhu mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); 162414972cbdSRoopa Prabhu 162514972cbdSRoopa Prabhu return mtu - lwtunnel_headroom(dst->lwtstate, mtu); 1626d33e4553SDavid S. Miller } 1627d33e4553SDavid S. Miller 16283b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 16293b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 16305d0bbeebSThomas Graf 16313b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 163287a11578SDavid S. Miller struct flowi6 *fl6) 16331da177e4SLinus Torvalds { 163487a11578SDavid S. Miller struct dst_entry *dst; 16351da177e4SLinus Torvalds struct rt6_info *rt; 16361da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1637c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 16381da177e4SLinus Torvalds 163938308473SDavid S. Miller if (unlikely(!idev)) 1640122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 16411da177e4SLinus Torvalds 1642ad706862SMartin KaFai Lau rt = ip6_dst_alloc(net, dev, 0); 164338308473SDavid S. Miller if (unlikely(!rt)) { 16441da177e4SLinus Torvalds in6_dev_put(idev); 164587a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 16461da177e4SLinus Torvalds goto out; 16471da177e4SLinus Torvalds } 16481da177e4SLinus Torvalds 16498e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 16508e2ec639SYan, Zheng rt->dst.output = ip6_output; 1651d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 1652550bab42SJulian Anastasov rt->rt6i_gateway = fl6->daddr; 165387a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 16548e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 16558e2ec639SYan, Zheng rt->rt6i_idev = idev; 165614edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 16571da177e4SLinus Torvalds 16583b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1659d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1660d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 16613b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 16621da177e4SLinus Torvalds 16635578689aSDaniel Lezcano fib6_force_start_gc(net); 16641da177e4SLinus Torvalds 166587a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 166687a11578SDavid S. Miller 16671da177e4SLinus Torvalds out: 166887a11578SDavid S. Miller return dst; 16691da177e4SLinus Torvalds } 16701da177e4SLinus Torvalds 16713d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 16721da177e4SLinus Torvalds { 1673e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 16743d0f24a7SStephen Hemminger int more = 0; 16751da177e4SLinus Torvalds 16763b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 16773b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 16785d0bbeebSThomas Graf 16791da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 16801da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 16811da177e4SLinus Torvalds *pprev = dst->next; 16821da177e4SLinus Torvalds dst_free(dst); 16831da177e4SLinus Torvalds } else { 16841da177e4SLinus Torvalds pprev = &dst->next; 16853d0f24a7SStephen Hemminger ++more; 16861da177e4SLinus Torvalds } 16871da177e4SLinus Torvalds } 16881da177e4SLinus Torvalds 16893b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 16905d0bbeebSThomas Graf 16913d0f24a7SStephen Hemminger return more; 16921da177e4SLinus Torvalds } 16931da177e4SLinus Torvalds 16941e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 16951e493d19SDavid S. Miller void *arg) 16961e493d19SDavid S. Miller { 16971e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 16981e493d19SDavid S. Miller 16991e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 17001e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 17011e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 17021e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 17031e493d19SDavid S. Miller if (func(rt, arg)) { 17041e493d19SDavid S. Miller *pprev = dst->next; 17051e493d19SDavid S. Miller dst_free(dst); 17061e493d19SDavid S. Miller } else { 17071e493d19SDavid S. Miller pprev = &dst->next; 17081e493d19SDavid S. Miller } 17091e493d19SDavid S. Miller } 17101e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 17111e493d19SDavid S. Miller } 17121e493d19SDavid S. Miller 1713569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 17141da177e4SLinus Torvalds { 171586393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 17167019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 17177019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 17187019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 17197019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 17207019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1721fc66f95cSEric Dumazet int entries; 17221da177e4SLinus Torvalds 1723fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 172449a18d86SMichal Kubeček if (time_after(rt_last_gc + rt_min_interval, jiffies) && 1725fc66f95cSEric Dumazet entries <= rt_max_size) 17261da177e4SLinus Torvalds goto out; 17271da177e4SLinus Torvalds 17286891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 172914956643SLi RongQing fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true); 1730fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1731fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 17327019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 17331da177e4SLinus Torvalds out: 17347019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1735fc66f95cSEric Dumazet return entries > rt_max_size; 17361da177e4SLinus Torvalds } 17371da177e4SLinus Torvalds 1738e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc, 1739e715b6d3SFlorian Westphal const struct fib6_config *cfg) 1740e715b6d3SFlorian Westphal { 1741c3a8d947SDaniel Borkmann bool ecn_ca = false; 1742e715b6d3SFlorian Westphal struct nlattr *nla; 1743e715b6d3SFlorian Westphal int remaining; 1744e715b6d3SFlorian Westphal u32 *mp; 1745e715b6d3SFlorian Westphal 174663159f29SIan Morris if (!cfg->fc_mx) 1747e715b6d3SFlorian Westphal return 0; 1748e715b6d3SFlorian Westphal 1749e715b6d3SFlorian Westphal mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 1750e715b6d3SFlorian Westphal if (unlikely(!mp)) 1751e715b6d3SFlorian Westphal return -ENOMEM; 1752e715b6d3SFlorian Westphal 1753e715b6d3SFlorian Westphal nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 1754e715b6d3SFlorian Westphal int type = nla_type(nla); 1755ea697639SDaniel Borkmann u32 val; 1756ea697639SDaniel Borkmann 17571bb14807SDaniel Borkmann if (!type) 17581bb14807SDaniel Borkmann continue; 1759e715b6d3SFlorian Westphal if (unlikely(type > RTAX_MAX)) 1760e715b6d3SFlorian Westphal goto err; 17611bb14807SDaniel Borkmann 1762ea697639SDaniel Borkmann if (type == RTAX_CC_ALGO) { 1763ea697639SDaniel Borkmann char tmp[TCP_CA_NAME_MAX]; 1764e715b6d3SFlorian Westphal 1765ea697639SDaniel Borkmann nla_strlcpy(tmp, nla, sizeof(tmp)); 1766c3a8d947SDaniel Borkmann val = tcp_ca_get_key_by_name(tmp, &ecn_ca); 1767ea697639SDaniel Borkmann if (val == TCP_CA_UNSPEC) 1768ea697639SDaniel Borkmann goto err; 1769ea697639SDaniel Borkmann } else { 1770ea697639SDaniel Borkmann val = nla_get_u32(nla); 1771ea697639SDaniel Borkmann } 1772626abd59SPaolo Abeni if (type == RTAX_HOPLIMIT && val > 255) 1773626abd59SPaolo Abeni val = 255; 1774b8d3e416SDaniel Borkmann if (type == RTAX_FEATURES && (val & ~RTAX_FEATURE_MASK)) 1775b8d3e416SDaniel Borkmann goto err; 1776ea697639SDaniel Borkmann 1777ea697639SDaniel Borkmann mp[type - 1] = val; 1778e715b6d3SFlorian Westphal __set_bit(type - 1, mxc->mx_valid); 1779e715b6d3SFlorian Westphal } 1780e715b6d3SFlorian Westphal 1781c3a8d947SDaniel Borkmann if (ecn_ca) { 1782c3a8d947SDaniel Borkmann __set_bit(RTAX_FEATURES - 1, mxc->mx_valid); 1783c3a8d947SDaniel Borkmann mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA; 1784c3a8d947SDaniel Borkmann } 1785e715b6d3SFlorian Westphal 1786c3a8d947SDaniel Borkmann mxc->mx = mp; 1787e715b6d3SFlorian Westphal return 0; 1788e715b6d3SFlorian Westphal err: 1789e715b6d3SFlorian Westphal kfree(mp); 1790e715b6d3SFlorian Westphal return -EINVAL; 1791e715b6d3SFlorian Westphal } 17921da177e4SLinus Torvalds 17938c14586fSDavid Ahern static struct rt6_info *ip6_nh_lookup_table(struct net *net, 17948c14586fSDavid Ahern struct fib6_config *cfg, 17958c14586fSDavid Ahern const struct in6_addr *gw_addr) 17968c14586fSDavid Ahern { 17978c14586fSDavid Ahern struct flowi6 fl6 = { 17988c14586fSDavid Ahern .flowi6_oif = cfg->fc_ifindex, 17998c14586fSDavid Ahern .daddr = *gw_addr, 18008c14586fSDavid Ahern .saddr = cfg->fc_prefsrc, 18018c14586fSDavid Ahern }; 18028c14586fSDavid Ahern struct fib6_table *table; 18038c14586fSDavid Ahern struct rt6_info *rt; 1804d5d32e4bSDavid Ahern int flags = RT6_LOOKUP_F_IFACE | RT6_LOOKUP_F_IGNORE_LINKSTATE; 18058c14586fSDavid Ahern 18068c14586fSDavid Ahern table = fib6_get_table(net, cfg->fc_table); 18078c14586fSDavid Ahern if (!table) 18088c14586fSDavid Ahern return NULL; 18098c14586fSDavid Ahern 18108c14586fSDavid Ahern if (!ipv6_addr_any(&cfg->fc_prefsrc)) 18118c14586fSDavid Ahern flags |= RT6_LOOKUP_F_HAS_SADDR; 18128c14586fSDavid Ahern 18138c14586fSDavid Ahern rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, flags); 18148c14586fSDavid Ahern 18158c14586fSDavid Ahern /* if table lookup failed, fall back to full lookup */ 18168c14586fSDavid Ahern if (rt == net->ipv6.ip6_null_entry) { 18178c14586fSDavid Ahern ip6_rt_put(rt); 18188c14586fSDavid Ahern rt = NULL; 18198c14586fSDavid Ahern } 18208c14586fSDavid Ahern 18218c14586fSDavid Ahern return rt; 18228c14586fSDavid Ahern } 18238c14586fSDavid Ahern 18248c5b83f0SRoopa Prabhu static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg) 18251da177e4SLinus Torvalds { 18265578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 18271da177e4SLinus Torvalds struct rt6_info *rt = NULL; 18281da177e4SLinus Torvalds struct net_device *dev = NULL; 18291da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1830c71099acSThomas Graf struct fib6_table *table; 18311da177e4SLinus Torvalds int addr_type; 18328c5b83f0SRoopa Prabhu int err = -EINVAL; 18331da177e4SLinus Torvalds 183486872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 18358c5b83f0SRoopa Prabhu goto out; 18361da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 183786872cb5SThomas Graf if (cfg->fc_src_len) 18388c5b83f0SRoopa Prabhu goto out; 18391da177e4SLinus Torvalds #endif 184086872cb5SThomas Graf if (cfg->fc_ifindex) { 18411da177e4SLinus Torvalds err = -ENODEV; 18425578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 18431da177e4SLinus Torvalds if (!dev) 18441da177e4SLinus Torvalds goto out; 18451da177e4SLinus Torvalds idev = in6_dev_get(dev); 18461da177e4SLinus Torvalds if (!idev) 18471da177e4SLinus Torvalds goto out; 18481da177e4SLinus Torvalds } 18491da177e4SLinus Torvalds 185086872cb5SThomas Graf if (cfg->fc_metric == 0) 185186872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 18521da177e4SLinus Torvalds 1853c71099acSThomas Graf err = -ENOBUFS; 185438308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1855d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1856d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 185738308473SDavid S. Miller if (!table) { 1858f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1859d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1860d71314b4SMatti Vaittinen } 1861d71314b4SMatti Vaittinen } else { 1862d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1863d71314b4SMatti Vaittinen } 186438308473SDavid S. Miller 186538308473SDavid S. Miller if (!table) 1866c71099acSThomas Graf goto out; 1867c71099acSThomas Graf 1868ad706862SMartin KaFai Lau rt = ip6_dst_alloc(net, NULL, 1869ad706862SMartin KaFai Lau (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT); 18701da177e4SLinus Torvalds 187138308473SDavid S. Miller if (!rt) { 18721da177e4SLinus Torvalds err = -ENOMEM; 18731da177e4SLinus Torvalds goto out; 18741da177e4SLinus Torvalds } 18751da177e4SLinus Torvalds 18761716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 18771716a961SGao feng rt6_set_expires(rt, jiffies + 18781716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 18791716a961SGao feng else 18801716a961SGao feng rt6_clean_expires(rt); 18811da177e4SLinus Torvalds 188286872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 188386872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 188486872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 188586872cb5SThomas Graf 188686872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 18871da177e4SLinus Torvalds 18881da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1889d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1890ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1891ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 18921da177e4SLinus Torvalds else 1893d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 18941da177e4SLinus Torvalds 1895d8d1f30bSChangli Gao rt->dst.output = ip6_output; 18961da177e4SLinus Torvalds 189719e42e45SRoopa Prabhu if (cfg->fc_encap) { 189819e42e45SRoopa Prabhu struct lwtunnel_state *lwtstate; 189919e42e45SRoopa Prabhu 190030357d7dSDavid Ahern err = lwtunnel_build_state(cfg->fc_encap_type, 1901127eb7cdSTom Herbert cfg->fc_encap, AF_INET6, cfg, 1902127eb7cdSTom Herbert &lwtstate); 190319e42e45SRoopa Prabhu if (err) 190419e42e45SRoopa Prabhu goto out; 190561adedf3SJiri Benc rt->dst.lwtstate = lwtstate_get(lwtstate); 190661adedf3SJiri Benc if (lwtunnel_output_redirect(rt->dst.lwtstate)) { 190761adedf3SJiri Benc rt->dst.lwtstate->orig_output = rt->dst.output; 190861adedf3SJiri Benc rt->dst.output = lwtunnel_output; 190919e42e45SRoopa Prabhu } 191061adedf3SJiri Benc if (lwtunnel_input_redirect(rt->dst.lwtstate)) { 191161adedf3SJiri Benc rt->dst.lwtstate->orig_input = rt->dst.input; 191261adedf3SJiri Benc rt->dst.input = lwtunnel_input; 191325368623STom Herbert } 191425368623STom Herbert } 191519e42e45SRoopa Prabhu 191686872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 191786872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 1918afc4eef8SMartin KaFai Lau if (rt->rt6i_dst.plen == 128) 191911d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 19201da177e4SLinus Torvalds 19211da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 192286872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 192386872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 19241da177e4SLinus Torvalds #endif 19251da177e4SLinus Torvalds 192686872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 19271da177e4SLinus Torvalds 19281da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 19291da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 19301da177e4SLinus Torvalds */ 193186872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 193238308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 193338308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 193438308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 19351da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 19365578689aSDaniel Lezcano if (dev != net->loopback_dev) { 19371da177e4SLinus Torvalds if (dev) { 19381da177e4SLinus Torvalds dev_put(dev); 19391da177e4SLinus Torvalds in6_dev_put(idev); 19401da177e4SLinus Torvalds } 19415578689aSDaniel Lezcano dev = net->loopback_dev; 19421da177e4SLinus Torvalds dev_hold(dev); 19431da177e4SLinus Torvalds idev = in6_dev_get(dev); 19441da177e4SLinus Torvalds if (!idev) { 19451da177e4SLinus Torvalds err = -ENODEV; 19461da177e4SLinus Torvalds goto out; 19471da177e4SLinus Torvalds } 19481da177e4SLinus Torvalds } 19491da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1950ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1951ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1952ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 1953ede2059dSEric W. Biederman rt->dst.output = dst_discard_out; 19547150aedeSKamala R rt->dst.input = dst_discard; 1955ef2c7d7bSNicolas Dichtel break; 1956ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1957ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 19587150aedeSKamala R rt->dst.output = ip6_pkt_prohibit_out; 19597150aedeSKamala R rt->dst.input = ip6_pkt_prohibit; 1960ef2c7d7bSNicolas Dichtel break; 1961b4949ab2SNicolas Dichtel case RTN_THROW: 19620315e382SNikola Forró case RTN_UNREACHABLE: 1963ef2c7d7bSNicolas Dichtel default: 19647150aedeSKamala R rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN 19650315e382SNikola Forró : (cfg->fc_type == RTN_UNREACHABLE) 19660315e382SNikola Forró ? -EHOSTUNREACH : -ENETUNREACH; 19677150aedeSKamala R rt->dst.output = ip6_pkt_discard_out; 19687150aedeSKamala R rt->dst.input = ip6_pkt_discard; 1969ef2c7d7bSNicolas Dichtel break; 1970ef2c7d7bSNicolas Dichtel } 19711da177e4SLinus Torvalds goto install_route; 19721da177e4SLinus Torvalds } 19731da177e4SLinus Torvalds 197486872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1975b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 19761da177e4SLinus Torvalds int gwa_type; 19771da177e4SLinus Torvalds 197886872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 1979330567b7SFlorian Westphal gwa_type = ipv6_addr_type(gw_addr); 198048ed7b26SFlorian Westphal 198148ed7b26SFlorian Westphal /* if gw_addr is local we will fail to detect this in case 198248ed7b26SFlorian Westphal * address is still TENTATIVE (DAD in progress). rt6_lookup() 198348ed7b26SFlorian Westphal * will return already-added prefix route via interface that 198448ed7b26SFlorian Westphal * prefix route was assigned to, which might be non-loopback. 198548ed7b26SFlorian Westphal */ 198648ed7b26SFlorian Westphal err = -EINVAL; 1987330567b7SFlorian Westphal if (ipv6_chk_addr_and_flags(net, gw_addr, 1988330567b7SFlorian Westphal gwa_type & IPV6_ADDR_LINKLOCAL ? 1989330567b7SFlorian Westphal dev : NULL, 0, 0)) 199048ed7b26SFlorian Westphal goto out; 199148ed7b26SFlorian Westphal 19924e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 19931da177e4SLinus Torvalds 19941da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 19958c14586fSDavid Ahern struct rt6_info *grt = NULL; 19961da177e4SLinus Torvalds 19971da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 19981da177e4SLinus Torvalds addresses as nexthop address. 19991da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 20001da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 20011da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 20021da177e4SLinus Torvalds some exceptions. --ANK 200396d5822cSErik Nordmark We allow IPv4-mapped nexthops to support RFC4798-type 200496d5822cSErik Nordmark addressing 20051da177e4SLinus Torvalds */ 200696d5822cSErik Nordmark if (!(gwa_type & (IPV6_ADDR_UNICAST | 200796d5822cSErik Nordmark IPV6_ADDR_MAPPED))) 20081da177e4SLinus Torvalds goto out; 20091da177e4SLinus Torvalds 2010a435a07fSVincent Bernat if (cfg->fc_table) { 20118c14586fSDavid Ahern grt = ip6_nh_lookup_table(net, cfg, gw_addr); 20128c14586fSDavid Ahern 2013a435a07fSVincent Bernat if (grt) { 2014a435a07fSVincent Bernat if (grt->rt6i_flags & RTF_GATEWAY || 2015a435a07fSVincent Bernat (dev && dev != grt->dst.dev)) { 2016a435a07fSVincent Bernat ip6_rt_put(grt); 2017a435a07fSVincent Bernat grt = NULL; 2018a435a07fSVincent Bernat } 2019a435a07fSVincent Bernat } 2020a435a07fSVincent Bernat } 2021a435a07fSVincent Bernat 20228c14586fSDavid Ahern if (!grt) 20238c14586fSDavid Ahern grt = rt6_lookup(net, gw_addr, NULL, 20248c14586fSDavid Ahern cfg->fc_ifindex, 1); 20251da177e4SLinus Torvalds 20261da177e4SLinus Torvalds err = -EHOSTUNREACH; 202738308473SDavid S. Miller if (!grt) 20281da177e4SLinus Torvalds goto out; 20291da177e4SLinus Torvalds if (dev) { 2030d1918542SDavid S. Miller if (dev != grt->dst.dev) { 203194e187c0SAmerigo Wang ip6_rt_put(grt); 20321da177e4SLinus Torvalds goto out; 20331da177e4SLinus Torvalds } 20341da177e4SLinus Torvalds } else { 2035d1918542SDavid S. Miller dev = grt->dst.dev; 20361da177e4SLinus Torvalds idev = grt->rt6i_idev; 20371da177e4SLinus Torvalds dev_hold(dev); 20381da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 20391da177e4SLinus Torvalds } 20401da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 20411da177e4SLinus Torvalds err = 0; 204294e187c0SAmerigo Wang ip6_rt_put(grt); 20431da177e4SLinus Torvalds 20441da177e4SLinus Torvalds if (err) 20451da177e4SLinus Torvalds goto out; 20461da177e4SLinus Torvalds } 20471da177e4SLinus Torvalds err = -EINVAL; 204838308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 20491da177e4SLinus Torvalds goto out; 20501da177e4SLinus Torvalds } 20511da177e4SLinus Torvalds 20521da177e4SLinus Torvalds err = -ENODEV; 205338308473SDavid S. Miller if (!dev) 20541da177e4SLinus Torvalds goto out; 20551da177e4SLinus Torvalds 2056c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 2057c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 2058c3968a85SDaniel Walter err = -EINVAL; 2059c3968a85SDaniel Walter goto out; 2060c3968a85SDaniel Walter } 20614e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 2062c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 2063c3968a85SDaniel Walter } else 2064c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2065c3968a85SDaniel Walter 206686872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 20671da177e4SLinus Torvalds 20681da177e4SLinus Torvalds install_route: 2069d8d1f30bSChangli Gao rt->dst.dev = dev; 20701da177e4SLinus Torvalds rt->rt6i_idev = idev; 2071c71099acSThomas Graf rt->rt6i_table = table; 207263152fc0SDaniel Lezcano 2073c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 207463152fc0SDaniel Lezcano 20758c5b83f0SRoopa Prabhu return rt; 20761da177e4SLinus Torvalds out: 20771da177e4SLinus Torvalds if (dev) 20781da177e4SLinus Torvalds dev_put(dev); 20791da177e4SLinus Torvalds if (idev) 20801da177e4SLinus Torvalds in6_dev_put(idev); 20811da177e4SLinus Torvalds if (rt) 2082d8d1f30bSChangli Gao dst_free(&rt->dst); 20836b9ea5a6SRoopa Prabhu 20848c5b83f0SRoopa Prabhu return ERR_PTR(err); 20856b9ea5a6SRoopa Prabhu } 20866b9ea5a6SRoopa Prabhu 20876b9ea5a6SRoopa Prabhu int ip6_route_add(struct fib6_config *cfg) 20886b9ea5a6SRoopa Prabhu { 20896b9ea5a6SRoopa Prabhu struct mx6_config mxc = { .mx = NULL, }; 20908c5b83f0SRoopa Prabhu struct rt6_info *rt; 20916b9ea5a6SRoopa Prabhu int err; 20926b9ea5a6SRoopa Prabhu 20938c5b83f0SRoopa Prabhu rt = ip6_route_info_create(cfg); 20948c5b83f0SRoopa Prabhu if (IS_ERR(rt)) { 20958c5b83f0SRoopa Prabhu err = PTR_ERR(rt); 20968c5b83f0SRoopa Prabhu rt = NULL; 20976b9ea5a6SRoopa Prabhu goto out; 20988c5b83f0SRoopa Prabhu } 20996b9ea5a6SRoopa Prabhu 21006b9ea5a6SRoopa Prabhu err = ip6_convert_metrics(&mxc, cfg); 21016b9ea5a6SRoopa Prabhu if (err) 21026b9ea5a6SRoopa Prabhu goto out; 21036b9ea5a6SRoopa Prabhu 21046b9ea5a6SRoopa Prabhu err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); 21056b9ea5a6SRoopa Prabhu 21066b9ea5a6SRoopa Prabhu kfree(mxc.mx); 21076b9ea5a6SRoopa Prabhu 21086b9ea5a6SRoopa Prabhu return err; 21096b9ea5a6SRoopa Prabhu out: 21106b9ea5a6SRoopa Prabhu if (rt) 21116b9ea5a6SRoopa Prabhu dst_free(&rt->dst); 21126b9ea5a6SRoopa Prabhu 21131da177e4SLinus Torvalds return err; 21141da177e4SLinus Torvalds } 21151da177e4SLinus Torvalds 211686872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 21171da177e4SLinus Torvalds { 21181da177e4SLinus Torvalds int err; 2119c71099acSThomas Graf struct fib6_table *table; 2120d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 21211da177e4SLinus Torvalds 21228e3d5be7SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry || 21238e3d5be7SMartin KaFai Lau rt->dst.flags & DST_NOCACHE) { 21246825a26cSGao feng err = -ENOENT; 21256825a26cSGao feng goto out; 21266825a26cSGao feng } 21276c813a72SPatrick McHardy 2128c71099acSThomas Graf table = rt->rt6i_table; 2129c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 213086872cb5SThomas Graf err = fib6_del(rt, info); 2131c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 21321da177e4SLinus Torvalds 21336825a26cSGao feng out: 213494e187c0SAmerigo Wang ip6_rt_put(rt); 21351da177e4SLinus Torvalds return err; 21361da177e4SLinus Torvalds } 21371da177e4SLinus Torvalds 2138e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 2139e0a1ad73SThomas Graf { 21404d1169c1SDenis V. Lunev struct nl_info info = { 2141d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 21424d1169c1SDenis V. Lunev }; 2143528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 2144e0a1ad73SThomas Graf } 2145e0a1ad73SThomas Graf 21460ae81335SDavid Ahern static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg) 21470ae81335SDavid Ahern { 21480ae81335SDavid Ahern struct nl_info *info = &cfg->fc_nlinfo; 21490ae81335SDavid Ahern struct fib6_table *table; 21500ae81335SDavid Ahern int err; 21510ae81335SDavid Ahern 21520ae81335SDavid Ahern table = rt->rt6i_table; 21530ae81335SDavid Ahern write_lock_bh(&table->tb6_lock); 21540ae81335SDavid Ahern 21550ae81335SDavid Ahern if (rt->rt6i_nsiblings && cfg->fc_delete_all_nh) { 21560ae81335SDavid Ahern struct rt6_info *sibling, *next_sibling; 21570ae81335SDavid Ahern 21580ae81335SDavid Ahern list_for_each_entry_safe(sibling, next_sibling, 21590ae81335SDavid Ahern &rt->rt6i_siblings, 21600ae81335SDavid Ahern rt6i_siblings) { 21610ae81335SDavid Ahern err = fib6_del(sibling, info); 21620ae81335SDavid Ahern if (err) 21630ae81335SDavid Ahern goto out; 21640ae81335SDavid Ahern } 21650ae81335SDavid Ahern } 21660ae81335SDavid Ahern 21670ae81335SDavid Ahern err = fib6_del(rt, info); 21680ae81335SDavid Ahern out: 21690ae81335SDavid Ahern write_unlock_bh(&table->tb6_lock); 21700ae81335SDavid Ahern ip6_rt_put(rt); 21710ae81335SDavid Ahern return err; 21720ae81335SDavid Ahern } 21730ae81335SDavid Ahern 217486872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 21751da177e4SLinus Torvalds { 2176c71099acSThomas Graf struct fib6_table *table; 21771da177e4SLinus Torvalds struct fib6_node *fn; 21781da177e4SLinus Torvalds struct rt6_info *rt; 21791da177e4SLinus Torvalds int err = -ESRCH; 21801da177e4SLinus Torvalds 21815578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 218238308473SDavid S. Miller if (!table) 2183c71099acSThomas Graf return err; 21841da177e4SLinus Torvalds 2185c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2186c71099acSThomas Graf 2187c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 218886872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 218986872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 21901da177e4SLinus Torvalds 21911da177e4SLinus Torvalds if (fn) { 2192d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 21931f56a01fSMartin KaFai Lau if ((rt->rt6i_flags & RTF_CACHE) && 21941f56a01fSMartin KaFai Lau !(cfg->fc_flags & RTF_CACHE)) 21951f56a01fSMartin KaFai Lau continue; 219686872cb5SThomas Graf if (cfg->fc_ifindex && 2197d1918542SDavid S. Miller (!rt->dst.dev || 2198d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 21991da177e4SLinus Torvalds continue; 220086872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 220186872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 22021da177e4SLinus Torvalds continue; 220386872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 22041da177e4SLinus Torvalds continue; 2205c2ed1880SMantas M if (cfg->fc_protocol && cfg->fc_protocol != rt->rt6i_protocol) 2206c2ed1880SMantas M continue; 2207d8d1f30bSChangli Gao dst_hold(&rt->dst); 2208c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 22091da177e4SLinus Torvalds 22100ae81335SDavid Ahern /* if gateway was specified only delete the one hop */ 22110ae81335SDavid Ahern if (cfg->fc_flags & RTF_GATEWAY) 221286872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 22130ae81335SDavid Ahern 22140ae81335SDavid Ahern return __ip6_del_rt_siblings(rt, cfg); 22151da177e4SLinus Torvalds } 22161da177e4SLinus Torvalds } 2217c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 22181da177e4SLinus Torvalds 22191da177e4SLinus Torvalds return err; 22201da177e4SLinus Torvalds } 22211da177e4SLinus Torvalds 22226700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 2223a6279458SYOSHIFUJI Hideaki { 2224a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 2225e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 2226e8599ff4SDavid S. Miller struct ndisc_options ndopts; 2227e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 2228e8599ff4SDavid S. Miller struct neighbour *neigh; 222971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 struct rd_msg *msg; 22306e157b6aSDavid S. Miller int optlen, on_link; 22316e157b6aSDavid S. Miller u8 *lladdr; 2232e8599ff4SDavid S. Miller 223329a3cad5SSimon Horman optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 223471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 optlen -= sizeof(*msg); 2235e8599ff4SDavid S. Miller 2236e8599ff4SDavid S. Miller if (optlen < 0) { 22376e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 2238e8599ff4SDavid S. Miller return; 2239e8599ff4SDavid S. Miller } 2240e8599ff4SDavid S. Miller 224171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 msg = (struct rd_msg *)icmp6_hdr(skb); 2242e8599ff4SDavid S. Miller 224371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_is_multicast(&msg->dest)) { 22446e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 2245e8599ff4SDavid S. Miller return; 2246e8599ff4SDavid S. Miller } 2247e8599ff4SDavid S. Miller 22486e157b6aSDavid S. Miller on_link = 0; 224971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_equal(&msg->dest, &msg->target)) { 2250e8599ff4SDavid S. Miller on_link = 1; 225171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 } else if (ipv6_addr_type(&msg->target) != 2252e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 22536e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 2254e8599ff4SDavid S. Miller return; 2255e8599ff4SDavid S. Miller } 2256e8599ff4SDavid S. Miller 2257e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 2258e8599ff4SDavid S. Miller if (!in6_dev) 2259e8599ff4SDavid S. Miller return; 2260e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 2261e8599ff4SDavid S. Miller return; 2262e8599ff4SDavid S. Miller 2263e8599ff4SDavid S. Miller /* RFC2461 8.1: 2264e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 2265e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 2266e8599ff4SDavid S. Miller */ 2267e8599ff4SDavid S. Miller 2268f997c55cSAlexander Aring if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) { 2269e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 2270e8599ff4SDavid S. Miller return; 2271e8599ff4SDavid S. Miller } 22726e157b6aSDavid S. Miller 22736e157b6aSDavid S. Miller lladdr = NULL; 2274e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 2275e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 2276e8599ff4SDavid S. Miller skb->dev); 2277e8599ff4SDavid S. Miller if (!lladdr) { 2278e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 2279e8599ff4SDavid S. Miller return; 2280e8599ff4SDavid S. Miller } 2281e8599ff4SDavid S. Miller } 2282e8599ff4SDavid S. Miller 22836e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 2284ec13ad1dSMatthias Schiffer if (rt->rt6i_flags & RTF_REJECT) { 22856e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 22866e157b6aSDavid S. Miller return; 22876e157b6aSDavid S. Miller } 22886e157b6aSDavid S. Miller 22896e157b6aSDavid S. Miller /* Redirect received -> path was valid. 22906e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 22916e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 22926e157b6aSDavid S. Miller */ 22936e157b6aSDavid S. Miller dst_confirm(&rt->dst); 22946e157b6aSDavid S. Miller 229571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 2296e8599ff4SDavid S. Miller if (!neigh) 2297e8599ff4SDavid S. Miller return; 2298e8599ff4SDavid S. Miller 22991da177e4SLinus Torvalds /* 23001da177e4SLinus Torvalds * We have finally decided to accept it. 23011da177e4SLinus Torvalds */ 23021da177e4SLinus Torvalds 2303f997c55cSAlexander Aring ndisc_update(skb->dev, neigh, lladdr, NUD_STALE, 23041da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 23051da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 23061da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 2307f997c55cSAlexander Aring NEIGH_UPDATE_F_ISROUTER)), 2308f997c55cSAlexander Aring NDISC_REDIRECT, &ndopts); 23091da177e4SLinus Torvalds 231083a09abdSMartin KaFai Lau nrt = ip6_rt_cache_alloc(rt, &msg->dest, NULL); 231138308473SDavid S. Miller if (!nrt) 23121da177e4SLinus Torvalds goto out; 23131da177e4SLinus Torvalds 23141da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 23151da177e4SLinus Torvalds if (on_link) 23161da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 23171da177e4SLinus Torvalds 23184e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 23191da177e4SLinus Torvalds 232040e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 23211da177e4SLinus Torvalds goto out; 23221da177e4SLinus Torvalds 2323d8d1f30bSChangli Gao netevent.old = &rt->dst; 2324d8d1f30bSChangli Gao netevent.new = &nrt->dst; 232571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 netevent.daddr = &msg->dest; 232660592833SYOSHIFUJI Hideaki / 吉藤英明 netevent.neigh = neigh; 23278d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 23288d71740cSTom Tucker 23291da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 23306e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 2331e0a1ad73SThomas Graf ip6_del_rt(rt); 23321da177e4SLinus Torvalds } 23331da177e4SLinus Torvalds 23341da177e4SLinus Torvalds out: 2335e8599ff4SDavid S. Miller neigh_release(neigh); 23366e157b6aSDavid S. Miller } 23376e157b6aSDavid S. Miller 23381da177e4SLinus Torvalds /* 23391da177e4SLinus Torvalds * Misc support functions 23401da177e4SLinus Torvalds */ 23411da177e4SLinus Torvalds 23424b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) 23434b32b5adSMartin KaFai Lau { 23444b32b5adSMartin KaFai Lau BUG_ON(from->dst.from); 23454b32b5adSMartin KaFai Lau 23464b32b5adSMartin KaFai Lau rt->rt6i_flags &= ~RTF_EXPIRES; 23474b32b5adSMartin KaFai Lau dst_hold(&from->dst); 23484b32b5adSMartin KaFai Lau rt->dst.from = &from->dst; 23494b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true); 23504b32b5adSMartin KaFai Lau } 23514b32b5adSMartin KaFai Lau 235283a09abdSMartin KaFai Lau static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort) 23531da177e4SLinus Torvalds { 2354d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 2355d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 235683a09abdSMartin KaFai Lau rt->rt6i_dst = ort->rt6i_dst; 2357d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 23581da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 23591da177e4SLinus Torvalds if (rt->rt6i_idev) 23601da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 2361d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 23624e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 23631716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 23641716a961SGao feng rt6_set_from(rt, ort); 236583a09abdSMartin KaFai Lau rt->rt6i_metric = ort->rt6i_metric; 23661da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 236783a09abdSMartin KaFai Lau rt->rt6i_src = ort->rt6i_src; 23681da177e4SLinus Torvalds #endif 236983a09abdSMartin KaFai Lau rt->rt6i_prefsrc = ort->rt6i_prefsrc; 2370c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 237161adedf3SJiri Benc rt->dst.lwtstate = lwtstate_get(ort->dst.lwtstate); 23721da177e4SLinus Torvalds } 23731da177e4SLinus Torvalds 237470ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 2375efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 2376b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2377830218c1SDavid Ahern const struct in6_addr *gwaddr, 2378830218c1SDavid Ahern struct net_device *dev) 237970ceb4f5SYOSHIFUJI Hideaki { 2380830218c1SDavid Ahern u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO; 2381830218c1SDavid Ahern int ifindex = dev->ifindex; 238270ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 238370ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 2384c71099acSThomas Graf struct fib6_table *table; 238570ceb4f5SYOSHIFUJI Hideaki 2386830218c1SDavid Ahern table = fib6_get_table(net, tb_id); 238738308473SDavid S. Miller if (!table) 2388c71099acSThomas Graf return NULL; 2389c71099acSThomas Graf 23905744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2391c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0); 239270ceb4f5SYOSHIFUJI Hideaki if (!fn) 239370ceb4f5SYOSHIFUJI Hideaki goto out; 239470ceb4f5SYOSHIFUJI Hideaki 2395d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 2396d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 239770ceb4f5SYOSHIFUJI Hideaki continue; 239870ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 239970ceb4f5SYOSHIFUJI Hideaki continue; 240070ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 240170ceb4f5SYOSHIFUJI Hideaki continue; 2402d8d1f30bSChangli Gao dst_hold(&rt->dst); 240370ceb4f5SYOSHIFUJI Hideaki break; 240470ceb4f5SYOSHIFUJI Hideaki } 240570ceb4f5SYOSHIFUJI Hideaki out: 24065744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 240770ceb4f5SYOSHIFUJI Hideaki return rt; 240870ceb4f5SYOSHIFUJI Hideaki } 240970ceb4f5SYOSHIFUJI Hideaki 2410efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 2411b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2412830218c1SDavid Ahern const struct in6_addr *gwaddr, 2413830218c1SDavid Ahern struct net_device *dev, 241495c96174SEric Dumazet unsigned int pref) 241570ceb4f5SYOSHIFUJI Hideaki { 241686872cb5SThomas Graf struct fib6_config cfg = { 2417238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 2418830218c1SDavid Ahern .fc_ifindex = dev->ifindex, 241986872cb5SThomas Graf .fc_dst_len = prefixlen, 242086872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 242186872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 242215e47304SEric W. Biederman .fc_nlinfo.portid = 0, 2423efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 2424efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 242586872cb5SThomas Graf }; 242670ceb4f5SYOSHIFUJI Hideaki 2427830218c1SDavid Ahern cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO, 24284e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 24294e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 243086872cb5SThomas Graf 2431e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 2432e317da96SYOSHIFUJI Hideaki if (!prefixlen) 243386872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 243470ceb4f5SYOSHIFUJI Hideaki 243586872cb5SThomas Graf ip6_route_add(&cfg); 243670ceb4f5SYOSHIFUJI Hideaki 2437830218c1SDavid Ahern return rt6_get_route_info(net, prefix, prefixlen, gwaddr, dev); 243870ceb4f5SYOSHIFUJI Hideaki } 243970ceb4f5SYOSHIFUJI Hideaki #endif 244070ceb4f5SYOSHIFUJI Hideaki 2441b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 24421da177e4SLinus Torvalds { 2443830218c1SDavid Ahern u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT; 24441da177e4SLinus Torvalds struct rt6_info *rt; 2445c71099acSThomas Graf struct fib6_table *table; 24461da177e4SLinus Torvalds 2447830218c1SDavid Ahern table = fib6_get_table(dev_net(dev), tb_id); 244838308473SDavid S. Miller if (!table) 2449c71099acSThomas Graf return NULL; 24501da177e4SLinus Torvalds 24515744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2452d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 2453d1918542SDavid S. Miller if (dev == rt->dst.dev && 2454045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 24551da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 24561da177e4SLinus Torvalds break; 24571da177e4SLinus Torvalds } 24581da177e4SLinus Torvalds if (rt) 2459d8d1f30bSChangli Gao dst_hold(&rt->dst); 24605744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 24611da177e4SLinus Torvalds return rt; 24621da177e4SLinus Torvalds } 24631da177e4SLinus Torvalds 2464b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 2465ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 2466ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 24671da177e4SLinus Torvalds { 246886872cb5SThomas Graf struct fib6_config cfg = { 2469ca254490SDavid Ahern .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT, 2470238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 247186872cb5SThomas Graf .fc_ifindex = dev->ifindex, 247286872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 247386872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 247415e47304SEric W. Biederman .fc_nlinfo.portid = 0, 24755578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 2476c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 247786872cb5SThomas Graf }; 24781da177e4SLinus Torvalds 24794e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 24801da177e4SLinus Torvalds 2481830218c1SDavid Ahern if (!ip6_route_add(&cfg)) { 2482830218c1SDavid Ahern struct fib6_table *table; 2483830218c1SDavid Ahern 2484830218c1SDavid Ahern table = fib6_get_table(dev_net(dev), cfg.fc_table); 2485830218c1SDavid Ahern if (table) 2486830218c1SDavid Ahern table->flags |= RT6_TABLE_HAS_DFLT_ROUTER; 2487830218c1SDavid Ahern } 24881da177e4SLinus Torvalds 24891da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 24901da177e4SLinus Torvalds } 24911da177e4SLinus Torvalds 2492830218c1SDavid Ahern static void __rt6_purge_dflt_routers(struct fib6_table *table) 24931da177e4SLinus Torvalds { 24941da177e4SLinus Torvalds struct rt6_info *rt; 24951da177e4SLinus Torvalds 24961da177e4SLinus Torvalds restart: 2497c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2498d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 24993e8b0ac3SLorenzo Colitti if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 25003e8b0ac3SLorenzo Colitti (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { 2501d8d1f30bSChangli Gao dst_hold(&rt->dst); 2502c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2503e0a1ad73SThomas Graf ip6_del_rt(rt); 25041da177e4SLinus Torvalds goto restart; 25051da177e4SLinus Torvalds } 25061da177e4SLinus Torvalds } 2507c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2508830218c1SDavid Ahern 2509830218c1SDavid Ahern table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER; 2510830218c1SDavid Ahern } 2511830218c1SDavid Ahern 2512830218c1SDavid Ahern void rt6_purge_dflt_routers(struct net *net) 2513830218c1SDavid Ahern { 2514830218c1SDavid Ahern struct fib6_table *table; 2515830218c1SDavid Ahern struct hlist_head *head; 2516830218c1SDavid Ahern unsigned int h; 2517830218c1SDavid Ahern 2518830218c1SDavid Ahern rcu_read_lock(); 2519830218c1SDavid Ahern 2520830218c1SDavid Ahern for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { 2521830218c1SDavid Ahern head = &net->ipv6.fib_table_hash[h]; 2522830218c1SDavid Ahern hlist_for_each_entry_rcu(table, head, tb6_hlist) { 2523830218c1SDavid Ahern if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER) 2524830218c1SDavid Ahern __rt6_purge_dflt_routers(table); 2525830218c1SDavid Ahern } 2526830218c1SDavid Ahern } 2527830218c1SDavid Ahern 2528830218c1SDavid Ahern rcu_read_unlock(); 25291da177e4SLinus Torvalds } 25301da177e4SLinus Torvalds 25315578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 25325578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 253386872cb5SThomas Graf struct fib6_config *cfg) 253486872cb5SThomas Graf { 253586872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 253686872cb5SThomas Graf 2537ca254490SDavid Ahern cfg->fc_table = l3mdev_fib_table_by_index(net, rtmsg->rtmsg_ifindex) ? 2538ca254490SDavid Ahern : RT6_TABLE_MAIN; 253986872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 254086872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 254186872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 254286872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 254386872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 254486872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 254586872cb5SThomas Graf 25465578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 2547f1243c2dSBenjamin Thery 25484e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 25494e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 25504e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 255186872cb5SThomas Graf } 255286872cb5SThomas Graf 25535578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 25541da177e4SLinus Torvalds { 255586872cb5SThomas Graf struct fib6_config cfg; 25561da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 25571da177e4SLinus Torvalds int err; 25581da177e4SLinus Torvalds 25591da177e4SLinus Torvalds switch (cmd) { 25601da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 25611da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 2562af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 25631da177e4SLinus Torvalds return -EPERM; 25641da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 25651da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 25661da177e4SLinus Torvalds if (err) 25671da177e4SLinus Torvalds return -EFAULT; 25681da177e4SLinus Torvalds 25695578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 257086872cb5SThomas Graf 25711da177e4SLinus Torvalds rtnl_lock(); 25721da177e4SLinus Torvalds switch (cmd) { 25731da177e4SLinus Torvalds case SIOCADDRT: 257486872cb5SThomas Graf err = ip6_route_add(&cfg); 25751da177e4SLinus Torvalds break; 25761da177e4SLinus Torvalds case SIOCDELRT: 257786872cb5SThomas Graf err = ip6_route_del(&cfg); 25781da177e4SLinus Torvalds break; 25791da177e4SLinus Torvalds default: 25801da177e4SLinus Torvalds err = -EINVAL; 25811da177e4SLinus Torvalds } 25821da177e4SLinus Torvalds rtnl_unlock(); 25831da177e4SLinus Torvalds 25841da177e4SLinus Torvalds return err; 25853ff50b79SStephen Hemminger } 25861da177e4SLinus Torvalds 25871da177e4SLinus Torvalds return -EINVAL; 25881da177e4SLinus Torvalds } 25891da177e4SLinus Torvalds 25901da177e4SLinus Torvalds /* 25911da177e4SLinus Torvalds * Drop the packet on the floor 25921da177e4SLinus Torvalds */ 25931da177e4SLinus Torvalds 2594d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 25951da177e4SLinus Torvalds { 2596612f09e8SYOSHIFUJI Hideaki int type; 2597adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2598612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2599612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 26000660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 260145bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 26023bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 26033bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2604612f09e8SYOSHIFUJI Hideaki break; 2605612f09e8SYOSHIFUJI Hideaki } 2606612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2607612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 26083bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 26093bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2610612f09e8SYOSHIFUJI Hideaki break; 2611612f09e8SYOSHIFUJI Hideaki } 26123ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 26131da177e4SLinus Torvalds kfree_skb(skb); 26141da177e4SLinus Torvalds return 0; 26151da177e4SLinus Torvalds } 26161da177e4SLinus Torvalds 26179ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 26189ce8ade0SThomas Graf { 2619612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 26209ce8ade0SThomas Graf } 26219ce8ade0SThomas Graf 2622ede2059dSEric W. Biederman static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb) 26231da177e4SLinus Torvalds { 2624adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2625612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 26261da177e4SLinus Torvalds } 26271da177e4SLinus Torvalds 26289ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 26299ce8ade0SThomas Graf { 2630612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 26319ce8ade0SThomas Graf } 26329ce8ade0SThomas Graf 2633ede2059dSEric W. Biederman static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb) 26349ce8ade0SThomas Graf { 2635adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2636612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 26379ce8ade0SThomas Graf } 26389ce8ade0SThomas Graf 26391da177e4SLinus Torvalds /* 26401da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 26411da177e4SLinus Torvalds */ 26421da177e4SLinus Torvalds 26431da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 26441da177e4SLinus Torvalds const struct in6_addr *addr, 26458f031519SDavid S. Miller bool anycast) 26461da177e4SLinus Torvalds { 2647ca254490SDavid Ahern u32 tb_id; 2648c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 26495f02ce24SDavid Ahern struct net_device *dev = net->loopback_dev; 26505f02ce24SDavid Ahern struct rt6_info *rt; 26515f02ce24SDavid Ahern 26525f02ce24SDavid Ahern /* use L3 Master device as loopback for host routes if device 26535f02ce24SDavid Ahern * is enslaved and address is not link local or multicast 26545f02ce24SDavid Ahern */ 26555f02ce24SDavid Ahern if (!rt6_need_strict(addr)) 26565f02ce24SDavid Ahern dev = l3mdev_master_dev_rcu(idev->dev) ? : dev; 26575f02ce24SDavid Ahern 26585f02ce24SDavid Ahern rt = ip6_dst_alloc(net, dev, DST_NOCOUNT); 2659a3300ef4SHannes Frederic Sowa if (!rt) 26601da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 26611da177e4SLinus Torvalds 26621da177e4SLinus Torvalds in6_dev_hold(idev); 26631da177e4SLinus Torvalds 266411d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2665d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2666d8d1f30bSChangli Gao rt->dst.output = ip6_output; 26671da177e4SLinus Torvalds rt->rt6i_idev = idev; 26681da177e4SLinus Torvalds 266994b5e0f9SDavid Ahern rt->rt6i_protocol = RTPROT_KERNEL; 26701da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 267158c4fb86SYOSHIFUJI Hideaki if (anycast) 267258c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 267358c4fb86SYOSHIFUJI Hideaki else 26741da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 26751da177e4SLinus Torvalds 2676550bab42SJulian Anastasov rt->rt6i_gateway = *addr; 26774e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 26781da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 2679ca254490SDavid Ahern tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL; 2680ca254490SDavid Ahern rt->rt6i_table = fib6_get_table(net, tb_id); 26818e3d5be7SMartin KaFai Lau rt->dst.flags |= DST_NOCACHE; 26821da177e4SLinus Torvalds 2683d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 26841da177e4SLinus Torvalds 26851da177e4SLinus Torvalds return rt; 26861da177e4SLinus Torvalds } 26871da177e4SLinus Torvalds 2688c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2689c3968a85SDaniel Walter struct arg_dev_net_ip { 2690c3968a85SDaniel Walter struct net_device *dev; 2691c3968a85SDaniel Walter struct net *net; 2692c3968a85SDaniel Walter struct in6_addr *addr; 2693c3968a85SDaniel Walter }; 2694c3968a85SDaniel Walter 2695c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2696c3968a85SDaniel Walter { 2697c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2698c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2699c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2700c3968a85SDaniel Walter 2701d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2702c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2703c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2704c3968a85SDaniel Walter /* remove prefsrc entry */ 2705c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2706c3968a85SDaniel Walter } 2707c3968a85SDaniel Walter return 0; 2708c3968a85SDaniel Walter } 2709c3968a85SDaniel Walter 2710c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2711c3968a85SDaniel Walter { 2712c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2713c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2714c3968a85SDaniel Walter .dev = ifp->idev->dev, 2715c3968a85SDaniel Walter .net = net, 2716c3968a85SDaniel Walter .addr = &ifp->addr, 2717c3968a85SDaniel Walter }; 27180c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 2719c3968a85SDaniel Walter } 2720c3968a85SDaniel Walter 2721be7a010dSDuan Jiong #define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) 2722be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 2723be7a010dSDuan Jiong 2724be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */ 2725be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg) 2726be7a010dSDuan Jiong { 2727be7a010dSDuan Jiong struct in6_addr *gateway = (struct in6_addr *)arg; 2728be7a010dSDuan Jiong 2729be7a010dSDuan Jiong if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) || 2730be7a010dSDuan Jiong ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) && 2731be7a010dSDuan Jiong ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { 2732be7a010dSDuan Jiong return -1; 2733be7a010dSDuan Jiong } 2734be7a010dSDuan Jiong return 0; 2735be7a010dSDuan Jiong } 2736be7a010dSDuan Jiong 2737be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 2738be7a010dSDuan Jiong { 2739be7a010dSDuan Jiong fib6_clean_all(net, fib6_clean_tohost, gateway); 2740be7a010dSDuan Jiong } 2741be7a010dSDuan Jiong 27428ed67789SDaniel Lezcano struct arg_dev_net { 27438ed67789SDaniel Lezcano struct net_device *dev; 27448ed67789SDaniel Lezcano struct net *net; 27458ed67789SDaniel Lezcano }; 27468ed67789SDaniel Lezcano 2747a1a22c12SDavid Ahern /* called with write lock held for table with rt */ 27481da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 27491da177e4SLinus Torvalds { 2750bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2751bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 27528ed67789SDaniel Lezcano 2753d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2754a1a22c12SDavid Ahern rt != adn->net->ipv6.ip6_null_entry && 2755a1a22c12SDavid Ahern (rt->rt6i_nsiblings == 0 || 2756a1a22c12SDavid Ahern !rt->rt6i_idev->cnf.ignore_routes_with_linkdown)) 27571da177e4SLinus Torvalds return -1; 2758c159d30cSDavid S. Miller 27591da177e4SLinus Torvalds return 0; 27601da177e4SLinus Torvalds } 27611da177e4SLinus Torvalds 2762f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 27631da177e4SLinus Torvalds { 27648ed67789SDaniel Lezcano struct arg_dev_net adn = { 27658ed67789SDaniel Lezcano .dev = dev, 27668ed67789SDaniel Lezcano .net = net, 27678ed67789SDaniel Lezcano }; 27688ed67789SDaniel Lezcano 27690c3584d5SLi RongQing fib6_clean_all(net, fib6_ifdown, &adn); 27701e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 2771e332bc67SEric W. Biederman if (dev) 27728d0b94afSMartin KaFai Lau rt6_uncached_list_flush_dev(net, dev); 27731da177e4SLinus Torvalds } 27741da177e4SLinus Torvalds 277595c96174SEric Dumazet struct rt6_mtu_change_arg { 27761da177e4SLinus Torvalds struct net_device *dev; 277795c96174SEric Dumazet unsigned int mtu; 27781da177e4SLinus Torvalds }; 27791da177e4SLinus Torvalds 27801da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 27811da177e4SLinus Torvalds { 27821da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 27831da177e4SLinus Torvalds struct inet6_dev *idev; 27841da177e4SLinus Torvalds 27851da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 27861da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 27871da177e4SLinus Torvalds We still use this lock to block changes 27881da177e4SLinus Torvalds caused by addrconf/ndisc. 27891da177e4SLinus Torvalds */ 27901da177e4SLinus Torvalds 27911da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 279238308473SDavid S. Miller if (!idev) 27931da177e4SLinus Torvalds return 0; 27941da177e4SLinus Torvalds 27951da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 27961da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 27971da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 27981da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 27991da177e4SLinus Torvalds */ 28001da177e4SLinus Torvalds /* 28011da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 28021da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 28031da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 28041da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 28051da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 28061da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 280767c408cfSAlexander Alemayhu PMTU discovery. 28081da177e4SLinus Torvalds */ 2809d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 2810fb56be83SMaciej Żenczykowski dst_metric_raw(&rt->dst, RTAX_MTU) && 28114b32b5adSMartin KaFai Lau !dst_metric_locked(&rt->dst, RTAX_MTU)) { 28124b32b5adSMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) { 28134b32b5adSMartin KaFai Lau /* For RTF_CACHE with rt6i_pmtu == 0 28144b32b5adSMartin KaFai Lau * (i.e. a redirected route), 28154b32b5adSMartin KaFai Lau * the metrics of its rt->dst.from has already 28164b32b5adSMartin KaFai Lau * been updated. 28174b32b5adSMartin KaFai Lau */ 28184b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu) 28194b32b5adSMartin KaFai Lau rt->rt6i_pmtu = arg->mtu; 28204b32b5adSMartin KaFai Lau } else if (dst_mtu(&rt->dst) >= arg->mtu || 2821d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 28224b32b5adSMartin KaFai Lau dst_mtu(&rt->dst) == idev->cnf.mtu6)) { 2823defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2824566cfd8fSSimon Arlott } 28254b32b5adSMartin KaFai Lau } 28261da177e4SLinus Torvalds return 0; 28271da177e4SLinus Torvalds } 28281da177e4SLinus Torvalds 282995c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 28301da177e4SLinus Torvalds { 2831c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2832c71099acSThomas Graf .dev = dev, 2833c71099acSThomas Graf .mtu = mtu, 2834c71099acSThomas Graf }; 28351da177e4SLinus Torvalds 28360c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 28371da177e4SLinus Torvalds } 28381da177e4SLinus Torvalds 2839ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 28405176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 284186872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2842ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 284386872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 284486872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 284551ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2846c78ba6d6SLubomir Rintel [RTA_PREF] = { .type = NLA_U8 }, 284719e42e45SRoopa Prabhu [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, 284819e42e45SRoopa Prabhu [RTA_ENCAP] = { .type = NLA_NESTED }, 284932bc201eSXin Long [RTA_EXPIRES] = { .type = NLA_U32 }, 2850622ec2c9SLorenzo Colitti [RTA_UID] = { .type = NLA_U32 }, 285186872cb5SThomas Graf }; 285286872cb5SThomas Graf 285386872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 285486872cb5SThomas Graf struct fib6_config *cfg) 28551da177e4SLinus Torvalds { 285686872cb5SThomas Graf struct rtmsg *rtm; 285786872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 2858c78ba6d6SLubomir Rintel unsigned int pref; 285986872cb5SThomas Graf int err; 28601da177e4SLinus Torvalds 286186872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 286286872cb5SThomas Graf if (err < 0) 286386872cb5SThomas Graf goto errout; 28641da177e4SLinus Torvalds 286586872cb5SThomas Graf err = -EINVAL; 286686872cb5SThomas Graf rtm = nlmsg_data(nlh); 286786872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 286886872cb5SThomas Graf 286986872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 287086872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 287186872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 287286872cb5SThomas Graf cfg->fc_flags = RTF_UP; 287386872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2874ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 287586872cb5SThomas Graf 2876ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2877ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2878b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2879b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 288086872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 288186872cb5SThomas Graf 2882ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2883ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2884ab79ad14SMaciej Żenczykowski 28851f56a01fSMartin KaFai Lau if (rtm->rtm_flags & RTM_F_CLONED) 28861f56a01fSMartin KaFai Lau cfg->fc_flags |= RTF_CACHE; 28871f56a01fSMartin KaFai Lau 288815e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 288986872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 28903b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 289186872cb5SThomas Graf 289286872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 289367b61f6cSJiri Benc cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 289486872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 28951da177e4SLinus Torvalds } 289686872cb5SThomas Graf 289786872cb5SThomas Graf if (tb[RTA_DST]) { 289886872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 289986872cb5SThomas Graf 290086872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 290186872cb5SThomas Graf goto errout; 290286872cb5SThomas Graf 290386872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 29041da177e4SLinus Torvalds } 290586872cb5SThomas Graf 290686872cb5SThomas Graf if (tb[RTA_SRC]) { 290786872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 290886872cb5SThomas Graf 290986872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 291086872cb5SThomas Graf goto errout; 291186872cb5SThomas Graf 291286872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 29131da177e4SLinus Torvalds } 291486872cb5SThomas Graf 2915c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 291667b61f6cSJiri Benc cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 2917c3968a85SDaniel Walter 291886872cb5SThomas Graf if (tb[RTA_OIF]) 291986872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 292086872cb5SThomas Graf 292186872cb5SThomas Graf if (tb[RTA_PRIORITY]) 292286872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 292386872cb5SThomas Graf 292486872cb5SThomas Graf if (tb[RTA_METRICS]) { 292586872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 292686872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 29271da177e4SLinus Torvalds } 292886872cb5SThomas Graf 292986872cb5SThomas Graf if (tb[RTA_TABLE]) 293086872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 293186872cb5SThomas Graf 293251ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 293351ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 293451ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 29359ed59592SDavid Ahern 29369ed59592SDavid Ahern err = lwtunnel_valid_encap_type_attr(cfg->fc_mp, 29379ed59592SDavid Ahern cfg->fc_mp_len); 29389ed59592SDavid Ahern if (err < 0) 29399ed59592SDavid Ahern goto errout; 294051ebd318SNicolas Dichtel } 294151ebd318SNicolas Dichtel 2942c78ba6d6SLubomir Rintel if (tb[RTA_PREF]) { 2943c78ba6d6SLubomir Rintel pref = nla_get_u8(tb[RTA_PREF]); 2944c78ba6d6SLubomir Rintel if (pref != ICMPV6_ROUTER_PREF_LOW && 2945c78ba6d6SLubomir Rintel pref != ICMPV6_ROUTER_PREF_HIGH) 2946c78ba6d6SLubomir Rintel pref = ICMPV6_ROUTER_PREF_MEDIUM; 2947c78ba6d6SLubomir Rintel cfg->fc_flags |= RTF_PREF(pref); 2948c78ba6d6SLubomir Rintel } 2949c78ba6d6SLubomir Rintel 295019e42e45SRoopa Prabhu if (tb[RTA_ENCAP]) 295119e42e45SRoopa Prabhu cfg->fc_encap = tb[RTA_ENCAP]; 295219e42e45SRoopa Prabhu 29539ed59592SDavid Ahern if (tb[RTA_ENCAP_TYPE]) { 295419e42e45SRoopa Prabhu cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]); 295519e42e45SRoopa Prabhu 29569ed59592SDavid Ahern err = lwtunnel_valid_encap_type(cfg->fc_encap_type); 29579ed59592SDavid Ahern if (err < 0) 29589ed59592SDavid Ahern goto errout; 29599ed59592SDavid Ahern } 29609ed59592SDavid Ahern 296132bc201eSXin Long if (tb[RTA_EXPIRES]) { 296232bc201eSXin Long unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ); 296332bc201eSXin Long 296432bc201eSXin Long if (addrconf_finite_timeout(timeout)) { 296532bc201eSXin Long cfg->fc_expires = jiffies_to_clock_t(timeout * HZ); 296632bc201eSXin Long cfg->fc_flags |= RTF_EXPIRES; 296732bc201eSXin Long } 296832bc201eSXin Long } 296932bc201eSXin Long 297086872cb5SThomas Graf err = 0; 297186872cb5SThomas Graf errout: 297286872cb5SThomas Graf return err; 29731da177e4SLinus Torvalds } 29741da177e4SLinus Torvalds 29756b9ea5a6SRoopa Prabhu struct rt6_nh { 29766b9ea5a6SRoopa Prabhu struct rt6_info *rt6_info; 29776b9ea5a6SRoopa Prabhu struct fib6_config r_cfg; 29786b9ea5a6SRoopa Prabhu struct mx6_config mxc; 29796b9ea5a6SRoopa Prabhu struct list_head next; 29806b9ea5a6SRoopa Prabhu }; 29816b9ea5a6SRoopa Prabhu 29826b9ea5a6SRoopa Prabhu static void ip6_print_replace_route_err(struct list_head *rt6_nh_list) 29836b9ea5a6SRoopa Prabhu { 29846b9ea5a6SRoopa Prabhu struct rt6_nh *nh; 29856b9ea5a6SRoopa Prabhu 29866b9ea5a6SRoopa Prabhu list_for_each_entry(nh, rt6_nh_list, next) { 29876b9ea5a6SRoopa Prabhu pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6 nexthop %pI6 ifi %d\n", 29886b9ea5a6SRoopa Prabhu &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway, 29896b9ea5a6SRoopa Prabhu nh->r_cfg.fc_ifindex); 29906b9ea5a6SRoopa Prabhu } 29916b9ea5a6SRoopa Prabhu } 29926b9ea5a6SRoopa Prabhu 29936b9ea5a6SRoopa Prabhu static int ip6_route_info_append(struct list_head *rt6_nh_list, 29946b9ea5a6SRoopa Prabhu struct rt6_info *rt, struct fib6_config *r_cfg) 29956b9ea5a6SRoopa Prabhu { 29966b9ea5a6SRoopa Prabhu struct rt6_nh *nh; 29976b9ea5a6SRoopa Prabhu struct rt6_info *rtnh; 29986b9ea5a6SRoopa Prabhu int err = -EEXIST; 29996b9ea5a6SRoopa Prabhu 30006b9ea5a6SRoopa Prabhu list_for_each_entry(nh, rt6_nh_list, next) { 30016b9ea5a6SRoopa Prabhu /* check if rt6_info already exists */ 30026b9ea5a6SRoopa Prabhu rtnh = nh->rt6_info; 30036b9ea5a6SRoopa Prabhu 30046b9ea5a6SRoopa Prabhu if (rtnh->dst.dev == rt->dst.dev && 30056b9ea5a6SRoopa Prabhu rtnh->rt6i_idev == rt->rt6i_idev && 30066b9ea5a6SRoopa Prabhu ipv6_addr_equal(&rtnh->rt6i_gateway, 30076b9ea5a6SRoopa Prabhu &rt->rt6i_gateway)) 30086b9ea5a6SRoopa Prabhu return err; 30096b9ea5a6SRoopa Prabhu } 30106b9ea5a6SRoopa Prabhu 30116b9ea5a6SRoopa Prabhu nh = kzalloc(sizeof(*nh), GFP_KERNEL); 30126b9ea5a6SRoopa Prabhu if (!nh) 30136b9ea5a6SRoopa Prabhu return -ENOMEM; 30146b9ea5a6SRoopa Prabhu nh->rt6_info = rt; 30156b9ea5a6SRoopa Prabhu err = ip6_convert_metrics(&nh->mxc, r_cfg); 30166b9ea5a6SRoopa Prabhu if (err) { 30176b9ea5a6SRoopa Prabhu kfree(nh); 30186b9ea5a6SRoopa Prabhu return err; 30196b9ea5a6SRoopa Prabhu } 30206b9ea5a6SRoopa Prabhu memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg)); 30216b9ea5a6SRoopa Prabhu list_add_tail(&nh->next, rt6_nh_list); 30226b9ea5a6SRoopa Prabhu 30236b9ea5a6SRoopa Prabhu return 0; 30246b9ea5a6SRoopa Prabhu } 30256b9ea5a6SRoopa Prabhu 3026*3b1137feSDavid Ahern static void ip6_route_mpath_notify(struct rt6_info *rt, 3027*3b1137feSDavid Ahern struct rt6_info *rt_last, 3028*3b1137feSDavid Ahern struct nl_info *info, 3029*3b1137feSDavid Ahern __u16 nlflags) 3030*3b1137feSDavid Ahern { 3031*3b1137feSDavid Ahern /* if this is an APPEND route, then rt points to the first route 3032*3b1137feSDavid Ahern * inserted and rt_last points to last route inserted. Userspace 3033*3b1137feSDavid Ahern * wants a consistent dump of the route which starts at the first 3034*3b1137feSDavid Ahern * nexthop. Since sibling routes are always added at the end of 3035*3b1137feSDavid Ahern * the list, find the first sibling of the last route appended 3036*3b1137feSDavid Ahern */ 3037*3b1137feSDavid Ahern if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->rt6i_nsiblings) { 3038*3b1137feSDavid Ahern rt = list_first_entry(&rt_last->rt6i_siblings, 3039*3b1137feSDavid Ahern struct rt6_info, 3040*3b1137feSDavid Ahern rt6i_siblings); 3041*3b1137feSDavid Ahern } 3042*3b1137feSDavid Ahern 3043*3b1137feSDavid Ahern if (rt) 3044*3b1137feSDavid Ahern inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags); 3045*3b1137feSDavid Ahern } 3046*3b1137feSDavid Ahern 30476b9ea5a6SRoopa Prabhu static int ip6_route_multipath_add(struct fib6_config *cfg) 304851ebd318SNicolas Dichtel { 3049*3b1137feSDavid Ahern struct rt6_info *rt_notif = NULL, *rt_last = NULL; 3050*3b1137feSDavid Ahern struct nl_info *info = &cfg->fc_nlinfo; 305151ebd318SNicolas Dichtel struct fib6_config r_cfg; 305251ebd318SNicolas Dichtel struct rtnexthop *rtnh; 30536b9ea5a6SRoopa Prabhu struct rt6_info *rt; 30546b9ea5a6SRoopa Prabhu struct rt6_nh *err_nh; 30556b9ea5a6SRoopa Prabhu struct rt6_nh *nh, *nh_safe; 3056*3b1137feSDavid Ahern __u16 nlflags; 305751ebd318SNicolas Dichtel int remaining; 305851ebd318SNicolas Dichtel int attrlen; 30596b9ea5a6SRoopa Prabhu int err = 1; 30606b9ea5a6SRoopa Prabhu int nhn = 0; 30616b9ea5a6SRoopa Prabhu int replace = (cfg->fc_nlinfo.nlh && 30626b9ea5a6SRoopa Prabhu (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); 30636b9ea5a6SRoopa Prabhu LIST_HEAD(rt6_nh_list); 306451ebd318SNicolas Dichtel 3065*3b1137feSDavid Ahern nlflags = replace ? NLM_F_REPLACE : NLM_F_CREATE; 3066*3b1137feSDavid Ahern if (info->nlh && info->nlh->nlmsg_flags & NLM_F_APPEND) 3067*3b1137feSDavid Ahern nlflags |= NLM_F_APPEND; 3068*3b1137feSDavid Ahern 306935f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len; 307051ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 307151ebd318SNicolas Dichtel 30726b9ea5a6SRoopa Prabhu /* Parse a Multipath Entry and build a list (rt6_nh_list) of 30736b9ea5a6SRoopa Prabhu * rt6_info structs per nexthop 30746b9ea5a6SRoopa Prabhu */ 307551ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 307651ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 307751ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 307851ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 307951ebd318SNicolas Dichtel 308051ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 308151ebd318SNicolas Dichtel if (attrlen > 0) { 308251ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 308351ebd318SNicolas Dichtel 308451ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 308551ebd318SNicolas Dichtel if (nla) { 308667b61f6cSJiri Benc r_cfg.fc_gateway = nla_get_in6_addr(nla); 308751ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 308851ebd318SNicolas Dichtel } 308919e42e45SRoopa Prabhu r_cfg.fc_encap = nla_find(attrs, attrlen, RTA_ENCAP); 309019e42e45SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); 309119e42e45SRoopa Prabhu if (nla) 309219e42e45SRoopa Prabhu r_cfg.fc_encap_type = nla_get_u16(nla); 309351ebd318SNicolas Dichtel } 30946b9ea5a6SRoopa Prabhu 30958c5b83f0SRoopa Prabhu rt = ip6_route_info_create(&r_cfg); 30968c5b83f0SRoopa Prabhu if (IS_ERR(rt)) { 30978c5b83f0SRoopa Prabhu err = PTR_ERR(rt); 30988c5b83f0SRoopa Prabhu rt = NULL; 30996b9ea5a6SRoopa Prabhu goto cleanup; 31008c5b83f0SRoopa Prabhu } 31016b9ea5a6SRoopa Prabhu 31026b9ea5a6SRoopa Prabhu err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg); 310351ebd318SNicolas Dichtel if (err) { 31046b9ea5a6SRoopa Prabhu dst_free(&rt->dst); 31056b9ea5a6SRoopa Prabhu goto cleanup; 310651ebd318SNicolas Dichtel } 31076b9ea5a6SRoopa Prabhu 31086b9ea5a6SRoopa Prabhu rtnh = rtnh_next(rtnh, &remaining); 310951ebd318SNicolas Dichtel } 31106b9ea5a6SRoopa Prabhu 3111*3b1137feSDavid Ahern /* for add and replace send one notification with all nexthops. 3112*3b1137feSDavid Ahern * Skip the notification in fib6_add_rt2node and send one with 3113*3b1137feSDavid Ahern * the full route when done 3114*3b1137feSDavid Ahern */ 3115*3b1137feSDavid Ahern info->skip_notify = 1; 3116*3b1137feSDavid Ahern 31176b9ea5a6SRoopa Prabhu err_nh = NULL; 31186b9ea5a6SRoopa Prabhu list_for_each_entry(nh, &rt6_nh_list, next) { 3119*3b1137feSDavid Ahern rt_last = nh->rt6_info; 3120*3b1137feSDavid Ahern err = __ip6_ins_rt(nh->rt6_info, info, &nh->mxc); 3121*3b1137feSDavid Ahern /* save reference to first route for notification */ 3122*3b1137feSDavid Ahern if (!rt_notif && !err) 3123*3b1137feSDavid Ahern rt_notif = nh->rt6_info; 3124*3b1137feSDavid Ahern 31256b9ea5a6SRoopa Prabhu /* nh->rt6_info is used or freed at this point, reset to NULL*/ 31266b9ea5a6SRoopa Prabhu nh->rt6_info = NULL; 31276b9ea5a6SRoopa Prabhu if (err) { 31286b9ea5a6SRoopa Prabhu if (replace && nhn) 31296b9ea5a6SRoopa Prabhu ip6_print_replace_route_err(&rt6_nh_list); 31306b9ea5a6SRoopa Prabhu err_nh = nh; 31316b9ea5a6SRoopa Prabhu goto add_errout; 31326b9ea5a6SRoopa Prabhu } 31336b9ea5a6SRoopa Prabhu 31341a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 313527596472SMichal Kubeček * these flags after the first nexthop: if there is a collision, 313627596472SMichal Kubeček * we have already failed to add the first nexthop: 313727596472SMichal Kubeček * fib6_add_rt2node() has rejected it; when replacing, old 313827596472SMichal Kubeček * nexthops have been replaced by first new, the rest should 313927596472SMichal Kubeček * be added to it. 31401a72418bSNicolas Dichtel */ 314127596472SMichal Kubeček cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | 314227596472SMichal Kubeček NLM_F_REPLACE); 31436b9ea5a6SRoopa Prabhu nhn++; 31446b9ea5a6SRoopa Prabhu } 31456b9ea5a6SRoopa Prabhu 3146*3b1137feSDavid Ahern /* success ... tell user about new route */ 3147*3b1137feSDavid Ahern ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); 31486b9ea5a6SRoopa Prabhu goto cleanup; 31496b9ea5a6SRoopa Prabhu 31506b9ea5a6SRoopa Prabhu add_errout: 3151*3b1137feSDavid Ahern /* send notification for routes that were added so that 3152*3b1137feSDavid Ahern * the delete notifications sent by ip6_route_del are 3153*3b1137feSDavid Ahern * coherent 3154*3b1137feSDavid Ahern */ 3155*3b1137feSDavid Ahern if (rt_notif) 3156*3b1137feSDavid Ahern ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); 3157*3b1137feSDavid Ahern 31586b9ea5a6SRoopa Prabhu /* Delete routes that were already added */ 31596b9ea5a6SRoopa Prabhu list_for_each_entry(nh, &rt6_nh_list, next) { 31606b9ea5a6SRoopa Prabhu if (err_nh == nh) 31616b9ea5a6SRoopa Prabhu break; 31626b9ea5a6SRoopa Prabhu ip6_route_del(&nh->r_cfg); 31636b9ea5a6SRoopa Prabhu } 31646b9ea5a6SRoopa Prabhu 31656b9ea5a6SRoopa Prabhu cleanup: 31666b9ea5a6SRoopa Prabhu list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) { 31676b9ea5a6SRoopa Prabhu if (nh->rt6_info) 31686b9ea5a6SRoopa Prabhu dst_free(&nh->rt6_info->dst); 31696b9ea5a6SRoopa Prabhu kfree(nh->mxc.mx); 31706b9ea5a6SRoopa Prabhu list_del(&nh->next); 31716b9ea5a6SRoopa Prabhu kfree(nh); 31726b9ea5a6SRoopa Prabhu } 31736b9ea5a6SRoopa Prabhu 31746b9ea5a6SRoopa Prabhu return err; 31756b9ea5a6SRoopa Prabhu } 31766b9ea5a6SRoopa Prabhu 31776b9ea5a6SRoopa Prabhu static int ip6_route_multipath_del(struct fib6_config *cfg) 31786b9ea5a6SRoopa Prabhu { 31796b9ea5a6SRoopa Prabhu struct fib6_config r_cfg; 31806b9ea5a6SRoopa Prabhu struct rtnexthop *rtnh; 31816b9ea5a6SRoopa Prabhu int remaining; 31826b9ea5a6SRoopa Prabhu int attrlen; 31836b9ea5a6SRoopa Prabhu int err = 1, last_err = 0; 31846b9ea5a6SRoopa Prabhu 31856b9ea5a6SRoopa Prabhu remaining = cfg->fc_mp_len; 31866b9ea5a6SRoopa Prabhu rtnh = (struct rtnexthop *)cfg->fc_mp; 31876b9ea5a6SRoopa Prabhu 31886b9ea5a6SRoopa Prabhu /* Parse a Multipath Entry */ 31896b9ea5a6SRoopa Prabhu while (rtnh_ok(rtnh, remaining)) { 31906b9ea5a6SRoopa Prabhu memcpy(&r_cfg, cfg, sizeof(*cfg)); 31916b9ea5a6SRoopa Prabhu if (rtnh->rtnh_ifindex) 31926b9ea5a6SRoopa Prabhu r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 31936b9ea5a6SRoopa Prabhu 31946b9ea5a6SRoopa Prabhu attrlen = rtnh_attrlen(rtnh); 31956b9ea5a6SRoopa Prabhu if (attrlen > 0) { 31966b9ea5a6SRoopa Prabhu struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 31976b9ea5a6SRoopa Prabhu 31986b9ea5a6SRoopa Prabhu nla = nla_find(attrs, attrlen, RTA_GATEWAY); 31996b9ea5a6SRoopa Prabhu if (nla) { 32006b9ea5a6SRoopa Prabhu nla_memcpy(&r_cfg.fc_gateway, nla, 16); 32016b9ea5a6SRoopa Prabhu r_cfg.fc_flags |= RTF_GATEWAY; 32026b9ea5a6SRoopa Prabhu } 32036b9ea5a6SRoopa Prabhu } 32046b9ea5a6SRoopa Prabhu err = ip6_route_del(&r_cfg); 32056b9ea5a6SRoopa Prabhu if (err) 32066b9ea5a6SRoopa Prabhu last_err = err; 32076b9ea5a6SRoopa Prabhu 320851ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 320951ebd318SNicolas Dichtel } 321051ebd318SNicolas Dichtel 321151ebd318SNicolas Dichtel return last_err; 321251ebd318SNicolas Dichtel } 321351ebd318SNicolas Dichtel 3214661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) 32151da177e4SLinus Torvalds { 321686872cb5SThomas Graf struct fib6_config cfg; 321786872cb5SThomas Graf int err; 32181da177e4SLinus Torvalds 321986872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 322086872cb5SThomas Graf if (err < 0) 322186872cb5SThomas Graf return err; 322286872cb5SThomas Graf 322351ebd318SNicolas Dichtel if (cfg.fc_mp) 32246b9ea5a6SRoopa Prabhu return ip6_route_multipath_del(&cfg); 32250ae81335SDavid Ahern else { 32260ae81335SDavid Ahern cfg.fc_delete_all_nh = 1; 322786872cb5SThomas Graf return ip6_route_del(&cfg); 32281da177e4SLinus Torvalds } 32290ae81335SDavid Ahern } 32301da177e4SLinus Torvalds 3231661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) 32321da177e4SLinus Torvalds { 323386872cb5SThomas Graf struct fib6_config cfg; 323486872cb5SThomas Graf int err; 32351da177e4SLinus Torvalds 323686872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 323786872cb5SThomas Graf if (err < 0) 323886872cb5SThomas Graf return err; 323986872cb5SThomas Graf 324051ebd318SNicolas Dichtel if (cfg.fc_mp) 32416b9ea5a6SRoopa Prabhu return ip6_route_multipath_add(&cfg); 324251ebd318SNicolas Dichtel else 324386872cb5SThomas Graf return ip6_route_add(&cfg); 32441da177e4SLinus Torvalds } 32451da177e4SLinus Torvalds 3246beb1afacSDavid Ahern static size_t rt6_nlmsg_size(struct rt6_info *rt) 3247339bf98fSThomas Graf { 3248beb1afacSDavid Ahern int nexthop_len = 0; 3249beb1afacSDavid Ahern 3250beb1afacSDavid Ahern if (rt->rt6i_nsiblings) { 3251beb1afacSDavid Ahern nexthop_len = nla_total_size(0) /* RTA_MULTIPATH */ 3252beb1afacSDavid Ahern + NLA_ALIGN(sizeof(struct rtnexthop)) 3253beb1afacSDavid Ahern + nla_total_size(16) /* RTA_GATEWAY */ 3254beb1afacSDavid Ahern + nla_total_size(4) /* RTA_OIF */ 3255beb1afacSDavid Ahern + lwtunnel_get_encap_size(rt->dst.lwtstate); 3256beb1afacSDavid Ahern 3257beb1afacSDavid Ahern nexthop_len *= rt->rt6i_nsiblings; 3258beb1afacSDavid Ahern } 3259beb1afacSDavid Ahern 3260339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 3261339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 3262339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 3263339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 3264339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 3265339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 3266339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 3267339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 3268339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 32696a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 3270ea697639SDaniel Borkmann + nla_total_size(sizeof(struct rta_cacheinfo)) 3271c78ba6d6SLubomir Rintel + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 327219e42e45SRoopa Prabhu + nla_total_size(1) /* RTA_PREF */ 3273beb1afacSDavid Ahern + lwtunnel_get_encap_size(rt->dst.lwtstate) 3274beb1afacSDavid Ahern + nexthop_len; 3275beb1afacSDavid Ahern } 3276beb1afacSDavid Ahern 3277beb1afacSDavid Ahern static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt, 3278beb1afacSDavid Ahern unsigned int *flags) 3279beb1afacSDavid Ahern { 3280beb1afacSDavid Ahern if (!netif_running(rt->dst.dev) || !netif_carrier_ok(rt->dst.dev)) { 3281beb1afacSDavid Ahern *flags |= RTNH_F_LINKDOWN; 3282beb1afacSDavid Ahern if (rt->rt6i_idev->cnf.ignore_routes_with_linkdown) 3283beb1afacSDavid Ahern *flags |= RTNH_F_DEAD; 3284beb1afacSDavid Ahern } 3285beb1afacSDavid Ahern 3286beb1afacSDavid Ahern if (rt->rt6i_flags & RTF_GATEWAY) { 3287beb1afacSDavid Ahern if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0) 3288beb1afacSDavid Ahern goto nla_put_failure; 3289beb1afacSDavid Ahern } 3290beb1afacSDavid Ahern 3291beb1afacSDavid Ahern if (rt->dst.dev && 3292beb1afacSDavid Ahern nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 3293beb1afacSDavid Ahern goto nla_put_failure; 3294beb1afacSDavid Ahern 3295beb1afacSDavid Ahern if (rt->dst.lwtstate && 3296beb1afacSDavid Ahern lwtunnel_fill_encap(skb, rt->dst.lwtstate) < 0) 3297beb1afacSDavid Ahern goto nla_put_failure; 3298beb1afacSDavid Ahern 3299beb1afacSDavid Ahern return 0; 3300beb1afacSDavid Ahern 3301beb1afacSDavid Ahern nla_put_failure: 3302beb1afacSDavid Ahern return -EMSGSIZE; 3303beb1afacSDavid Ahern } 3304beb1afacSDavid Ahern 3305beb1afacSDavid Ahern static int rt6_add_nexthop(struct sk_buff *skb, struct rt6_info *rt) 3306beb1afacSDavid Ahern { 3307beb1afacSDavid Ahern struct rtnexthop *rtnh; 3308beb1afacSDavid Ahern unsigned int flags = 0; 3309beb1afacSDavid Ahern 3310beb1afacSDavid Ahern rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); 3311beb1afacSDavid Ahern if (!rtnh) 3312beb1afacSDavid Ahern goto nla_put_failure; 3313beb1afacSDavid Ahern 3314beb1afacSDavid Ahern rtnh->rtnh_hops = 0; 3315beb1afacSDavid Ahern rtnh->rtnh_ifindex = rt->dst.dev ? rt->dst.dev->ifindex : 0; 3316beb1afacSDavid Ahern 3317beb1afacSDavid Ahern if (rt6_nexthop_info(skb, rt, &flags) < 0) 3318beb1afacSDavid Ahern goto nla_put_failure; 3319beb1afacSDavid Ahern 3320beb1afacSDavid Ahern rtnh->rtnh_flags = flags; 3321beb1afacSDavid Ahern 3322beb1afacSDavid Ahern /* length of rtnetlink header + attributes */ 3323beb1afacSDavid Ahern rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh; 3324beb1afacSDavid Ahern 3325beb1afacSDavid Ahern return 0; 3326beb1afacSDavid Ahern 3327beb1afacSDavid Ahern nla_put_failure: 3328beb1afacSDavid Ahern return -EMSGSIZE; 3329339bf98fSThomas Graf } 3330339bf98fSThomas Graf 3331191cd582SBrian Haley static int rt6_fill_node(struct net *net, 3332191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 33330d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 333415e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 3335f8cfe2ceSDavid Ahern unsigned int flags) 33361da177e4SLinus Torvalds { 33374b32b5adSMartin KaFai Lau u32 metrics[RTAX_MAX]; 33381da177e4SLinus Torvalds struct rtmsg *rtm; 33391da177e4SLinus Torvalds struct nlmsghdr *nlh; 3340e3703b3dSThomas Graf long expires; 33419e762a4aSPatrick McHardy u32 table; 33421da177e4SLinus Torvalds 334315e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 334438308473SDavid S. Miller if (!nlh) 334526932566SPatrick McHardy return -EMSGSIZE; 33462d7202bfSThomas Graf 33472d7202bfSThomas Graf rtm = nlmsg_data(nlh); 33481da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 33491da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 33501da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 33511da177e4SLinus Torvalds rtm->rtm_tos = 0; 3352c71099acSThomas Graf if (rt->rt6i_table) 33539e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 3354c71099acSThomas Graf else 33559e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 33569e762a4aSPatrick McHardy rtm->rtm_table = table; 3357c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 3358c78679e8SDavid S. Miller goto nla_put_failure; 3359ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 3360ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 3361ef2c7d7bSNicolas Dichtel case -EINVAL: 3362ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 3363ef2c7d7bSNicolas Dichtel break; 3364ef2c7d7bSNicolas Dichtel case -EACCES: 3365ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 3366ef2c7d7bSNicolas Dichtel break; 3367b4949ab2SNicolas Dichtel case -EAGAIN: 3368b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 3369b4949ab2SNicolas Dichtel break; 3370ef2c7d7bSNicolas Dichtel default: 33711da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 3372ef2c7d7bSNicolas Dichtel break; 3373ef2c7d7bSNicolas Dichtel } 3374ef2c7d7bSNicolas Dichtel } 3375ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 3376ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 3377d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 33781da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 33791da177e4SLinus Torvalds else 33801da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 33811da177e4SLinus Torvalds rtm->rtm_flags = 0; 33821da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 33831da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 33841da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 33851da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 3386f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 3387f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 33881da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 3389f0396f60SDenis Ovsienko else 3390f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 3391f0396f60SDenis Ovsienko } 33921da177e4SLinus Torvalds 33931da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 33941da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 33951da177e4SLinus Torvalds 33961da177e4SLinus Torvalds if (dst) { 3397930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, dst)) 3398c78679e8SDavid S. Miller goto nla_put_failure; 33991da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 34001da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 3401930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr)) 3402c78679e8SDavid S. Miller goto nla_put_failure; 34031da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 34041da177e4SLinus Torvalds if (src) { 3405930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_SRC, src)) 3406c78679e8SDavid S. Miller goto nla_put_failure; 34071da177e4SLinus Torvalds rtm->rtm_src_len = 128; 3408c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 3409930345eaSJiri Benc nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr)) 3410c78679e8SDavid S. Miller goto nla_put_failure; 34111da177e4SLinus Torvalds #endif 34127bc570c8SYOSHIFUJI Hideaki if (iif) { 34137bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 34147bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 3415fd61c6baSDavid Ahern int err = ip6mr_get_route(net, skb, rtm, portid); 34162cf75070SNikolay Aleksandrov 34177bc570c8SYOSHIFUJI Hideaki if (err == 0) 34187bc570c8SYOSHIFUJI Hideaki return 0; 3419fd61c6baSDavid Ahern if (err < 0) 34207bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 34217bc570c8SYOSHIFUJI Hideaki } else 34227bc570c8SYOSHIFUJI Hideaki #endif 3423c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 3424c78679e8SDavid S. Miller goto nla_put_failure; 34257bc570c8SYOSHIFUJI Hideaki } else if (dst) { 34261da177e4SLinus Torvalds struct in6_addr saddr_buf; 3427c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 3428930345eaSJiri Benc nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 3429c78679e8SDavid S. Miller goto nla_put_failure; 3430c3968a85SDaniel Walter } 3431c3968a85SDaniel Walter 3432c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 3433c3968a85SDaniel Walter struct in6_addr saddr_buf; 34344e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 3435930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 3436c78679e8SDavid S. Miller goto nla_put_failure; 34371da177e4SLinus Torvalds } 34382d7202bfSThomas Graf 34394b32b5adSMartin KaFai Lau memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); 34404b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu) 34414b32b5adSMartin KaFai Lau metrics[RTAX_MTU - 1] = rt->rt6i_pmtu; 34424b32b5adSMartin KaFai Lau if (rtnetlink_put_metrics(skb, metrics) < 0) 34432d7202bfSThomas Graf goto nla_put_failure; 34442d7202bfSThomas Graf 3445beb1afacSDavid Ahern if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 3446beb1afacSDavid Ahern goto nla_put_failure; 3447beb1afacSDavid Ahern 3448beb1afacSDavid Ahern /* For multipath routes, walk the siblings list and add 3449beb1afacSDavid Ahern * each as a nexthop within RTA_MULTIPATH. 3450beb1afacSDavid Ahern */ 3451beb1afacSDavid Ahern if (rt->rt6i_nsiblings) { 3452beb1afacSDavid Ahern struct rt6_info *sibling, *next_sibling; 3453beb1afacSDavid Ahern struct nlattr *mp; 3454beb1afacSDavid Ahern 3455beb1afacSDavid Ahern mp = nla_nest_start(skb, RTA_MULTIPATH); 3456beb1afacSDavid Ahern if (!mp) 3457beb1afacSDavid Ahern goto nla_put_failure; 3458beb1afacSDavid Ahern 3459beb1afacSDavid Ahern if (rt6_add_nexthop(skb, rt) < 0) 3460beb1afacSDavid Ahern goto nla_put_failure; 3461beb1afacSDavid Ahern 3462beb1afacSDavid Ahern list_for_each_entry_safe(sibling, next_sibling, 3463beb1afacSDavid Ahern &rt->rt6i_siblings, rt6i_siblings) { 3464beb1afacSDavid Ahern if (rt6_add_nexthop(skb, sibling) < 0) 346594f826b8SEric Dumazet goto nla_put_failure; 346694f826b8SEric Dumazet } 34672d7202bfSThomas Graf 3468beb1afacSDavid Ahern nla_nest_end(skb, mp); 3469beb1afacSDavid Ahern } else { 3470beb1afacSDavid Ahern if (rt6_nexthop_info(skb, rt, &rtm->rtm_flags) < 0) 3471c78679e8SDavid S. Miller goto nla_put_failure; 3472beb1afacSDavid Ahern } 34738253947eSLi Wei 34748253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 347569cdf8f9SYOSHIFUJI Hideaki 347687a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 3477e3703b3dSThomas Graf goto nla_put_failure; 34781da177e4SLinus Torvalds 3479c78ba6d6SLubomir Rintel if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) 3480c78ba6d6SLubomir Rintel goto nla_put_failure; 3481c78ba6d6SLubomir Rintel 348219e42e45SRoopa Prabhu 3483053c095aSJohannes Berg nlmsg_end(skb, nlh); 3484053c095aSJohannes Berg return 0; 34852d7202bfSThomas Graf 34862d7202bfSThomas Graf nla_put_failure: 348726932566SPatrick McHardy nlmsg_cancel(skb, nlh); 348826932566SPatrick McHardy return -EMSGSIZE; 34891da177e4SLinus Torvalds } 34901da177e4SLinus Torvalds 34911b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 34921da177e4SLinus Torvalds { 34931da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 34941f17e2f2SDavid Ahern struct net *net = arg->net; 34951f17e2f2SDavid Ahern 34961f17e2f2SDavid Ahern if (rt == net->ipv6.ip6_null_entry) 34971f17e2f2SDavid Ahern return 0; 34981da177e4SLinus Torvalds 34992d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 35002d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 3501f8cfe2ceSDavid Ahern 3502f8cfe2ceSDavid Ahern /* user wants prefix routes only */ 3503f8cfe2ceSDavid Ahern if (rtm->rtm_flags & RTM_F_PREFIX && 3504f8cfe2ceSDavid Ahern !(rt->rt6i_flags & RTF_PREFIX_RT)) { 3505f8cfe2ceSDavid Ahern /* success since this is not a prefix route */ 3506f8cfe2ceSDavid Ahern return 1; 3507f8cfe2ceSDavid Ahern } 3508f8cfe2ceSDavid Ahern } 35091da177e4SLinus Torvalds 35101f17e2f2SDavid Ahern return rt6_fill_node(net, 3511191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 351215e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 3513f8cfe2ceSDavid Ahern NLM_F_MULTI); 35141da177e4SLinus Torvalds } 35151da177e4SLinus Torvalds 3516661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) 35171da177e4SLinus Torvalds { 35183b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 3519ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 35201da177e4SLinus Torvalds struct rt6_info *rt; 3521ab364a6fSThomas Graf struct sk_buff *skb; 3522ab364a6fSThomas Graf struct rtmsg *rtm; 35234c9483b2SDavid S. Miller struct flowi6 fl6; 352472331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 3525ab364a6fSThomas Graf 3526ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 3527ab364a6fSThomas Graf if (err < 0) 3528ab364a6fSThomas Graf goto errout; 3529ab364a6fSThomas Graf 3530ab364a6fSThomas Graf err = -EINVAL; 35314c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 353238b7097bSHannes Frederic Sowa rtm = nlmsg_data(nlh); 353338b7097bSHannes Frederic Sowa fl6.flowlabel = ip6_make_flowinfo(rtm->rtm_tos, 0); 3534ab364a6fSThomas Graf 3535ab364a6fSThomas Graf if (tb[RTA_SRC]) { 3536ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 3537ab364a6fSThomas Graf goto errout; 3538ab364a6fSThomas Graf 35394e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 3540ab364a6fSThomas Graf } 3541ab364a6fSThomas Graf 3542ab364a6fSThomas Graf if (tb[RTA_DST]) { 3543ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 3544ab364a6fSThomas Graf goto errout; 3545ab364a6fSThomas Graf 35464e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 3547ab364a6fSThomas Graf } 3548ab364a6fSThomas Graf 3549ab364a6fSThomas Graf if (tb[RTA_IIF]) 3550ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 3551ab364a6fSThomas Graf 3552ab364a6fSThomas Graf if (tb[RTA_OIF]) 355372331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 3554ab364a6fSThomas Graf 35552e47b291SLorenzo Colitti if (tb[RTA_MARK]) 35562e47b291SLorenzo Colitti fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 35572e47b291SLorenzo Colitti 3558622ec2c9SLorenzo Colitti if (tb[RTA_UID]) 3559622ec2c9SLorenzo Colitti fl6.flowi6_uid = make_kuid(current_user_ns(), 3560622ec2c9SLorenzo Colitti nla_get_u32(tb[RTA_UID])); 3561622ec2c9SLorenzo Colitti else 3562622ec2c9SLorenzo Colitti fl6.flowi6_uid = iif ? INVALID_UID : current_uid(); 3563622ec2c9SLorenzo Colitti 3564ab364a6fSThomas Graf if (iif) { 3565ab364a6fSThomas Graf struct net_device *dev; 356672331bc0SShmulik Ladkani int flags = 0; 356772331bc0SShmulik Ladkani 35685578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 3569ab364a6fSThomas Graf if (!dev) { 3570ab364a6fSThomas Graf err = -ENODEV; 3571ab364a6fSThomas Graf goto errout; 3572ab364a6fSThomas Graf } 357372331bc0SShmulik Ladkani 357472331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 357572331bc0SShmulik Ladkani 357672331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 357772331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 357872331bc0SShmulik Ladkani 357972331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 358072331bc0SShmulik Ladkani flags); 358172331bc0SShmulik Ladkani } else { 358272331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 358372331bc0SShmulik Ladkani 358472331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 3585ab364a6fSThomas Graf } 35861da177e4SLinus Torvalds 35871da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 358838308473SDavid S. Miller if (!skb) { 358994e187c0SAmerigo Wang ip6_rt_put(rt); 3590ab364a6fSThomas Graf err = -ENOBUFS; 3591ab364a6fSThomas Graf goto errout; 3592ab364a6fSThomas Graf } 35931da177e4SLinus Torvalds 3594d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 35951da177e4SLinus Torvalds 35964c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 359715e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 3598f8cfe2ceSDavid Ahern nlh->nlmsg_seq, 0); 35991da177e4SLinus Torvalds if (err < 0) { 3600ab364a6fSThomas Graf kfree_skb(skb); 3601ab364a6fSThomas Graf goto errout; 36021da177e4SLinus Torvalds } 36031da177e4SLinus Torvalds 360415e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 3605ab364a6fSThomas Graf errout: 36061da177e4SLinus Torvalds return err; 36071da177e4SLinus Torvalds } 36081da177e4SLinus Torvalds 360937a1d361SRoopa Prabhu void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info, 361037a1d361SRoopa Prabhu unsigned int nlm_flags) 36111da177e4SLinus Torvalds { 36121da177e4SLinus Torvalds struct sk_buff *skb; 36135578689aSDaniel Lezcano struct net *net = info->nl_net; 3614528c4cebSDenis V. Lunev u32 seq; 3615528c4cebSDenis V. Lunev int err; 36160d51aa80SJamal Hadi Salim 3617528c4cebSDenis V. Lunev err = -ENOBUFS; 361838308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 361986872cb5SThomas Graf 362019e42e45SRoopa Prabhu skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any()); 362138308473SDavid S. Miller if (!skb) 362221713ebcSThomas Graf goto errout; 36231da177e4SLinus Torvalds 3624191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 3625f8cfe2ceSDavid Ahern event, info->portid, seq, nlm_flags); 362626932566SPatrick McHardy if (err < 0) { 362726932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 362826932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 362926932566SPatrick McHardy kfree_skb(skb); 363026932566SPatrick McHardy goto errout; 363126932566SPatrick McHardy } 363215e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 36335578689aSDaniel Lezcano info->nlh, gfp_any()); 36341ce85fe4SPablo Neira Ayuso return; 363521713ebcSThomas Graf errout: 363621713ebcSThomas Graf if (err < 0) 36375578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 36381da177e4SLinus Torvalds } 36391da177e4SLinus Torvalds 36408ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 3641351638e7SJiri Pirko unsigned long event, void *ptr) 36428ed67789SDaniel Lezcano { 3643351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 3644c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 36458ed67789SDaniel Lezcano 36468ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 3647d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 36488ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 36498ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3650d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 36518ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 3652d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 36538ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 36548ed67789SDaniel Lezcano #endif 36558ed67789SDaniel Lezcano } 36568ed67789SDaniel Lezcano 36578ed67789SDaniel Lezcano return NOTIFY_OK; 36588ed67789SDaniel Lezcano } 36598ed67789SDaniel Lezcano 36601da177e4SLinus Torvalds /* 36611da177e4SLinus Torvalds * /proc 36621da177e4SLinus Torvalds */ 36631da177e4SLinus Torvalds 36641da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 36651da177e4SLinus Torvalds 366633120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 366733120b30SAlexey Dobriyan .owner = THIS_MODULE, 366833120b30SAlexey Dobriyan .open = ipv6_route_open, 366933120b30SAlexey Dobriyan .read = seq_read, 367033120b30SAlexey Dobriyan .llseek = seq_lseek, 36718d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 367233120b30SAlexey Dobriyan }; 367333120b30SAlexey Dobriyan 36741da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 36751da177e4SLinus Torvalds { 367669ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 36771da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 367869ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 367969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 368069ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 368169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 368269ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 3683fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 368469ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 36851da177e4SLinus Torvalds 36861da177e4SLinus Torvalds return 0; 36871da177e4SLinus Torvalds } 36881da177e4SLinus Torvalds 36891da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 36901da177e4SLinus Torvalds { 3691de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 369269ddb805SDaniel Lezcano } 369369ddb805SDaniel Lezcano 36949a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 36951da177e4SLinus Torvalds .owner = THIS_MODULE, 36961da177e4SLinus Torvalds .open = rt6_stats_seq_open, 36971da177e4SLinus Torvalds .read = seq_read, 36981da177e4SLinus Torvalds .llseek = seq_lseek, 3699b6fcbdb4SPavel Emelyanov .release = single_release_net, 37001da177e4SLinus Torvalds }; 37011da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 37021da177e4SLinus Torvalds 37031da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 37041da177e4SLinus Torvalds 37051da177e4SLinus Torvalds static 3706fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 37071da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 37081da177e4SLinus Torvalds { 3709c486da34SLucian Adrian Grijincu struct net *net; 3710c486da34SLucian Adrian Grijincu int delay; 3711c486da34SLucian Adrian Grijincu if (!write) 3712c486da34SLucian Adrian Grijincu return -EINVAL; 3713c486da34SLucian Adrian Grijincu 3714c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 3715c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 37168d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 37172ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 37181da177e4SLinus Torvalds return 0; 37191da177e4SLinus Torvalds } 37201da177e4SLinus Torvalds 3721fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 37221da177e4SLinus Torvalds { 37231da177e4SLinus Torvalds .procname = "flush", 37244990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 37251da177e4SLinus Torvalds .maxlen = sizeof(int), 372689c8b3a1SDave Jones .mode = 0200, 37276d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 37281da177e4SLinus Torvalds }, 37291da177e4SLinus Torvalds { 37301da177e4SLinus Torvalds .procname = "gc_thresh", 37319a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 37321da177e4SLinus Torvalds .maxlen = sizeof(int), 37331da177e4SLinus Torvalds .mode = 0644, 37346d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 37351da177e4SLinus Torvalds }, 37361da177e4SLinus Torvalds { 37371da177e4SLinus Torvalds .procname = "max_size", 37384990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 37391da177e4SLinus Torvalds .maxlen = sizeof(int), 37401da177e4SLinus Torvalds .mode = 0644, 37416d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 37421da177e4SLinus Torvalds }, 37431da177e4SLinus Torvalds { 37441da177e4SLinus Torvalds .procname = "gc_min_interval", 37454990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 37461da177e4SLinus Torvalds .maxlen = sizeof(int), 37471da177e4SLinus Torvalds .mode = 0644, 37486d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 37491da177e4SLinus Torvalds }, 37501da177e4SLinus Torvalds { 37511da177e4SLinus Torvalds .procname = "gc_timeout", 37524990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 37531da177e4SLinus Torvalds .maxlen = sizeof(int), 37541da177e4SLinus Torvalds .mode = 0644, 37556d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 37561da177e4SLinus Torvalds }, 37571da177e4SLinus Torvalds { 37581da177e4SLinus Torvalds .procname = "gc_interval", 37594990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 37601da177e4SLinus Torvalds .maxlen = sizeof(int), 37611da177e4SLinus Torvalds .mode = 0644, 37626d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 37631da177e4SLinus Torvalds }, 37641da177e4SLinus Torvalds { 37651da177e4SLinus Torvalds .procname = "gc_elasticity", 37664990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 37671da177e4SLinus Torvalds .maxlen = sizeof(int), 37681da177e4SLinus Torvalds .mode = 0644, 3769f3d3f616SMin Zhang .proc_handler = proc_dointvec, 37701da177e4SLinus Torvalds }, 37711da177e4SLinus Torvalds { 37721da177e4SLinus Torvalds .procname = "mtu_expires", 37734990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 37741da177e4SLinus Torvalds .maxlen = sizeof(int), 37751da177e4SLinus Torvalds .mode = 0644, 37766d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 37771da177e4SLinus Torvalds }, 37781da177e4SLinus Torvalds { 37791da177e4SLinus Torvalds .procname = "min_adv_mss", 37804990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 37811da177e4SLinus Torvalds .maxlen = sizeof(int), 37821da177e4SLinus Torvalds .mode = 0644, 3783f3d3f616SMin Zhang .proc_handler = proc_dointvec, 37841da177e4SLinus Torvalds }, 37851da177e4SLinus Torvalds { 37861da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 37874990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 37881da177e4SLinus Torvalds .maxlen = sizeof(int), 37891da177e4SLinus Torvalds .mode = 0644, 37906d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 37911da177e4SLinus Torvalds }, 3792f8572d8fSEric W. Biederman { } 37931da177e4SLinus Torvalds }; 37941da177e4SLinus Torvalds 37952c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 3796760f2d01SDaniel Lezcano { 3797760f2d01SDaniel Lezcano struct ctl_table *table; 3798760f2d01SDaniel Lezcano 3799760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 3800760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 3801760f2d01SDaniel Lezcano GFP_KERNEL); 38025ee09105SYOSHIFUJI Hideaki 38035ee09105SYOSHIFUJI Hideaki if (table) { 38045ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 3805c486da34SLucian Adrian Grijincu table[0].extra1 = net; 380686393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 38075ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 38085ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 38095ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 38105ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 38115ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 38125ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 38135ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 38149c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 3815464dc801SEric W. Biederman 3816464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 3817464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 3818464dc801SEric W. Biederman table[0].procname = NULL; 38195ee09105SYOSHIFUJI Hideaki } 38205ee09105SYOSHIFUJI Hideaki 3821760f2d01SDaniel Lezcano return table; 3822760f2d01SDaniel Lezcano } 38231da177e4SLinus Torvalds #endif 38241da177e4SLinus Torvalds 38252c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 3826cdb18761SDaniel Lezcano { 3827633d424bSPavel Emelyanov int ret = -ENOMEM; 38288ed67789SDaniel Lezcano 382986393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 383086393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 3831f2fc6a54SBenjamin Thery 3832fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 3833fc66f95cSEric Dumazet goto out_ip6_dst_ops; 3834fc66f95cSEric Dumazet 38358ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 38368ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 38378ed67789SDaniel Lezcano GFP_KERNEL); 38388ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 3839fc66f95cSEric Dumazet goto out_ip6_dst_entries; 3840d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 38418ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 3842d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 384362fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 384462fa8a84SDavid S. Miller ip6_template_metrics, true); 38458ed67789SDaniel Lezcano 38468ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 38478ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 38488ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 38498ed67789SDaniel Lezcano GFP_KERNEL); 385068fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 385168fffc67SPeter Zijlstra goto out_ip6_null_entry; 3852d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 38538ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 3854d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 385562fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 385662fa8a84SDavid S. Miller ip6_template_metrics, true); 38578ed67789SDaniel Lezcano 38588ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 38598ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 38608ed67789SDaniel Lezcano GFP_KERNEL); 386168fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 386268fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 3863d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 38648ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 3865d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 386662fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 386762fa8a84SDavid S. Miller ip6_template_metrics, true); 38688ed67789SDaniel Lezcano #endif 38698ed67789SDaniel Lezcano 3870b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 3871b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 3872b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 3873b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 3874b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 3875b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 3876b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 3877b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 3878b339a47cSPeter Zijlstra 38796891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 38806891a346SBenjamin Thery 38818ed67789SDaniel Lezcano ret = 0; 38828ed67789SDaniel Lezcano out: 38838ed67789SDaniel Lezcano return ret; 3884f2fc6a54SBenjamin Thery 388568fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 388668fffc67SPeter Zijlstra out_ip6_prohibit_entry: 388768fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 388868fffc67SPeter Zijlstra out_ip6_null_entry: 388968fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 389068fffc67SPeter Zijlstra #endif 3891fc66f95cSEric Dumazet out_ip6_dst_entries: 3892fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3893f2fc6a54SBenjamin Thery out_ip6_dst_ops: 3894f2fc6a54SBenjamin Thery goto out; 3895cdb18761SDaniel Lezcano } 3896cdb18761SDaniel Lezcano 38972c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 3898cdb18761SDaniel Lezcano { 38998ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 39008ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 39018ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 39028ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 39038ed67789SDaniel Lezcano #endif 390441bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3905cdb18761SDaniel Lezcano } 3906cdb18761SDaniel Lezcano 3907d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 3908d189634eSThomas Graf { 3909d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3910d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 3911d4beaa66SGao feng proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops); 3912d189634eSThomas Graf #endif 3913d189634eSThomas Graf return 0; 3914d189634eSThomas Graf } 3915d189634eSThomas Graf 3916d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 3917d189634eSThomas Graf { 3918d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3919ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 3920ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 3921d189634eSThomas Graf #endif 3922d189634eSThomas Graf } 3923d189634eSThomas Graf 3924cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 3925cdb18761SDaniel Lezcano .init = ip6_route_net_init, 3926cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 3927cdb18761SDaniel Lezcano }; 3928cdb18761SDaniel Lezcano 3929c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 3930c3426b47SDavid S. Miller { 3931c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 3932c3426b47SDavid S. Miller 3933c3426b47SDavid S. Miller if (!bp) 3934c3426b47SDavid S. Miller return -ENOMEM; 3935c3426b47SDavid S. Miller inet_peer_base_init(bp); 3936c3426b47SDavid S. Miller net->ipv6.peers = bp; 3937c3426b47SDavid S. Miller return 0; 3938c3426b47SDavid S. Miller } 3939c3426b47SDavid S. Miller 3940c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 3941c3426b47SDavid S. Miller { 3942c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 3943c3426b47SDavid S. Miller 3944c3426b47SDavid S. Miller net->ipv6.peers = NULL; 394556a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3946c3426b47SDavid S. Miller kfree(bp); 3947c3426b47SDavid S. Miller } 3948c3426b47SDavid S. Miller 39492b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3950c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3951c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3952c3426b47SDavid S. Miller }; 3953c3426b47SDavid S. Miller 3954d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3955d189634eSThomas Graf .init = ip6_route_net_init_late, 3956d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3957d189634eSThomas Graf }; 3958d189634eSThomas Graf 39598ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 39608ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 39618ed67789SDaniel Lezcano .priority = 0, 39628ed67789SDaniel Lezcano }; 39638ed67789SDaniel Lezcano 3964433d49c3SDaniel Lezcano int __init ip6_route_init(void) 39651da177e4SLinus Torvalds { 3966433d49c3SDaniel Lezcano int ret; 39678d0b94afSMartin KaFai Lau int cpu; 3968433d49c3SDaniel Lezcano 39699a7ec3a9SDaniel Lezcano ret = -ENOMEM; 39709a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 39719a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 39729a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 39739a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3974c19a28e1SFernando Carrijo goto out; 397514e50e57SDavid S. Miller 3976fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 39778ed67789SDaniel Lezcano if (ret) 3978bdb3289fSDaniel Lezcano goto out_kmem_cache; 3979bdb3289fSDaniel Lezcano 3980c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3981c3426b47SDavid S. Miller if (ret) 3982e8803b6cSDavid S. Miller goto out_dst_entries; 39832a0c451aSThomas Graf 39847e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 39857e52b33bSDavid S. Miller if (ret) 39867e52b33bSDavid S. Miller goto out_register_inetpeer; 3987c3426b47SDavid S. Miller 39885dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 39895dc121e9SArnaud Ebalard 39908ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 39918ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 39928ed67789SDaniel Lezcano * manually for init_net */ 3993d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 39948ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3995bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3996d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 39978ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3998d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 39998ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 4000bdb3289fSDaniel Lezcano #endif 4001e8803b6cSDavid S. Miller ret = fib6_init(); 4002433d49c3SDaniel Lezcano if (ret) 40038ed67789SDaniel Lezcano goto out_register_subsys; 4004433d49c3SDaniel Lezcano 4005433d49c3SDaniel Lezcano ret = xfrm6_init(); 4006433d49c3SDaniel Lezcano if (ret) 4007e8803b6cSDavid S. Miller goto out_fib6_init; 4008c35b7e72SDaniel Lezcano 4009433d49c3SDaniel Lezcano ret = fib6_rules_init(); 4010433d49c3SDaniel Lezcano if (ret) 4011433d49c3SDaniel Lezcano goto xfrm6_init; 40127e5449c2SDaniel Lezcano 4013d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 4014d189634eSThomas Graf if (ret) 4015d189634eSThomas Graf goto fib6_rules_init; 4016d189634eSThomas Graf 4017433d49c3SDaniel Lezcano ret = -ENOBUFS; 4018c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 4019c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 4020c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 4021d189634eSThomas Graf goto out_register_late_subsys; 4022433d49c3SDaniel Lezcano 40238ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 4024cdb18761SDaniel Lezcano if (ret) 4025d189634eSThomas Graf goto out_register_late_subsys; 40268ed67789SDaniel Lezcano 40278d0b94afSMartin KaFai Lau for_each_possible_cpu(cpu) { 40288d0b94afSMartin KaFai Lau struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); 40298d0b94afSMartin KaFai Lau 40308d0b94afSMartin KaFai Lau INIT_LIST_HEAD(&ul->head); 40318d0b94afSMartin KaFai Lau spin_lock_init(&ul->lock); 40328d0b94afSMartin KaFai Lau } 40338d0b94afSMartin KaFai Lau 4034433d49c3SDaniel Lezcano out: 4035433d49c3SDaniel Lezcano return ret; 4036433d49c3SDaniel Lezcano 4037d189634eSThomas Graf out_register_late_subsys: 4038d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 4039433d49c3SDaniel Lezcano fib6_rules_init: 4040433d49c3SDaniel Lezcano fib6_rules_cleanup(); 4041433d49c3SDaniel Lezcano xfrm6_init: 4042433d49c3SDaniel Lezcano xfrm6_fini(); 40432a0c451aSThomas Graf out_fib6_init: 40442a0c451aSThomas Graf fib6_gc_cleanup(); 40458ed67789SDaniel Lezcano out_register_subsys: 40468ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 40477e52b33bSDavid S. Miller out_register_inetpeer: 40487e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 4049fc66f95cSEric Dumazet out_dst_entries: 4050fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 4051433d49c3SDaniel Lezcano out_kmem_cache: 4052f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 4053433d49c3SDaniel Lezcano goto out; 40541da177e4SLinus Torvalds } 40551da177e4SLinus Torvalds 40561da177e4SLinus Torvalds void ip6_route_cleanup(void) 40571da177e4SLinus Torvalds { 40588ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 4059d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 4060101367c2SThomas Graf fib6_rules_cleanup(); 40611da177e4SLinus Torvalds xfrm6_fini(); 40621da177e4SLinus Torvalds fib6_gc_cleanup(); 4063c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 40648ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 406541bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 4066f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 40671da177e4SLinus Torvalds } 4068