11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Linux INET6 implementation 31da177e4SLinus Torvalds * FIB front-end. 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Authors: 61da177e4SLinus Torvalds * Pedro Roque <roque@di.fc.ul.pt> 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 91da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 101da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 111da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds /* Changes: 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * YOSHIFUJI Hideaki @USAGI 171da177e4SLinus Torvalds * reworked default router selection. 181da177e4SLinus Torvalds * - respect outgoing interface 191da177e4SLinus Torvalds * - select from (probably) reachable routers (i.e. 201da177e4SLinus Torvalds * routers in REACHABLE, STALE, DELAY or PROBE states). 211da177e4SLinus Torvalds * - always select the same router if it is (probably) 221da177e4SLinus Torvalds * reachable. otherwise, round-robin the list. 23c0bece9fSYOSHIFUJI Hideaki * Ville Nuorvala 24c0bece9fSYOSHIFUJI Hideaki * Fixed routing subtrees. 251da177e4SLinus Torvalds */ 261da177e4SLinus Torvalds 27f3213831SJoe Perches #define pr_fmt(fmt) "IPv6: " fmt 28f3213831SJoe Perches 294fc268d2SRandy Dunlap #include <linux/capability.h> 301da177e4SLinus Torvalds #include <linux/errno.h> 31bc3b2d7fSPaul Gortmaker #include <linux/export.h> 321da177e4SLinus Torvalds #include <linux/types.h> 331da177e4SLinus Torvalds #include <linux/times.h> 341da177e4SLinus Torvalds #include <linux/socket.h> 351da177e4SLinus Torvalds #include <linux/sockios.h> 361da177e4SLinus Torvalds #include <linux/net.h> 371da177e4SLinus Torvalds #include <linux/route.h> 381da177e4SLinus Torvalds #include <linux/netdevice.h> 391da177e4SLinus Torvalds #include <linux/in6.h> 407bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h> 411da177e4SLinus Torvalds #include <linux/init.h> 421da177e4SLinus Torvalds #include <linux/if_arp.h> 431da177e4SLinus Torvalds #include <linux/proc_fs.h> 441da177e4SLinus Torvalds #include <linux/seq_file.h> 455b7c931dSDaniel Lezcano #include <linux/nsproxy.h> 465a0e3ad6STejun Heo #include <linux/slab.h> 47457c4cbcSEric W. Biederman #include <net/net_namespace.h> 481da177e4SLinus Torvalds #include <net/snmp.h> 491da177e4SLinus Torvalds #include <net/ipv6.h> 501da177e4SLinus Torvalds #include <net/ip6_fib.h> 511da177e4SLinus Torvalds #include <net/ip6_route.h> 521da177e4SLinus Torvalds #include <net/ndisc.h> 531da177e4SLinus Torvalds #include <net/addrconf.h> 541da177e4SLinus Torvalds #include <net/tcp.h> 551da177e4SLinus Torvalds #include <linux/rtnetlink.h> 561da177e4SLinus Torvalds #include <net/dst.h> 571da177e4SLinus Torvalds #include <net/xfrm.h> 588d71740cSTom Tucker #include <net/netevent.h> 5921713ebcSThomas Graf #include <net/netlink.h> 6051ebd318SNicolas Dichtel #include <net/nexthop.h> 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds #include <asm/uaccess.h> 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 651da177e4SLinus Torvalds #include <linux/sysctl.h> 661da177e4SLinus Torvalds #endif 671da177e4SLinus Torvalds 68afc154e9SHannes Frederic Sowa enum rt6_nud_state { 697e980569SJiri Benc RT6_NUD_FAIL_HARD = -3, 707e980569SJiri Benc RT6_NUD_FAIL_PROBE = -2, 717e980569SJiri Benc RT6_NUD_FAIL_DO_RR = -1, 72afc154e9SHannes Frederic Sowa RT6_NUD_SUCCEED = 1 73afc154e9SHannes Frederic Sowa }; 74afc154e9SHannes Frederic Sowa 751716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 7621efcfa0SEric Dumazet const struct in6_addr *dest); 771da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 780dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst); 79ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst); 801da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *); 811da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *); 821da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *, 831da177e4SLinus Torvalds struct net_device *dev, int how); 84569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops); 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds static int ip6_pkt_discard(struct sk_buff *skb); 87aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb); 887150aedeSKamala R static int ip6_pkt_prohibit(struct sk_buff *skb); 89aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb); 901da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb); 916700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 926700c270SDavid S. Miller struct sk_buff *skb, u32 mtu); 936700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, 946700c270SDavid S. Miller struct sk_buff *skb); 954b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt); 9652bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict); 971da177e4SLinus Torvalds 9870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 99efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 100b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 101b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 10295c96174SEric Dumazet unsigned int pref); 103efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 104b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 105b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex); 10670ceb4f5SYOSHIFUJI Hideaki #endif 10770ceb4f5SYOSHIFUJI Hideaki 10806582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) 10906582540SDavid S. Miller { 11006582540SDavid S. Miller struct rt6_info *rt = (struct rt6_info *)dst; 11106582540SDavid S. Miller 1124b32b5adSMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) 1134b32b5adSMartin KaFai Lau return NULL; 1144b32b5adSMartin KaFai Lau else 1153b471175SMartin KaFai Lau return dst_cow_metrics_generic(dst, old); 11606582540SDavid S. Miller } 11706582540SDavid S. Miller 118f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, 119f894cbf8SDavid S. Miller struct sk_buff *skb, 120f894cbf8SDavid S. Miller const void *daddr) 12139232973SDavid S. Miller { 12239232973SDavid S. Miller struct in6_addr *p = &rt->rt6i_gateway; 12339232973SDavid S. Miller 124a7563f34SDavid S. Miller if (!ipv6_addr_any(p)) 12539232973SDavid S. Miller return (const void *) p; 126f894cbf8SDavid S. Miller else if (skb) 127f894cbf8SDavid S. Miller return &ipv6_hdr(skb)->daddr; 12839232973SDavid S. Miller return daddr; 12939232973SDavid S. Miller } 13039232973SDavid S. Miller 131f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, 132f894cbf8SDavid S. Miller struct sk_buff *skb, 133f894cbf8SDavid S. Miller const void *daddr) 134d3aaeb38SDavid S. Miller { 13539232973SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 13639232973SDavid S. Miller struct neighbour *n; 13739232973SDavid S. Miller 138f894cbf8SDavid S. Miller daddr = choose_neigh_daddr(rt, skb, daddr); 1398e022ee6SYOSHIFUJI Hideaki / 吉藤英明 n = __ipv6_neigh_lookup(dst->dev, daddr); 140f83c7790SDavid S. Miller if (n) 141f83c7790SDavid S. Miller return n; 142f83c7790SDavid S. Miller return neigh_create(&nd_tbl, daddr, dst->dev); 143f83c7790SDavid S. Miller } 144f83c7790SDavid S. Miller 1459a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 1461da177e4SLinus Torvalds .family = AF_INET6, 1471da177e4SLinus Torvalds .gc = ip6_dst_gc, 1481da177e4SLinus Torvalds .gc_thresh = 1024, 1491da177e4SLinus Torvalds .check = ip6_dst_check, 1500dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 151ebb762f2SSteffen Klassert .mtu = ip6_mtu, 15206582540SDavid S. Miller .cow_metrics = ipv6_cow_metrics, 1531da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 1541da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 1551da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 1561da177e4SLinus Torvalds .link_failure = ip6_link_failure, 1571da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 1586e157b6aSDavid S. Miller .redirect = rt6_do_redirect, 1591ac06e03SHerbert Xu .local_out = __ip6_local_out, 160d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 1611da177e4SLinus Torvalds }; 1621da177e4SLinus Torvalds 163ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 164ec831ea7SRoland Dreier { 165618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 166618f9bc7SSteffen Klassert 167618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 168ec831ea7SRoland Dreier } 169ec831ea7SRoland Dreier 1706700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, 1716700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 17214e50e57SDavid S. Miller { 17314e50e57SDavid S. Miller } 17414e50e57SDavid S. Miller 1756700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, 1766700c270SDavid S. Miller struct sk_buff *skb) 177b587ee3bSDavid S. Miller { 178b587ee3bSDavid S. Miller } 179b587ee3bSDavid S. Miller 1800972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, 1810972ddb2SHeld Bernhard unsigned long old) 1820972ddb2SHeld Bernhard { 1830972ddb2SHeld Bernhard return NULL; 1840972ddb2SHeld Bernhard } 1850972ddb2SHeld Bernhard 18614e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 18714e50e57SDavid S. Miller .family = AF_INET6, 18814e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 18914e50e57SDavid S. Miller .check = ip6_dst_check, 190ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 191214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 19214e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 193b587ee3bSDavid S. Miller .redirect = ip6_rt_blackhole_redirect, 1940972ddb2SHeld Bernhard .cow_metrics = ip6_rt_blackhole_cow_metrics, 195d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 19614e50e57SDavid S. Miller }; 19714e50e57SDavid S. Miller 19862fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 19914edd87dSLi RongQing [RTAX_HOPLIMIT - 1] = 0, 20062fa8a84SDavid S. Miller }; 20162fa8a84SDavid S. Miller 202fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = { 2031da177e4SLinus Torvalds .dst = { 2041da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 2051da177e4SLinus Torvalds .__use = 1, 2062c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 2071da177e4SLinus Torvalds .error = -ENETUNREACH, 2081da177e4SLinus Torvalds .input = ip6_pkt_discard, 2091da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 2101da177e4SLinus Torvalds }, 2111da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2124f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 2131da177e4SLinus Torvalds .rt6i_metric = ~(u32) 0, 2141da177e4SLinus Torvalds .rt6i_ref = ATOMIC_INIT(1), 2151da177e4SLinus Torvalds }; 2161da177e4SLinus Torvalds 217101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 218101367c2SThomas Graf 219fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = { 220101367c2SThomas Graf .dst = { 221101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 222101367c2SThomas Graf .__use = 1, 2232c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 224101367c2SThomas Graf .error = -EACCES, 2259ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 2269ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 227101367c2SThomas Graf }, 228101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2294f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 230101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 231101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 232101367c2SThomas Graf }; 233101367c2SThomas Graf 234fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = { 235101367c2SThomas Graf .dst = { 236101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 237101367c2SThomas Graf .__use = 1, 2382c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 239101367c2SThomas Graf .error = -EINVAL, 240352e512cSHerbert Xu .input = dst_discard, 241aad88724SEric Dumazet .output = dst_discard_sk, 242101367c2SThomas Graf }, 243101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2444f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 245101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 246101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 247101367c2SThomas Graf }; 248101367c2SThomas Graf 249101367c2SThomas Graf #endif 250101367c2SThomas Graf 2511da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 25297bab73fSDavid S. Miller static inline struct rt6_info *ip6_dst_alloc(struct net *net, 253957c665fSDavid S. Miller struct net_device *dev, 2548b96d22dSDavid S. Miller int flags, 2558b96d22dSDavid S. Miller struct fib6_table *table) 2561da177e4SLinus Torvalds { 25797bab73fSDavid S. Miller struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 2586f3118b5SNicolas Dichtel 0, DST_OBSOLETE_FORCE_CHK, flags); 259cf911662SDavid S. Miller 26097bab73fSDavid S. Miller if (rt) { 2618104891bSSteffen Klassert struct dst_entry *dst = &rt->dst; 2628104891bSSteffen Klassert 2638104891bSSteffen Klassert memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 26451ebd318SNicolas Dichtel INIT_LIST_HEAD(&rt->rt6i_siblings); 26597bab73fSDavid S. Miller } 266cf911662SDavid S. Miller return rt; 2671da177e4SLinus Torvalds } 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 2701da177e4SLinus Torvalds { 2711da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 2721da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 273ecd98837SYOSHIFUJI Hideaki / 吉藤英明 struct dst_entry *from = dst->from; 2741da177e4SLinus Torvalds 2758e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 2768e2ec639SYan, Zheng 27738308473SDavid S. Miller if (idev) { 2781da177e4SLinus Torvalds rt->rt6i_idev = NULL; 2791da177e4SLinus Torvalds in6_dev_put(idev); 2801da177e4SLinus Torvalds } 2811716a961SGao feng 282ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst->from = NULL; 283ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst_release(from); 284b3419363SDavid S. Miller } 285b3419363SDavid S. Miller 2861da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 2871da177e4SLinus Torvalds int how) 2881da177e4SLinus Torvalds { 2891da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 2901da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 2915a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 292c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 2931da177e4SLinus Torvalds 29497cac082SDavid S. Miller if (dev != loopback_dev) { 29597cac082SDavid S. Miller if (idev && idev->dev == dev) { 2965a3e55d6SDenis V. Lunev struct inet6_dev *loopback_idev = 2975a3e55d6SDenis V. Lunev in6_dev_get(loopback_dev); 29838308473SDavid S. Miller if (loopback_idev) { 2991da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 3001da177e4SLinus Torvalds in6_dev_put(idev); 3011da177e4SLinus Torvalds } 3021da177e4SLinus Torvalds } 30397cac082SDavid S. Miller } 3041da177e4SLinus Torvalds } 3051da177e4SLinus Torvalds 306a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt) 3071da177e4SLinus Torvalds { 3081716a961SGao feng if (rt->rt6i_flags & RTF_EXPIRES) { 3091716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 310a50feda5SEric Dumazet return true; 3111716a961SGao feng } else if (rt->dst.from) { 3123fd91fb3SLi RongQing return rt6_check_expired((struct rt6_info *) rt->dst.from); 3131716a961SGao feng } 314a50feda5SEric Dumazet return false; 3151da177e4SLinus Torvalds } 3161da177e4SLinus Torvalds 31751ebd318SNicolas Dichtel /* Multipath route selection: 31851ebd318SNicolas Dichtel * Hash based function using packet header and flowlabel. 31951ebd318SNicolas Dichtel * Adapted from fib_info_hashfn() 32051ebd318SNicolas Dichtel */ 32151ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count, 32251ebd318SNicolas Dichtel const struct flowi6 *fl6) 32351ebd318SNicolas Dichtel { 32451ebd318SNicolas Dichtel unsigned int val = fl6->flowi6_proto; 32551ebd318SNicolas Dichtel 326c08977bbSYOSHIFUJI Hideaki / 吉藤英明 val ^= ipv6_addr_hash(&fl6->daddr); 327c08977bbSYOSHIFUJI Hideaki / 吉藤英明 val ^= ipv6_addr_hash(&fl6->saddr); 32851ebd318SNicolas Dichtel 32951ebd318SNicolas Dichtel /* Work only if this not encapsulated */ 33051ebd318SNicolas Dichtel switch (fl6->flowi6_proto) { 33151ebd318SNicolas Dichtel case IPPROTO_UDP: 33251ebd318SNicolas Dichtel case IPPROTO_TCP: 33351ebd318SNicolas Dichtel case IPPROTO_SCTP: 334b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_sport; 335b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_dport; 33651ebd318SNicolas Dichtel break; 33751ebd318SNicolas Dichtel 33851ebd318SNicolas Dichtel case IPPROTO_ICMPV6: 339b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_icmp_type; 340b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_icmp_code; 34151ebd318SNicolas Dichtel break; 34251ebd318SNicolas Dichtel } 34351ebd318SNicolas Dichtel /* RFC6438 recommands to use flowlabel */ 344b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->flowlabel; 34551ebd318SNicolas Dichtel 34651ebd318SNicolas Dichtel /* Perhaps, we need to tune, this function? */ 34751ebd318SNicolas Dichtel val = val ^ (val >> 7) ^ (val >> 12); 34851ebd318SNicolas Dichtel return val % candidate_count; 34951ebd318SNicolas Dichtel } 35051ebd318SNicolas Dichtel 35151ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match, 35252bd4c0cSNicolas Dichtel struct flowi6 *fl6, int oif, 35352bd4c0cSNicolas Dichtel int strict) 35451ebd318SNicolas Dichtel { 35551ebd318SNicolas Dichtel struct rt6_info *sibling, *next_sibling; 35651ebd318SNicolas Dichtel int route_choosen; 35751ebd318SNicolas Dichtel 35851ebd318SNicolas Dichtel route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6); 35951ebd318SNicolas Dichtel /* Don't change the route, if route_choosen == 0 36051ebd318SNicolas Dichtel * (siblings does not include ourself) 36151ebd318SNicolas Dichtel */ 36251ebd318SNicolas Dichtel if (route_choosen) 36351ebd318SNicolas Dichtel list_for_each_entry_safe(sibling, next_sibling, 36451ebd318SNicolas Dichtel &match->rt6i_siblings, rt6i_siblings) { 36551ebd318SNicolas Dichtel route_choosen--; 36651ebd318SNicolas Dichtel if (route_choosen == 0) { 36752bd4c0cSNicolas Dichtel if (rt6_score_route(sibling, oif, strict) < 0) 36852bd4c0cSNicolas Dichtel break; 36951ebd318SNicolas Dichtel match = sibling; 37051ebd318SNicolas Dichtel break; 37151ebd318SNicolas Dichtel } 37251ebd318SNicolas Dichtel } 37351ebd318SNicolas Dichtel return match; 37451ebd318SNicolas Dichtel } 37551ebd318SNicolas Dichtel 3761da177e4SLinus Torvalds /* 377c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 3781da177e4SLinus Torvalds */ 3791da177e4SLinus Torvalds 3808ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 3818ed67789SDaniel Lezcano struct rt6_info *rt, 382b71d1d42SEric Dumazet const struct in6_addr *saddr, 3831da177e4SLinus Torvalds int oif, 384d420895eSYOSHIFUJI Hideaki int flags) 3851da177e4SLinus Torvalds { 3861da177e4SLinus Torvalds struct rt6_info *local = NULL; 3871da177e4SLinus Torvalds struct rt6_info *sprt; 3881da177e4SLinus Torvalds 389dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 390dd3abc4eSYOSHIFUJI Hideaki goto out; 391dd3abc4eSYOSHIFUJI Hideaki 392d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 393d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 394dd3abc4eSYOSHIFUJI Hideaki 395dd3abc4eSYOSHIFUJI Hideaki if (oif) { 3961da177e4SLinus Torvalds if (dev->ifindex == oif) 3971da177e4SLinus Torvalds return sprt; 3981da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 39938308473SDavid S. Miller if (!sprt->rt6i_idev || 4001da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 401d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE && oif) 4021da177e4SLinus Torvalds continue; 4031da177e4SLinus Torvalds if (local && (!oif || 4041da177e4SLinus Torvalds local->rt6i_idev->dev->ifindex == oif)) 4051da177e4SLinus Torvalds continue; 4061da177e4SLinus Torvalds } 4071da177e4SLinus Torvalds local = sprt; 4081da177e4SLinus Torvalds } 409dd3abc4eSYOSHIFUJI Hideaki } else { 410dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 411dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 412dd3abc4eSYOSHIFUJI Hideaki return sprt; 413dd3abc4eSYOSHIFUJI Hideaki } 4141da177e4SLinus Torvalds } 4151da177e4SLinus Torvalds 416dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4171da177e4SLinus Torvalds if (local) 4181da177e4SLinus Torvalds return local; 4191da177e4SLinus Torvalds 420d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 4218ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 4221da177e4SLinus Torvalds } 423dd3abc4eSYOSHIFUJI Hideaki out: 4241da177e4SLinus Torvalds return rt; 4251da177e4SLinus Torvalds } 4261da177e4SLinus Torvalds 42727097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 428c2f17e82SHannes Frederic Sowa struct __rt6_probe_work { 429c2f17e82SHannes Frederic Sowa struct work_struct work; 430c2f17e82SHannes Frederic Sowa struct in6_addr target; 431c2f17e82SHannes Frederic Sowa struct net_device *dev; 432c2f17e82SHannes Frederic Sowa }; 433c2f17e82SHannes Frederic Sowa 434c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w) 435c2f17e82SHannes Frederic Sowa { 436c2f17e82SHannes Frederic Sowa struct in6_addr mcaddr; 437c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work = 438c2f17e82SHannes Frederic Sowa container_of(w, struct __rt6_probe_work, work); 439c2f17e82SHannes Frederic Sowa 440c2f17e82SHannes Frederic Sowa addrconf_addr_solict_mult(&work->target, &mcaddr); 441c2f17e82SHannes Frederic Sowa ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL); 442c2f17e82SHannes Frederic Sowa dev_put(work->dev); 443662f5533SMichael Büsch kfree(work); 444c2f17e82SHannes Frederic Sowa } 445c2f17e82SHannes Frederic Sowa 44627097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 44727097255SYOSHIFUJI Hideaki { 448f2c31e32SEric Dumazet struct neighbour *neigh; 44927097255SYOSHIFUJI Hideaki /* 45027097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 45127097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 45227097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 45327097255SYOSHIFUJI Hideaki * 45427097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 45527097255SYOSHIFUJI Hideaki * to no more than one per minute. 45627097255SYOSHIFUJI Hideaki */ 4572152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!rt || !(rt->rt6i_flags & RTF_GATEWAY)) 458fdd6681dSAmerigo Wang return; 4592152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 4602152caeaSYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 4612152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 4622152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_lock(&neigh->lock); 4632152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh->nud_state & NUD_VALID) 4642152caeaSYOSHIFUJI Hideaki / 吉藤英明 goto out; 4657ff74a59SYOSHIFUJI Hideaki / 吉藤英明 } 4662152caeaSYOSHIFUJI Hideaki / 吉藤英明 4672152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!neigh || 46852e16356SYOSHIFUJI Hideaki time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { 469c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work; 47027097255SYOSHIFUJI Hideaki 471c2f17e82SHannes Frederic Sowa work = kmalloc(sizeof(*work), GFP_ATOMIC); 472c2f17e82SHannes Frederic Sowa 473c2f17e82SHannes Frederic Sowa if (neigh && work) 4747e980569SJiri Benc __neigh_set_probe_once(neigh); 4752152caeaSYOSHIFUJI Hideaki / 吉藤英明 476c2f17e82SHannes Frederic Sowa if (neigh) 477c2f17e82SHannes Frederic Sowa write_unlock(&neigh->lock); 478c2f17e82SHannes Frederic Sowa 479c2f17e82SHannes Frederic Sowa if (work) { 480c2f17e82SHannes Frederic Sowa INIT_WORK(&work->work, rt6_probe_deferred); 481c2f17e82SHannes Frederic Sowa work->target = rt->rt6i_gateway; 482c2f17e82SHannes Frederic Sowa dev_hold(rt->dst.dev); 483c2f17e82SHannes Frederic Sowa work->dev = rt->dst.dev; 484c2f17e82SHannes Frederic Sowa schedule_work(&work->work); 485c2f17e82SHannes Frederic Sowa } 486f2c31e32SEric Dumazet } else { 4872152caeaSYOSHIFUJI Hideaki / 吉藤英明 out: 4882152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_unlock(&neigh->lock); 48927097255SYOSHIFUJI Hideaki } 4902152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 491f2c31e32SEric Dumazet } 49227097255SYOSHIFUJI Hideaki #else 49327097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 49427097255SYOSHIFUJI Hideaki { 49527097255SYOSHIFUJI Hideaki } 49627097255SYOSHIFUJI Hideaki #endif 49727097255SYOSHIFUJI Hideaki 4981da177e4SLinus Torvalds /* 499554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 5001da177e4SLinus Torvalds */ 501b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 5021da177e4SLinus Torvalds { 503d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 504161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 505554cfb7eSYOSHIFUJI Hideaki return 2; 506161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 507161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 508161980f4SDavid S. Miller return 1; 509554cfb7eSYOSHIFUJI Hideaki return 0; 5101da177e4SLinus Torvalds } 5111da177e4SLinus Torvalds 512afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt) 5131da177e4SLinus Torvalds { 514f2c31e32SEric Dumazet struct neighbour *neigh; 515afc154e9SHannes Frederic Sowa enum rt6_nud_state ret = RT6_NUD_FAIL_HARD; 516f2c31e32SEric Dumazet 5174d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 5184d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 519afc154e9SHannes Frederic Sowa return RT6_NUD_SUCCEED; 520145a3621SYOSHIFUJI Hideaki / 吉藤英明 521145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 522145a3621SYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 523145a3621SYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 524145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_lock(&neigh->lock); 525554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 526afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 527398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 528a5a81f0bSPaul Marks else if (!(neigh->nud_state & NUD_FAILED)) 529afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 5307e980569SJiri Benc else 5317e980569SJiri Benc ret = RT6_NUD_FAIL_PROBE; 532398bcbebSYOSHIFUJI Hideaki #endif 533145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_unlock(&neigh->lock); 534afc154e9SHannes Frederic Sowa } else { 535afc154e9SHannes Frederic Sowa ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 5367e980569SJiri Benc RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; 537a5a81f0bSPaul Marks } 538145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 539145a3621SYOSHIFUJI Hideaki / 吉藤英明 540a5a81f0bSPaul Marks return ret; 5411da177e4SLinus Torvalds } 5421da177e4SLinus Torvalds 543554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 544554cfb7eSYOSHIFUJI Hideaki int strict) 545554cfb7eSYOSHIFUJI Hideaki { 546a5a81f0bSPaul Marks int m; 5474d0c5911SYOSHIFUJI Hideaki 5484d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 54977d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 550afc154e9SHannes Frederic Sowa return RT6_NUD_FAIL_HARD; 551ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 552ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 553ebacaaa0SYOSHIFUJI Hideaki #endif 554afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) { 555afc154e9SHannes Frederic Sowa int n = rt6_check_neigh(rt); 556afc154e9SHannes Frederic Sowa if (n < 0) 557afc154e9SHannes Frederic Sowa return n; 558afc154e9SHannes Frederic Sowa } 559554cfb7eSYOSHIFUJI Hideaki return m; 560554cfb7eSYOSHIFUJI Hideaki } 561554cfb7eSYOSHIFUJI Hideaki 562f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 563afc154e9SHannes Frederic Sowa int *mpri, struct rt6_info *match, 564afc154e9SHannes Frederic Sowa bool *do_rr) 565554cfb7eSYOSHIFUJI Hideaki { 566554cfb7eSYOSHIFUJI Hideaki int m; 567afc154e9SHannes Frederic Sowa bool match_do_rr = false; 568554cfb7eSYOSHIFUJI Hideaki 569554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 570f11e6659SDavid S. Miller goto out; 571554cfb7eSYOSHIFUJI Hideaki 572554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 5737e980569SJiri Benc if (m == RT6_NUD_FAIL_DO_RR) { 574afc154e9SHannes Frederic Sowa match_do_rr = true; 575afc154e9SHannes Frederic Sowa m = 0; /* lowest valid score */ 5767e980569SJiri Benc } else if (m == RT6_NUD_FAIL_HARD) { 577f11e6659SDavid S. Miller goto out; 5781da177e4SLinus Torvalds } 579f11e6659SDavid S. Miller 580afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) 581afc154e9SHannes Frederic Sowa rt6_probe(rt); 582afc154e9SHannes Frederic Sowa 5837e980569SJiri Benc /* note that m can be RT6_NUD_FAIL_PROBE at this point */ 584afc154e9SHannes Frederic Sowa if (m > *mpri) { 585afc154e9SHannes Frederic Sowa *do_rr = match_do_rr; 586afc154e9SHannes Frederic Sowa *mpri = m; 587afc154e9SHannes Frederic Sowa match = rt; 588afc154e9SHannes Frederic Sowa } 589f11e6659SDavid S. Miller out: 590f11e6659SDavid S. Miller return match; 5911da177e4SLinus Torvalds } 5921da177e4SLinus Torvalds 593f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 594f11e6659SDavid S. Miller struct rt6_info *rr_head, 595afc154e9SHannes Frederic Sowa u32 metric, int oif, int strict, 596afc154e9SHannes Frederic Sowa bool *do_rr) 597f11e6659SDavid S. Miller { 5989fbdcfafSSteffen Klassert struct rt6_info *rt, *match, *cont; 599f11e6659SDavid S. Miller int mpri = -1; 600f11e6659SDavid S. Miller 601f11e6659SDavid S. Miller match = NULL; 6029fbdcfafSSteffen Klassert cont = NULL; 6039fbdcfafSSteffen Klassert for (rt = rr_head; rt; rt = rt->dst.rt6_next) { 6049fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 6059fbdcfafSSteffen Klassert cont = rt; 6069fbdcfafSSteffen Klassert break; 6079fbdcfafSSteffen Klassert } 6089fbdcfafSSteffen Klassert 609afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 6109fbdcfafSSteffen Klassert } 6119fbdcfafSSteffen Klassert 6129fbdcfafSSteffen Klassert for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) { 6139fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 6149fbdcfafSSteffen Klassert cont = rt; 6159fbdcfafSSteffen Klassert break; 6169fbdcfafSSteffen Klassert } 6179fbdcfafSSteffen Klassert 6189fbdcfafSSteffen Klassert match = find_match(rt, oif, strict, &mpri, match, do_rr); 6199fbdcfafSSteffen Klassert } 6209fbdcfafSSteffen Klassert 6219fbdcfafSSteffen Klassert if (match || !cont) 6229fbdcfafSSteffen Klassert return match; 6239fbdcfafSSteffen Klassert 6249fbdcfafSSteffen Klassert for (rt = cont; rt; rt = rt->dst.rt6_next) 625afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 626f11e6659SDavid S. Miller 627f11e6659SDavid S. Miller return match; 628f11e6659SDavid S. Miller } 629f11e6659SDavid S. Miller 630f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 631f11e6659SDavid S. Miller { 632f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 6338ed67789SDaniel Lezcano struct net *net; 634afc154e9SHannes Frederic Sowa bool do_rr = false; 635f11e6659SDavid S. Miller 636f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 637f11e6659SDavid S. Miller if (!rt0) 638f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 639f11e6659SDavid S. Miller 640afc154e9SHannes Frederic Sowa match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict, 641afc154e9SHannes Frederic Sowa &do_rr); 642f11e6659SDavid S. Miller 643afc154e9SHannes Frederic Sowa if (do_rr) { 644d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 645f11e6659SDavid S. Miller 646554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 647f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 648f11e6659SDavid S. Miller next = fn->leaf; 649f11e6659SDavid S. Miller 650f11e6659SDavid S. Miller if (next != rt0) 651f11e6659SDavid S. Miller fn->rr_ptr = next; 652554cfb7eSYOSHIFUJI Hideaki } 653554cfb7eSYOSHIFUJI Hideaki 654d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 655a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 6561da177e4SLinus Torvalds } 6571da177e4SLinus Torvalds 6588b9df265SMartin KaFai Lau static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt) 6598b9df265SMartin KaFai Lau { 6608b9df265SMartin KaFai Lau return (rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY)); 6618b9df265SMartin KaFai Lau } 6628b9df265SMartin KaFai Lau 66370ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 66470ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 665b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 66670ceb4f5SYOSHIFUJI Hideaki { 667c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 66870ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 66970ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 67070ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 6714bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 67270ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 67370ceb4f5SYOSHIFUJI Hideaki 67470ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 67570ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 67670ceb4f5SYOSHIFUJI Hideaki } 67770ceb4f5SYOSHIFUJI Hideaki 67870ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 67970ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 68070ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 68170ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 68270ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 68370ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 68470ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 68570ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 68670ceb4f5SYOSHIFUJI Hideaki } 68770ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 68870ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 68970ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 69070ceb4f5SYOSHIFUJI Hideaki } 69170ceb4f5SYOSHIFUJI Hideaki } 69270ceb4f5SYOSHIFUJI Hideaki 69370ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 69470ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 6953933fc95SJens Rosenboom return -EINVAL; 69670ceb4f5SYOSHIFUJI Hideaki 6974bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 69870ceb4f5SYOSHIFUJI Hideaki 69970ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 70070ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 70170ceb4f5SYOSHIFUJI Hideaki else { 70270ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 70370ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 70470ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 70570ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 70670ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 70770ceb4f5SYOSHIFUJI Hideaki } 70870ceb4f5SYOSHIFUJI Hideaki 709f104a567SDuan Jiong if (rinfo->prefix_len == 0) 710f104a567SDuan Jiong rt = rt6_get_dflt_router(gwaddr, dev); 711f104a567SDuan Jiong else 712f104a567SDuan Jiong rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, 713f104a567SDuan Jiong gwaddr, dev->ifindex); 71470ceb4f5SYOSHIFUJI Hideaki 71570ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 716e0a1ad73SThomas Graf ip6_del_rt(rt); 71770ceb4f5SYOSHIFUJI Hideaki rt = NULL; 71870ceb4f5SYOSHIFUJI Hideaki } 71970ceb4f5SYOSHIFUJI Hideaki 72070ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 721efa2cea0SDaniel Lezcano rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 72270ceb4f5SYOSHIFUJI Hideaki pref); 72370ceb4f5SYOSHIFUJI Hideaki else if (rt) 72470ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 72570ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 72670ceb4f5SYOSHIFUJI Hideaki 72770ceb4f5SYOSHIFUJI Hideaki if (rt) { 7281716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 7291716a961SGao feng rt6_clean_expires(rt); 7301716a961SGao feng else 7311716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 7321716a961SGao feng 73394e187c0SAmerigo Wang ip6_rt_put(rt); 73470ceb4f5SYOSHIFUJI Hideaki } 73570ceb4f5SYOSHIFUJI Hideaki return 0; 73670ceb4f5SYOSHIFUJI Hideaki } 73770ceb4f5SYOSHIFUJI Hideaki #endif 73870ceb4f5SYOSHIFUJI Hideaki 739a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn, 740a3c00e46SMartin KaFai Lau struct in6_addr *saddr) 741a3c00e46SMartin KaFai Lau { 742a3c00e46SMartin KaFai Lau struct fib6_node *pn; 743a3c00e46SMartin KaFai Lau while (1) { 744a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_TL_ROOT) 745a3c00e46SMartin KaFai Lau return NULL; 746a3c00e46SMartin KaFai Lau pn = fn->parent; 747a3c00e46SMartin KaFai Lau if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) 748a3c00e46SMartin KaFai Lau fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); 749a3c00e46SMartin KaFai Lau else 750a3c00e46SMartin KaFai Lau fn = pn; 751a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_RTINFO) 752a3c00e46SMartin KaFai Lau return fn; 753a3c00e46SMartin KaFai Lau } 754a3c00e46SMartin KaFai Lau } 755c71099acSThomas Graf 7568ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 7578ed67789SDaniel Lezcano struct fib6_table *table, 7584c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 7591da177e4SLinus Torvalds { 7601da177e4SLinus Torvalds struct fib6_node *fn; 7611da177e4SLinus Torvalds struct rt6_info *rt; 7621da177e4SLinus Torvalds 763c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 7644c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 765c71099acSThomas Graf restart: 766c71099acSThomas Graf rt = fn->leaf; 7674c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 76851ebd318SNicolas Dichtel if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) 76952bd4c0cSNicolas Dichtel rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags); 770a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 771a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 772a3c00e46SMartin KaFai Lau if (fn) 773a3c00e46SMartin KaFai Lau goto restart; 774a3c00e46SMartin KaFai Lau } 775d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 776c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 7771da177e4SLinus Torvalds return rt; 778c71099acSThomas Graf 779c71099acSThomas Graf } 780c71099acSThomas Graf 781ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, 782ea6e574eSFlorian Westphal int flags) 783ea6e574eSFlorian Westphal { 784ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 785ea6e574eSFlorian Westphal } 786ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 787ea6e574eSFlorian Westphal 7889acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 7899acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 790c71099acSThomas Graf { 7914c9483b2SDavid S. Miller struct flowi6 fl6 = { 7924c9483b2SDavid S. Miller .flowi6_oif = oif, 7934c9483b2SDavid S. Miller .daddr = *daddr, 794c71099acSThomas Graf }; 795c71099acSThomas Graf struct dst_entry *dst; 79677d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 797c71099acSThomas Graf 798adaa70bbSThomas Graf if (saddr) { 7994c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 800adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 801adaa70bbSThomas Graf } 802adaa70bbSThomas Graf 8034c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 804c71099acSThomas Graf if (dst->error == 0) 805c71099acSThomas Graf return (struct rt6_info *) dst; 806c71099acSThomas Graf 807c71099acSThomas Graf dst_release(dst); 808c71099acSThomas Graf 8091da177e4SLinus Torvalds return NULL; 8101da177e4SLinus Torvalds } 8117159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 8127159039aSYOSHIFUJI Hideaki 813c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 8141da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 8151da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 8161da177e4SLinus Torvalds be destroyed. 8171da177e4SLinus Torvalds */ 8181da177e4SLinus Torvalds 819e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, 820e715b6d3SFlorian Westphal struct mx6_config *mxc) 8211da177e4SLinus Torvalds { 8221da177e4SLinus Torvalds int err; 823c71099acSThomas Graf struct fib6_table *table; 8241da177e4SLinus Torvalds 825c71099acSThomas Graf table = rt->rt6i_table; 826c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 827e715b6d3SFlorian Westphal err = fib6_add(&table->tb6_root, rt, info, mxc); 828c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 8291da177e4SLinus Torvalds 8301da177e4SLinus Torvalds return err; 8311da177e4SLinus Torvalds } 8321da177e4SLinus Torvalds 83340e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 83440e22e8fSThomas Graf { 835e715b6d3SFlorian Westphal struct nl_info info = { .nl_net = dev_net(rt->dst.dev), }; 836e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 837e715b6d3SFlorian Westphal 838e715b6d3SFlorian Westphal return __ip6_ins_rt(rt, &info, &mxc); 83940e22e8fSThomas Graf } 84040e22e8fSThomas Graf 8418b9df265SMartin KaFai Lau static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, 84221efcfa0SEric Dumazet const struct in6_addr *daddr, 843b71d1d42SEric Dumazet const struct in6_addr *saddr) 8441da177e4SLinus Torvalds { 8451da177e4SLinus Torvalds struct rt6_info *rt; 8461da177e4SLinus Torvalds 8471da177e4SLinus Torvalds /* 8481da177e4SLinus Torvalds * Clone the route. 8491da177e4SLinus Torvalds */ 8501da177e4SLinus Torvalds 85121efcfa0SEric Dumazet rt = ip6_rt_copy(ort, daddr); 8521da177e4SLinus Torvalds 8531da177e4SLinus Torvalds if (rt) { 8548b9df265SMartin KaFai Lau rt->rt6i_flags |= RTF_CACHE; 8558b9df265SMartin KaFai Lau 8568b9df265SMartin KaFai Lau if (!rt6_is_gw_or_nonexthop(ort)) { 857bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 85821efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 85958c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 8601da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 8611da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 8624e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 8631da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 8641da177e4SLinus Torvalds } 8651da177e4SLinus Torvalds #endif 86695a9a5baSYOSHIFUJI Hideaki } 8671da177e4SLinus Torvalds } 86895a9a5baSYOSHIFUJI Hideaki 869299d9939SYOSHIFUJI Hideaki return rt; 870299d9939SYOSHIFUJI Hideaki } 871299d9939SYOSHIFUJI Hideaki 8728ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, 8734c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 8741da177e4SLinus Torvalds { 875367efcb9SMartin KaFai Lau struct fib6_node *fn, *saved_fn; 87645e4fd26SMartin KaFai Lau struct rt6_info *rt; 877c71099acSThomas Graf int strict = 0; 8781da177e4SLinus Torvalds 87977d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 880367efcb9SMartin KaFai Lau if (net->ipv6.devconf_all->forwarding == 0) 881367efcb9SMartin KaFai Lau strict |= RT6_LOOKUP_F_REACHABLE; 8821da177e4SLinus Torvalds 883c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 8841da177e4SLinus Torvalds 8854c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 886367efcb9SMartin KaFai Lau saved_fn = fn; 8871da177e4SLinus Torvalds 888a3c00e46SMartin KaFai Lau redo_rt6_select: 889367efcb9SMartin KaFai Lau rt = rt6_select(fn, oif, strict); 89052bd4c0cSNicolas Dichtel if (rt->rt6i_nsiblings) 891367efcb9SMartin KaFai Lau rt = rt6_multipath_select(rt, fl6, oif, strict); 892a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 893a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 894a3c00e46SMartin KaFai Lau if (fn) 895a3c00e46SMartin KaFai Lau goto redo_rt6_select; 896367efcb9SMartin KaFai Lau else if (strict & RT6_LOOKUP_F_REACHABLE) { 897367efcb9SMartin KaFai Lau /* also consider unreachable route */ 898367efcb9SMartin KaFai Lau strict &= ~RT6_LOOKUP_F_REACHABLE; 899367efcb9SMartin KaFai Lau fn = saved_fn; 900367efcb9SMartin KaFai Lau goto redo_rt6_select; 901367efcb9SMartin KaFai Lau } 902a3c00e46SMartin KaFai Lau } 903a3c00e46SMartin KaFai Lau 904*3da59bd9SMartin KaFai Lau dst_use(&rt->dst, jiffies); 905c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 9061da177e4SLinus Torvalds 907*3da59bd9SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) { 908*3da59bd9SMartin KaFai Lau goto done; 909*3da59bd9SMartin KaFai Lau } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && 910*3da59bd9SMartin KaFai Lau !(rt->rt6i_flags & RTF_GATEWAY))) { 911*3da59bd9SMartin KaFai Lau /* Create a RTF_CACHE clone which will not be 912*3da59bd9SMartin KaFai Lau * owned by the fib6 tree. It is for the special case where 913*3da59bd9SMartin KaFai Lau * the daddr in the skb during the neighbor look-up is different 914*3da59bd9SMartin KaFai Lau * from the fl6->daddr used to look-up route here. 915*3da59bd9SMartin KaFai Lau */ 916c71099acSThomas Graf 917*3da59bd9SMartin KaFai Lau struct rt6_info *uncached_rt; 918*3da59bd9SMartin KaFai Lau 919*3da59bd9SMartin KaFai Lau uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL); 920*3da59bd9SMartin KaFai Lau dst_release(&rt->dst); 921*3da59bd9SMartin KaFai Lau 922*3da59bd9SMartin KaFai Lau if (uncached_rt) 923*3da59bd9SMartin KaFai Lau uncached_rt->dst.flags |= DST_NOCACHE; 924*3da59bd9SMartin KaFai Lau else 925*3da59bd9SMartin KaFai Lau uncached_rt = net->ipv6.ip6_null_entry; 926*3da59bd9SMartin KaFai Lau dst_hold(&uncached_rt->dst); 927*3da59bd9SMartin KaFai Lau return uncached_rt; 928*3da59bd9SMartin KaFai Lau } 929*3da59bd9SMartin KaFai Lau 930*3da59bd9SMartin KaFai Lau done: 931*3da59bd9SMartin KaFai Lau rt6_dst_from_metrics_check(rt); 932c71099acSThomas Graf return rt; 933c71099acSThomas Graf } 934c71099acSThomas Graf 9358ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 9364c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 9374acad72dSPavel Emelyanov { 9384c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 9394acad72dSPavel Emelyanov } 9404acad72dSPavel Emelyanov 94172331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 94272331bc0SShmulik Ladkani struct net_device *dev, 94372331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 94472331bc0SShmulik Ladkani { 94572331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 94672331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 94772331bc0SShmulik Ladkani 94872331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 94972331bc0SShmulik Ladkani } 95072331bc0SShmulik Ladkani 951c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 952c71099acSThomas Graf { 953b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 954c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 955adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 9564c9483b2SDavid S. Miller struct flowi6 fl6 = { 9574c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 9584c9483b2SDavid S. Miller .daddr = iph->daddr, 9594c9483b2SDavid S. Miller .saddr = iph->saddr, 9606502ca52SYOSHIFUJI Hideaki / 吉藤英明 .flowlabel = ip6_flowinfo(iph), 9614c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 9624c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 963c71099acSThomas Graf }; 964adaa70bbSThomas Graf 96572331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 966c71099acSThomas Graf } 967c71099acSThomas Graf 9688ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 9694c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 970c71099acSThomas Graf { 9714c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 972c71099acSThomas Graf } 973c71099acSThomas Graf 9749c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk, 9754c9483b2SDavid S. Miller struct flowi6 *fl6) 976c71099acSThomas Graf { 977c71099acSThomas Graf int flags = 0; 978c71099acSThomas Graf 9791fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 9804dc27d1cSDavid McCullough 9814c9483b2SDavid S. Miller if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) 98277d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 983c71099acSThomas Graf 9844c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 985adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 9860c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 9870c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 988adaa70bbSThomas Graf 9894c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 9901da177e4SLinus Torvalds } 9917159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 9921da177e4SLinus Torvalds 9932774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 99414e50e57SDavid S. Miller { 9955c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 99614e50e57SDavid S. Miller struct dst_entry *new = NULL; 99714e50e57SDavid S. Miller 998f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 99914e50e57SDavid S. Miller if (rt) { 1000d8d1f30bSChangli Gao new = &rt->dst; 100114e50e57SDavid S. Miller 10028104891bSSteffen Klassert memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); 10038104891bSSteffen Klassert 100414e50e57SDavid S. Miller new->__use = 1; 1005352e512cSHerbert Xu new->input = dst_discard; 1006aad88724SEric Dumazet new->output = dst_discard_sk; 100714e50e57SDavid S. Miller 100821efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 100921efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 101021efcfa0SEric Dumazet else 1011defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 101214e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 101314e50e57SDavid S. Miller if (rt->rt6i_idev) 101414e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 101514e50e57SDavid S. Miller 10164e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 10171716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 101814e50e57SDavid S. Miller rt->rt6i_metric = 0; 101914e50e57SDavid S. Miller 102014e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 102114e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 102214e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 102314e50e57SDavid S. Miller #endif 102414e50e57SDavid S. Miller 102514e50e57SDavid S. Miller dst_free(new); 102614e50e57SDavid S. Miller } 102714e50e57SDavid S. Miller 102869ead7afSDavid S. Miller dst_release(dst_orig); 102969ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 103014e50e57SDavid S. Miller } 103114e50e57SDavid S. Miller 10321da177e4SLinus Torvalds /* 10331da177e4SLinus Torvalds * Destination cache support functions 10341da177e4SLinus Torvalds */ 10351da177e4SLinus Torvalds 10364b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt) 10374b32b5adSMartin KaFai Lau { 10384b32b5adSMartin KaFai Lau if (rt->dst.from && 10394b32b5adSMartin KaFai Lau dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from)) 10404b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true); 10414b32b5adSMartin KaFai Lau } 10424b32b5adSMartin KaFai Lau 1043*3da59bd9SMartin KaFai Lau static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie) 1044*3da59bd9SMartin KaFai Lau { 1045*3da59bd9SMartin KaFai Lau if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) 1046*3da59bd9SMartin KaFai Lau return NULL; 1047*3da59bd9SMartin KaFai Lau 1048*3da59bd9SMartin KaFai Lau if (rt6_check_expired(rt)) 1049*3da59bd9SMartin KaFai Lau return NULL; 1050*3da59bd9SMartin KaFai Lau 1051*3da59bd9SMartin KaFai Lau return &rt->dst; 1052*3da59bd9SMartin KaFai Lau } 1053*3da59bd9SMartin KaFai Lau 1054*3da59bd9SMartin KaFai Lau static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie) 1055*3da59bd9SMartin KaFai Lau { 1056*3da59bd9SMartin KaFai Lau if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && 1057*3da59bd9SMartin KaFai Lau rt6_check((struct rt6_info *)(rt->dst.from), cookie)) 1058*3da59bd9SMartin KaFai Lau return &rt->dst; 1059*3da59bd9SMartin KaFai Lau else 1060*3da59bd9SMartin KaFai Lau return NULL; 1061*3da59bd9SMartin KaFai Lau } 1062*3da59bd9SMartin KaFai Lau 10631da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 10641da177e4SLinus Torvalds { 10651da177e4SLinus Torvalds struct rt6_info *rt; 10661da177e4SLinus Torvalds 10671da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 10681da177e4SLinus Torvalds 10696f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 10706f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 10716f3118b5SNicolas Dichtel * into this function always. 10726f3118b5SNicolas Dichtel */ 1073e3bc10bdSHannes Frederic Sowa 10744b32b5adSMartin KaFai Lau rt6_dst_from_metrics_check(rt); 10754b32b5adSMartin KaFai Lau 1076*3da59bd9SMartin KaFai Lau if (unlikely(dst->flags & DST_NOCACHE)) 1077*3da59bd9SMartin KaFai Lau return rt6_dst_from_check(rt, cookie); 1078*3da59bd9SMartin KaFai Lau else 1079*3da59bd9SMartin KaFai Lau return rt6_check(rt, cookie); 10801da177e4SLinus Torvalds } 10811da177e4SLinus Torvalds 10821da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 10831da177e4SLinus Torvalds { 10841da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 10851da177e4SLinus Torvalds 10861da177e4SLinus Torvalds if (rt) { 108754c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 108854c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1089e0a1ad73SThomas Graf ip6_del_rt(rt); 109054c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 10911da177e4SLinus Torvalds } 109254c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 109354c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 109454c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 109554c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 109654c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 109754c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 10981da177e4SLinus Torvalds } 10991da177e4SLinus Torvalds 11001da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 11011da177e4SLinus Torvalds { 11021da177e4SLinus Torvalds struct rt6_info *rt; 11031da177e4SLinus Torvalds 11043ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 11051da177e4SLinus Torvalds 1106adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 11071da177e4SLinus Torvalds if (rt) { 11081eb4f758SHannes Frederic Sowa if (rt->rt6i_flags & RTF_CACHE) { 11091eb4f758SHannes Frederic Sowa dst_hold(&rt->dst); 11101eb4f758SHannes Frederic Sowa if (ip6_del_rt(rt)) 11111eb4f758SHannes Frederic Sowa dst_free(&rt->dst); 11121eb4f758SHannes Frederic Sowa } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) { 11131da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 11141da177e4SLinus Torvalds } 11151da177e4SLinus Torvalds } 11161eb4f758SHannes Frederic Sowa } 11171da177e4SLinus Torvalds 111845e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) 111945e4fd26SMartin KaFai Lau { 112045e4fd26SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 112145e4fd26SMartin KaFai Lau 112245e4fd26SMartin KaFai Lau rt->rt6i_flags |= RTF_MODIFIED; 112345e4fd26SMartin KaFai Lau rt->rt6i_pmtu = mtu; 112445e4fd26SMartin KaFai Lau rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); 112545e4fd26SMartin KaFai Lau } 112645e4fd26SMartin KaFai Lau 112745e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, 112845e4fd26SMartin KaFai Lau const struct ipv6hdr *iph, u32 mtu) 11291da177e4SLinus Torvalds { 11301da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info *)dst; 11311da177e4SLinus Torvalds 113245e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_LOCAL) 113345e4fd26SMartin KaFai Lau return; 113445e4fd26SMartin KaFai Lau 113581aded24SDavid S. Miller dst_confirm(dst); 113645e4fd26SMartin KaFai Lau mtu = max_t(u32, mtu, IPV6_MIN_MTU); 113745e4fd26SMartin KaFai Lau if (mtu >= dst_mtu(dst)) 113845e4fd26SMartin KaFai Lau return; 113981aded24SDavid S. Miller 114045e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_CACHE) { 114145e4fd26SMartin KaFai Lau rt6_do_update_pmtu(rt6, mtu); 114245e4fd26SMartin KaFai Lau } else { 114345e4fd26SMartin KaFai Lau const struct in6_addr *daddr, *saddr; 114445e4fd26SMartin KaFai Lau struct rt6_info *nrt6; 11459d289715SHagen Paul Pfeifer 114645e4fd26SMartin KaFai Lau if (iph) { 114745e4fd26SMartin KaFai Lau daddr = &iph->daddr; 114845e4fd26SMartin KaFai Lau saddr = &iph->saddr; 114945e4fd26SMartin KaFai Lau } else if (sk) { 115045e4fd26SMartin KaFai Lau daddr = &sk->sk_v6_daddr; 115145e4fd26SMartin KaFai Lau saddr = &inet6_sk(sk)->saddr; 115245e4fd26SMartin KaFai Lau } else { 115345e4fd26SMartin KaFai Lau return; 11541da177e4SLinus Torvalds } 115545e4fd26SMartin KaFai Lau nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr); 115645e4fd26SMartin KaFai Lau if (nrt6) { 115745e4fd26SMartin KaFai Lau rt6_do_update_pmtu(nrt6, mtu); 115845e4fd26SMartin KaFai Lau 115945e4fd26SMartin KaFai Lau /* ip6_ins_rt(nrt6) will bump the 116045e4fd26SMartin KaFai Lau * rt6->rt6i_node->fn_sernum 116145e4fd26SMartin KaFai Lau * which will fail the next rt6_check() and 116245e4fd26SMartin KaFai Lau * invalidate the sk->sk_dst_cache. 116345e4fd26SMartin KaFai Lau */ 116445e4fd26SMartin KaFai Lau ip6_ins_rt(nrt6); 116545e4fd26SMartin KaFai Lau } 116645e4fd26SMartin KaFai Lau } 116745e4fd26SMartin KaFai Lau } 116845e4fd26SMartin KaFai Lau 116945e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 117045e4fd26SMartin KaFai Lau struct sk_buff *skb, u32 mtu) 117145e4fd26SMartin KaFai Lau { 117245e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu); 11731da177e4SLinus Torvalds } 11741da177e4SLinus Torvalds 117542ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 117642ae66c8SDavid S. Miller int oif, u32 mark) 117781aded24SDavid S. Miller { 117881aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 117981aded24SDavid S. Miller struct dst_entry *dst; 118081aded24SDavid S. Miller struct flowi6 fl6; 118181aded24SDavid S. Miller 118281aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 118381aded24SDavid S. Miller fl6.flowi6_oif = oif; 11841b3c61dcSLorenzo Colitti fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark); 118581aded24SDavid S. Miller fl6.daddr = iph->daddr; 118681aded24SDavid S. Miller fl6.saddr = iph->saddr; 11876502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 118881aded24SDavid S. Miller 118981aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 119081aded24SDavid S. Miller if (!dst->error) 119145e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu)); 119281aded24SDavid S. Miller dst_release(dst); 119381aded24SDavid S. Miller } 119481aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 119581aded24SDavid S. Miller 119681aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 119781aded24SDavid S. Miller { 119881aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 119981aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 120081aded24SDavid S. Miller } 120181aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 120281aded24SDavid S. Miller 1203b55b76b2SDuan Jiong /* Handle redirects */ 1204b55b76b2SDuan Jiong struct ip6rd_flowi { 1205b55b76b2SDuan Jiong struct flowi6 fl6; 1206b55b76b2SDuan Jiong struct in6_addr gateway; 1207b55b76b2SDuan Jiong }; 1208b55b76b2SDuan Jiong 1209b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net, 1210b55b76b2SDuan Jiong struct fib6_table *table, 1211b55b76b2SDuan Jiong struct flowi6 *fl6, 1212b55b76b2SDuan Jiong int flags) 1213b55b76b2SDuan Jiong { 1214b55b76b2SDuan Jiong struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 1215b55b76b2SDuan Jiong struct rt6_info *rt; 1216b55b76b2SDuan Jiong struct fib6_node *fn; 1217b55b76b2SDuan Jiong 1218b55b76b2SDuan Jiong /* Get the "current" route for this destination and 1219b55b76b2SDuan Jiong * check if the redirect has come from approriate router. 1220b55b76b2SDuan Jiong * 1221b55b76b2SDuan Jiong * RFC 4861 specifies that redirects should only be 1222b55b76b2SDuan Jiong * accepted if they come from the nexthop to the target. 1223b55b76b2SDuan Jiong * Due to the way the routes are chosen, this notion 1224b55b76b2SDuan Jiong * is a bit fuzzy and one might need to check all possible 1225b55b76b2SDuan Jiong * routes. 1226b55b76b2SDuan Jiong */ 1227b55b76b2SDuan Jiong 1228b55b76b2SDuan Jiong read_lock_bh(&table->tb6_lock); 1229b55b76b2SDuan Jiong fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1230b55b76b2SDuan Jiong restart: 1231b55b76b2SDuan Jiong for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1232b55b76b2SDuan Jiong if (rt6_check_expired(rt)) 1233b55b76b2SDuan Jiong continue; 1234b55b76b2SDuan Jiong if (rt->dst.error) 1235b55b76b2SDuan Jiong break; 1236b55b76b2SDuan Jiong if (!(rt->rt6i_flags & RTF_GATEWAY)) 1237b55b76b2SDuan Jiong continue; 1238b55b76b2SDuan Jiong if (fl6->flowi6_oif != rt->dst.dev->ifindex) 1239b55b76b2SDuan Jiong continue; 1240b55b76b2SDuan Jiong if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) 1241b55b76b2SDuan Jiong continue; 1242b55b76b2SDuan Jiong break; 1243b55b76b2SDuan Jiong } 1244b55b76b2SDuan Jiong 1245b55b76b2SDuan Jiong if (!rt) 1246b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1247b55b76b2SDuan Jiong else if (rt->dst.error) { 1248b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1249b0a1ba59SMartin KaFai Lau goto out; 1250b0a1ba59SMartin KaFai Lau } 1251b0a1ba59SMartin KaFai Lau 1252b0a1ba59SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1253a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1254a3c00e46SMartin KaFai Lau if (fn) 1255a3c00e46SMartin KaFai Lau goto restart; 1256b55b76b2SDuan Jiong } 1257a3c00e46SMartin KaFai Lau 1258b0a1ba59SMartin KaFai Lau out: 1259b55b76b2SDuan Jiong dst_hold(&rt->dst); 1260b55b76b2SDuan Jiong 1261b55b76b2SDuan Jiong read_unlock_bh(&table->tb6_lock); 1262b55b76b2SDuan Jiong 1263b55b76b2SDuan Jiong return rt; 1264b55b76b2SDuan Jiong }; 1265b55b76b2SDuan Jiong 1266b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net, 1267b55b76b2SDuan Jiong const struct flowi6 *fl6, 1268b55b76b2SDuan Jiong const struct in6_addr *gateway) 1269b55b76b2SDuan Jiong { 1270b55b76b2SDuan Jiong int flags = RT6_LOOKUP_F_HAS_SADDR; 1271b55b76b2SDuan Jiong struct ip6rd_flowi rdfl; 1272b55b76b2SDuan Jiong 1273b55b76b2SDuan Jiong rdfl.fl6 = *fl6; 1274b55b76b2SDuan Jiong rdfl.gateway = *gateway; 1275b55b76b2SDuan Jiong 1276b55b76b2SDuan Jiong return fib6_rule_lookup(net, &rdfl.fl6, 1277b55b76b2SDuan Jiong flags, __ip6_route_redirect); 1278b55b76b2SDuan Jiong } 1279b55b76b2SDuan Jiong 12803a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 12813a5ad2eeSDavid S. Miller { 12823a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 12833a5ad2eeSDavid S. Miller struct dst_entry *dst; 12843a5ad2eeSDavid S. Miller struct flowi6 fl6; 12853a5ad2eeSDavid S. Miller 12863a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 1287e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 12883a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 12893a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 12903a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 12913a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 12926502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 12933a5ad2eeSDavid S. Miller 1294b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); 12956700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 12963a5ad2eeSDavid S. Miller dst_release(dst); 12973a5ad2eeSDavid S. Miller } 12983a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 12993a5ad2eeSDavid S. Miller 1300c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, 1301c92a59ecSDuan Jiong u32 mark) 1302c92a59ecSDuan Jiong { 1303c92a59ecSDuan Jiong const struct ipv6hdr *iph = ipv6_hdr(skb); 1304c92a59ecSDuan Jiong const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 1305c92a59ecSDuan Jiong struct dst_entry *dst; 1306c92a59ecSDuan Jiong struct flowi6 fl6; 1307c92a59ecSDuan Jiong 1308c92a59ecSDuan Jiong memset(&fl6, 0, sizeof(fl6)); 1309e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 1310c92a59ecSDuan Jiong fl6.flowi6_oif = oif; 1311c92a59ecSDuan Jiong fl6.flowi6_mark = mark; 1312c92a59ecSDuan Jiong fl6.daddr = msg->dest; 1313c92a59ecSDuan Jiong fl6.saddr = iph->daddr; 1314c92a59ecSDuan Jiong 1315b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &iph->saddr); 1316c92a59ecSDuan Jiong rt6_do_redirect(dst, NULL, skb); 1317c92a59ecSDuan Jiong dst_release(dst); 1318c92a59ecSDuan Jiong } 1319c92a59ecSDuan Jiong 13203a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 13213a5ad2eeSDavid S. Miller { 13223a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 13233a5ad2eeSDavid S. Miller } 13243a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 13253a5ad2eeSDavid S. Miller 13260dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 13271da177e4SLinus Torvalds { 13280dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 13290dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 13300dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 13310dbaee3bSDavid S. Miller 13321da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 13331da177e4SLinus Torvalds 13345578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 13355578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 13361da177e4SLinus Torvalds 13371da177e4SLinus Torvalds /* 13381da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 13391da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 13401da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 13411da177e4SLinus Torvalds * rely only on pmtu discovery" 13421da177e4SLinus Torvalds */ 13431da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 13441da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 13451da177e4SLinus Torvalds return mtu; 13461da177e4SLinus Torvalds } 13471da177e4SLinus Torvalds 1348ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1349d33e4553SDavid S. Miller { 13504b32b5adSMartin KaFai Lau const struct rt6_info *rt = (const struct rt6_info *)dst; 13514b32b5adSMartin KaFai Lau unsigned int mtu = rt->rt6i_pmtu; 1352d33e4553SDavid S. Miller struct inet6_dev *idev; 1353618f9bc7SSteffen Klassert 1354618f9bc7SSteffen Klassert if (mtu) 135530f78d8eSEric Dumazet goto out; 1356618f9bc7SSteffen Klassert 13574b32b5adSMartin KaFai Lau mtu = dst_metric_raw(dst, RTAX_MTU); 13584b32b5adSMartin KaFai Lau if (mtu) 13594b32b5adSMartin KaFai Lau goto out; 13604b32b5adSMartin KaFai Lau 1361618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1362d33e4553SDavid S. Miller 1363d33e4553SDavid S. Miller rcu_read_lock(); 1364d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1365d33e4553SDavid S. Miller if (idev) 1366d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1367d33e4553SDavid S. Miller rcu_read_unlock(); 1368d33e4553SDavid S. Miller 136930f78d8eSEric Dumazet out: 137030f78d8eSEric Dumazet return min_t(unsigned int, mtu, IP6_MAX_MTU); 1371d33e4553SDavid S. Miller } 1372d33e4553SDavid S. Miller 13733b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 13743b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 13755d0bbeebSThomas Graf 13763b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 137787a11578SDavid S. Miller struct flowi6 *fl6) 13781da177e4SLinus Torvalds { 137987a11578SDavid S. Miller struct dst_entry *dst; 13801da177e4SLinus Torvalds struct rt6_info *rt; 13811da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1382c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 13831da177e4SLinus Torvalds 138438308473SDavid S. Miller if (unlikely(!idev)) 1385122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 13861da177e4SLinus Torvalds 13878b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, dev, 0, NULL); 138838308473SDavid S. Miller if (unlikely(!rt)) { 13891da177e4SLinus Torvalds in6_dev_put(idev); 139087a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 13911da177e4SLinus Torvalds goto out; 13921da177e4SLinus Torvalds } 13931da177e4SLinus Torvalds 13948e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 13958e2ec639SYan, Zheng rt->dst.output = ip6_output; 1396d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 1397550bab42SJulian Anastasov rt->rt6i_gateway = fl6->daddr; 139887a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 13998e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 14008e2ec639SYan, Zheng rt->rt6i_idev = idev; 140114edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 14021da177e4SLinus Torvalds 14033b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1404d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1405d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 14063b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 14071da177e4SLinus Torvalds 14085578689aSDaniel Lezcano fib6_force_start_gc(net); 14091da177e4SLinus Torvalds 141087a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 141187a11578SDavid S. Miller 14121da177e4SLinus Torvalds out: 141387a11578SDavid S. Miller return dst; 14141da177e4SLinus Torvalds } 14151da177e4SLinus Torvalds 14163d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 14171da177e4SLinus Torvalds { 1418e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 14193d0f24a7SStephen Hemminger int more = 0; 14201da177e4SLinus Torvalds 14213b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 14223b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 14235d0bbeebSThomas Graf 14241da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 14251da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 14261da177e4SLinus Torvalds *pprev = dst->next; 14271da177e4SLinus Torvalds dst_free(dst); 14281da177e4SLinus Torvalds } else { 14291da177e4SLinus Torvalds pprev = &dst->next; 14303d0f24a7SStephen Hemminger ++more; 14311da177e4SLinus Torvalds } 14321da177e4SLinus Torvalds } 14331da177e4SLinus Torvalds 14343b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 14355d0bbeebSThomas Graf 14363d0f24a7SStephen Hemminger return more; 14371da177e4SLinus Torvalds } 14381da177e4SLinus Torvalds 14391e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 14401e493d19SDavid S. Miller void *arg) 14411e493d19SDavid S. Miller { 14421e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 14431e493d19SDavid S. Miller 14441e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 14451e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 14461e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 14471e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 14481e493d19SDavid S. Miller if (func(rt, arg)) { 14491e493d19SDavid S. Miller *pprev = dst->next; 14501e493d19SDavid S. Miller dst_free(dst); 14511e493d19SDavid S. Miller } else { 14521e493d19SDavid S. Miller pprev = &dst->next; 14531e493d19SDavid S. Miller } 14541e493d19SDavid S. Miller } 14551e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 14561e493d19SDavid S. Miller } 14571e493d19SDavid S. Miller 1458569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 14591da177e4SLinus Torvalds { 146086393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 14617019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 14627019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 14637019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 14647019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 14657019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1466fc66f95cSEric Dumazet int entries; 14671da177e4SLinus Torvalds 1468fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 146949a18d86SMichal Kubeček if (time_after(rt_last_gc + rt_min_interval, jiffies) && 1470fc66f95cSEric Dumazet entries <= rt_max_size) 14711da177e4SLinus Torvalds goto out; 14721da177e4SLinus Torvalds 14736891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 147414956643SLi RongQing fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true); 1475fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1476fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 14777019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 14781da177e4SLinus Torvalds out: 14797019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1480fc66f95cSEric Dumazet return entries > rt_max_size; 14811da177e4SLinus Torvalds } 14821da177e4SLinus Torvalds 1483e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc, 1484e715b6d3SFlorian Westphal const struct fib6_config *cfg) 1485e715b6d3SFlorian Westphal { 1486e715b6d3SFlorian Westphal struct nlattr *nla; 1487e715b6d3SFlorian Westphal int remaining; 1488e715b6d3SFlorian Westphal u32 *mp; 1489e715b6d3SFlorian Westphal 149063159f29SIan Morris if (!cfg->fc_mx) 1491e715b6d3SFlorian Westphal return 0; 1492e715b6d3SFlorian Westphal 1493e715b6d3SFlorian Westphal mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 1494e715b6d3SFlorian Westphal if (unlikely(!mp)) 1495e715b6d3SFlorian Westphal return -ENOMEM; 1496e715b6d3SFlorian Westphal 1497e715b6d3SFlorian Westphal nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 1498e715b6d3SFlorian Westphal int type = nla_type(nla); 1499e715b6d3SFlorian Westphal 1500e715b6d3SFlorian Westphal if (type) { 1501ea697639SDaniel Borkmann u32 val; 1502ea697639SDaniel Borkmann 1503e715b6d3SFlorian Westphal if (unlikely(type > RTAX_MAX)) 1504e715b6d3SFlorian Westphal goto err; 1505ea697639SDaniel Borkmann if (type == RTAX_CC_ALGO) { 1506ea697639SDaniel Borkmann char tmp[TCP_CA_NAME_MAX]; 1507e715b6d3SFlorian Westphal 1508ea697639SDaniel Borkmann nla_strlcpy(tmp, nla, sizeof(tmp)); 1509ea697639SDaniel Borkmann val = tcp_ca_get_key_by_name(tmp); 1510ea697639SDaniel Borkmann if (val == TCP_CA_UNSPEC) 1511ea697639SDaniel Borkmann goto err; 1512ea697639SDaniel Borkmann } else { 1513ea697639SDaniel Borkmann val = nla_get_u32(nla); 1514ea697639SDaniel Borkmann } 1515ea697639SDaniel Borkmann 1516ea697639SDaniel Borkmann mp[type - 1] = val; 1517e715b6d3SFlorian Westphal __set_bit(type - 1, mxc->mx_valid); 1518e715b6d3SFlorian Westphal } 1519e715b6d3SFlorian Westphal } 1520e715b6d3SFlorian Westphal 1521e715b6d3SFlorian Westphal mxc->mx = mp; 1522e715b6d3SFlorian Westphal 1523e715b6d3SFlorian Westphal return 0; 1524e715b6d3SFlorian Westphal err: 1525e715b6d3SFlorian Westphal kfree(mp); 1526e715b6d3SFlorian Westphal return -EINVAL; 1527e715b6d3SFlorian Westphal } 15281da177e4SLinus Torvalds 152986872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg) 15301da177e4SLinus Torvalds { 15311da177e4SLinus Torvalds int err; 15325578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 15331da177e4SLinus Torvalds struct rt6_info *rt = NULL; 15341da177e4SLinus Torvalds struct net_device *dev = NULL; 15351da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1536c71099acSThomas Graf struct fib6_table *table; 1537e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 15381da177e4SLinus Torvalds int addr_type; 15391da177e4SLinus Torvalds 154086872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 15411da177e4SLinus Torvalds return -EINVAL; 15421da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 154386872cb5SThomas Graf if (cfg->fc_src_len) 15441da177e4SLinus Torvalds return -EINVAL; 15451da177e4SLinus Torvalds #endif 154686872cb5SThomas Graf if (cfg->fc_ifindex) { 15471da177e4SLinus Torvalds err = -ENODEV; 15485578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 15491da177e4SLinus Torvalds if (!dev) 15501da177e4SLinus Torvalds goto out; 15511da177e4SLinus Torvalds idev = in6_dev_get(dev); 15521da177e4SLinus Torvalds if (!idev) 15531da177e4SLinus Torvalds goto out; 15541da177e4SLinus Torvalds } 15551da177e4SLinus Torvalds 155686872cb5SThomas Graf if (cfg->fc_metric == 0) 155786872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 15581da177e4SLinus Torvalds 1559c71099acSThomas Graf err = -ENOBUFS; 156038308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1561d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1562d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 156338308473SDavid S. Miller if (!table) { 1564f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1565d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1566d71314b4SMatti Vaittinen } 1567d71314b4SMatti Vaittinen } else { 1568d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1569d71314b4SMatti Vaittinen } 157038308473SDavid S. Miller 157138308473SDavid S. Miller if (!table) 1572c71099acSThomas Graf goto out; 1573c71099acSThomas Graf 1574c88507fbSSabrina Dubroca rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table); 15751da177e4SLinus Torvalds 157638308473SDavid S. Miller if (!rt) { 15771da177e4SLinus Torvalds err = -ENOMEM; 15781da177e4SLinus Torvalds goto out; 15791da177e4SLinus Torvalds } 15801da177e4SLinus Torvalds 15811716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 15821716a961SGao feng rt6_set_expires(rt, jiffies + 15831716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 15841716a961SGao feng else 15851716a961SGao feng rt6_clean_expires(rt); 15861da177e4SLinus Torvalds 158786872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 158886872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 158986872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 159086872cb5SThomas Graf 159186872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 15921da177e4SLinus Torvalds 15931da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1594d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1595ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1596ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 15971da177e4SLinus Torvalds else 1598d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 15991da177e4SLinus Torvalds 1600d8d1f30bSChangli Gao rt->dst.output = ip6_output; 16011da177e4SLinus Torvalds 160286872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 160386872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 1604afc4eef8SMartin KaFai Lau if (rt->rt6i_dst.plen == 128) 160511d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 16061da177e4SLinus Torvalds 16071da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 160886872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 160986872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 16101da177e4SLinus Torvalds #endif 16111da177e4SLinus Torvalds 161286872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 16131da177e4SLinus Torvalds 16141da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 16151da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 16161da177e4SLinus Torvalds */ 161786872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 161838308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 161938308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 162038308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 16211da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 16225578689aSDaniel Lezcano if (dev != net->loopback_dev) { 16231da177e4SLinus Torvalds if (dev) { 16241da177e4SLinus Torvalds dev_put(dev); 16251da177e4SLinus Torvalds in6_dev_put(idev); 16261da177e4SLinus Torvalds } 16275578689aSDaniel Lezcano dev = net->loopback_dev; 16281da177e4SLinus Torvalds dev_hold(dev); 16291da177e4SLinus Torvalds idev = in6_dev_get(dev); 16301da177e4SLinus Torvalds if (!idev) { 16311da177e4SLinus Torvalds err = -ENODEV; 16321da177e4SLinus Torvalds goto out; 16331da177e4SLinus Torvalds } 16341da177e4SLinus Torvalds } 16351da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1636ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1637ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1638ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 1639aad88724SEric Dumazet rt->dst.output = dst_discard_sk; 16407150aedeSKamala R rt->dst.input = dst_discard; 1641ef2c7d7bSNicolas Dichtel break; 1642ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1643ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 16447150aedeSKamala R rt->dst.output = ip6_pkt_prohibit_out; 16457150aedeSKamala R rt->dst.input = ip6_pkt_prohibit; 1646ef2c7d7bSNicolas Dichtel break; 1647b4949ab2SNicolas Dichtel case RTN_THROW: 1648ef2c7d7bSNicolas Dichtel default: 16497150aedeSKamala R rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN 16507150aedeSKamala R : -ENETUNREACH; 16517150aedeSKamala R rt->dst.output = ip6_pkt_discard_out; 16527150aedeSKamala R rt->dst.input = ip6_pkt_discard; 1653ef2c7d7bSNicolas Dichtel break; 1654ef2c7d7bSNicolas Dichtel } 16551da177e4SLinus Torvalds goto install_route; 16561da177e4SLinus Torvalds } 16571da177e4SLinus Torvalds 165886872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1659b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 16601da177e4SLinus Torvalds int gwa_type; 16611da177e4SLinus Torvalds 166286872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 166348ed7b26SFlorian Westphal 166448ed7b26SFlorian Westphal /* if gw_addr is local we will fail to detect this in case 166548ed7b26SFlorian Westphal * address is still TENTATIVE (DAD in progress). rt6_lookup() 166648ed7b26SFlorian Westphal * will return already-added prefix route via interface that 166748ed7b26SFlorian Westphal * prefix route was assigned to, which might be non-loopback. 166848ed7b26SFlorian Westphal */ 166948ed7b26SFlorian Westphal err = -EINVAL; 167048ed7b26SFlorian Westphal if (ipv6_chk_addr_and_flags(net, gw_addr, NULL, 0, 0)) 167148ed7b26SFlorian Westphal goto out; 167248ed7b26SFlorian Westphal 16734e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 16741da177e4SLinus Torvalds gwa_type = ipv6_addr_type(gw_addr); 16751da177e4SLinus Torvalds 16761da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 16771da177e4SLinus Torvalds struct rt6_info *grt; 16781da177e4SLinus Torvalds 16791da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 16801da177e4SLinus Torvalds addresses as nexthop address. 16811da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 16821da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 16831da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 16841da177e4SLinus Torvalds some exceptions. --ANK 16851da177e4SLinus Torvalds */ 16861da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 16871da177e4SLinus Torvalds goto out; 16881da177e4SLinus Torvalds 16895578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 16901da177e4SLinus Torvalds 16911da177e4SLinus Torvalds err = -EHOSTUNREACH; 169238308473SDavid S. Miller if (!grt) 16931da177e4SLinus Torvalds goto out; 16941da177e4SLinus Torvalds if (dev) { 1695d1918542SDavid S. Miller if (dev != grt->dst.dev) { 169694e187c0SAmerigo Wang ip6_rt_put(grt); 16971da177e4SLinus Torvalds goto out; 16981da177e4SLinus Torvalds } 16991da177e4SLinus Torvalds } else { 1700d1918542SDavid S. Miller dev = grt->dst.dev; 17011da177e4SLinus Torvalds idev = grt->rt6i_idev; 17021da177e4SLinus Torvalds dev_hold(dev); 17031da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 17041da177e4SLinus Torvalds } 17051da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 17061da177e4SLinus Torvalds err = 0; 170794e187c0SAmerigo Wang ip6_rt_put(grt); 17081da177e4SLinus Torvalds 17091da177e4SLinus Torvalds if (err) 17101da177e4SLinus Torvalds goto out; 17111da177e4SLinus Torvalds } 17121da177e4SLinus Torvalds err = -EINVAL; 171338308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 17141da177e4SLinus Torvalds goto out; 17151da177e4SLinus Torvalds } 17161da177e4SLinus Torvalds 17171da177e4SLinus Torvalds err = -ENODEV; 171838308473SDavid S. Miller if (!dev) 17191da177e4SLinus Torvalds goto out; 17201da177e4SLinus Torvalds 1721c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1722c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1723c3968a85SDaniel Walter err = -EINVAL; 1724c3968a85SDaniel Walter goto out; 1725c3968a85SDaniel Walter } 17264e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1727c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1728c3968a85SDaniel Walter } else 1729c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1730c3968a85SDaniel Walter 173186872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 17321da177e4SLinus Torvalds 17331da177e4SLinus Torvalds install_route: 1734d8d1f30bSChangli Gao rt->dst.dev = dev; 17351da177e4SLinus Torvalds rt->rt6i_idev = idev; 1736c71099acSThomas Graf rt->rt6i_table = table; 173763152fc0SDaniel Lezcano 1738c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 173963152fc0SDaniel Lezcano 1740e715b6d3SFlorian Westphal err = ip6_convert_metrics(&mxc, cfg); 1741e715b6d3SFlorian Westphal if (err) 1742e715b6d3SFlorian Westphal goto out; 17431da177e4SLinus Torvalds 1744e715b6d3SFlorian Westphal err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); 1745e715b6d3SFlorian Westphal 1746e715b6d3SFlorian Westphal kfree(mxc.mx); 1747e715b6d3SFlorian Westphal return err; 17481da177e4SLinus Torvalds out: 17491da177e4SLinus Torvalds if (dev) 17501da177e4SLinus Torvalds dev_put(dev); 17511da177e4SLinus Torvalds if (idev) 17521da177e4SLinus Torvalds in6_dev_put(idev); 17531da177e4SLinus Torvalds if (rt) 1754d8d1f30bSChangli Gao dst_free(&rt->dst); 17551da177e4SLinus Torvalds return err; 17561da177e4SLinus Torvalds } 17571da177e4SLinus Torvalds 175886872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 17591da177e4SLinus Torvalds { 17601da177e4SLinus Torvalds int err; 1761c71099acSThomas Graf struct fib6_table *table; 1762d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 17631da177e4SLinus Torvalds 17646825a26cSGao feng if (rt == net->ipv6.ip6_null_entry) { 17656825a26cSGao feng err = -ENOENT; 17666825a26cSGao feng goto out; 17676825a26cSGao feng } 17686c813a72SPatrick McHardy 1769c71099acSThomas Graf table = rt->rt6i_table; 1770c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 177186872cb5SThomas Graf err = fib6_del(rt, info); 1772c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 17731da177e4SLinus Torvalds 17746825a26cSGao feng out: 177594e187c0SAmerigo Wang ip6_rt_put(rt); 17761da177e4SLinus Torvalds return err; 17771da177e4SLinus Torvalds } 17781da177e4SLinus Torvalds 1779e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 1780e0a1ad73SThomas Graf { 17814d1169c1SDenis V. Lunev struct nl_info info = { 1782d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 17834d1169c1SDenis V. Lunev }; 1784528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 1785e0a1ad73SThomas Graf } 1786e0a1ad73SThomas Graf 178786872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 17881da177e4SLinus Torvalds { 1789c71099acSThomas Graf struct fib6_table *table; 17901da177e4SLinus Torvalds struct fib6_node *fn; 17911da177e4SLinus Torvalds struct rt6_info *rt; 17921da177e4SLinus Torvalds int err = -ESRCH; 17931da177e4SLinus Torvalds 17945578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 179538308473SDavid S. Miller if (!table) 1796c71099acSThomas Graf return err; 17971da177e4SLinus Torvalds 1798c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1799c71099acSThomas Graf 1800c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 180186872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 180286872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 18031da177e4SLinus Torvalds 18041da177e4SLinus Torvalds if (fn) { 1805d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 18061f56a01fSMartin KaFai Lau if ((rt->rt6i_flags & RTF_CACHE) && 18071f56a01fSMartin KaFai Lau !(cfg->fc_flags & RTF_CACHE)) 18081f56a01fSMartin KaFai Lau continue; 180986872cb5SThomas Graf if (cfg->fc_ifindex && 1810d1918542SDavid S. Miller (!rt->dst.dev || 1811d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 18121da177e4SLinus Torvalds continue; 181386872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 181486872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 18151da177e4SLinus Torvalds continue; 181686872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 18171da177e4SLinus Torvalds continue; 1818d8d1f30bSChangli Gao dst_hold(&rt->dst); 1819c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 18201da177e4SLinus Torvalds 182186872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 18221da177e4SLinus Torvalds } 18231da177e4SLinus Torvalds } 1824c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 18251da177e4SLinus Torvalds 18261da177e4SLinus Torvalds return err; 18271da177e4SLinus Torvalds } 18281da177e4SLinus Torvalds 18296700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 1830a6279458SYOSHIFUJI Hideaki { 1831e8599ff4SDavid S. Miller struct net *net = dev_net(skb->dev); 1832a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 1833e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 1834e8599ff4SDavid S. Miller struct ndisc_options ndopts; 1835e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 1836e8599ff4SDavid S. Miller struct neighbour *neigh; 183771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 struct rd_msg *msg; 18386e157b6aSDavid S. Miller int optlen, on_link; 18396e157b6aSDavid S. Miller u8 *lladdr; 1840e8599ff4SDavid S. Miller 184129a3cad5SSimon Horman optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 184271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 optlen -= sizeof(*msg); 1843e8599ff4SDavid S. Miller 1844e8599ff4SDavid S. Miller if (optlen < 0) { 18456e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 1846e8599ff4SDavid S. Miller return; 1847e8599ff4SDavid S. Miller } 1848e8599ff4SDavid S. Miller 184971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 msg = (struct rd_msg *)icmp6_hdr(skb); 1850e8599ff4SDavid S. Miller 185171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_is_multicast(&msg->dest)) { 18526e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 1853e8599ff4SDavid S. Miller return; 1854e8599ff4SDavid S. Miller } 1855e8599ff4SDavid S. Miller 18566e157b6aSDavid S. Miller on_link = 0; 185771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_equal(&msg->dest, &msg->target)) { 1858e8599ff4SDavid S. Miller on_link = 1; 185971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 } else if (ipv6_addr_type(&msg->target) != 1860e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 18616e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 1862e8599ff4SDavid S. Miller return; 1863e8599ff4SDavid S. Miller } 1864e8599ff4SDavid S. Miller 1865e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 1866e8599ff4SDavid S. Miller if (!in6_dev) 1867e8599ff4SDavid S. Miller return; 1868e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 1869e8599ff4SDavid S. Miller return; 1870e8599ff4SDavid S. Miller 1871e8599ff4SDavid S. Miller /* RFC2461 8.1: 1872e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 1873e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 1874e8599ff4SDavid S. Miller */ 1875e8599ff4SDavid S. Miller 187671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) { 1877e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 1878e8599ff4SDavid S. Miller return; 1879e8599ff4SDavid S. Miller } 18806e157b6aSDavid S. Miller 18816e157b6aSDavid S. Miller lladdr = NULL; 1882e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 1883e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 1884e8599ff4SDavid S. Miller skb->dev); 1885e8599ff4SDavid S. Miller if (!lladdr) { 1886e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 1887e8599ff4SDavid S. Miller return; 1888e8599ff4SDavid S. Miller } 1889e8599ff4SDavid S. Miller } 1890e8599ff4SDavid S. Miller 18916e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 18926e157b6aSDavid S. Miller if (rt == net->ipv6.ip6_null_entry) { 18936e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 18946e157b6aSDavid S. Miller return; 18956e157b6aSDavid S. Miller } 18966e157b6aSDavid S. Miller 18976e157b6aSDavid S. Miller /* Redirect received -> path was valid. 18986e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 18996e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 19006e157b6aSDavid S. Miller */ 19016e157b6aSDavid S. Miller dst_confirm(&rt->dst); 19026e157b6aSDavid S. Miller 190371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 1904e8599ff4SDavid S. Miller if (!neigh) 1905e8599ff4SDavid S. Miller return; 1906e8599ff4SDavid S. Miller 19071da177e4SLinus Torvalds /* 19081da177e4SLinus Torvalds * We have finally decided to accept it. 19091da177e4SLinus Torvalds */ 19101da177e4SLinus Torvalds 19111da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 19121da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 19131da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 19141da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 19151da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 19161da177e4SLinus Torvalds ); 19171da177e4SLinus Torvalds 191871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 nrt = ip6_rt_copy(rt, &msg->dest); 191938308473SDavid S. Miller if (!nrt) 19201da177e4SLinus Torvalds goto out; 19211da177e4SLinus Torvalds 19221da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 19231da177e4SLinus Torvalds if (on_link) 19241da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 19251da177e4SLinus Torvalds 19264e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 19271da177e4SLinus Torvalds 192840e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 19291da177e4SLinus Torvalds goto out; 19301da177e4SLinus Torvalds 1931d8d1f30bSChangli Gao netevent.old = &rt->dst; 1932d8d1f30bSChangli Gao netevent.new = &nrt->dst; 193371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 netevent.daddr = &msg->dest; 193460592833SYOSHIFUJI Hideaki / 吉藤英明 netevent.neigh = neigh; 19358d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 19368d71740cSTom Tucker 19371da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 19386e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 1939e0a1ad73SThomas Graf ip6_del_rt(rt); 19401da177e4SLinus Torvalds } 19411da177e4SLinus Torvalds 19421da177e4SLinus Torvalds out: 1943e8599ff4SDavid S. Miller neigh_release(neigh); 19446e157b6aSDavid S. Miller } 19456e157b6aSDavid S. Miller 19461da177e4SLinus Torvalds /* 19471da177e4SLinus Torvalds * Misc support functions 19481da177e4SLinus Torvalds */ 19491da177e4SLinus Torvalds 19504b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) 19514b32b5adSMartin KaFai Lau { 19524b32b5adSMartin KaFai Lau BUG_ON(from->dst.from); 19534b32b5adSMartin KaFai Lau 19544b32b5adSMartin KaFai Lau rt->rt6i_flags &= ~RTF_EXPIRES; 19554b32b5adSMartin KaFai Lau dst_hold(&from->dst); 19564b32b5adSMartin KaFai Lau rt->dst.from = &from->dst; 19574b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true); 19584b32b5adSMartin KaFai Lau } 19594b32b5adSMartin KaFai Lau 19601716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 196121efcfa0SEric Dumazet const struct in6_addr *dest) 19621da177e4SLinus Torvalds { 1963d1918542SDavid S. Miller struct net *net = dev_net(ort->dst.dev); 19644b32b5adSMartin KaFai Lau struct rt6_info *rt; 19654b32b5adSMartin KaFai Lau 19664b32b5adSMartin KaFai Lau if (ort->rt6i_flags & RTF_CACHE) 19674b32b5adSMartin KaFai Lau ort = (struct rt6_info *)ort->dst.from; 19684b32b5adSMartin KaFai Lau 19694b32b5adSMartin KaFai Lau rt = ip6_dst_alloc(net, ort->dst.dev, 0, 19708b96d22dSDavid S. Miller ort->rt6i_table); 19711da177e4SLinus Torvalds 19721da177e4SLinus Torvalds if (rt) { 1973d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 1974d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 19758e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 19761da177e4SLinus Torvalds 19774e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *dest; 19788e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 1979d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 19801da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 19811da177e4SLinus Torvalds if (rt->rt6i_idev) 19821da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 1983d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 19844e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 19851716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 19861716a961SGao feng rt6_set_from(rt, ort); 19871da177e4SLinus Torvalds rt->rt6i_metric = 0; 19881da177e4SLinus Torvalds 19891da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 19901da177e4SLinus Torvalds memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 19911da177e4SLinus Torvalds #endif 19920f6c6392SFlorian Westphal memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key)); 1993c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 19941da177e4SLinus Torvalds } 19951da177e4SLinus Torvalds return rt; 19961da177e4SLinus Torvalds } 19971da177e4SLinus Torvalds 199870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 1999efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 2000b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2001b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 200270ceb4f5SYOSHIFUJI Hideaki { 200370ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 200470ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 2005c71099acSThomas Graf struct fib6_table *table; 200670ceb4f5SYOSHIFUJI Hideaki 2007efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 200838308473SDavid S. Miller if (!table) 2009c71099acSThomas Graf return NULL; 2010c71099acSThomas Graf 20115744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2012c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0); 201370ceb4f5SYOSHIFUJI Hideaki if (!fn) 201470ceb4f5SYOSHIFUJI Hideaki goto out; 201570ceb4f5SYOSHIFUJI Hideaki 2016d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 2017d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 201870ceb4f5SYOSHIFUJI Hideaki continue; 201970ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 202070ceb4f5SYOSHIFUJI Hideaki continue; 202170ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 202270ceb4f5SYOSHIFUJI Hideaki continue; 2023d8d1f30bSChangli Gao dst_hold(&rt->dst); 202470ceb4f5SYOSHIFUJI Hideaki break; 202570ceb4f5SYOSHIFUJI Hideaki } 202670ceb4f5SYOSHIFUJI Hideaki out: 20275744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 202870ceb4f5SYOSHIFUJI Hideaki return rt; 202970ceb4f5SYOSHIFUJI Hideaki } 203070ceb4f5SYOSHIFUJI Hideaki 2031efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 2032b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2033b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 203495c96174SEric Dumazet unsigned int pref) 203570ceb4f5SYOSHIFUJI Hideaki { 203686872cb5SThomas Graf struct fib6_config cfg = { 203786872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 2038238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 203986872cb5SThomas Graf .fc_ifindex = ifindex, 204086872cb5SThomas Graf .fc_dst_len = prefixlen, 204186872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 204286872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 204315e47304SEric W. Biederman .fc_nlinfo.portid = 0, 2044efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 2045efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 204686872cb5SThomas Graf }; 204770ceb4f5SYOSHIFUJI Hideaki 20484e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 20494e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 205086872cb5SThomas Graf 2051e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 2052e317da96SYOSHIFUJI Hideaki if (!prefixlen) 205386872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 205470ceb4f5SYOSHIFUJI Hideaki 205586872cb5SThomas Graf ip6_route_add(&cfg); 205670ceb4f5SYOSHIFUJI Hideaki 2057efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 205870ceb4f5SYOSHIFUJI Hideaki } 205970ceb4f5SYOSHIFUJI Hideaki #endif 206070ceb4f5SYOSHIFUJI Hideaki 2061b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 20621da177e4SLinus Torvalds { 20631da177e4SLinus Torvalds struct rt6_info *rt; 2064c71099acSThomas Graf struct fib6_table *table; 20651da177e4SLinus Torvalds 2066c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 206738308473SDavid S. Miller if (!table) 2068c71099acSThomas Graf return NULL; 20691da177e4SLinus Torvalds 20705744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2071d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 2072d1918542SDavid S. Miller if (dev == rt->dst.dev && 2073045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 20741da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 20751da177e4SLinus Torvalds break; 20761da177e4SLinus Torvalds } 20771da177e4SLinus Torvalds if (rt) 2078d8d1f30bSChangli Gao dst_hold(&rt->dst); 20795744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 20801da177e4SLinus Torvalds return rt; 20811da177e4SLinus Torvalds } 20821da177e4SLinus Torvalds 2083b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 2084ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 2085ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 20861da177e4SLinus Torvalds { 208786872cb5SThomas Graf struct fib6_config cfg = { 208886872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 2089238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 209086872cb5SThomas Graf .fc_ifindex = dev->ifindex, 209186872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 209286872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 209315e47304SEric W. Biederman .fc_nlinfo.portid = 0, 20945578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 2095c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 209686872cb5SThomas Graf }; 20971da177e4SLinus Torvalds 20984e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 20991da177e4SLinus Torvalds 210086872cb5SThomas Graf ip6_route_add(&cfg); 21011da177e4SLinus Torvalds 21021da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 21031da177e4SLinus Torvalds } 21041da177e4SLinus Torvalds 21057b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 21061da177e4SLinus Torvalds { 21071da177e4SLinus Torvalds struct rt6_info *rt; 2108c71099acSThomas Graf struct fib6_table *table; 2109c71099acSThomas Graf 2110c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 21117b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 211238308473SDavid S. Miller if (!table) 2113c71099acSThomas Graf return; 21141da177e4SLinus Torvalds 21151da177e4SLinus Torvalds restart: 2116c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2117d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 21183e8b0ac3SLorenzo Colitti if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 21193e8b0ac3SLorenzo Colitti (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { 2120d8d1f30bSChangli Gao dst_hold(&rt->dst); 2121c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2122e0a1ad73SThomas Graf ip6_del_rt(rt); 21231da177e4SLinus Torvalds goto restart; 21241da177e4SLinus Torvalds } 21251da177e4SLinus Torvalds } 2126c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 21271da177e4SLinus Torvalds } 21281da177e4SLinus Torvalds 21295578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 21305578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 213186872cb5SThomas Graf struct fib6_config *cfg) 213286872cb5SThomas Graf { 213386872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 213486872cb5SThomas Graf 213586872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 213686872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 213786872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 213886872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 213986872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 214086872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 214186872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 214286872cb5SThomas Graf 21435578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 2144f1243c2dSBenjamin Thery 21454e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 21464e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 21474e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 214886872cb5SThomas Graf } 214986872cb5SThomas Graf 21505578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 21511da177e4SLinus Torvalds { 215286872cb5SThomas Graf struct fib6_config cfg; 21531da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 21541da177e4SLinus Torvalds int err; 21551da177e4SLinus Torvalds 21561da177e4SLinus Torvalds switch (cmd) { 21571da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 21581da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 2159af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 21601da177e4SLinus Torvalds return -EPERM; 21611da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 21621da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 21631da177e4SLinus Torvalds if (err) 21641da177e4SLinus Torvalds return -EFAULT; 21651da177e4SLinus Torvalds 21665578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 216786872cb5SThomas Graf 21681da177e4SLinus Torvalds rtnl_lock(); 21691da177e4SLinus Torvalds switch (cmd) { 21701da177e4SLinus Torvalds case SIOCADDRT: 217186872cb5SThomas Graf err = ip6_route_add(&cfg); 21721da177e4SLinus Torvalds break; 21731da177e4SLinus Torvalds case SIOCDELRT: 217486872cb5SThomas Graf err = ip6_route_del(&cfg); 21751da177e4SLinus Torvalds break; 21761da177e4SLinus Torvalds default: 21771da177e4SLinus Torvalds err = -EINVAL; 21781da177e4SLinus Torvalds } 21791da177e4SLinus Torvalds rtnl_unlock(); 21801da177e4SLinus Torvalds 21811da177e4SLinus Torvalds return err; 21823ff50b79SStephen Hemminger } 21831da177e4SLinus Torvalds 21841da177e4SLinus Torvalds return -EINVAL; 21851da177e4SLinus Torvalds } 21861da177e4SLinus Torvalds 21871da177e4SLinus Torvalds /* 21881da177e4SLinus Torvalds * Drop the packet on the floor 21891da177e4SLinus Torvalds */ 21901da177e4SLinus Torvalds 2191d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 21921da177e4SLinus Torvalds { 2193612f09e8SYOSHIFUJI Hideaki int type; 2194adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2195612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2196612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 21970660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 219845bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 21993bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 22003bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2201612f09e8SYOSHIFUJI Hideaki break; 2202612f09e8SYOSHIFUJI Hideaki } 2203612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2204612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 22053bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 22063bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2207612f09e8SYOSHIFUJI Hideaki break; 2208612f09e8SYOSHIFUJI Hideaki } 22093ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 22101da177e4SLinus Torvalds kfree_skb(skb); 22111da177e4SLinus Torvalds return 0; 22121da177e4SLinus Torvalds } 22131da177e4SLinus Torvalds 22149ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 22159ce8ade0SThomas Graf { 2216612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 22179ce8ade0SThomas Graf } 22189ce8ade0SThomas Graf 2219aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb) 22201da177e4SLinus Torvalds { 2221adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2222612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 22231da177e4SLinus Torvalds } 22241da177e4SLinus Torvalds 22259ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 22269ce8ade0SThomas Graf { 2227612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 22289ce8ade0SThomas Graf } 22299ce8ade0SThomas Graf 2230aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb) 22319ce8ade0SThomas Graf { 2232adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2233612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 22349ce8ade0SThomas Graf } 22359ce8ade0SThomas Graf 22361da177e4SLinus Torvalds /* 22371da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 22381da177e4SLinus Torvalds */ 22391da177e4SLinus Torvalds 22401da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 22411da177e4SLinus Torvalds const struct in6_addr *addr, 22428f031519SDavid S. Miller bool anycast) 22431da177e4SLinus Torvalds { 2244c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 2245a3300ef4SHannes Frederic Sowa struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 2246a3300ef4SHannes Frederic Sowa DST_NOCOUNT, NULL); 2247a3300ef4SHannes Frederic Sowa if (!rt) 22481da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 22491da177e4SLinus Torvalds 22501da177e4SLinus Torvalds in6_dev_hold(idev); 22511da177e4SLinus Torvalds 225211d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2253d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2254d8d1f30bSChangli Gao rt->dst.output = ip6_output; 22551da177e4SLinus Torvalds rt->rt6i_idev = idev; 22561da177e4SLinus Torvalds 22571da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 225858c4fb86SYOSHIFUJI Hideaki if (anycast) 225958c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 226058c4fb86SYOSHIFUJI Hideaki else 22611da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 22621da177e4SLinus Torvalds 2263550bab42SJulian Anastasov rt->rt6i_gateway = *addr; 22644e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 22651da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 22665578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 22671da177e4SLinus Torvalds 2268d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 22691da177e4SLinus Torvalds 22701da177e4SLinus Torvalds return rt; 22711da177e4SLinus Torvalds } 22721da177e4SLinus Torvalds 2273c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2274c3968a85SDaniel Walter struct rt6_info *rt, 2275b71d1d42SEric Dumazet const struct in6_addr *daddr, 2276c3968a85SDaniel Walter unsigned int prefs, 2277c3968a85SDaniel Walter struct in6_addr *saddr) 2278c3968a85SDaniel Walter { 2279e16e888bSMarkus Stenberg struct inet6_dev *idev = 2280e16e888bSMarkus Stenberg rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL; 2281c3968a85SDaniel Walter int err = 0; 2282e16e888bSMarkus Stenberg if (rt && rt->rt6i_prefsrc.plen) 22834e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2284c3968a85SDaniel Walter else 2285c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2286c3968a85SDaniel Walter daddr, prefs, saddr); 2287c3968a85SDaniel Walter return err; 2288c3968a85SDaniel Walter } 2289c3968a85SDaniel Walter 2290c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2291c3968a85SDaniel Walter struct arg_dev_net_ip { 2292c3968a85SDaniel Walter struct net_device *dev; 2293c3968a85SDaniel Walter struct net *net; 2294c3968a85SDaniel Walter struct in6_addr *addr; 2295c3968a85SDaniel Walter }; 2296c3968a85SDaniel Walter 2297c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2298c3968a85SDaniel Walter { 2299c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2300c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2301c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2302c3968a85SDaniel Walter 2303d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2304c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2305c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2306c3968a85SDaniel Walter /* remove prefsrc entry */ 2307c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2308c3968a85SDaniel Walter } 2309c3968a85SDaniel Walter return 0; 2310c3968a85SDaniel Walter } 2311c3968a85SDaniel Walter 2312c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2313c3968a85SDaniel Walter { 2314c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2315c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2316c3968a85SDaniel Walter .dev = ifp->idev->dev, 2317c3968a85SDaniel Walter .net = net, 2318c3968a85SDaniel Walter .addr = &ifp->addr, 2319c3968a85SDaniel Walter }; 23200c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 2321c3968a85SDaniel Walter } 2322c3968a85SDaniel Walter 2323be7a010dSDuan Jiong #define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) 2324be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 2325be7a010dSDuan Jiong 2326be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */ 2327be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg) 2328be7a010dSDuan Jiong { 2329be7a010dSDuan Jiong struct in6_addr *gateway = (struct in6_addr *)arg; 2330be7a010dSDuan Jiong 2331be7a010dSDuan Jiong if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) || 2332be7a010dSDuan Jiong ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) && 2333be7a010dSDuan Jiong ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { 2334be7a010dSDuan Jiong return -1; 2335be7a010dSDuan Jiong } 2336be7a010dSDuan Jiong return 0; 2337be7a010dSDuan Jiong } 2338be7a010dSDuan Jiong 2339be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 2340be7a010dSDuan Jiong { 2341be7a010dSDuan Jiong fib6_clean_all(net, fib6_clean_tohost, gateway); 2342be7a010dSDuan Jiong } 2343be7a010dSDuan Jiong 23448ed67789SDaniel Lezcano struct arg_dev_net { 23458ed67789SDaniel Lezcano struct net_device *dev; 23468ed67789SDaniel Lezcano struct net *net; 23478ed67789SDaniel Lezcano }; 23488ed67789SDaniel Lezcano 23491da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 23501da177e4SLinus Torvalds { 2351bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2352bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 23538ed67789SDaniel Lezcano 2354d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2355c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 23561da177e4SLinus Torvalds return -1; 2357c159d30cSDavid S. Miller 23581da177e4SLinus Torvalds return 0; 23591da177e4SLinus Torvalds } 23601da177e4SLinus Torvalds 2361f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 23621da177e4SLinus Torvalds { 23638ed67789SDaniel Lezcano struct arg_dev_net adn = { 23648ed67789SDaniel Lezcano .dev = dev, 23658ed67789SDaniel Lezcano .net = net, 23668ed67789SDaniel Lezcano }; 23678ed67789SDaniel Lezcano 23680c3584d5SLi RongQing fib6_clean_all(net, fib6_ifdown, &adn); 23691e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 23701da177e4SLinus Torvalds } 23711da177e4SLinus Torvalds 237295c96174SEric Dumazet struct rt6_mtu_change_arg { 23731da177e4SLinus Torvalds struct net_device *dev; 237495c96174SEric Dumazet unsigned int mtu; 23751da177e4SLinus Torvalds }; 23761da177e4SLinus Torvalds 23771da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 23781da177e4SLinus Torvalds { 23791da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 23801da177e4SLinus Torvalds struct inet6_dev *idev; 23811da177e4SLinus Torvalds 23821da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 23831da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 23841da177e4SLinus Torvalds We still use this lock to block changes 23851da177e4SLinus Torvalds caused by addrconf/ndisc. 23861da177e4SLinus Torvalds */ 23871da177e4SLinus Torvalds 23881da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 238938308473SDavid S. Miller if (!idev) 23901da177e4SLinus Torvalds return 0; 23911da177e4SLinus Torvalds 23921da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 23931da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 23941da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 23951da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 23961da177e4SLinus Torvalds */ 23971da177e4SLinus Torvalds /* 23981da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 23991da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 24001da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 24011da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 24021da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 24031da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 24041da177e4SLinus Torvalds PMTU discouvery. 24051da177e4SLinus Torvalds */ 2406d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 24074b32b5adSMartin KaFai Lau !dst_metric_locked(&rt->dst, RTAX_MTU)) { 24084b32b5adSMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) { 24094b32b5adSMartin KaFai Lau /* For RTF_CACHE with rt6i_pmtu == 0 24104b32b5adSMartin KaFai Lau * (i.e. a redirected route), 24114b32b5adSMartin KaFai Lau * the metrics of its rt->dst.from has already 24124b32b5adSMartin KaFai Lau * been updated. 24134b32b5adSMartin KaFai Lau */ 24144b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu) 24154b32b5adSMartin KaFai Lau rt->rt6i_pmtu = arg->mtu; 24164b32b5adSMartin KaFai Lau } else if (dst_mtu(&rt->dst) >= arg->mtu || 2417d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 24184b32b5adSMartin KaFai Lau dst_mtu(&rt->dst) == idev->cnf.mtu6)) { 2419defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2420566cfd8fSSimon Arlott } 24214b32b5adSMartin KaFai Lau } 24221da177e4SLinus Torvalds return 0; 24231da177e4SLinus Torvalds } 24241da177e4SLinus Torvalds 242595c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 24261da177e4SLinus Torvalds { 2427c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2428c71099acSThomas Graf .dev = dev, 2429c71099acSThomas Graf .mtu = mtu, 2430c71099acSThomas Graf }; 24311da177e4SLinus Torvalds 24320c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 24331da177e4SLinus Torvalds } 24341da177e4SLinus Torvalds 2435ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 24365176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 243786872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2438ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 243986872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 244086872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 244151ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2442c78ba6d6SLubomir Rintel [RTA_PREF] = { .type = NLA_U8 }, 244386872cb5SThomas Graf }; 244486872cb5SThomas Graf 244586872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 244686872cb5SThomas Graf struct fib6_config *cfg) 24471da177e4SLinus Torvalds { 244886872cb5SThomas Graf struct rtmsg *rtm; 244986872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 2450c78ba6d6SLubomir Rintel unsigned int pref; 245186872cb5SThomas Graf int err; 24521da177e4SLinus Torvalds 245386872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 245486872cb5SThomas Graf if (err < 0) 245586872cb5SThomas Graf goto errout; 24561da177e4SLinus Torvalds 245786872cb5SThomas Graf err = -EINVAL; 245886872cb5SThomas Graf rtm = nlmsg_data(nlh); 245986872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 246086872cb5SThomas Graf 246186872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 246286872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 246386872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 246486872cb5SThomas Graf cfg->fc_flags = RTF_UP; 246586872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2466ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 246786872cb5SThomas Graf 2468ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2469ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2470b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2471b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 247286872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 247386872cb5SThomas Graf 2474ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2475ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2476ab79ad14SMaciej Żenczykowski 24771f56a01fSMartin KaFai Lau if (rtm->rtm_flags & RTM_F_CLONED) 24781f56a01fSMartin KaFai Lau cfg->fc_flags |= RTF_CACHE; 24791f56a01fSMartin KaFai Lau 248015e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 248186872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 24823b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 248386872cb5SThomas Graf 248486872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 248567b61f6cSJiri Benc cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 248686872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 24871da177e4SLinus Torvalds } 248886872cb5SThomas Graf 248986872cb5SThomas Graf if (tb[RTA_DST]) { 249086872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 249186872cb5SThomas Graf 249286872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 249386872cb5SThomas Graf goto errout; 249486872cb5SThomas Graf 249586872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 24961da177e4SLinus Torvalds } 249786872cb5SThomas Graf 249886872cb5SThomas Graf if (tb[RTA_SRC]) { 249986872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 250086872cb5SThomas Graf 250186872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 250286872cb5SThomas Graf goto errout; 250386872cb5SThomas Graf 250486872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 25051da177e4SLinus Torvalds } 250686872cb5SThomas Graf 2507c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 250867b61f6cSJiri Benc cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 2509c3968a85SDaniel Walter 251086872cb5SThomas Graf if (tb[RTA_OIF]) 251186872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 251286872cb5SThomas Graf 251386872cb5SThomas Graf if (tb[RTA_PRIORITY]) 251486872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 251586872cb5SThomas Graf 251686872cb5SThomas Graf if (tb[RTA_METRICS]) { 251786872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 251886872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 25191da177e4SLinus Torvalds } 252086872cb5SThomas Graf 252186872cb5SThomas Graf if (tb[RTA_TABLE]) 252286872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 252386872cb5SThomas Graf 252451ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 252551ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 252651ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 252751ebd318SNicolas Dichtel } 252851ebd318SNicolas Dichtel 2529c78ba6d6SLubomir Rintel if (tb[RTA_PREF]) { 2530c78ba6d6SLubomir Rintel pref = nla_get_u8(tb[RTA_PREF]); 2531c78ba6d6SLubomir Rintel if (pref != ICMPV6_ROUTER_PREF_LOW && 2532c78ba6d6SLubomir Rintel pref != ICMPV6_ROUTER_PREF_HIGH) 2533c78ba6d6SLubomir Rintel pref = ICMPV6_ROUTER_PREF_MEDIUM; 2534c78ba6d6SLubomir Rintel cfg->fc_flags |= RTF_PREF(pref); 2535c78ba6d6SLubomir Rintel } 2536c78ba6d6SLubomir Rintel 253786872cb5SThomas Graf err = 0; 253886872cb5SThomas Graf errout: 253986872cb5SThomas Graf return err; 25401da177e4SLinus Torvalds } 25411da177e4SLinus Torvalds 254251ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add) 254351ebd318SNicolas Dichtel { 254451ebd318SNicolas Dichtel struct fib6_config r_cfg; 254551ebd318SNicolas Dichtel struct rtnexthop *rtnh; 254651ebd318SNicolas Dichtel int remaining; 254751ebd318SNicolas Dichtel int attrlen; 254851ebd318SNicolas Dichtel int err = 0, last_err = 0; 254951ebd318SNicolas Dichtel 255035f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len; 255151ebd318SNicolas Dichtel beginning: 255251ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 255351ebd318SNicolas Dichtel 255451ebd318SNicolas Dichtel /* Parse a Multipath Entry */ 255551ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 255651ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 255751ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 255851ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 255951ebd318SNicolas Dichtel 256051ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 256151ebd318SNicolas Dichtel if (attrlen > 0) { 256251ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 256351ebd318SNicolas Dichtel 256451ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 256551ebd318SNicolas Dichtel if (nla) { 256667b61f6cSJiri Benc r_cfg.fc_gateway = nla_get_in6_addr(nla); 256751ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 256851ebd318SNicolas Dichtel } 256951ebd318SNicolas Dichtel } 257051ebd318SNicolas Dichtel err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg); 257151ebd318SNicolas Dichtel if (err) { 257251ebd318SNicolas Dichtel last_err = err; 257351ebd318SNicolas Dichtel /* If we are trying to remove a route, do not stop the 257451ebd318SNicolas Dichtel * loop when ip6_route_del() fails (because next hop is 257551ebd318SNicolas Dichtel * already gone), we should try to remove all next hops. 257651ebd318SNicolas Dichtel */ 257751ebd318SNicolas Dichtel if (add) { 257851ebd318SNicolas Dichtel /* If add fails, we should try to delete all 257951ebd318SNicolas Dichtel * next hops that have been already added. 258051ebd318SNicolas Dichtel */ 258151ebd318SNicolas Dichtel add = 0; 258235f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len - remaining; 258351ebd318SNicolas Dichtel goto beginning; 258451ebd318SNicolas Dichtel } 258551ebd318SNicolas Dichtel } 25861a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 258727596472SMichal Kubeček * these flags after the first nexthop: if there is a collision, 258827596472SMichal Kubeček * we have already failed to add the first nexthop: 258927596472SMichal Kubeček * fib6_add_rt2node() has rejected it; when replacing, old 259027596472SMichal Kubeček * nexthops have been replaced by first new, the rest should 259127596472SMichal Kubeček * be added to it. 25921a72418bSNicolas Dichtel */ 259327596472SMichal Kubeček cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | 259427596472SMichal Kubeček NLM_F_REPLACE); 259551ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 259651ebd318SNicolas Dichtel } 259751ebd318SNicolas Dichtel 259851ebd318SNicolas Dichtel return last_err; 259951ebd318SNicolas Dichtel } 260051ebd318SNicolas Dichtel 2601661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) 26021da177e4SLinus Torvalds { 260386872cb5SThomas Graf struct fib6_config cfg; 260486872cb5SThomas Graf int err; 26051da177e4SLinus Torvalds 260686872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 260786872cb5SThomas Graf if (err < 0) 260886872cb5SThomas Graf return err; 260986872cb5SThomas Graf 261051ebd318SNicolas Dichtel if (cfg.fc_mp) 261151ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 0); 261251ebd318SNicolas Dichtel else 261386872cb5SThomas Graf return ip6_route_del(&cfg); 26141da177e4SLinus Torvalds } 26151da177e4SLinus Torvalds 2616661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) 26171da177e4SLinus Torvalds { 261886872cb5SThomas Graf struct fib6_config cfg; 261986872cb5SThomas Graf int err; 26201da177e4SLinus Torvalds 262186872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 262286872cb5SThomas Graf if (err < 0) 262386872cb5SThomas Graf return err; 262486872cb5SThomas Graf 262551ebd318SNicolas Dichtel if (cfg.fc_mp) 262651ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 1); 262751ebd318SNicolas Dichtel else 262886872cb5SThomas Graf return ip6_route_add(&cfg); 26291da177e4SLinus Torvalds } 26301da177e4SLinus Torvalds 2631339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void) 2632339bf98fSThomas Graf { 2633339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 2634339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 2635339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 2636339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 2637339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 2638339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 2639339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 2640339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 2641339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 26426a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 2643ea697639SDaniel Borkmann + nla_total_size(sizeof(struct rta_cacheinfo)) 2644c78ba6d6SLubomir Rintel + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 2645c78ba6d6SLubomir Rintel + nla_total_size(1); /* RTA_PREF */ 2646339bf98fSThomas Graf } 2647339bf98fSThomas Graf 2648191cd582SBrian Haley static int rt6_fill_node(struct net *net, 2649191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 26500d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 265115e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 26527bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 26531da177e4SLinus Torvalds { 26544b32b5adSMartin KaFai Lau u32 metrics[RTAX_MAX]; 26551da177e4SLinus Torvalds struct rtmsg *rtm; 26561da177e4SLinus Torvalds struct nlmsghdr *nlh; 2657e3703b3dSThomas Graf long expires; 26589e762a4aSPatrick McHardy u32 table; 26591da177e4SLinus Torvalds 26601da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 26611da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 26621da177e4SLinus Torvalds /* success since this is not a prefix route */ 26631da177e4SLinus Torvalds return 1; 26641da177e4SLinus Torvalds } 26651da177e4SLinus Torvalds } 26661da177e4SLinus Torvalds 266715e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 266838308473SDavid S. Miller if (!nlh) 266926932566SPatrick McHardy return -EMSGSIZE; 26702d7202bfSThomas Graf 26712d7202bfSThomas Graf rtm = nlmsg_data(nlh); 26721da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 26731da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 26741da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 26751da177e4SLinus Torvalds rtm->rtm_tos = 0; 2676c71099acSThomas Graf if (rt->rt6i_table) 26779e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 2678c71099acSThomas Graf else 26799e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 26809e762a4aSPatrick McHardy rtm->rtm_table = table; 2681c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 2682c78679e8SDavid S. Miller goto nla_put_failure; 2683ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 2684ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 2685ef2c7d7bSNicolas Dichtel case -EINVAL: 2686ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 2687ef2c7d7bSNicolas Dichtel break; 2688ef2c7d7bSNicolas Dichtel case -EACCES: 2689ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 2690ef2c7d7bSNicolas Dichtel break; 2691b4949ab2SNicolas Dichtel case -EAGAIN: 2692b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 2693b4949ab2SNicolas Dichtel break; 2694ef2c7d7bSNicolas Dichtel default: 26951da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 2696ef2c7d7bSNicolas Dichtel break; 2697ef2c7d7bSNicolas Dichtel } 2698ef2c7d7bSNicolas Dichtel } 2699ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 2700ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 2701d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 27021da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 27031da177e4SLinus Torvalds else 27041da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 27051da177e4SLinus Torvalds rtm->rtm_flags = 0; 27061da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 27071da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 27081da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 27091da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 2710f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 2711f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 27121da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 2713f0396f60SDenis Ovsienko else 2714f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 2715f0396f60SDenis Ovsienko } 27161da177e4SLinus Torvalds 27171da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 27181da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 27191da177e4SLinus Torvalds 27201da177e4SLinus Torvalds if (dst) { 2721930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, dst)) 2722c78679e8SDavid S. Miller goto nla_put_failure; 27231da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 27241da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 2725930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr)) 2726c78679e8SDavid S. Miller goto nla_put_failure; 27271da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 27281da177e4SLinus Torvalds if (src) { 2729930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_SRC, src)) 2730c78679e8SDavid S. Miller goto nla_put_failure; 27311da177e4SLinus Torvalds rtm->rtm_src_len = 128; 2732c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 2733930345eaSJiri Benc nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr)) 2734c78679e8SDavid S. Miller goto nla_put_failure; 27351da177e4SLinus Torvalds #endif 27367bc570c8SYOSHIFUJI Hideaki if (iif) { 27377bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 27387bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 27398229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 27407bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 27417bc570c8SYOSHIFUJI Hideaki if (!nowait) { 27427bc570c8SYOSHIFUJI Hideaki if (err == 0) 27437bc570c8SYOSHIFUJI Hideaki return 0; 27447bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 27457bc570c8SYOSHIFUJI Hideaki } else { 27467bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 27477bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 27487bc570c8SYOSHIFUJI Hideaki } 27497bc570c8SYOSHIFUJI Hideaki } 27507bc570c8SYOSHIFUJI Hideaki } else 27517bc570c8SYOSHIFUJI Hideaki #endif 2752c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 2753c78679e8SDavid S. Miller goto nla_put_failure; 27547bc570c8SYOSHIFUJI Hideaki } else if (dst) { 27551da177e4SLinus Torvalds struct in6_addr saddr_buf; 2756c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 2757930345eaSJiri Benc nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 2758c78679e8SDavid S. Miller goto nla_put_failure; 2759c3968a85SDaniel Walter } 2760c3968a85SDaniel Walter 2761c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 2762c3968a85SDaniel Walter struct in6_addr saddr_buf; 27634e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 2764930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 2765c78679e8SDavid S. Miller goto nla_put_failure; 27661da177e4SLinus Torvalds } 27672d7202bfSThomas Graf 27684b32b5adSMartin KaFai Lau memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); 27694b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu) 27704b32b5adSMartin KaFai Lau metrics[RTAX_MTU - 1] = rt->rt6i_pmtu; 27714b32b5adSMartin KaFai Lau if (rtnetlink_put_metrics(skb, metrics) < 0) 27722d7202bfSThomas Graf goto nla_put_failure; 27732d7202bfSThomas Graf 2774dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_GATEWAY) { 2775930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0) 277694f826b8SEric Dumazet goto nla_put_failure; 277794f826b8SEric Dumazet } 27782d7202bfSThomas Graf 2779c78679e8SDavid S. Miller if (rt->dst.dev && 2780c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 2781c78679e8SDavid S. Miller goto nla_put_failure; 2782c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 2783c78679e8SDavid S. Miller goto nla_put_failure; 27848253947eSLi Wei 27858253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 278669cdf8f9SYOSHIFUJI Hideaki 278787a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 2788e3703b3dSThomas Graf goto nla_put_failure; 27891da177e4SLinus Torvalds 2790c78ba6d6SLubomir Rintel if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) 2791c78ba6d6SLubomir Rintel goto nla_put_failure; 2792c78ba6d6SLubomir Rintel 2793053c095aSJohannes Berg nlmsg_end(skb, nlh); 2794053c095aSJohannes Berg return 0; 27952d7202bfSThomas Graf 27962d7202bfSThomas Graf nla_put_failure: 279726932566SPatrick McHardy nlmsg_cancel(skb, nlh); 279826932566SPatrick McHardy return -EMSGSIZE; 27991da177e4SLinus Torvalds } 28001da177e4SLinus Torvalds 28011b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 28021da177e4SLinus Torvalds { 28031da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 28041da177e4SLinus Torvalds int prefix; 28051da177e4SLinus Torvalds 28062d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 28072d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 28081da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 28091da177e4SLinus Torvalds } else 28101da177e4SLinus Torvalds prefix = 0; 28111da177e4SLinus Torvalds 2812191cd582SBrian Haley return rt6_fill_node(arg->net, 2813191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 281415e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 28157bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 28161da177e4SLinus Torvalds } 28171da177e4SLinus Torvalds 2818661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) 28191da177e4SLinus Torvalds { 28203b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 2821ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 28221da177e4SLinus Torvalds struct rt6_info *rt; 2823ab364a6fSThomas Graf struct sk_buff *skb; 2824ab364a6fSThomas Graf struct rtmsg *rtm; 28254c9483b2SDavid S. Miller struct flowi6 fl6; 282672331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 2827ab364a6fSThomas Graf 2828ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 2829ab364a6fSThomas Graf if (err < 0) 2830ab364a6fSThomas Graf goto errout; 2831ab364a6fSThomas Graf 2832ab364a6fSThomas Graf err = -EINVAL; 28334c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2834ab364a6fSThomas Graf 2835ab364a6fSThomas Graf if (tb[RTA_SRC]) { 2836ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 2837ab364a6fSThomas Graf goto errout; 2838ab364a6fSThomas Graf 28394e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 2840ab364a6fSThomas Graf } 2841ab364a6fSThomas Graf 2842ab364a6fSThomas Graf if (tb[RTA_DST]) { 2843ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 2844ab364a6fSThomas Graf goto errout; 2845ab364a6fSThomas Graf 28464e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 2847ab364a6fSThomas Graf } 2848ab364a6fSThomas Graf 2849ab364a6fSThomas Graf if (tb[RTA_IIF]) 2850ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 2851ab364a6fSThomas Graf 2852ab364a6fSThomas Graf if (tb[RTA_OIF]) 285372331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 2854ab364a6fSThomas Graf 28552e47b291SLorenzo Colitti if (tb[RTA_MARK]) 28562e47b291SLorenzo Colitti fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 28572e47b291SLorenzo Colitti 2858ab364a6fSThomas Graf if (iif) { 2859ab364a6fSThomas Graf struct net_device *dev; 286072331bc0SShmulik Ladkani int flags = 0; 286172331bc0SShmulik Ladkani 28625578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 2863ab364a6fSThomas Graf if (!dev) { 2864ab364a6fSThomas Graf err = -ENODEV; 2865ab364a6fSThomas Graf goto errout; 2866ab364a6fSThomas Graf } 286772331bc0SShmulik Ladkani 286872331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 286972331bc0SShmulik Ladkani 287072331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 287172331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 287272331bc0SShmulik Ladkani 287372331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 287472331bc0SShmulik Ladkani flags); 287572331bc0SShmulik Ladkani } else { 287672331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 287772331bc0SShmulik Ladkani 287872331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 2879ab364a6fSThomas Graf } 28801da177e4SLinus Torvalds 28811da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 288238308473SDavid S. Miller if (!skb) { 288394e187c0SAmerigo Wang ip6_rt_put(rt); 2884ab364a6fSThomas Graf err = -ENOBUFS; 2885ab364a6fSThomas Graf goto errout; 2886ab364a6fSThomas Graf } 28871da177e4SLinus Torvalds 28881da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 28891da177e4SLinus Torvalds through good chunk of routing engine. 28901da177e4SLinus Torvalds */ 2891459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 28921da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 28931da177e4SLinus Torvalds 2894d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 28951da177e4SLinus Torvalds 28964c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 289715e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 28987bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 28991da177e4SLinus Torvalds if (err < 0) { 2900ab364a6fSThomas Graf kfree_skb(skb); 2901ab364a6fSThomas Graf goto errout; 29021da177e4SLinus Torvalds } 29031da177e4SLinus Torvalds 290415e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 2905ab364a6fSThomas Graf errout: 29061da177e4SLinus Torvalds return err; 29071da177e4SLinus Torvalds } 29081da177e4SLinus Torvalds 290986872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) 29101da177e4SLinus Torvalds { 29111da177e4SLinus Torvalds struct sk_buff *skb; 29125578689aSDaniel Lezcano struct net *net = info->nl_net; 2913528c4cebSDenis V. Lunev u32 seq; 2914528c4cebSDenis V. Lunev int err; 29150d51aa80SJamal Hadi Salim 2916528c4cebSDenis V. Lunev err = -ENOBUFS; 291738308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 291886872cb5SThomas Graf 2919339bf98fSThomas Graf skb = nlmsg_new(rt6_nlmsg_size(), gfp_any()); 292038308473SDavid S. Miller if (!skb) 292121713ebcSThomas Graf goto errout; 29221da177e4SLinus Torvalds 2923191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 292415e47304SEric W. Biederman event, info->portid, seq, 0, 0, 0); 292526932566SPatrick McHardy if (err < 0) { 292626932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 292726932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 292826932566SPatrick McHardy kfree_skb(skb); 292926932566SPatrick McHardy goto errout; 293026932566SPatrick McHardy } 293115e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 29325578689aSDaniel Lezcano info->nlh, gfp_any()); 29331ce85fe4SPablo Neira Ayuso return; 293421713ebcSThomas Graf errout: 293521713ebcSThomas Graf if (err < 0) 29365578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 29371da177e4SLinus Torvalds } 29381da177e4SLinus Torvalds 29398ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 2940351638e7SJiri Pirko unsigned long event, void *ptr) 29418ed67789SDaniel Lezcano { 2942351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 2943c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 29448ed67789SDaniel Lezcano 29458ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 2946d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 29478ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 29488ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 2949d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 29508ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 2951d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 29528ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 29538ed67789SDaniel Lezcano #endif 29548ed67789SDaniel Lezcano } 29558ed67789SDaniel Lezcano 29568ed67789SDaniel Lezcano return NOTIFY_OK; 29578ed67789SDaniel Lezcano } 29588ed67789SDaniel Lezcano 29591da177e4SLinus Torvalds /* 29601da177e4SLinus Torvalds * /proc 29611da177e4SLinus Torvalds */ 29621da177e4SLinus Torvalds 29631da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 29641da177e4SLinus Torvalds 296533120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 296633120b30SAlexey Dobriyan .owner = THIS_MODULE, 296733120b30SAlexey Dobriyan .open = ipv6_route_open, 296833120b30SAlexey Dobriyan .read = seq_read, 296933120b30SAlexey Dobriyan .llseek = seq_lseek, 29708d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 297133120b30SAlexey Dobriyan }; 297233120b30SAlexey Dobriyan 29731da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 29741da177e4SLinus Torvalds { 297569ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 29761da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 297769ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 297869ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 297969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 298069ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 298169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 2982fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 298369ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 29841da177e4SLinus Torvalds 29851da177e4SLinus Torvalds return 0; 29861da177e4SLinus Torvalds } 29871da177e4SLinus Torvalds 29881da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 29891da177e4SLinus Torvalds { 2990de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 299169ddb805SDaniel Lezcano } 299269ddb805SDaniel Lezcano 29939a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 29941da177e4SLinus Torvalds .owner = THIS_MODULE, 29951da177e4SLinus Torvalds .open = rt6_stats_seq_open, 29961da177e4SLinus Torvalds .read = seq_read, 29971da177e4SLinus Torvalds .llseek = seq_lseek, 2998b6fcbdb4SPavel Emelyanov .release = single_release_net, 29991da177e4SLinus Torvalds }; 30001da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 30011da177e4SLinus Torvalds 30021da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 30031da177e4SLinus Torvalds 30041da177e4SLinus Torvalds static 3005fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 30061da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 30071da177e4SLinus Torvalds { 3008c486da34SLucian Adrian Grijincu struct net *net; 3009c486da34SLucian Adrian Grijincu int delay; 3010c486da34SLucian Adrian Grijincu if (!write) 3011c486da34SLucian Adrian Grijincu return -EINVAL; 3012c486da34SLucian Adrian Grijincu 3013c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 3014c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 30158d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 30162ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 30171da177e4SLinus Torvalds return 0; 30181da177e4SLinus Torvalds } 30191da177e4SLinus Torvalds 3020fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 30211da177e4SLinus Torvalds { 30221da177e4SLinus Torvalds .procname = "flush", 30234990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 30241da177e4SLinus Torvalds .maxlen = sizeof(int), 302589c8b3a1SDave Jones .mode = 0200, 30266d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 30271da177e4SLinus Torvalds }, 30281da177e4SLinus Torvalds { 30291da177e4SLinus Torvalds .procname = "gc_thresh", 30309a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 30311da177e4SLinus Torvalds .maxlen = sizeof(int), 30321da177e4SLinus Torvalds .mode = 0644, 30336d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 30341da177e4SLinus Torvalds }, 30351da177e4SLinus Torvalds { 30361da177e4SLinus Torvalds .procname = "max_size", 30374990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 30381da177e4SLinus Torvalds .maxlen = sizeof(int), 30391da177e4SLinus Torvalds .mode = 0644, 30406d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 30411da177e4SLinus Torvalds }, 30421da177e4SLinus Torvalds { 30431da177e4SLinus Torvalds .procname = "gc_min_interval", 30444990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 30451da177e4SLinus Torvalds .maxlen = sizeof(int), 30461da177e4SLinus Torvalds .mode = 0644, 30476d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 30481da177e4SLinus Torvalds }, 30491da177e4SLinus Torvalds { 30501da177e4SLinus Torvalds .procname = "gc_timeout", 30514990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 30521da177e4SLinus Torvalds .maxlen = sizeof(int), 30531da177e4SLinus Torvalds .mode = 0644, 30546d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 30551da177e4SLinus Torvalds }, 30561da177e4SLinus Torvalds { 30571da177e4SLinus Torvalds .procname = "gc_interval", 30584990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 30591da177e4SLinus Torvalds .maxlen = sizeof(int), 30601da177e4SLinus Torvalds .mode = 0644, 30616d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 30621da177e4SLinus Torvalds }, 30631da177e4SLinus Torvalds { 30641da177e4SLinus Torvalds .procname = "gc_elasticity", 30654990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 30661da177e4SLinus Torvalds .maxlen = sizeof(int), 30671da177e4SLinus Torvalds .mode = 0644, 3068f3d3f616SMin Zhang .proc_handler = proc_dointvec, 30691da177e4SLinus Torvalds }, 30701da177e4SLinus Torvalds { 30711da177e4SLinus Torvalds .procname = "mtu_expires", 30724990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 30731da177e4SLinus Torvalds .maxlen = sizeof(int), 30741da177e4SLinus Torvalds .mode = 0644, 30756d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 30761da177e4SLinus Torvalds }, 30771da177e4SLinus Torvalds { 30781da177e4SLinus Torvalds .procname = "min_adv_mss", 30794990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 30801da177e4SLinus Torvalds .maxlen = sizeof(int), 30811da177e4SLinus Torvalds .mode = 0644, 3082f3d3f616SMin Zhang .proc_handler = proc_dointvec, 30831da177e4SLinus Torvalds }, 30841da177e4SLinus Torvalds { 30851da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 30864990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 30871da177e4SLinus Torvalds .maxlen = sizeof(int), 30881da177e4SLinus Torvalds .mode = 0644, 30896d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 30901da177e4SLinus Torvalds }, 3091f8572d8fSEric W. Biederman { } 30921da177e4SLinus Torvalds }; 30931da177e4SLinus Torvalds 30942c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 3095760f2d01SDaniel Lezcano { 3096760f2d01SDaniel Lezcano struct ctl_table *table; 3097760f2d01SDaniel Lezcano 3098760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 3099760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 3100760f2d01SDaniel Lezcano GFP_KERNEL); 31015ee09105SYOSHIFUJI Hideaki 31025ee09105SYOSHIFUJI Hideaki if (table) { 31035ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 3104c486da34SLucian Adrian Grijincu table[0].extra1 = net; 310586393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 31065ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 31075ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 31085ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 31095ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 31105ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 31115ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 31125ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 31139c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 3114464dc801SEric W. Biederman 3115464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 3116464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 3117464dc801SEric W. Biederman table[0].procname = NULL; 31185ee09105SYOSHIFUJI Hideaki } 31195ee09105SYOSHIFUJI Hideaki 3120760f2d01SDaniel Lezcano return table; 3121760f2d01SDaniel Lezcano } 31221da177e4SLinus Torvalds #endif 31231da177e4SLinus Torvalds 31242c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 3125cdb18761SDaniel Lezcano { 3126633d424bSPavel Emelyanov int ret = -ENOMEM; 31278ed67789SDaniel Lezcano 312886393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 312986393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 3130f2fc6a54SBenjamin Thery 3131fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 3132fc66f95cSEric Dumazet goto out_ip6_dst_ops; 3133fc66f95cSEric Dumazet 31348ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 31358ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 31368ed67789SDaniel Lezcano GFP_KERNEL); 31378ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 3138fc66f95cSEric Dumazet goto out_ip6_dst_entries; 3139d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 31408ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 3141d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 314262fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 314362fa8a84SDavid S. Miller ip6_template_metrics, true); 31448ed67789SDaniel Lezcano 31458ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 31468ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 31478ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 31488ed67789SDaniel Lezcano GFP_KERNEL); 314968fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 315068fffc67SPeter Zijlstra goto out_ip6_null_entry; 3151d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 31528ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 3153d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 315462fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 315562fa8a84SDavid S. Miller ip6_template_metrics, true); 31568ed67789SDaniel Lezcano 31578ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 31588ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 31598ed67789SDaniel Lezcano GFP_KERNEL); 316068fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 316168fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 3162d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 31638ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 3164d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 316562fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 316662fa8a84SDavid S. Miller ip6_template_metrics, true); 31678ed67789SDaniel Lezcano #endif 31688ed67789SDaniel Lezcano 3169b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 3170b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 3171b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 3172b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 3173b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 3174b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 3175b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 3176b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 3177b339a47cSPeter Zijlstra 31786891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 31796891a346SBenjamin Thery 31808ed67789SDaniel Lezcano ret = 0; 31818ed67789SDaniel Lezcano out: 31828ed67789SDaniel Lezcano return ret; 3183f2fc6a54SBenjamin Thery 318468fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 318568fffc67SPeter Zijlstra out_ip6_prohibit_entry: 318668fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 318768fffc67SPeter Zijlstra out_ip6_null_entry: 318868fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 318968fffc67SPeter Zijlstra #endif 3190fc66f95cSEric Dumazet out_ip6_dst_entries: 3191fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3192f2fc6a54SBenjamin Thery out_ip6_dst_ops: 3193f2fc6a54SBenjamin Thery goto out; 3194cdb18761SDaniel Lezcano } 3195cdb18761SDaniel Lezcano 31962c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 3197cdb18761SDaniel Lezcano { 31988ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 31998ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 32008ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 32018ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 32028ed67789SDaniel Lezcano #endif 320341bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3204cdb18761SDaniel Lezcano } 3205cdb18761SDaniel Lezcano 3206d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 3207d189634eSThomas Graf { 3208d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3209d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 3210d4beaa66SGao feng proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops); 3211d189634eSThomas Graf #endif 3212d189634eSThomas Graf return 0; 3213d189634eSThomas Graf } 3214d189634eSThomas Graf 3215d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 3216d189634eSThomas Graf { 3217d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3218ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 3219ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 3220d189634eSThomas Graf #endif 3221d189634eSThomas Graf } 3222d189634eSThomas Graf 3223cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 3224cdb18761SDaniel Lezcano .init = ip6_route_net_init, 3225cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 3226cdb18761SDaniel Lezcano }; 3227cdb18761SDaniel Lezcano 3228c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 3229c3426b47SDavid S. Miller { 3230c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 3231c3426b47SDavid S. Miller 3232c3426b47SDavid S. Miller if (!bp) 3233c3426b47SDavid S. Miller return -ENOMEM; 3234c3426b47SDavid S. Miller inet_peer_base_init(bp); 3235c3426b47SDavid S. Miller net->ipv6.peers = bp; 3236c3426b47SDavid S. Miller return 0; 3237c3426b47SDavid S. Miller } 3238c3426b47SDavid S. Miller 3239c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 3240c3426b47SDavid S. Miller { 3241c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 3242c3426b47SDavid S. Miller 3243c3426b47SDavid S. Miller net->ipv6.peers = NULL; 324456a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3245c3426b47SDavid S. Miller kfree(bp); 3246c3426b47SDavid S. Miller } 3247c3426b47SDavid S. Miller 32482b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3249c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3250c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3251c3426b47SDavid S. Miller }; 3252c3426b47SDavid S. Miller 3253d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3254d189634eSThomas Graf .init = ip6_route_net_init_late, 3255d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3256d189634eSThomas Graf }; 3257d189634eSThomas Graf 32588ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 32598ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 32608ed67789SDaniel Lezcano .priority = 0, 32618ed67789SDaniel Lezcano }; 32628ed67789SDaniel Lezcano 3263433d49c3SDaniel Lezcano int __init ip6_route_init(void) 32641da177e4SLinus Torvalds { 3265433d49c3SDaniel Lezcano int ret; 3266433d49c3SDaniel Lezcano 32679a7ec3a9SDaniel Lezcano ret = -ENOMEM; 32689a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 32699a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 32709a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 32719a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3272c19a28e1SFernando Carrijo goto out; 327314e50e57SDavid S. Miller 3274fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 32758ed67789SDaniel Lezcano if (ret) 3276bdb3289fSDaniel Lezcano goto out_kmem_cache; 3277bdb3289fSDaniel Lezcano 3278c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3279c3426b47SDavid S. Miller if (ret) 3280e8803b6cSDavid S. Miller goto out_dst_entries; 32812a0c451aSThomas Graf 32827e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 32837e52b33bSDavid S. Miller if (ret) 32847e52b33bSDavid S. Miller goto out_register_inetpeer; 3285c3426b47SDavid S. Miller 32865dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 32875dc121e9SArnaud Ebalard 32888ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 32898ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 32908ed67789SDaniel Lezcano * manually for init_net */ 3291d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 32928ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3293bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3294d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 32958ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3296d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 32978ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3298bdb3289fSDaniel Lezcano #endif 3299e8803b6cSDavid S. Miller ret = fib6_init(); 3300433d49c3SDaniel Lezcano if (ret) 33018ed67789SDaniel Lezcano goto out_register_subsys; 3302433d49c3SDaniel Lezcano 3303433d49c3SDaniel Lezcano ret = xfrm6_init(); 3304433d49c3SDaniel Lezcano if (ret) 3305e8803b6cSDavid S. Miller goto out_fib6_init; 3306c35b7e72SDaniel Lezcano 3307433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3308433d49c3SDaniel Lezcano if (ret) 3309433d49c3SDaniel Lezcano goto xfrm6_init; 33107e5449c2SDaniel Lezcano 3311d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3312d189634eSThomas Graf if (ret) 3313d189634eSThomas Graf goto fib6_rules_init; 3314d189634eSThomas Graf 3315433d49c3SDaniel Lezcano ret = -ENOBUFS; 3316c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3317c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3318c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3319d189634eSThomas Graf goto out_register_late_subsys; 3320433d49c3SDaniel Lezcano 33218ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3322cdb18761SDaniel Lezcano if (ret) 3323d189634eSThomas Graf goto out_register_late_subsys; 33248ed67789SDaniel Lezcano 3325433d49c3SDaniel Lezcano out: 3326433d49c3SDaniel Lezcano return ret; 3327433d49c3SDaniel Lezcano 3328d189634eSThomas Graf out_register_late_subsys: 3329d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3330433d49c3SDaniel Lezcano fib6_rules_init: 3331433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3332433d49c3SDaniel Lezcano xfrm6_init: 3333433d49c3SDaniel Lezcano xfrm6_fini(); 33342a0c451aSThomas Graf out_fib6_init: 33352a0c451aSThomas Graf fib6_gc_cleanup(); 33368ed67789SDaniel Lezcano out_register_subsys: 33378ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 33387e52b33bSDavid S. Miller out_register_inetpeer: 33397e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3340fc66f95cSEric Dumazet out_dst_entries: 3341fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3342433d49c3SDaniel Lezcano out_kmem_cache: 3343f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3344433d49c3SDaniel Lezcano goto out; 33451da177e4SLinus Torvalds } 33461da177e4SLinus Torvalds 33471da177e4SLinus Torvalds void ip6_route_cleanup(void) 33481da177e4SLinus Torvalds { 33498ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3350d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3351101367c2SThomas Graf fib6_rules_cleanup(); 33521da177e4SLinus Torvalds xfrm6_fini(); 33531da177e4SLinus Torvalds fib6_gc_cleanup(); 3354c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 33558ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 335641bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3357f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 33581da177e4SLinus Torvalds } 3359