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; 876*45e4fd26SMartin 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 904d8d1f30bSChangli Gao dst_hold(&rt->dst); 905c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 9061da177e4SLinus Torvalds 9074b32b5adSMartin KaFai Lau rt6_dst_from_metrics_check(rt); 908d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 909d8d1f30bSChangli Gao rt->dst.__use++; 910c71099acSThomas Graf 911c71099acSThomas Graf return rt; 912c71099acSThomas Graf } 913c71099acSThomas Graf 9148ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 9154c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 9164acad72dSPavel Emelyanov { 9174c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 9184acad72dSPavel Emelyanov } 9194acad72dSPavel Emelyanov 92072331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 92172331bc0SShmulik Ladkani struct net_device *dev, 92272331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 92372331bc0SShmulik Ladkani { 92472331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 92572331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 92672331bc0SShmulik Ladkani 92772331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 92872331bc0SShmulik Ladkani } 92972331bc0SShmulik Ladkani 930c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 931c71099acSThomas Graf { 932b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 933c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 934adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 9354c9483b2SDavid S. Miller struct flowi6 fl6 = { 9364c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 9374c9483b2SDavid S. Miller .daddr = iph->daddr, 9384c9483b2SDavid S. Miller .saddr = iph->saddr, 9396502ca52SYOSHIFUJI Hideaki / 吉藤英明 .flowlabel = ip6_flowinfo(iph), 9404c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 9414c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 942c71099acSThomas Graf }; 943adaa70bbSThomas Graf 94472331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 945c71099acSThomas Graf } 946c71099acSThomas Graf 9478ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 9484c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 949c71099acSThomas Graf { 9504c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 951c71099acSThomas Graf } 952c71099acSThomas Graf 9539c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk, 9544c9483b2SDavid S. Miller struct flowi6 *fl6) 955c71099acSThomas Graf { 956c71099acSThomas Graf int flags = 0; 957c71099acSThomas Graf 9581fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 9594dc27d1cSDavid McCullough 9604c9483b2SDavid S. Miller if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) 96177d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 962c71099acSThomas Graf 9634c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 964adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 9650c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 9660c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 967adaa70bbSThomas Graf 9684c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 9691da177e4SLinus Torvalds } 9707159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 9711da177e4SLinus Torvalds 9722774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 97314e50e57SDavid S. Miller { 9745c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 97514e50e57SDavid S. Miller struct dst_entry *new = NULL; 97614e50e57SDavid S. Miller 977f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 97814e50e57SDavid S. Miller if (rt) { 979d8d1f30bSChangli Gao new = &rt->dst; 98014e50e57SDavid S. Miller 9818104891bSSteffen Klassert memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); 9828104891bSSteffen Klassert 98314e50e57SDavid S. Miller new->__use = 1; 984352e512cSHerbert Xu new->input = dst_discard; 985aad88724SEric Dumazet new->output = dst_discard_sk; 98614e50e57SDavid S. Miller 98721efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 98821efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 98921efcfa0SEric Dumazet else 990defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 99114e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 99214e50e57SDavid S. Miller if (rt->rt6i_idev) 99314e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 99414e50e57SDavid S. Miller 9954e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 9961716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 99714e50e57SDavid S. Miller rt->rt6i_metric = 0; 99814e50e57SDavid S. Miller 99914e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 100014e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 100114e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 100214e50e57SDavid S. Miller #endif 100314e50e57SDavid S. Miller 100414e50e57SDavid S. Miller dst_free(new); 100514e50e57SDavid S. Miller } 100614e50e57SDavid S. Miller 100769ead7afSDavid S. Miller dst_release(dst_orig); 100869ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 100914e50e57SDavid S. Miller } 101014e50e57SDavid S. Miller 10111da177e4SLinus Torvalds /* 10121da177e4SLinus Torvalds * Destination cache support functions 10131da177e4SLinus Torvalds */ 10141da177e4SLinus Torvalds 10154b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt) 10164b32b5adSMartin KaFai Lau { 10174b32b5adSMartin KaFai Lau if (rt->dst.from && 10184b32b5adSMartin KaFai Lau dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from)) 10194b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true); 10204b32b5adSMartin KaFai Lau } 10214b32b5adSMartin KaFai Lau 10221da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 10231da177e4SLinus Torvalds { 10241da177e4SLinus Torvalds struct rt6_info *rt; 10251da177e4SLinus Torvalds 10261da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 10271da177e4SLinus Torvalds 10286f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 10296f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 10306f3118b5SNicolas Dichtel * into this function always. 10316f3118b5SNicolas Dichtel */ 1032e3bc10bdSHannes Frederic Sowa if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) 10331da177e4SLinus Torvalds return NULL; 1034e3bc10bdSHannes Frederic Sowa 1035e3bc10bdSHannes Frederic Sowa if (rt6_check_expired(rt)) 1036e3bc10bdSHannes Frederic Sowa return NULL; 1037e3bc10bdSHannes Frederic Sowa 10384b32b5adSMartin KaFai Lau rt6_dst_from_metrics_check(rt); 10394b32b5adSMartin KaFai Lau 1040e3bc10bdSHannes Frederic Sowa return dst; 10411da177e4SLinus Torvalds } 10421da177e4SLinus Torvalds 10431da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 10441da177e4SLinus Torvalds { 10451da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 10461da177e4SLinus Torvalds 10471da177e4SLinus Torvalds if (rt) { 104854c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 104954c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1050e0a1ad73SThomas Graf ip6_del_rt(rt); 105154c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 10521da177e4SLinus Torvalds } 105354c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 105454c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 105554c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 105654c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 105754c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 105854c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 10591da177e4SLinus Torvalds } 10601da177e4SLinus Torvalds 10611da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 10621da177e4SLinus Torvalds { 10631da177e4SLinus Torvalds struct rt6_info *rt; 10641da177e4SLinus Torvalds 10653ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 10661da177e4SLinus Torvalds 1067adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 10681da177e4SLinus Torvalds if (rt) { 10691eb4f758SHannes Frederic Sowa if (rt->rt6i_flags & RTF_CACHE) { 10701eb4f758SHannes Frederic Sowa dst_hold(&rt->dst); 10711eb4f758SHannes Frederic Sowa if (ip6_del_rt(rt)) 10721eb4f758SHannes Frederic Sowa dst_free(&rt->dst); 10731eb4f758SHannes Frederic Sowa } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) { 10741da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 10751da177e4SLinus Torvalds } 10761da177e4SLinus Torvalds } 10771eb4f758SHannes Frederic Sowa } 10781da177e4SLinus Torvalds 1079*45e4fd26SMartin KaFai Lau static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) 1080*45e4fd26SMartin KaFai Lau { 1081*45e4fd26SMartin KaFai Lau struct net *net = dev_net(rt->dst.dev); 1082*45e4fd26SMartin KaFai Lau 1083*45e4fd26SMartin KaFai Lau rt->rt6i_flags |= RTF_MODIFIED; 1084*45e4fd26SMartin KaFai Lau rt->rt6i_pmtu = mtu; 1085*45e4fd26SMartin KaFai Lau rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); 1086*45e4fd26SMartin KaFai Lau } 1087*45e4fd26SMartin KaFai Lau 1088*45e4fd26SMartin KaFai Lau static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, 1089*45e4fd26SMartin KaFai Lau const struct ipv6hdr *iph, u32 mtu) 10901da177e4SLinus Torvalds { 10911da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info *)dst; 10921da177e4SLinus Torvalds 1093*45e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_LOCAL) 1094*45e4fd26SMartin KaFai Lau return; 1095*45e4fd26SMartin KaFai Lau 109681aded24SDavid S. Miller dst_confirm(dst); 1097*45e4fd26SMartin KaFai Lau mtu = max_t(u32, mtu, IPV6_MIN_MTU); 1098*45e4fd26SMartin KaFai Lau if (mtu >= dst_mtu(dst)) 1099*45e4fd26SMartin KaFai Lau return; 110081aded24SDavid S. Miller 1101*45e4fd26SMartin KaFai Lau if (rt6->rt6i_flags & RTF_CACHE) { 1102*45e4fd26SMartin KaFai Lau rt6_do_update_pmtu(rt6, mtu); 1103*45e4fd26SMartin KaFai Lau } else { 1104*45e4fd26SMartin KaFai Lau const struct in6_addr *daddr, *saddr; 1105*45e4fd26SMartin KaFai Lau struct rt6_info *nrt6; 11069d289715SHagen Paul Pfeifer 1107*45e4fd26SMartin KaFai Lau if (iph) { 1108*45e4fd26SMartin KaFai Lau daddr = &iph->daddr; 1109*45e4fd26SMartin KaFai Lau saddr = &iph->saddr; 1110*45e4fd26SMartin KaFai Lau } else if (sk) { 1111*45e4fd26SMartin KaFai Lau daddr = &sk->sk_v6_daddr; 1112*45e4fd26SMartin KaFai Lau saddr = &inet6_sk(sk)->saddr; 1113*45e4fd26SMartin KaFai Lau } else { 1114*45e4fd26SMartin KaFai Lau return; 11151da177e4SLinus Torvalds } 1116*45e4fd26SMartin KaFai Lau nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr); 1117*45e4fd26SMartin KaFai Lau if (nrt6) { 1118*45e4fd26SMartin KaFai Lau rt6_do_update_pmtu(nrt6, mtu); 1119*45e4fd26SMartin KaFai Lau 1120*45e4fd26SMartin KaFai Lau /* ip6_ins_rt(nrt6) will bump the 1121*45e4fd26SMartin KaFai Lau * rt6->rt6i_node->fn_sernum 1122*45e4fd26SMartin KaFai Lau * which will fail the next rt6_check() and 1123*45e4fd26SMartin KaFai Lau * invalidate the sk->sk_dst_cache. 1124*45e4fd26SMartin KaFai Lau */ 1125*45e4fd26SMartin KaFai Lau ip6_ins_rt(nrt6); 1126*45e4fd26SMartin KaFai Lau } 1127*45e4fd26SMartin KaFai Lau } 1128*45e4fd26SMartin KaFai Lau } 1129*45e4fd26SMartin KaFai Lau 1130*45e4fd26SMartin KaFai Lau static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 1131*45e4fd26SMartin KaFai Lau struct sk_buff *skb, u32 mtu) 1132*45e4fd26SMartin KaFai Lau { 1133*45e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, sk, skb ? ipv6_hdr(skb) : NULL, mtu); 11341da177e4SLinus Torvalds } 11351da177e4SLinus Torvalds 113642ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 113742ae66c8SDavid S. Miller int oif, u32 mark) 113881aded24SDavid S. Miller { 113981aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 114081aded24SDavid S. Miller struct dst_entry *dst; 114181aded24SDavid S. Miller struct flowi6 fl6; 114281aded24SDavid S. Miller 114381aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 114481aded24SDavid S. Miller fl6.flowi6_oif = oif; 11451b3c61dcSLorenzo Colitti fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark); 114681aded24SDavid S. Miller fl6.daddr = iph->daddr; 114781aded24SDavid S. Miller fl6.saddr = iph->saddr; 11486502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 114981aded24SDavid S. Miller 115081aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 115181aded24SDavid S. Miller if (!dst->error) 1152*45e4fd26SMartin KaFai Lau __ip6_rt_update_pmtu(dst, NULL, iph, ntohl(mtu)); 115381aded24SDavid S. Miller dst_release(dst); 115481aded24SDavid S. Miller } 115581aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 115681aded24SDavid S. Miller 115781aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 115881aded24SDavid S. Miller { 115981aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 116081aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 116181aded24SDavid S. Miller } 116281aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 116381aded24SDavid S. Miller 1164b55b76b2SDuan Jiong /* Handle redirects */ 1165b55b76b2SDuan Jiong struct ip6rd_flowi { 1166b55b76b2SDuan Jiong struct flowi6 fl6; 1167b55b76b2SDuan Jiong struct in6_addr gateway; 1168b55b76b2SDuan Jiong }; 1169b55b76b2SDuan Jiong 1170b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net, 1171b55b76b2SDuan Jiong struct fib6_table *table, 1172b55b76b2SDuan Jiong struct flowi6 *fl6, 1173b55b76b2SDuan Jiong int flags) 1174b55b76b2SDuan Jiong { 1175b55b76b2SDuan Jiong struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 1176b55b76b2SDuan Jiong struct rt6_info *rt; 1177b55b76b2SDuan Jiong struct fib6_node *fn; 1178b55b76b2SDuan Jiong 1179b55b76b2SDuan Jiong /* Get the "current" route for this destination and 1180b55b76b2SDuan Jiong * check if the redirect has come from approriate router. 1181b55b76b2SDuan Jiong * 1182b55b76b2SDuan Jiong * RFC 4861 specifies that redirects should only be 1183b55b76b2SDuan Jiong * accepted if they come from the nexthop to the target. 1184b55b76b2SDuan Jiong * Due to the way the routes are chosen, this notion 1185b55b76b2SDuan Jiong * is a bit fuzzy and one might need to check all possible 1186b55b76b2SDuan Jiong * routes. 1187b55b76b2SDuan Jiong */ 1188b55b76b2SDuan Jiong 1189b55b76b2SDuan Jiong read_lock_bh(&table->tb6_lock); 1190b55b76b2SDuan Jiong fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1191b55b76b2SDuan Jiong restart: 1192b55b76b2SDuan Jiong for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1193b55b76b2SDuan Jiong if (rt6_check_expired(rt)) 1194b55b76b2SDuan Jiong continue; 1195b55b76b2SDuan Jiong if (rt->dst.error) 1196b55b76b2SDuan Jiong break; 1197b55b76b2SDuan Jiong if (!(rt->rt6i_flags & RTF_GATEWAY)) 1198b55b76b2SDuan Jiong continue; 1199b55b76b2SDuan Jiong if (fl6->flowi6_oif != rt->dst.dev->ifindex) 1200b55b76b2SDuan Jiong continue; 1201b55b76b2SDuan Jiong if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) 1202b55b76b2SDuan Jiong continue; 1203b55b76b2SDuan Jiong break; 1204b55b76b2SDuan Jiong } 1205b55b76b2SDuan Jiong 1206b55b76b2SDuan Jiong if (!rt) 1207b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1208b55b76b2SDuan Jiong else if (rt->dst.error) { 1209b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1210b0a1ba59SMartin KaFai Lau goto out; 1211b0a1ba59SMartin KaFai Lau } 1212b0a1ba59SMartin KaFai Lau 1213b0a1ba59SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1214a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1215a3c00e46SMartin KaFai Lau if (fn) 1216a3c00e46SMartin KaFai Lau goto restart; 1217b55b76b2SDuan Jiong } 1218a3c00e46SMartin KaFai Lau 1219b0a1ba59SMartin KaFai Lau out: 1220b55b76b2SDuan Jiong dst_hold(&rt->dst); 1221b55b76b2SDuan Jiong 1222b55b76b2SDuan Jiong read_unlock_bh(&table->tb6_lock); 1223b55b76b2SDuan Jiong 1224b55b76b2SDuan Jiong return rt; 1225b55b76b2SDuan Jiong }; 1226b55b76b2SDuan Jiong 1227b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net, 1228b55b76b2SDuan Jiong const struct flowi6 *fl6, 1229b55b76b2SDuan Jiong const struct in6_addr *gateway) 1230b55b76b2SDuan Jiong { 1231b55b76b2SDuan Jiong int flags = RT6_LOOKUP_F_HAS_SADDR; 1232b55b76b2SDuan Jiong struct ip6rd_flowi rdfl; 1233b55b76b2SDuan Jiong 1234b55b76b2SDuan Jiong rdfl.fl6 = *fl6; 1235b55b76b2SDuan Jiong rdfl.gateway = *gateway; 1236b55b76b2SDuan Jiong 1237b55b76b2SDuan Jiong return fib6_rule_lookup(net, &rdfl.fl6, 1238b55b76b2SDuan Jiong flags, __ip6_route_redirect); 1239b55b76b2SDuan Jiong } 1240b55b76b2SDuan Jiong 12413a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 12423a5ad2eeSDavid S. Miller { 12433a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 12443a5ad2eeSDavid S. Miller struct dst_entry *dst; 12453a5ad2eeSDavid S. Miller struct flowi6 fl6; 12463a5ad2eeSDavid S. Miller 12473a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 1248e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 12493a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 12503a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 12513a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 12523a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 12536502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 12543a5ad2eeSDavid S. Miller 1255b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); 12566700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 12573a5ad2eeSDavid S. Miller dst_release(dst); 12583a5ad2eeSDavid S. Miller } 12593a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 12603a5ad2eeSDavid S. Miller 1261c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, 1262c92a59ecSDuan Jiong u32 mark) 1263c92a59ecSDuan Jiong { 1264c92a59ecSDuan Jiong const struct ipv6hdr *iph = ipv6_hdr(skb); 1265c92a59ecSDuan Jiong const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 1266c92a59ecSDuan Jiong struct dst_entry *dst; 1267c92a59ecSDuan Jiong struct flowi6 fl6; 1268c92a59ecSDuan Jiong 1269c92a59ecSDuan Jiong memset(&fl6, 0, sizeof(fl6)); 1270e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 1271c92a59ecSDuan Jiong fl6.flowi6_oif = oif; 1272c92a59ecSDuan Jiong fl6.flowi6_mark = mark; 1273c92a59ecSDuan Jiong fl6.daddr = msg->dest; 1274c92a59ecSDuan Jiong fl6.saddr = iph->daddr; 1275c92a59ecSDuan Jiong 1276b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &iph->saddr); 1277c92a59ecSDuan Jiong rt6_do_redirect(dst, NULL, skb); 1278c92a59ecSDuan Jiong dst_release(dst); 1279c92a59ecSDuan Jiong } 1280c92a59ecSDuan Jiong 12813a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 12823a5ad2eeSDavid S. Miller { 12833a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 12843a5ad2eeSDavid S. Miller } 12853a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 12863a5ad2eeSDavid S. Miller 12870dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 12881da177e4SLinus Torvalds { 12890dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 12900dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 12910dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 12920dbaee3bSDavid S. Miller 12931da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 12941da177e4SLinus Torvalds 12955578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 12965578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 12971da177e4SLinus Torvalds 12981da177e4SLinus Torvalds /* 12991da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 13001da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 13011da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 13021da177e4SLinus Torvalds * rely only on pmtu discovery" 13031da177e4SLinus Torvalds */ 13041da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 13051da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 13061da177e4SLinus Torvalds return mtu; 13071da177e4SLinus Torvalds } 13081da177e4SLinus Torvalds 1309ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1310d33e4553SDavid S. Miller { 13114b32b5adSMartin KaFai Lau const struct rt6_info *rt = (const struct rt6_info *)dst; 13124b32b5adSMartin KaFai Lau unsigned int mtu = rt->rt6i_pmtu; 1313d33e4553SDavid S. Miller struct inet6_dev *idev; 1314618f9bc7SSteffen Klassert 1315618f9bc7SSteffen Klassert if (mtu) 131630f78d8eSEric Dumazet goto out; 1317618f9bc7SSteffen Klassert 13184b32b5adSMartin KaFai Lau mtu = dst_metric_raw(dst, RTAX_MTU); 13194b32b5adSMartin KaFai Lau if (mtu) 13204b32b5adSMartin KaFai Lau goto out; 13214b32b5adSMartin KaFai Lau 1322618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1323d33e4553SDavid S. Miller 1324d33e4553SDavid S. Miller rcu_read_lock(); 1325d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1326d33e4553SDavid S. Miller if (idev) 1327d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1328d33e4553SDavid S. Miller rcu_read_unlock(); 1329d33e4553SDavid S. Miller 133030f78d8eSEric Dumazet out: 133130f78d8eSEric Dumazet return min_t(unsigned int, mtu, IP6_MAX_MTU); 1332d33e4553SDavid S. Miller } 1333d33e4553SDavid S. Miller 13343b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 13353b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 13365d0bbeebSThomas Graf 13373b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 133887a11578SDavid S. Miller struct flowi6 *fl6) 13391da177e4SLinus Torvalds { 134087a11578SDavid S. Miller struct dst_entry *dst; 13411da177e4SLinus Torvalds struct rt6_info *rt; 13421da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1343c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 13441da177e4SLinus Torvalds 134538308473SDavid S. Miller if (unlikely(!idev)) 1346122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 13471da177e4SLinus Torvalds 13488b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, dev, 0, NULL); 134938308473SDavid S. Miller if (unlikely(!rt)) { 13501da177e4SLinus Torvalds in6_dev_put(idev); 135187a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 13521da177e4SLinus Torvalds goto out; 13531da177e4SLinus Torvalds } 13541da177e4SLinus Torvalds 13558e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 13568e2ec639SYan, Zheng rt->dst.output = ip6_output; 1357d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 1358550bab42SJulian Anastasov rt->rt6i_gateway = fl6->daddr; 135987a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 13608e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 13618e2ec639SYan, Zheng rt->rt6i_idev = idev; 136214edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 13631da177e4SLinus Torvalds 13643b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1365d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1366d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 13673b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 13681da177e4SLinus Torvalds 13695578689aSDaniel Lezcano fib6_force_start_gc(net); 13701da177e4SLinus Torvalds 137187a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 137287a11578SDavid S. Miller 13731da177e4SLinus Torvalds out: 137487a11578SDavid S. Miller return dst; 13751da177e4SLinus Torvalds } 13761da177e4SLinus Torvalds 13773d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 13781da177e4SLinus Torvalds { 1379e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 13803d0f24a7SStephen Hemminger int more = 0; 13811da177e4SLinus Torvalds 13823b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 13833b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 13845d0bbeebSThomas Graf 13851da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 13861da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 13871da177e4SLinus Torvalds *pprev = dst->next; 13881da177e4SLinus Torvalds dst_free(dst); 13891da177e4SLinus Torvalds } else { 13901da177e4SLinus Torvalds pprev = &dst->next; 13913d0f24a7SStephen Hemminger ++more; 13921da177e4SLinus Torvalds } 13931da177e4SLinus Torvalds } 13941da177e4SLinus Torvalds 13953b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 13965d0bbeebSThomas Graf 13973d0f24a7SStephen Hemminger return more; 13981da177e4SLinus Torvalds } 13991da177e4SLinus Torvalds 14001e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 14011e493d19SDavid S. Miller void *arg) 14021e493d19SDavid S. Miller { 14031e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 14041e493d19SDavid S. Miller 14051e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 14061e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 14071e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 14081e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 14091e493d19SDavid S. Miller if (func(rt, arg)) { 14101e493d19SDavid S. Miller *pprev = dst->next; 14111e493d19SDavid S. Miller dst_free(dst); 14121e493d19SDavid S. Miller } else { 14131e493d19SDavid S. Miller pprev = &dst->next; 14141e493d19SDavid S. Miller } 14151e493d19SDavid S. Miller } 14161e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 14171e493d19SDavid S. Miller } 14181e493d19SDavid S. Miller 1419569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 14201da177e4SLinus Torvalds { 142186393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 14227019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 14237019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 14247019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 14257019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 14267019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1427fc66f95cSEric Dumazet int entries; 14281da177e4SLinus Torvalds 1429fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 143049a18d86SMichal Kubeček if (time_after(rt_last_gc + rt_min_interval, jiffies) && 1431fc66f95cSEric Dumazet entries <= rt_max_size) 14321da177e4SLinus Torvalds goto out; 14331da177e4SLinus Torvalds 14346891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 143514956643SLi RongQing fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true); 1436fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1437fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 14387019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 14391da177e4SLinus Torvalds out: 14407019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1441fc66f95cSEric Dumazet return entries > rt_max_size; 14421da177e4SLinus Torvalds } 14431da177e4SLinus Torvalds 1444e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc, 1445e715b6d3SFlorian Westphal const struct fib6_config *cfg) 1446e715b6d3SFlorian Westphal { 1447e715b6d3SFlorian Westphal struct nlattr *nla; 1448e715b6d3SFlorian Westphal int remaining; 1449e715b6d3SFlorian Westphal u32 *mp; 1450e715b6d3SFlorian Westphal 145163159f29SIan Morris if (!cfg->fc_mx) 1452e715b6d3SFlorian Westphal return 0; 1453e715b6d3SFlorian Westphal 1454e715b6d3SFlorian Westphal mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 1455e715b6d3SFlorian Westphal if (unlikely(!mp)) 1456e715b6d3SFlorian Westphal return -ENOMEM; 1457e715b6d3SFlorian Westphal 1458e715b6d3SFlorian Westphal nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 1459e715b6d3SFlorian Westphal int type = nla_type(nla); 1460e715b6d3SFlorian Westphal 1461e715b6d3SFlorian Westphal if (type) { 1462ea697639SDaniel Borkmann u32 val; 1463ea697639SDaniel Borkmann 1464e715b6d3SFlorian Westphal if (unlikely(type > RTAX_MAX)) 1465e715b6d3SFlorian Westphal goto err; 1466ea697639SDaniel Borkmann if (type == RTAX_CC_ALGO) { 1467ea697639SDaniel Borkmann char tmp[TCP_CA_NAME_MAX]; 1468e715b6d3SFlorian Westphal 1469ea697639SDaniel Borkmann nla_strlcpy(tmp, nla, sizeof(tmp)); 1470ea697639SDaniel Borkmann val = tcp_ca_get_key_by_name(tmp); 1471ea697639SDaniel Borkmann if (val == TCP_CA_UNSPEC) 1472ea697639SDaniel Borkmann goto err; 1473ea697639SDaniel Borkmann } else { 1474ea697639SDaniel Borkmann val = nla_get_u32(nla); 1475ea697639SDaniel Borkmann } 1476ea697639SDaniel Borkmann 1477ea697639SDaniel Borkmann mp[type - 1] = val; 1478e715b6d3SFlorian Westphal __set_bit(type - 1, mxc->mx_valid); 1479e715b6d3SFlorian Westphal } 1480e715b6d3SFlorian Westphal } 1481e715b6d3SFlorian Westphal 1482e715b6d3SFlorian Westphal mxc->mx = mp; 1483e715b6d3SFlorian Westphal 1484e715b6d3SFlorian Westphal return 0; 1485e715b6d3SFlorian Westphal err: 1486e715b6d3SFlorian Westphal kfree(mp); 1487e715b6d3SFlorian Westphal return -EINVAL; 1488e715b6d3SFlorian Westphal } 14891da177e4SLinus Torvalds 149086872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg) 14911da177e4SLinus Torvalds { 14921da177e4SLinus Torvalds int err; 14935578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 14941da177e4SLinus Torvalds struct rt6_info *rt = NULL; 14951da177e4SLinus Torvalds struct net_device *dev = NULL; 14961da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1497c71099acSThomas Graf struct fib6_table *table; 1498e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 14991da177e4SLinus Torvalds int addr_type; 15001da177e4SLinus Torvalds 150186872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 15021da177e4SLinus Torvalds return -EINVAL; 15031da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 150486872cb5SThomas Graf if (cfg->fc_src_len) 15051da177e4SLinus Torvalds return -EINVAL; 15061da177e4SLinus Torvalds #endif 150786872cb5SThomas Graf if (cfg->fc_ifindex) { 15081da177e4SLinus Torvalds err = -ENODEV; 15095578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 15101da177e4SLinus Torvalds if (!dev) 15111da177e4SLinus Torvalds goto out; 15121da177e4SLinus Torvalds idev = in6_dev_get(dev); 15131da177e4SLinus Torvalds if (!idev) 15141da177e4SLinus Torvalds goto out; 15151da177e4SLinus Torvalds } 15161da177e4SLinus Torvalds 151786872cb5SThomas Graf if (cfg->fc_metric == 0) 151886872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 15191da177e4SLinus Torvalds 1520c71099acSThomas Graf err = -ENOBUFS; 152138308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1522d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1523d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 152438308473SDavid S. Miller if (!table) { 1525f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1526d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1527d71314b4SMatti Vaittinen } 1528d71314b4SMatti Vaittinen } else { 1529d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1530d71314b4SMatti Vaittinen } 153138308473SDavid S. Miller 153238308473SDavid S. Miller if (!table) 1533c71099acSThomas Graf goto out; 1534c71099acSThomas Graf 1535c88507fbSSabrina Dubroca rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table); 15361da177e4SLinus Torvalds 153738308473SDavid S. Miller if (!rt) { 15381da177e4SLinus Torvalds err = -ENOMEM; 15391da177e4SLinus Torvalds goto out; 15401da177e4SLinus Torvalds } 15411da177e4SLinus Torvalds 15421716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 15431716a961SGao feng rt6_set_expires(rt, jiffies + 15441716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 15451716a961SGao feng else 15461716a961SGao feng rt6_clean_expires(rt); 15471da177e4SLinus Torvalds 154886872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 154986872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 155086872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 155186872cb5SThomas Graf 155286872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 15531da177e4SLinus Torvalds 15541da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1555d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1556ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1557ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 15581da177e4SLinus Torvalds else 1559d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 15601da177e4SLinus Torvalds 1561d8d1f30bSChangli Gao rt->dst.output = ip6_output; 15621da177e4SLinus Torvalds 156386872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 156486872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 1565afc4eef8SMartin KaFai Lau if (rt->rt6i_dst.plen == 128) 156611d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 15671da177e4SLinus Torvalds 15681da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 156986872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 157086872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 15711da177e4SLinus Torvalds #endif 15721da177e4SLinus Torvalds 157386872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 15741da177e4SLinus Torvalds 15751da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 15761da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 15771da177e4SLinus Torvalds */ 157886872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 157938308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 158038308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 158138308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 15821da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 15835578689aSDaniel Lezcano if (dev != net->loopback_dev) { 15841da177e4SLinus Torvalds if (dev) { 15851da177e4SLinus Torvalds dev_put(dev); 15861da177e4SLinus Torvalds in6_dev_put(idev); 15871da177e4SLinus Torvalds } 15885578689aSDaniel Lezcano dev = net->loopback_dev; 15891da177e4SLinus Torvalds dev_hold(dev); 15901da177e4SLinus Torvalds idev = in6_dev_get(dev); 15911da177e4SLinus Torvalds if (!idev) { 15921da177e4SLinus Torvalds err = -ENODEV; 15931da177e4SLinus Torvalds goto out; 15941da177e4SLinus Torvalds } 15951da177e4SLinus Torvalds } 15961da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1597ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1598ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1599ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 1600aad88724SEric Dumazet rt->dst.output = dst_discard_sk; 16017150aedeSKamala R rt->dst.input = dst_discard; 1602ef2c7d7bSNicolas Dichtel break; 1603ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1604ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 16057150aedeSKamala R rt->dst.output = ip6_pkt_prohibit_out; 16067150aedeSKamala R rt->dst.input = ip6_pkt_prohibit; 1607ef2c7d7bSNicolas Dichtel break; 1608b4949ab2SNicolas Dichtel case RTN_THROW: 1609ef2c7d7bSNicolas Dichtel default: 16107150aedeSKamala R rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN 16117150aedeSKamala R : -ENETUNREACH; 16127150aedeSKamala R rt->dst.output = ip6_pkt_discard_out; 16137150aedeSKamala R rt->dst.input = ip6_pkt_discard; 1614ef2c7d7bSNicolas Dichtel break; 1615ef2c7d7bSNicolas Dichtel } 16161da177e4SLinus Torvalds goto install_route; 16171da177e4SLinus Torvalds } 16181da177e4SLinus Torvalds 161986872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1620b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 16211da177e4SLinus Torvalds int gwa_type; 16221da177e4SLinus Torvalds 162386872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 162448ed7b26SFlorian Westphal 162548ed7b26SFlorian Westphal /* if gw_addr is local we will fail to detect this in case 162648ed7b26SFlorian Westphal * address is still TENTATIVE (DAD in progress). rt6_lookup() 162748ed7b26SFlorian Westphal * will return already-added prefix route via interface that 162848ed7b26SFlorian Westphal * prefix route was assigned to, which might be non-loopback. 162948ed7b26SFlorian Westphal */ 163048ed7b26SFlorian Westphal err = -EINVAL; 163148ed7b26SFlorian Westphal if (ipv6_chk_addr_and_flags(net, gw_addr, NULL, 0, 0)) 163248ed7b26SFlorian Westphal goto out; 163348ed7b26SFlorian Westphal 16344e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 16351da177e4SLinus Torvalds gwa_type = ipv6_addr_type(gw_addr); 16361da177e4SLinus Torvalds 16371da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 16381da177e4SLinus Torvalds struct rt6_info *grt; 16391da177e4SLinus Torvalds 16401da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 16411da177e4SLinus Torvalds addresses as nexthop address. 16421da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 16431da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 16441da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 16451da177e4SLinus Torvalds some exceptions. --ANK 16461da177e4SLinus Torvalds */ 16471da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 16481da177e4SLinus Torvalds goto out; 16491da177e4SLinus Torvalds 16505578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 16511da177e4SLinus Torvalds 16521da177e4SLinus Torvalds err = -EHOSTUNREACH; 165338308473SDavid S. Miller if (!grt) 16541da177e4SLinus Torvalds goto out; 16551da177e4SLinus Torvalds if (dev) { 1656d1918542SDavid S. Miller if (dev != grt->dst.dev) { 165794e187c0SAmerigo Wang ip6_rt_put(grt); 16581da177e4SLinus Torvalds goto out; 16591da177e4SLinus Torvalds } 16601da177e4SLinus Torvalds } else { 1661d1918542SDavid S. Miller dev = grt->dst.dev; 16621da177e4SLinus Torvalds idev = grt->rt6i_idev; 16631da177e4SLinus Torvalds dev_hold(dev); 16641da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 16651da177e4SLinus Torvalds } 16661da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 16671da177e4SLinus Torvalds err = 0; 166894e187c0SAmerigo Wang ip6_rt_put(grt); 16691da177e4SLinus Torvalds 16701da177e4SLinus Torvalds if (err) 16711da177e4SLinus Torvalds goto out; 16721da177e4SLinus Torvalds } 16731da177e4SLinus Torvalds err = -EINVAL; 167438308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 16751da177e4SLinus Torvalds goto out; 16761da177e4SLinus Torvalds } 16771da177e4SLinus Torvalds 16781da177e4SLinus Torvalds err = -ENODEV; 167938308473SDavid S. Miller if (!dev) 16801da177e4SLinus Torvalds goto out; 16811da177e4SLinus Torvalds 1682c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1683c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1684c3968a85SDaniel Walter err = -EINVAL; 1685c3968a85SDaniel Walter goto out; 1686c3968a85SDaniel Walter } 16874e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1688c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1689c3968a85SDaniel Walter } else 1690c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1691c3968a85SDaniel Walter 169286872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 16931da177e4SLinus Torvalds 16941da177e4SLinus Torvalds install_route: 1695d8d1f30bSChangli Gao rt->dst.dev = dev; 16961da177e4SLinus Torvalds rt->rt6i_idev = idev; 1697c71099acSThomas Graf rt->rt6i_table = table; 169863152fc0SDaniel Lezcano 1699c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 170063152fc0SDaniel Lezcano 1701e715b6d3SFlorian Westphal err = ip6_convert_metrics(&mxc, cfg); 1702e715b6d3SFlorian Westphal if (err) 1703e715b6d3SFlorian Westphal goto out; 17041da177e4SLinus Torvalds 1705e715b6d3SFlorian Westphal err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); 1706e715b6d3SFlorian Westphal 1707e715b6d3SFlorian Westphal kfree(mxc.mx); 1708e715b6d3SFlorian Westphal return err; 17091da177e4SLinus Torvalds out: 17101da177e4SLinus Torvalds if (dev) 17111da177e4SLinus Torvalds dev_put(dev); 17121da177e4SLinus Torvalds if (idev) 17131da177e4SLinus Torvalds in6_dev_put(idev); 17141da177e4SLinus Torvalds if (rt) 1715d8d1f30bSChangli Gao dst_free(&rt->dst); 17161da177e4SLinus Torvalds return err; 17171da177e4SLinus Torvalds } 17181da177e4SLinus Torvalds 171986872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 17201da177e4SLinus Torvalds { 17211da177e4SLinus Torvalds int err; 1722c71099acSThomas Graf struct fib6_table *table; 1723d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 17241da177e4SLinus Torvalds 17256825a26cSGao feng if (rt == net->ipv6.ip6_null_entry) { 17266825a26cSGao feng err = -ENOENT; 17276825a26cSGao feng goto out; 17286825a26cSGao feng } 17296c813a72SPatrick McHardy 1730c71099acSThomas Graf table = rt->rt6i_table; 1731c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 173286872cb5SThomas Graf err = fib6_del(rt, info); 1733c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 17341da177e4SLinus Torvalds 17356825a26cSGao feng out: 173694e187c0SAmerigo Wang ip6_rt_put(rt); 17371da177e4SLinus Torvalds return err; 17381da177e4SLinus Torvalds } 17391da177e4SLinus Torvalds 1740e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 1741e0a1ad73SThomas Graf { 17424d1169c1SDenis V. Lunev struct nl_info info = { 1743d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 17444d1169c1SDenis V. Lunev }; 1745528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 1746e0a1ad73SThomas Graf } 1747e0a1ad73SThomas Graf 174886872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 17491da177e4SLinus Torvalds { 1750c71099acSThomas Graf struct fib6_table *table; 17511da177e4SLinus Torvalds struct fib6_node *fn; 17521da177e4SLinus Torvalds struct rt6_info *rt; 17531da177e4SLinus Torvalds int err = -ESRCH; 17541da177e4SLinus Torvalds 17555578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 175638308473SDavid S. Miller if (!table) 1757c71099acSThomas Graf return err; 17581da177e4SLinus Torvalds 1759c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1760c71099acSThomas Graf 1761c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 176286872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 176386872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 17641da177e4SLinus Torvalds 17651da177e4SLinus Torvalds if (fn) { 1766d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 17671f56a01fSMartin KaFai Lau if ((rt->rt6i_flags & RTF_CACHE) && 17681f56a01fSMartin KaFai Lau !(cfg->fc_flags & RTF_CACHE)) 17691f56a01fSMartin KaFai Lau continue; 177086872cb5SThomas Graf if (cfg->fc_ifindex && 1771d1918542SDavid S. Miller (!rt->dst.dev || 1772d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 17731da177e4SLinus Torvalds continue; 177486872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 177586872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 17761da177e4SLinus Torvalds continue; 177786872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 17781da177e4SLinus Torvalds continue; 1779d8d1f30bSChangli Gao dst_hold(&rt->dst); 1780c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 17811da177e4SLinus Torvalds 178286872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 17831da177e4SLinus Torvalds } 17841da177e4SLinus Torvalds } 1785c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 17861da177e4SLinus Torvalds 17871da177e4SLinus Torvalds return err; 17881da177e4SLinus Torvalds } 17891da177e4SLinus Torvalds 17906700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 1791a6279458SYOSHIFUJI Hideaki { 1792e8599ff4SDavid S. Miller struct net *net = dev_net(skb->dev); 1793a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 1794e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 1795e8599ff4SDavid S. Miller struct ndisc_options ndopts; 1796e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 1797e8599ff4SDavid S. Miller struct neighbour *neigh; 179871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 struct rd_msg *msg; 17996e157b6aSDavid S. Miller int optlen, on_link; 18006e157b6aSDavid S. Miller u8 *lladdr; 1801e8599ff4SDavid S. Miller 180229a3cad5SSimon Horman optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 180371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 optlen -= sizeof(*msg); 1804e8599ff4SDavid S. Miller 1805e8599ff4SDavid S. Miller if (optlen < 0) { 18066e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 1807e8599ff4SDavid S. Miller return; 1808e8599ff4SDavid S. Miller } 1809e8599ff4SDavid S. Miller 181071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 msg = (struct rd_msg *)icmp6_hdr(skb); 1811e8599ff4SDavid S. Miller 181271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_is_multicast(&msg->dest)) { 18136e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 1814e8599ff4SDavid S. Miller return; 1815e8599ff4SDavid S. Miller } 1816e8599ff4SDavid S. Miller 18176e157b6aSDavid S. Miller on_link = 0; 181871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_equal(&msg->dest, &msg->target)) { 1819e8599ff4SDavid S. Miller on_link = 1; 182071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 } else if (ipv6_addr_type(&msg->target) != 1821e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 18226e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 1823e8599ff4SDavid S. Miller return; 1824e8599ff4SDavid S. Miller } 1825e8599ff4SDavid S. Miller 1826e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 1827e8599ff4SDavid S. Miller if (!in6_dev) 1828e8599ff4SDavid S. Miller return; 1829e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 1830e8599ff4SDavid S. Miller return; 1831e8599ff4SDavid S. Miller 1832e8599ff4SDavid S. Miller /* RFC2461 8.1: 1833e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 1834e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 1835e8599ff4SDavid S. Miller */ 1836e8599ff4SDavid S. Miller 183771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) { 1838e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 1839e8599ff4SDavid S. Miller return; 1840e8599ff4SDavid S. Miller } 18416e157b6aSDavid S. Miller 18426e157b6aSDavid S. Miller lladdr = NULL; 1843e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 1844e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 1845e8599ff4SDavid S. Miller skb->dev); 1846e8599ff4SDavid S. Miller if (!lladdr) { 1847e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 1848e8599ff4SDavid S. Miller return; 1849e8599ff4SDavid S. Miller } 1850e8599ff4SDavid S. Miller } 1851e8599ff4SDavid S. Miller 18526e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 18536e157b6aSDavid S. Miller if (rt == net->ipv6.ip6_null_entry) { 18546e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 18556e157b6aSDavid S. Miller return; 18566e157b6aSDavid S. Miller } 18576e157b6aSDavid S. Miller 18586e157b6aSDavid S. Miller /* Redirect received -> path was valid. 18596e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 18606e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 18616e157b6aSDavid S. Miller */ 18626e157b6aSDavid S. Miller dst_confirm(&rt->dst); 18636e157b6aSDavid S. Miller 186471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 1865e8599ff4SDavid S. Miller if (!neigh) 1866e8599ff4SDavid S. Miller return; 1867e8599ff4SDavid S. Miller 18681da177e4SLinus Torvalds /* 18691da177e4SLinus Torvalds * We have finally decided to accept it. 18701da177e4SLinus Torvalds */ 18711da177e4SLinus Torvalds 18721da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 18731da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 18741da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 18751da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 18761da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 18771da177e4SLinus Torvalds ); 18781da177e4SLinus Torvalds 187971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 nrt = ip6_rt_copy(rt, &msg->dest); 188038308473SDavid S. Miller if (!nrt) 18811da177e4SLinus Torvalds goto out; 18821da177e4SLinus Torvalds 18831da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 18841da177e4SLinus Torvalds if (on_link) 18851da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 18861da177e4SLinus Torvalds 18874e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 18881da177e4SLinus Torvalds 188940e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 18901da177e4SLinus Torvalds goto out; 18911da177e4SLinus Torvalds 1892d8d1f30bSChangli Gao netevent.old = &rt->dst; 1893d8d1f30bSChangli Gao netevent.new = &nrt->dst; 189471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 netevent.daddr = &msg->dest; 189560592833SYOSHIFUJI Hideaki / 吉藤英明 netevent.neigh = neigh; 18968d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 18978d71740cSTom Tucker 18981da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 18996e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 1900e0a1ad73SThomas Graf ip6_del_rt(rt); 19011da177e4SLinus Torvalds } 19021da177e4SLinus Torvalds 19031da177e4SLinus Torvalds out: 1904e8599ff4SDavid S. Miller neigh_release(neigh); 19056e157b6aSDavid S. Miller } 19066e157b6aSDavid S. Miller 19071da177e4SLinus Torvalds /* 19081da177e4SLinus Torvalds * Misc support functions 19091da177e4SLinus Torvalds */ 19101da177e4SLinus Torvalds 19114b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) 19124b32b5adSMartin KaFai Lau { 19134b32b5adSMartin KaFai Lau BUG_ON(from->dst.from); 19144b32b5adSMartin KaFai Lau 19154b32b5adSMartin KaFai Lau rt->rt6i_flags &= ~RTF_EXPIRES; 19164b32b5adSMartin KaFai Lau dst_hold(&from->dst); 19174b32b5adSMartin KaFai Lau rt->dst.from = &from->dst; 19184b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true); 19194b32b5adSMartin KaFai Lau } 19204b32b5adSMartin KaFai Lau 19211716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 192221efcfa0SEric Dumazet const struct in6_addr *dest) 19231da177e4SLinus Torvalds { 1924d1918542SDavid S. Miller struct net *net = dev_net(ort->dst.dev); 19254b32b5adSMartin KaFai Lau struct rt6_info *rt; 19264b32b5adSMartin KaFai Lau 19274b32b5adSMartin KaFai Lau if (ort->rt6i_flags & RTF_CACHE) 19284b32b5adSMartin KaFai Lau ort = (struct rt6_info *)ort->dst.from; 19294b32b5adSMartin KaFai Lau 19304b32b5adSMartin KaFai Lau rt = ip6_dst_alloc(net, ort->dst.dev, 0, 19318b96d22dSDavid S. Miller ort->rt6i_table); 19321da177e4SLinus Torvalds 19331da177e4SLinus Torvalds if (rt) { 1934d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 1935d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 19368e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 19371da177e4SLinus Torvalds 19384e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *dest; 19398e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 1940d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 19411da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 19421da177e4SLinus Torvalds if (rt->rt6i_idev) 19431da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 1944d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 19454e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 19461716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 19471716a961SGao feng rt6_set_from(rt, ort); 19481da177e4SLinus Torvalds rt->rt6i_metric = 0; 19491da177e4SLinus Torvalds 19501da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 19511da177e4SLinus Torvalds memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 19521da177e4SLinus Torvalds #endif 19530f6c6392SFlorian Westphal memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key)); 1954c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 19551da177e4SLinus Torvalds } 19561da177e4SLinus Torvalds return rt; 19571da177e4SLinus Torvalds } 19581da177e4SLinus Torvalds 195970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 1960efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 1961b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1962b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 196370ceb4f5SYOSHIFUJI Hideaki { 196470ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 196570ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 1966c71099acSThomas Graf struct fib6_table *table; 196770ceb4f5SYOSHIFUJI Hideaki 1968efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 196938308473SDavid S. Miller if (!table) 1970c71099acSThomas Graf return NULL; 1971c71099acSThomas Graf 19725744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 1973c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0); 197470ceb4f5SYOSHIFUJI Hideaki if (!fn) 197570ceb4f5SYOSHIFUJI Hideaki goto out; 197670ceb4f5SYOSHIFUJI Hideaki 1977d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1978d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 197970ceb4f5SYOSHIFUJI Hideaki continue; 198070ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 198170ceb4f5SYOSHIFUJI Hideaki continue; 198270ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 198370ceb4f5SYOSHIFUJI Hideaki continue; 1984d8d1f30bSChangli Gao dst_hold(&rt->dst); 198570ceb4f5SYOSHIFUJI Hideaki break; 198670ceb4f5SYOSHIFUJI Hideaki } 198770ceb4f5SYOSHIFUJI Hideaki out: 19885744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 198970ceb4f5SYOSHIFUJI Hideaki return rt; 199070ceb4f5SYOSHIFUJI Hideaki } 199170ceb4f5SYOSHIFUJI Hideaki 1992efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 1993b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1994b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 199595c96174SEric Dumazet unsigned int pref) 199670ceb4f5SYOSHIFUJI Hideaki { 199786872cb5SThomas Graf struct fib6_config cfg = { 199886872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 1999238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 200086872cb5SThomas Graf .fc_ifindex = ifindex, 200186872cb5SThomas Graf .fc_dst_len = prefixlen, 200286872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 200386872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 200415e47304SEric W. Biederman .fc_nlinfo.portid = 0, 2005efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 2006efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 200786872cb5SThomas Graf }; 200870ceb4f5SYOSHIFUJI Hideaki 20094e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 20104e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 201186872cb5SThomas Graf 2012e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 2013e317da96SYOSHIFUJI Hideaki if (!prefixlen) 201486872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 201570ceb4f5SYOSHIFUJI Hideaki 201686872cb5SThomas Graf ip6_route_add(&cfg); 201770ceb4f5SYOSHIFUJI Hideaki 2018efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 201970ceb4f5SYOSHIFUJI Hideaki } 202070ceb4f5SYOSHIFUJI Hideaki #endif 202170ceb4f5SYOSHIFUJI Hideaki 2022b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 20231da177e4SLinus Torvalds { 20241da177e4SLinus Torvalds struct rt6_info *rt; 2025c71099acSThomas Graf struct fib6_table *table; 20261da177e4SLinus Torvalds 2027c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 202838308473SDavid S. Miller if (!table) 2029c71099acSThomas Graf return NULL; 20301da177e4SLinus Torvalds 20315744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2032d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 2033d1918542SDavid S. Miller if (dev == rt->dst.dev && 2034045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 20351da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 20361da177e4SLinus Torvalds break; 20371da177e4SLinus Torvalds } 20381da177e4SLinus Torvalds if (rt) 2039d8d1f30bSChangli Gao dst_hold(&rt->dst); 20405744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 20411da177e4SLinus Torvalds return rt; 20421da177e4SLinus Torvalds } 20431da177e4SLinus Torvalds 2044b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 2045ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 2046ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 20471da177e4SLinus Torvalds { 204886872cb5SThomas Graf struct fib6_config cfg = { 204986872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 2050238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 205186872cb5SThomas Graf .fc_ifindex = dev->ifindex, 205286872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 205386872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 205415e47304SEric W. Biederman .fc_nlinfo.portid = 0, 20555578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 2056c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 205786872cb5SThomas Graf }; 20581da177e4SLinus Torvalds 20594e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 20601da177e4SLinus Torvalds 206186872cb5SThomas Graf ip6_route_add(&cfg); 20621da177e4SLinus Torvalds 20631da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 20641da177e4SLinus Torvalds } 20651da177e4SLinus Torvalds 20667b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 20671da177e4SLinus Torvalds { 20681da177e4SLinus Torvalds struct rt6_info *rt; 2069c71099acSThomas Graf struct fib6_table *table; 2070c71099acSThomas Graf 2071c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 20727b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 207338308473SDavid S. Miller if (!table) 2074c71099acSThomas Graf return; 20751da177e4SLinus Torvalds 20761da177e4SLinus Torvalds restart: 2077c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2078d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 20793e8b0ac3SLorenzo Colitti if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 20803e8b0ac3SLorenzo Colitti (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { 2081d8d1f30bSChangli Gao dst_hold(&rt->dst); 2082c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2083e0a1ad73SThomas Graf ip6_del_rt(rt); 20841da177e4SLinus Torvalds goto restart; 20851da177e4SLinus Torvalds } 20861da177e4SLinus Torvalds } 2087c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 20881da177e4SLinus Torvalds } 20891da177e4SLinus Torvalds 20905578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 20915578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 209286872cb5SThomas Graf struct fib6_config *cfg) 209386872cb5SThomas Graf { 209486872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 209586872cb5SThomas Graf 209686872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 209786872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 209886872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 209986872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 210086872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 210186872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 210286872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 210386872cb5SThomas Graf 21045578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 2105f1243c2dSBenjamin Thery 21064e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 21074e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 21084e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 210986872cb5SThomas Graf } 211086872cb5SThomas Graf 21115578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 21121da177e4SLinus Torvalds { 211386872cb5SThomas Graf struct fib6_config cfg; 21141da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 21151da177e4SLinus Torvalds int err; 21161da177e4SLinus Torvalds 21171da177e4SLinus Torvalds switch (cmd) { 21181da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 21191da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 2120af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 21211da177e4SLinus Torvalds return -EPERM; 21221da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 21231da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 21241da177e4SLinus Torvalds if (err) 21251da177e4SLinus Torvalds return -EFAULT; 21261da177e4SLinus Torvalds 21275578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 212886872cb5SThomas Graf 21291da177e4SLinus Torvalds rtnl_lock(); 21301da177e4SLinus Torvalds switch (cmd) { 21311da177e4SLinus Torvalds case SIOCADDRT: 213286872cb5SThomas Graf err = ip6_route_add(&cfg); 21331da177e4SLinus Torvalds break; 21341da177e4SLinus Torvalds case SIOCDELRT: 213586872cb5SThomas Graf err = ip6_route_del(&cfg); 21361da177e4SLinus Torvalds break; 21371da177e4SLinus Torvalds default: 21381da177e4SLinus Torvalds err = -EINVAL; 21391da177e4SLinus Torvalds } 21401da177e4SLinus Torvalds rtnl_unlock(); 21411da177e4SLinus Torvalds 21421da177e4SLinus Torvalds return err; 21433ff50b79SStephen Hemminger } 21441da177e4SLinus Torvalds 21451da177e4SLinus Torvalds return -EINVAL; 21461da177e4SLinus Torvalds } 21471da177e4SLinus Torvalds 21481da177e4SLinus Torvalds /* 21491da177e4SLinus Torvalds * Drop the packet on the floor 21501da177e4SLinus Torvalds */ 21511da177e4SLinus Torvalds 2152d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 21531da177e4SLinus Torvalds { 2154612f09e8SYOSHIFUJI Hideaki int type; 2155adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2156612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2157612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 21580660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 215945bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 21603bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 21613bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2162612f09e8SYOSHIFUJI Hideaki break; 2163612f09e8SYOSHIFUJI Hideaki } 2164612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2165612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 21663bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 21673bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2168612f09e8SYOSHIFUJI Hideaki break; 2169612f09e8SYOSHIFUJI Hideaki } 21703ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 21711da177e4SLinus Torvalds kfree_skb(skb); 21721da177e4SLinus Torvalds return 0; 21731da177e4SLinus Torvalds } 21741da177e4SLinus Torvalds 21759ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 21769ce8ade0SThomas Graf { 2177612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 21789ce8ade0SThomas Graf } 21799ce8ade0SThomas Graf 2180aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb) 21811da177e4SLinus Torvalds { 2182adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2183612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 21841da177e4SLinus Torvalds } 21851da177e4SLinus Torvalds 21869ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 21879ce8ade0SThomas Graf { 2188612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 21899ce8ade0SThomas Graf } 21909ce8ade0SThomas Graf 2191aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb) 21929ce8ade0SThomas Graf { 2193adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2194612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 21959ce8ade0SThomas Graf } 21969ce8ade0SThomas Graf 21971da177e4SLinus Torvalds /* 21981da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 21991da177e4SLinus Torvalds */ 22001da177e4SLinus Torvalds 22011da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 22021da177e4SLinus Torvalds const struct in6_addr *addr, 22038f031519SDavid S. Miller bool anycast) 22041da177e4SLinus Torvalds { 2205c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 2206a3300ef4SHannes Frederic Sowa struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 2207a3300ef4SHannes Frederic Sowa DST_NOCOUNT, NULL); 2208a3300ef4SHannes Frederic Sowa if (!rt) 22091da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 22101da177e4SLinus Torvalds 22111da177e4SLinus Torvalds in6_dev_hold(idev); 22121da177e4SLinus Torvalds 221311d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2214d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2215d8d1f30bSChangli Gao rt->dst.output = ip6_output; 22161da177e4SLinus Torvalds rt->rt6i_idev = idev; 22171da177e4SLinus Torvalds 22181da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 221958c4fb86SYOSHIFUJI Hideaki if (anycast) 222058c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 222158c4fb86SYOSHIFUJI Hideaki else 22221da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 22231da177e4SLinus Torvalds 2224550bab42SJulian Anastasov rt->rt6i_gateway = *addr; 22254e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 22261da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 22275578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 22281da177e4SLinus Torvalds 2229d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 22301da177e4SLinus Torvalds 22311da177e4SLinus Torvalds return rt; 22321da177e4SLinus Torvalds } 22331da177e4SLinus Torvalds 2234c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2235c3968a85SDaniel Walter struct rt6_info *rt, 2236b71d1d42SEric Dumazet const struct in6_addr *daddr, 2237c3968a85SDaniel Walter unsigned int prefs, 2238c3968a85SDaniel Walter struct in6_addr *saddr) 2239c3968a85SDaniel Walter { 2240e16e888bSMarkus Stenberg struct inet6_dev *idev = 2241e16e888bSMarkus Stenberg rt ? ip6_dst_idev((struct dst_entry *)rt) : NULL; 2242c3968a85SDaniel Walter int err = 0; 2243e16e888bSMarkus Stenberg if (rt && rt->rt6i_prefsrc.plen) 22444e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2245c3968a85SDaniel Walter else 2246c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2247c3968a85SDaniel Walter daddr, prefs, saddr); 2248c3968a85SDaniel Walter return err; 2249c3968a85SDaniel Walter } 2250c3968a85SDaniel Walter 2251c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2252c3968a85SDaniel Walter struct arg_dev_net_ip { 2253c3968a85SDaniel Walter struct net_device *dev; 2254c3968a85SDaniel Walter struct net *net; 2255c3968a85SDaniel Walter struct in6_addr *addr; 2256c3968a85SDaniel Walter }; 2257c3968a85SDaniel Walter 2258c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2259c3968a85SDaniel Walter { 2260c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2261c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2262c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2263c3968a85SDaniel Walter 2264d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2265c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2266c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2267c3968a85SDaniel Walter /* remove prefsrc entry */ 2268c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2269c3968a85SDaniel Walter } 2270c3968a85SDaniel Walter return 0; 2271c3968a85SDaniel Walter } 2272c3968a85SDaniel Walter 2273c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2274c3968a85SDaniel Walter { 2275c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2276c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2277c3968a85SDaniel Walter .dev = ifp->idev->dev, 2278c3968a85SDaniel Walter .net = net, 2279c3968a85SDaniel Walter .addr = &ifp->addr, 2280c3968a85SDaniel Walter }; 22810c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 2282c3968a85SDaniel Walter } 2283c3968a85SDaniel Walter 2284be7a010dSDuan Jiong #define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) 2285be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 2286be7a010dSDuan Jiong 2287be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */ 2288be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg) 2289be7a010dSDuan Jiong { 2290be7a010dSDuan Jiong struct in6_addr *gateway = (struct in6_addr *)arg; 2291be7a010dSDuan Jiong 2292be7a010dSDuan Jiong if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) || 2293be7a010dSDuan Jiong ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) && 2294be7a010dSDuan Jiong ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { 2295be7a010dSDuan Jiong return -1; 2296be7a010dSDuan Jiong } 2297be7a010dSDuan Jiong return 0; 2298be7a010dSDuan Jiong } 2299be7a010dSDuan Jiong 2300be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 2301be7a010dSDuan Jiong { 2302be7a010dSDuan Jiong fib6_clean_all(net, fib6_clean_tohost, gateway); 2303be7a010dSDuan Jiong } 2304be7a010dSDuan Jiong 23058ed67789SDaniel Lezcano struct arg_dev_net { 23068ed67789SDaniel Lezcano struct net_device *dev; 23078ed67789SDaniel Lezcano struct net *net; 23088ed67789SDaniel Lezcano }; 23098ed67789SDaniel Lezcano 23101da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 23111da177e4SLinus Torvalds { 2312bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2313bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 23148ed67789SDaniel Lezcano 2315d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2316c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 23171da177e4SLinus Torvalds return -1; 2318c159d30cSDavid S. Miller 23191da177e4SLinus Torvalds return 0; 23201da177e4SLinus Torvalds } 23211da177e4SLinus Torvalds 2322f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 23231da177e4SLinus Torvalds { 23248ed67789SDaniel Lezcano struct arg_dev_net adn = { 23258ed67789SDaniel Lezcano .dev = dev, 23268ed67789SDaniel Lezcano .net = net, 23278ed67789SDaniel Lezcano }; 23288ed67789SDaniel Lezcano 23290c3584d5SLi RongQing fib6_clean_all(net, fib6_ifdown, &adn); 23301e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 23311da177e4SLinus Torvalds } 23321da177e4SLinus Torvalds 233395c96174SEric Dumazet struct rt6_mtu_change_arg { 23341da177e4SLinus Torvalds struct net_device *dev; 233595c96174SEric Dumazet unsigned int mtu; 23361da177e4SLinus Torvalds }; 23371da177e4SLinus Torvalds 23381da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 23391da177e4SLinus Torvalds { 23401da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 23411da177e4SLinus Torvalds struct inet6_dev *idev; 23421da177e4SLinus Torvalds 23431da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 23441da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 23451da177e4SLinus Torvalds We still use this lock to block changes 23461da177e4SLinus Torvalds caused by addrconf/ndisc. 23471da177e4SLinus Torvalds */ 23481da177e4SLinus Torvalds 23491da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 235038308473SDavid S. Miller if (!idev) 23511da177e4SLinus Torvalds return 0; 23521da177e4SLinus Torvalds 23531da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 23541da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 23551da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 23561da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 23571da177e4SLinus Torvalds */ 23581da177e4SLinus Torvalds /* 23591da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 23601da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 23611da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 23621da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 23631da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 23641da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 23651da177e4SLinus Torvalds PMTU discouvery. 23661da177e4SLinus Torvalds */ 2367d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 23684b32b5adSMartin KaFai Lau !dst_metric_locked(&rt->dst, RTAX_MTU)) { 23694b32b5adSMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) { 23704b32b5adSMartin KaFai Lau /* For RTF_CACHE with rt6i_pmtu == 0 23714b32b5adSMartin KaFai Lau * (i.e. a redirected route), 23724b32b5adSMartin KaFai Lau * the metrics of its rt->dst.from has already 23734b32b5adSMartin KaFai Lau * been updated. 23744b32b5adSMartin KaFai Lau */ 23754b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu) 23764b32b5adSMartin KaFai Lau rt->rt6i_pmtu = arg->mtu; 23774b32b5adSMartin KaFai Lau } else if (dst_mtu(&rt->dst) >= arg->mtu || 2378d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 23794b32b5adSMartin KaFai Lau dst_mtu(&rt->dst) == idev->cnf.mtu6)) { 2380defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2381566cfd8fSSimon Arlott } 23824b32b5adSMartin KaFai Lau } 23831da177e4SLinus Torvalds return 0; 23841da177e4SLinus Torvalds } 23851da177e4SLinus Torvalds 238695c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 23871da177e4SLinus Torvalds { 2388c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2389c71099acSThomas Graf .dev = dev, 2390c71099acSThomas Graf .mtu = mtu, 2391c71099acSThomas Graf }; 23921da177e4SLinus Torvalds 23930c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 23941da177e4SLinus Torvalds } 23951da177e4SLinus Torvalds 2396ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 23975176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 239886872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2399ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 240086872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 240186872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 240251ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2403c78ba6d6SLubomir Rintel [RTA_PREF] = { .type = NLA_U8 }, 240486872cb5SThomas Graf }; 240586872cb5SThomas Graf 240686872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 240786872cb5SThomas Graf struct fib6_config *cfg) 24081da177e4SLinus Torvalds { 240986872cb5SThomas Graf struct rtmsg *rtm; 241086872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 2411c78ba6d6SLubomir Rintel unsigned int pref; 241286872cb5SThomas Graf int err; 24131da177e4SLinus Torvalds 241486872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 241586872cb5SThomas Graf if (err < 0) 241686872cb5SThomas Graf goto errout; 24171da177e4SLinus Torvalds 241886872cb5SThomas Graf err = -EINVAL; 241986872cb5SThomas Graf rtm = nlmsg_data(nlh); 242086872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 242186872cb5SThomas Graf 242286872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 242386872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 242486872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 242586872cb5SThomas Graf cfg->fc_flags = RTF_UP; 242686872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2427ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 242886872cb5SThomas Graf 2429ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2430ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2431b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2432b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 243386872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 243486872cb5SThomas Graf 2435ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2436ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2437ab79ad14SMaciej Żenczykowski 24381f56a01fSMartin KaFai Lau if (rtm->rtm_flags & RTM_F_CLONED) 24391f56a01fSMartin KaFai Lau cfg->fc_flags |= RTF_CACHE; 24401f56a01fSMartin KaFai Lau 244115e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 244286872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 24433b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 244486872cb5SThomas Graf 244586872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 244667b61f6cSJiri Benc cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 244786872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 24481da177e4SLinus Torvalds } 244986872cb5SThomas Graf 245086872cb5SThomas Graf if (tb[RTA_DST]) { 245186872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 245286872cb5SThomas Graf 245386872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 245486872cb5SThomas Graf goto errout; 245586872cb5SThomas Graf 245686872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 24571da177e4SLinus Torvalds } 245886872cb5SThomas Graf 245986872cb5SThomas Graf if (tb[RTA_SRC]) { 246086872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 246186872cb5SThomas Graf 246286872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 246386872cb5SThomas Graf goto errout; 246486872cb5SThomas Graf 246586872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 24661da177e4SLinus Torvalds } 246786872cb5SThomas Graf 2468c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 246967b61f6cSJiri Benc cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 2470c3968a85SDaniel Walter 247186872cb5SThomas Graf if (tb[RTA_OIF]) 247286872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 247386872cb5SThomas Graf 247486872cb5SThomas Graf if (tb[RTA_PRIORITY]) 247586872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 247686872cb5SThomas Graf 247786872cb5SThomas Graf if (tb[RTA_METRICS]) { 247886872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 247986872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 24801da177e4SLinus Torvalds } 248186872cb5SThomas Graf 248286872cb5SThomas Graf if (tb[RTA_TABLE]) 248386872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 248486872cb5SThomas Graf 248551ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 248651ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 248751ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 248851ebd318SNicolas Dichtel } 248951ebd318SNicolas Dichtel 2490c78ba6d6SLubomir Rintel if (tb[RTA_PREF]) { 2491c78ba6d6SLubomir Rintel pref = nla_get_u8(tb[RTA_PREF]); 2492c78ba6d6SLubomir Rintel if (pref != ICMPV6_ROUTER_PREF_LOW && 2493c78ba6d6SLubomir Rintel pref != ICMPV6_ROUTER_PREF_HIGH) 2494c78ba6d6SLubomir Rintel pref = ICMPV6_ROUTER_PREF_MEDIUM; 2495c78ba6d6SLubomir Rintel cfg->fc_flags |= RTF_PREF(pref); 2496c78ba6d6SLubomir Rintel } 2497c78ba6d6SLubomir Rintel 249886872cb5SThomas Graf err = 0; 249986872cb5SThomas Graf errout: 250086872cb5SThomas Graf return err; 25011da177e4SLinus Torvalds } 25021da177e4SLinus Torvalds 250351ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add) 250451ebd318SNicolas Dichtel { 250551ebd318SNicolas Dichtel struct fib6_config r_cfg; 250651ebd318SNicolas Dichtel struct rtnexthop *rtnh; 250751ebd318SNicolas Dichtel int remaining; 250851ebd318SNicolas Dichtel int attrlen; 250951ebd318SNicolas Dichtel int err = 0, last_err = 0; 251051ebd318SNicolas Dichtel 251135f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len; 251251ebd318SNicolas Dichtel beginning: 251351ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 251451ebd318SNicolas Dichtel 251551ebd318SNicolas Dichtel /* Parse a Multipath Entry */ 251651ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 251751ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 251851ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 251951ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 252051ebd318SNicolas Dichtel 252151ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 252251ebd318SNicolas Dichtel if (attrlen > 0) { 252351ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 252451ebd318SNicolas Dichtel 252551ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 252651ebd318SNicolas Dichtel if (nla) { 252767b61f6cSJiri Benc r_cfg.fc_gateway = nla_get_in6_addr(nla); 252851ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 252951ebd318SNicolas Dichtel } 253051ebd318SNicolas Dichtel } 253151ebd318SNicolas Dichtel err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg); 253251ebd318SNicolas Dichtel if (err) { 253351ebd318SNicolas Dichtel last_err = err; 253451ebd318SNicolas Dichtel /* If we are trying to remove a route, do not stop the 253551ebd318SNicolas Dichtel * loop when ip6_route_del() fails (because next hop is 253651ebd318SNicolas Dichtel * already gone), we should try to remove all next hops. 253751ebd318SNicolas Dichtel */ 253851ebd318SNicolas Dichtel if (add) { 253951ebd318SNicolas Dichtel /* If add fails, we should try to delete all 254051ebd318SNicolas Dichtel * next hops that have been already added. 254151ebd318SNicolas Dichtel */ 254251ebd318SNicolas Dichtel add = 0; 254335f1b4e9SMichal Kubeček remaining = cfg->fc_mp_len - remaining; 254451ebd318SNicolas Dichtel goto beginning; 254551ebd318SNicolas Dichtel } 254651ebd318SNicolas Dichtel } 25471a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 254827596472SMichal Kubeček * these flags after the first nexthop: if there is a collision, 254927596472SMichal Kubeček * we have already failed to add the first nexthop: 255027596472SMichal Kubeček * fib6_add_rt2node() has rejected it; when replacing, old 255127596472SMichal Kubeček * nexthops have been replaced by first new, the rest should 255227596472SMichal Kubeček * be added to it. 25531a72418bSNicolas Dichtel */ 255427596472SMichal Kubeček cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | 255527596472SMichal Kubeček NLM_F_REPLACE); 255651ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 255751ebd318SNicolas Dichtel } 255851ebd318SNicolas Dichtel 255951ebd318SNicolas Dichtel return last_err; 256051ebd318SNicolas Dichtel } 256151ebd318SNicolas Dichtel 2562661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) 25631da177e4SLinus Torvalds { 256486872cb5SThomas Graf struct fib6_config cfg; 256586872cb5SThomas Graf int err; 25661da177e4SLinus Torvalds 256786872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 256886872cb5SThomas Graf if (err < 0) 256986872cb5SThomas Graf return err; 257086872cb5SThomas Graf 257151ebd318SNicolas Dichtel if (cfg.fc_mp) 257251ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 0); 257351ebd318SNicolas Dichtel else 257486872cb5SThomas Graf return ip6_route_del(&cfg); 25751da177e4SLinus Torvalds } 25761da177e4SLinus Torvalds 2577661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) 25781da177e4SLinus Torvalds { 257986872cb5SThomas Graf struct fib6_config cfg; 258086872cb5SThomas Graf int err; 25811da177e4SLinus Torvalds 258286872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 258386872cb5SThomas Graf if (err < 0) 258486872cb5SThomas Graf return err; 258586872cb5SThomas Graf 258651ebd318SNicolas Dichtel if (cfg.fc_mp) 258751ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 1); 258851ebd318SNicolas Dichtel else 258986872cb5SThomas Graf return ip6_route_add(&cfg); 25901da177e4SLinus Torvalds } 25911da177e4SLinus Torvalds 2592339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void) 2593339bf98fSThomas Graf { 2594339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 2595339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 2596339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 2597339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 2598339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 2599339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 2600339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 2601339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 2602339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 26036a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 2604ea697639SDaniel Borkmann + nla_total_size(sizeof(struct rta_cacheinfo)) 2605c78ba6d6SLubomir Rintel + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 2606c78ba6d6SLubomir Rintel + nla_total_size(1); /* RTA_PREF */ 2607339bf98fSThomas Graf } 2608339bf98fSThomas Graf 2609191cd582SBrian Haley static int rt6_fill_node(struct net *net, 2610191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 26110d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 261215e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 26137bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 26141da177e4SLinus Torvalds { 26154b32b5adSMartin KaFai Lau u32 metrics[RTAX_MAX]; 26161da177e4SLinus Torvalds struct rtmsg *rtm; 26171da177e4SLinus Torvalds struct nlmsghdr *nlh; 2618e3703b3dSThomas Graf long expires; 26199e762a4aSPatrick McHardy u32 table; 26201da177e4SLinus Torvalds 26211da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 26221da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 26231da177e4SLinus Torvalds /* success since this is not a prefix route */ 26241da177e4SLinus Torvalds return 1; 26251da177e4SLinus Torvalds } 26261da177e4SLinus Torvalds } 26271da177e4SLinus Torvalds 262815e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 262938308473SDavid S. Miller if (!nlh) 263026932566SPatrick McHardy return -EMSGSIZE; 26312d7202bfSThomas Graf 26322d7202bfSThomas Graf rtm = nlmsg_data(nlh); 26331da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 26341da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 26351da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 26361da177e4SLinus Torvalds rtm->rtm_tos = 0; 2637c71099acSThomas Graf if (rt->rt6i_table) 26389e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 2639c71099acSThomas Graf else 26409e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 26419e762a4aSPatrick McHardy rtm->rtm_table = table; 2642c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 2643c78679e8SDavid S. Miller goto nla_put_failure; 2644ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 2645ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 2646ef2c7d7bSNicolas Dichtel case -EINVAL: 2647ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 2648ef2c7d7bSNicolas Dichtel break; 2649ef2c7d7bSNicolas Dichtel case -EACCES: 2650ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 2651ef2c7d7bSNicolas Dichtel break; 2652b4949ab2SNicolas Dichtel case -EAGAIN: 2653b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 2654b4949ab2SNicolas Dichtel break; 2655ef2c7d7bSNicolas Dichtel default: 26561da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 2657ef2c7d7bSNicolas Dichtel break; 2658ef2c7d7bSNicolas Dichtel } 2659ef2c7d7bSNicolas Dichtel } 2660ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 2661ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 2662d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 26631da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 26641da177e4SLinus Torvalds else 26651da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 26661da177e4SLinus Torvalds rtm->rtm_flags = 0; 26671da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 26681da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 26691da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 26701da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 2671f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 2672f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 26731da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 2674f0396f60SDenis Ovsienko else 2675f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 2676f0396f60SDenis Ovsienko } 26771da177e4SLinus Torvalds 26781da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 26791da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 26801da177e4SLinus Torvalds 26811da177e4SLinus Torvalds if (dst) { 2682930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, dst)) 2683c78679e8SDavid S. Miller goto nla_put_failure; 26841da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 26851da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 2686930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr)) 2687c78679e8SDavid S. Miller goto nla_put_failure; 26881da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 26891da177e4SLinus Torvalds if (src) { 2690930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_SRC, src)) 2691c78679e8SDavid S. Miller goto nla_put_failure; 26921da177e4SLinus Torvalds rtm->rtm_src_len = 128; 2693c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 2694930345eaSJiri Benc nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr)) 2695c78679e8SDavid S. Miller goto nla_put_failure; 26961da177e4SLinus Torvalds #endif 26977bc570c8SYOSHIFUJI Hideaki if (iif) { 26987bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 26997bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 27008229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 27017bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 27027bc570c8SYOSHIFUJI Hideaki if (!nowait) { 27037bc570c8SYOSHIFUJI Hideaki if (err == 0) 27047bc570c8SYOSHIFUJI Hideaki return 0; 27057bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 27067bc570c8SYOSHIFUJI Hideaki } else { 27077bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 27087bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 27097bc570c8SYOSHIFUJI Hideaki } 27107bc570c8SYOSHIFUJI Hideaki } 27117bc570c8SYOSHIFUJI Hideaki } else 27127bc570c8SYOSHIFUJI Hideaki #endif 2713c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 2714c78679e8SDavid S. Miller goto nla_put_failure; 27157bc570c8SYOSHIFUJI Hideaki } else if (dst) { 27161da177e4SLinus Torvalds struct in6_addr saddr_buf; 2717c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 2718930345eaSJiri Benc nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 2719c78679e8SDavid S. Miller goto nla_put_failure; 2720c3968a85SDaniel Walter } 2721c3968a85SDaniel Walter 2722c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 2723c3968a85SDaniel Walter struct in6_addr saddr_buf; 27244e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 2725930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 2726c78679e8SDavid S. Miller goto nla_put_failure; 27271da177e4SLinus Torvalds } 27282d7202bfSThomas Graf 27294b32b5adSMartin KaFai Lau memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); 27304b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu) 27314b32b5adSMartin KaFai Lau metrics[RTAX_MTU - 1] = rt->rt6i_pmtu; 27324b32b5adSMartin KaFai Lau if (rtnetlink_put_metrics(skb, metrics) < 0) 27332d7202bfSThomas Graf goto nla_put_failure; 27342d7202bfSThomas Graf 2735dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_GATEWAY) { 2736930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0) 273794f826b8SEric Dumazet goto nla_put_failure; 273894f826b8SEric Dumazet } 27392d7202bfSThomas Graf 2740c78679e8SDavid S. Miller if (rt->dst.dev && 2741c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 2742c78679e8SDavid S. Miller goto nla_put_failure; 2743c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 2744c78679e8SDavid S. Miller goto nla_put_failure; 27458253947eSLi Wei 27468253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 274769cdf8f9SYOSHIFUJI Hideaki 274887a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 2749e3703b3dSThomas Graf goto nla_put_failure; 27501da177e4SLinus Torvalds 2751c78ba6d6SLubomir Rintel if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) 2752c78ba6d6SLubomir Rintel goto nla_put_failure; 2753c78ba6d6SLubomir Rintel 2754053c095aSJohannes Berg nlmsg_end(skb, nlh); 2755053c095aSJohannes Berg return 0; 27562d7202bfSThomas Graf 27572d7202bfSThomas Graf nla_put_failure: 275826932566SPatrick McHardy nlmsg_cancel(skb, nlh); 275926932566SPatrick McHardy return -EMSGSIZE; 27601da177e4SLinus Torvalds } 27611da177e4SLinus Torvalds 27621b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 27631da177e4SLinus Torvalds { 27641da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 27651da177e4SLinus Torvalds int prefix; 27661da177e4SLinus Torvalds 27672d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 27682d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 27691da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 27701da177e4SLinus Torvalds } else 27711da177e4SLinus Torvalds prefix = 0; 27721da177e4SLinus Torvalds 2773191cd582SBrian Haley return rt6_fill_node(arg->net, 2774191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 277515e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 27767bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 27771da177e4SLinus Torvalds } 27781da177e4SLinus Torvalds 2779661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) 27801da177e4SLinus Torvalds { 27813b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 2782ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 27831da177e4SLinus Torvalds struct rt6_info *rt; 2784ab364a6fSThomas Graf struct sk_buff *skb; 2785ab364a6fSThomas Graf struct rtmsg *rtm; 27864c9483b2SDavid S. Miller struct flowi6 fl6; 278772331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 2788ab364a6fSThomas Graf 2789ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 2790ab364a6fSThomas Graf if (err < 0) 2791ab364a6fSThomas Graf goto errout; 2792ab364a6fSThomas Graf 2793ab364a6fSThomas Graf err = -EINVAL; 27944c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2795ab364a6fSThomas Graf 2796ab364a6fSThomas Graf if (tb[RTA_SRC]) { 2797ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 2798ab364a6fSThomas Graf goto errout; 2799ab364a6fSThomas Graf 28004e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 2801ab364a6fSThomas Graf } 2802ab364a6fSThomas Graf 2803ab364a6fSThomas Graf if (tb[RTA_DST]) { 2804ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 2805ab364a6fSThomas Graf goto errout; 2806ab364a6fSThomas Graf 28074e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 2808ab364a6fSThomas Graf } 2809ab364a6fSThomas Graf 2810ab364a6fSThomas Graf if (tb[RTA_IIF]) 2811ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 2812ab364a6fSThomas Graf 2813ab364a6fSThomas Graf if (tb[RTA_OIF]) 281472331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 2815ab364a6fSThomas Graf 28162e47b291SLorenzo Colitti if (tb[RTA_MARK]) 28172e47b291SLorenzo Colitti fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 28182e47b291SLorenzo Colitti 2819ab364a6fSThomas Graf if (iif) { 2820ab364a6fSThomas Graf struct net_device *dev; 282172331bc0SShmulik Ladkani int flags = 0; 282272331bc0SShmulik Ladkani 28235578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 2824ab364a6fSThomas Graf if (!dev) { 2825ab364a6fSThomas Graf err = -ENODEV; 2826ab364a6fSThomas Graf goto errout; 2827ab364a6fSThomas Graf } 282872331bc0SShmulik Ladkani 282972331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 283072331bc0SShmulik Ladkani 283172331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 283272331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 283372331bc0SShmulik Ladkani 283472331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 283572331bc0SShmulik Ladkani flags); 283672331bc0SShmulik Ladkani } else { 283772331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 283872331bc0SShmulik Ladkani 283972331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 2840ab364a6fSThomas Graf } 28411da177e4SLinus Torvalds 28421da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 284338308473SDavid S. Miller if (!skb) { 284494e187c0SAmerigo Wang ip6_rt_put(rt); 2845ab364a6fSThomas Graf err = -ENOBUFS; 2846ab364a6fSThomas Graf goto errout; 2847ab364a6fSThomas Graf } 28481da177e4SLinus Torvalds 28491da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 28501da177e4SLinus Torvalds through good chunk of routing engine. 28511da177e4SLinus Torvalds */ 2852459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 28531da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 28541da177e4SLinus Torvalds 2855d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 28561da177e4SLinus Torvalds 28574c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 285815e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 28597bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 28601da177e4SLinus Torvalds if (err < 0) { 2861ab364a6fSThomas Graf kfree_skb(skb); 2862ab364a6fSThomas Graf goto errout; 28631da177e4SLinus Torvalds } 28641da177e4SLinus Torvalds 286515e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 2866ab364a6fSThomas Graf errout: 28671da177e4SLinus Torvalds return err; 28681da177e4SLinus Torvalds } 28691da177e4SLinus Torvalds 287086872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) 28711da177e4SLinus Torvalds { 28721da177e4SLinus Torvalds struct sk_buff *skb; 28735578689aSDaniel Lezcano struct net *net = info->nl_net; 2874528c4cebSDenis V. Lunev u32 seq; 2875528c4cebSDenis V. Lunev int err; 28760d51aa80SJamal Hadi Salim 2877528c4cebSDenis V. Lunev err = -ENOBUFS; 287838308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 287986872cb5SThomas Graf 2880339bf98fSThomas Graf skb = nlmsg_new(rt6_nlmsg_size(), gfp_any()); 288138308473SDavid S. Miller if (!skb) 288221713ebcSThomas Graf goto errout; 28831da177e4SLinus Torvalds 2884191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 288515e47304SEric W. Biederman event, info->portid, seq, 0, 0, 0); 288626932566SPatrick McHardy if (err < 0) { 288726932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 288826932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 288926932566SPatrick McHardy kfree_skb(skb); 289026932566SPatrick McHardy goto errout; 289126932566SPatrick McHardy } 289215e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 28935578689aSDaniel Lezcano info->nlh, gfp_any()); 28941ce85fe4SPablo Neira Ayuso return; 289521713ebcSThomas Graf errout: 289621713ebcSThomas Graf if (err < 0) 28975578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 28981da177e4SLinus Torvalds } 28991da177e4SLinus Torvalds 29008ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 2901351638e7SJiri Pirko unsigned long event, void *ptr) 29028ed67789SDaniel Lezcano { 2903351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 2904c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 29058ed67789SDaniel Lezcano 29068ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 2907d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 29088ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 29098ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 2910d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 29118ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 2912d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 29138ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 29148ed67789SDaniel Lezcano #endif 29158ed67789SDaniel Lezcano } 29168ed67789SDaniel Lezcano 29178ed67789SDaniel Lezcano return NOTIFY_OK; 29188ed67789SDaniel Lezcano } 29198ed67789SDaniel Lezcano 29201da177e4SLinus Torvalds /* 29211da177e4SLinus Torvalds * /proc 29221da177e4SLinus Torvalds */ 29231da177e4SLinus Torvalds 29241da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 29251da177e4SLinus Torvalds 292633120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 292733120b30SAlexey Dobriyan .owner = THIS_MODULE, 292833120b30SAlexey Dobriyan .open = ipv6_route_open, 292933120b30SAlexey Dobriyan .read = seq_read, 293033120b30SAlexey Dobriyan .llseek = seq_lseek, 29318d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 293233120b30SAlexey Dobriyan }; 293333120b30SAlexey Dobriyan 29341da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 29351da177e4SLinus Torvalds { 293669ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 29371da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 293869ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 293969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 294069ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 294169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 294269ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 2943fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 294469ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 29451da177e4SLinus Torvalds 29461da177e4SLinus Torvalds return 0; 29471da177e4SLinus Torvalds } 29481da177e4SLinus Torvalds 29491da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 29501da177e4SLinus Torvalds { 2951de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 295269ddb805SDaniel Lezcano } 295369ddb805SDaniel Lezcano 29549a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 29551da177e4SLinus Torvalds .owner = THIS_MODULE, 29561da177e4SLinus Torvalds .open = rt6_stats_seq_open, 29571da177e4SLinus Torvalds .read = seq_read, 29581da177e4SLinus Torvalds .llseek = seq_lseek, 2959b6fcbdb4SPavel Emelyanov .release = single_release_net, 29601da177e4SLinus Torvalds }; 29611da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 29621da177e4SLinus Torvalds 29631da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 29641da177e4SLinus Torvalds 29651da177e4SLinus Torvalds static 2966fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 29671da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 29681da177e4SLinus Torvalds { 2969c486da34SLucian Adrian Grijincu struct net *net; 2970c486da34SLucian Adrian Grijincu int delay; 2971c486da34SLucian Adrian Grijincu if (!write) 2972c486da34SLucian Adrian Grijincu return -EINVAL; 2973c486da34SLucian Adrian Grijincu 2974c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 2975c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 29768d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 29772ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 29781da177e4SLinus Torvalds return 0; 29791da177e4SLinus Torvalds } 29801da177e4SLinus Torvalds 2981fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 29821da177e4SLinus Torvalds { 29831da177e4SLinus Torvalds .procname = "flush", 29844990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 29851da177e4SLinus Torvalds .maxlen = sizeof(int), 298689c8b3a1SDave Jones .mode = 0200, 29876d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 29881da177e4SLinus Torvalds }, 29891da177e4SLinus Torvalds { 29901da177e4SLinus Torvalds .procname = "gc_thresh", 29919a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 29921da177e4SLinus Torvalds .maxlen = sizeof(int), 29931da177e4SLinus Torvalds .mode = 0644, 29946d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 29951da177e4SLinus Torvalds }, 29961da177e4SLinus Torvalds { 29971da177e4SLinus Torvalds .procname = "max_size", 29984990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 29991da177e4SLinus Torvalds .maxlen = sizeof(int), 30001da177e4SLinus Torvalds .mode = 0644, 30016d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 30021da177e4SLinus Torvalds }, 30031da177e4SLinus Torvalds { 30041da177e4SLinus Torvalds .procname = "gc_min_interval", 30054990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 30061da177e4SLinus Torvalds .maxlen = sizeof(int), 30071da177e4SLinus Torvalds .mode = 0644, 30086d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 30091da177e4SLinus Torvalds }, 30101da177e4SLinus Torvalds { 30111da177e4SLinus Torvalds .procname = "gc_timeout", 30124990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 30131da177e4SLinus Torvalds .maxlen = sizeof(int), 30141da177e4SLinus Torvalds .mode = 0644, 30156d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 30161da177e4SLinus Torvalds }, 30171da177e4SLinus Torvalds { 30181da177e4SLinus Torvalds .procname = "gc_interval", 30194990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 30201da177e4SLinus Torvalds .maxlen = sizeof(int), 30211da177e4SLinus Torvalds .mode = 0644, 30226d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 30231da177e4SLinus Torvalds }, 30241da177e4SLinus Torvalds { 30251da177e4SLinus Torvalds .procname = "gc_elasticity", 30264990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 30271da177e4SLinus Torvalds .maxlen = sizeof(int), 30281da177e4SLinus Torvalds .mode = 0644, 3029f3d3f616SMin Zhang .proc_handler = proc_dointvec, 30301da177e4SLinus Torvalds }, 30311da177e4SLinus Torvalds { 30321da177e4SLinus Torvalds .procname = "mtu_expires", 30334990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 30341da177e4SLinus Torvalds .maxlen = sizeof(int), 30351da177e4SLinus Torvalds .mode = 0644, 30366d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 30371da177e4SLinus Torvalds }, 30381da177e4SLinus Torvalds { 30391da177e4SLinus Torvalds .procname = "min_adv_mss", 30404990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 30411da177e4SLinus Torvalds .maxlen = sizeof(int), 30421da177e4SLinus Torvalds .mode = 0644, 3043f3d3f616SMin Zhang .proc_handler = proc_dointvec, 30441da177e4SLinus Torvalds }, 30451da177e4SLinus Torvalds { 30461da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 30474990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 30481da177e4SLinus Torvalds .maxlen = sizeof(int), 30491da177e4SLinus Torvalds .mode = 0644, 30506d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 30511da177e4SLinus Torvalds }, 3052f8572d8fSEric W. Biederman { } 30531da177e4SLinus Torvalds }; 30541da177e4SLinus Torvalds 30552c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 3056760f2d01SDaniel Lezcano { 3057760f2d01SDaniel Lezcano struct ctl_table *table; 3058760f2d01SDaniel Lezcano 3059760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 3060760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 3061760f2d01SDaniel Lezcano GFP_KERNEL); 30625ee09105SYOSHIFUJI Hideaki 30635ee09105SYOSHIFUJI Hideaki if (table) { 30645ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 3065c486da34SLucian Adrian Grijincu table[0].extra1 = net; 306686393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 30675ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 30685ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 30695ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 30705ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 30715ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 30725ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 30735ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 30749c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 3075464dc801SEric W. Biederman 3076464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 3077464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 3078464dc801SEric W. Biederman table[0].procname = NULL; 30795ee09105SYOSHIFUJI Hideaki } 30805ee09105SYOSHIFUJI Hideaki 3081760f2d01SDaniel Lezcano return table; 3082760f2d01SDaniel Lezcano } 30831da177e4SLinus Torvalds #endif 30841da177e4SLinus Torvalds 30852c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 3086cdb18761SDaniel Lezcano { 3087633d424bSPavel Emelyanov int ret = -ENOMEM; 30888ed67789SDaniel Lezcano 308986393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 309086393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 3091f2fc6a54SBenjamin Thery 3092fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 3093fc66f95cSEric Dumazet goto out_ip6_dst_ops; 3094fc66f95cSEric Dumazet 30958ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 30968ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 30978ed67789SDaniel Lezcano GFP_KERNEL); 30988ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 3099fc66f95cSEric Dumazet goto out_ip6_dst_entries; 3100d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 31018ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 3102d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 310362fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 310462fa8a84SDavid S. Miller ip6_template_metrics, true); 31058ed67789SDaniel Lezcano 31068ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 31078ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 31088ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 31098ed67789SDaniel Lezcano GFP_KERNEL); 311068fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 311168fffc67SPeter Zijlstra goto out_ip6_null_entry; 3112d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 31138ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 3114d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 311562fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 311662fa8a84SDavid S. Miller ip6_template_metrics, true); 31178ed67789SDaniel Lezcano 31188ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 31198ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 31208ed67789SDaniel Lezcano GFP_KERNEL); 312168fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 312268fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 3123d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 31248ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 3125d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 312662fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 312762fa8a84SDavid S. Miller ip6_template_metrics, true); 31288ed67789SDaniel Lezcano #endif 31298ed67789SDaniel Lezcano 3130b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 3131b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 3132b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 3133b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 3134b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 3135b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 3136b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 3137b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 3138b339a47cSPeter Zijlstra 31396891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 31406891a346SBenjamin Thery 31418ed67789SDaniel Lezcano ret = 0; 31428ed67789SDaniel Lezcano out: 31438ed67789SDaniel Lezcano return ret; 3144f2fc6a54SBenjamin Thery 314568fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 314668fffc67SPeter Zijlstra out_ip6_prohibit_entry: 314768fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 314868fffc67SPeter Zijlstra out_ip6_null_entry: 314968fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 315068fffc67SPeter Zijlstra #endif 3151fc66f95cSEric Dumazet out_ip6_dst_entries: 3152fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3153f2fc6a54SBenjamin Thery out_ip6_dst_ops: 3154f2fc6a54SBenjamin Thery goto out; 3155cdb18761SDaniel Lezcano } 3156cdb18761SDaniel Lezcano 31572c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 3158cdb18761SDaniel Lezcano { 31598ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 31608ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 31618ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 31628ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 31638ed67789SDaniel Lezcano #endif 316441bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3165cdb18761SDaniel Lezcano } 3166cdb18761SDaniel Lezcano 3167d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 3168d189634eSThomas Graf { 3169d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3170d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 3171d4beaa66SGao feng proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops); 3172d189634eSThomas Graf #endif 3173d189634eSThomas Graf return 0; 3174d189634eSThomas Graf } 3175d189634eSThomas Graf 3176d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 3177d189634eSThomas Graf { 3178d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3179ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 3180ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 3181d189634eSThomas Graf #endif 3182d189634eSThomas Graf } 3183d189634eSThomas Graf 3184cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 3185cdb18761SDaniel Lezcano .init = ip6_route_net_init, 3186cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 3187cdb18761SDaniel Lezcano }; 3188cdb18761SDaniel Lezcano 3189c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 3190c3426b47SDavid S. Miller { 3191c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 3192c3426b47SDavid S. Miller 3193c3426b47SDavid S. Miller if (!bp) 3194c3426b47SDavid S. Miller return -ENOMEM; 3195c3426b47SDavid S. Miller inet_peer_base_init(bp); 3196c3426b47SDavid S. Miller net->ipv6.peers = bp; 3197c3426b47SDavid S. Miller return 0; 3198c3426b47SDavid S. Miller } 3199c3426b47SDavid S. Miller 3200c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 3201c3426b47SDavid S. Miller { 3202c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 3203c3426b47SDavid S. Miller 3204c3426b47SDavid S. Miller net->ipv6.peers = NULL; 320556a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3206c3426b47SDavid S. Miller kfree(bp); 3207c3426b47SDavid S. Miller } 3208c3426b47SDavid S. Miller 32092b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3210c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3211c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3212c3426b47SDavid S. Miller }; 3213c3426b47SDavid S. Miller 3214d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3215d189634eSThomas Graf .init = ip6_route_net_init_late, 3216d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3217d189634eSThomas Graf }; 3218d189634eSThomas Graf 32198ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 32208ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 32218ed67789SDaniel Lezcano .priority = 0, 32228ed67789SDaniel Lezcano }; 32238ed67789SDaniel Lezcano 3224433d49c3SDaniel Lezcano int __init ip6_route_init(void) 32251da177e4SLinus Torvalds { 3226433d49c3SDaniel Lezcano int ret; 3227433d49c3SDaniel Lezcano 32289a7ec3a9SDaniel Lezcano ret = -ENOMEM; 32299a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 32309a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 32319a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 32329a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3233c19a28e1SFernando Carrijo goto out; 323414e50e57SDavid S. Miller 3235fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 32368ed67789SDaniel Lezcano if (ret) 3237bdb3289fSDaniel Lezcano goto out_kmem_cache; 3238bdb3289fSDaniel Lezcano 3239c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3240c3426b47SDavid S. Miller if (ret) 3241e8803b6cSDavid S. Miller goto out_dst_entries; 32422a0c451aSThomas Graf 32437e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 32447e52b33bSDavid S. Miller if (ret) 32457e52b33bSDavid S. Miller goto out_register_inetpeer; 3246c3426b47SDavid S. Miller 32475dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 32485dc121e9SArnaud Ebalard 32498ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 32508ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 32518ed67789SDaniel Lezcano * manually for init_net */ 3252d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 32538ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3254bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3255d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 32568ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3257d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 32588ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3259bdb3289fSDaniel Lezcano #endif 3260e8803b6cSDavid S. Miller ret = fib6_init(); 3261433d49c3SDaniel Lezcano if (ret) 32628ed67789SDaniel Lezcano goto out_register_subsys; 3263433d49c3SDaniel Lezcano 3264433d49c3SDaniel Lezcano ret = xfrm6_init(); 3265433d49c3SDaniel Lezcano if (ret) 3266e8803b6cSDavid S. Miller goto out_fib6_init; 3267c35b7e72SDaniel Lezcano 3268433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3269433d49c3SDaniel Lezcano if (ret) 3270433d49c3SDaniel Lezcano goto xfrm6_init; 32717e5449c2SDaniel Lezcano 3272d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3273d189634eSThomas Graf if (ret) 3274d189634eSThomas Graf goto fib6_rules_init; 3275d189634eSThomas Graf 3276433d49c3SDaniel Lezcano ret = -ENOBUFS; 3277c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3278c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3279c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3280d189634eSThomas Graf goto out_register_late_subsys; 3281433d49c3SDaniel Lezcano 32828ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3283cdb18761SDaniel Lezcano if (ret) 3284d189634eSThomas Graf goto out_register_late_subsys; 32858ed67789SDaniel Lezcano 3286433d49c3SDaniel Lezcano out: 3287433d49c3SDaniel Lezcano return ret; 3288433d49c3SDaniel Lezcano 3289d189634eSThomas Graf out_register_late_subsys: 3290d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3291433d49c3SDaniel Lezcano fib6_rules_init: 3292433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3293433d49c3SDaniel Lezcano xfrm6_init: 3294433d49c3SDaniel Lezcano xfrm6_fini(); 32952a0c451aSThomas Graf out_fib6_init: 32962a0c451aSThomas Graf fib6_gc_cleanup(); 32978ed67789SDaniel Lezcano out_register_subsys: 32988ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 32997e52b33bSDavid S. Miller out_register_inetpeer: 33007e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3301fc66f95cSEric Dumazet out_dst_entries: 3302fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3303433d49c3SDaniel Lezcano out_kmem_cache: 3304f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3305433d49c3SDaniel Lezcano goto out; 33061da177e4SLinus Torvalds } 33071da177e4SLinus Torvalds 33081da177e4SLinus Torvalds void ip6_route_cleanup(void) 33091da177e4SLinus Torvalds { 33108ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3311d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3312101367c2SThomas Graf fib6_rules_cleanup(); 33131da177e4SLinus Torvalds xfrm6_fini(); 33141da177e4SLinus Torvalds fib6_gc_cleanup(); 3315c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 33168ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 331741bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3318f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 33191da177e4SLinus Torvalds } 3320