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 681716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 6921efcfa0SEric Dumazet const struct in6_addr *dest); 701da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 710dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst); 72ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst); 731da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *); 741da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *); 751da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *, 761da177e4SLinus Torvalds struct net_device *dev, int how); 77569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops); 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds static int ip6_pkt_discard(struct sk_buff *skb); 801da177e4SLinus Torvalds static int ip6_pkt_discard_out(struct sk_buff *skb); 811da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb); 826700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 836700c270SDavid S. Miller struct sk_buff *skb, u32 mtu); 846700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, 856700c270SDavid S. Miller struct sk_buff *skb); 861da177e4SLinus Torvalds 8770ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 88efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 89b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 90b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 9195c96174SEric Dumazet unsigned int pref); 92efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 93b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 94b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex); 9570ceb4f5SYOSHIFUJI Hideaki #endif 9670ceb4f5SYOSHIFUJI Hideaki 9706582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) 9806582540SDavid S. Miller { 9906582540SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 10006582540SDavid S. Miller struct inet_peer *peer; 10106582540SDavid S. Miller u32 *p = NULL; 10206582540SDavid S. Miller 1038e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST)) 1048e2ec639SYan, Zheng return NULL; 1058e2ec639SYan, Zheng 106fbfe95a4SDavid S. Miller peer = rt6_get_peer_create(rt); 10706582540SDavid S. Miller if (peer) { 10806582540SDavid S. Miller u32 *old_p = __DST_METRICS_PTR(old); 10906582540SDavid S. Miller unsigned long prev, new; 11006582540SDavid S. Miller 11106582540SDavid S. Miller p = peer->metrics; 11206582540SDavid S. Miller if (inet_metrics_new(peer)) 11306582540SDavid S. Miller memcpy(p, old_p, sizeof(u32) * RTAX_MAX); 11406582540SDavid S. Miller 11506582540SDavid S. Miller new = (unsigned long) p; 11606582540SDavid S. Miller prev = cmpxchg(&dst->_metrics, old, new); 11706582540SDavid S. Miller 11806582540SDavid S. Miller if (prev != old) { 11906582540SDavid S. Miller p = __DST_METRICS_PTR(prev); 12006582540SDavid S. Miller if (prev & DST_METRICS_READ_ONLY) 12106582540SDavid S. Miller p = NULL; 12206582540SDavid S. Miller } 12306582540SDavid S. Miller } 12406582540SDavid S. Miller return p; 12506582540SDavid S. Miller } 12606582540SDavid S. Miller 127f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, 128f894cbf8SDavid S. Miller struct sk_buff *skb, 129f894cbf8SDavid S. Miller const void *daddr) 13039232973SDavid S. Miller { 13139232973SDavid S. Miller struct in6_addr *p = &rt->rt6i_gateway; 13239232973SDavid S. Miller 133a7563f34SDavid S. Miller if (!ipv6_addr_any(p)) 13439232973SDavid S. Miller return (const void *) p; 135f894cbf8SDavid S. Miller else if (skb) 136f894cbf8SDavid S. Miller return &ipv6_hdr(skb)->daddr; 13739232973SDavid S. Miller return daddr; 13839232973SDavid S. Miller } 13939232973SDavid S. Miller 140f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, 141f894cbf8SDavid S. Miller struct sk_buff *skb, 142f894cbf8SDavid S. Miller const void *daddr) 143d3aaeb38SDavid S. Miller { 14439232973SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 14539232973SDavid S. Miller struct neighbour *n; 14639232973SDavid S. Miller 147f894cbf8SDavid S. Miller daddr = choose_neigh_daddr(rt, skb, daddr); 14839232973SDavid S. Miller n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr); 149f83c7790SDavid S. Miller if (n) 150f83c7790SDavid S. Miller return n; 151f83c7790SDavid S. Miller return neigh_create(&nd_tbl, daddr, dst->dev); 152f83c7790SDavid S. Miller } 153f83c7790SDavid S. Miller 1548ade06c6SDavid S. Miller static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev) 155f83c7790SDavid S. Miller { 1568ade06c6SDavid S. Miller struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dev, &rt->rt6i_gateway); 1578ade06c6SDavid S. Miller if (!n) { 1588ade06c6SDavid S. Miller n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev); 159f83c7790SDavid S. Miller if (IS_ERR(n)) 160f83c7790SDavid S. Miller return PTR_ERR(n); 1618ade06c6SDavid S. Miller } 16297cac082SDavid S. Miller rt->n = n; 163f83c7790SDavid S. Miller 164f83c7790SDavid S. Miller return 0; 165d3aaeb38SDavid S. Miller } 166d3aaeb38SDavid S. Miller 1679a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 1681da177e4SLinus Torvalds .family = AF_INET6, 16909640e63SHarvey Harrison .protocol = cpu_to_be16(ETH_P_IPV6), 1701da177e4SLinus Torvalds .gc = ip6_dst_gc, 1711da177e4SLinus Torvalds .gc_thresh = 1024, 1721da177e4SLinus Torvalds .check = ip6_dst_check, 1730dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 174ebb762f2SSteffen Klassert .mtu = ip6_mtu, 17506582540SDavid S. Miller .cow_metrics = ipv6_cow_metrics, 1761da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 1771da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 1781da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 1791da177e4SLinus Torvalds .link_failure = ip6_link_failure, 1801da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 1816e157b6aSDavid S. Miller .redirect = rt6_do_redirect, 1821ac06e03SHerbert Xu .local_out = __ip6_local_out, 183d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 1841da177e4SLinus Torvalds }; 1851da177e4SLinus Torvalds 186ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 187ec831ea7SRoland Dreier { 188618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 189618f9bc7SSteffen Klassert 190618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 191ec831ea7SRoland Dreier } 192ec831ea7SRoland Dreier 1936700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, 1946700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 19514e50e57SDavid S. Miller { 19614e50e57SDavid S. Miller } 19714e50e57SDavid S. Miller 1986700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, 1996700c270SDavid S. Miller struct sk_buff *skb) 200b587ee3bSDavid S. Miller { 201b587ee3bSDavid S. Miller } 202b587ee3bSDavid S. Miller 2030972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, 2040972ddb2SHeld Bernhard unsigned long old) 2050972ddb2SHeld Bernhard { 2060972ddb2SHeld Bernhard return NULL; 2070972ddb2SHeld Bernhard } 2080972ddb2SHeld Bernhard 20914e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 21014e50e57SDavid S. Miller .family = AF_INET6, 21109640e63SHarvey Harrison .protocol = cpu_to_be16(ETH_P_IPV6), 21214e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 21314e50e57SDavid S. Miller .check = ip6_dst_check, 214ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 215214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 21614e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 217b587ee3bSDavid S. Miller .redirect = ip6_rt_blackhole_redirect, 2180972ddb2SHeld Bernhard .cow_metrics = ip6_rt_blackhole_cow_metrics, 219d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 22014e50e57SDavid S. Miller }; 22114e50e57SDavid S. Miller 22262fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 22314edd87dSLi RongQing [RTAX_HOPLIMIT - 1] = 0, 22462fa8a84SDavid S. Miller }; 22562fa8a84SDavid S. Miller 226fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = { 2271da177e4SLinus Torvalds .dst = { 2281da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 2291da177e4SLinus Torvalds .__use = 1, 2302c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 2311da177e4SLinus Torvalds .error = -ENETUNREACH, 2321da177e4SLinus Torvalds .input = ip6_pkt_discard, 2331da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 2341da177e4SLinus Torvalds }, 2351da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2364f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 2371da177e4SLinus Torvalds .rt6i_metric = ~(u32) 0, 2381da177e4SLinus Torvalds .rt6i_ref = ATOMIC_INIT(1), 2391da177e4SLinus Torvalds }; 2401da177e4SLinus Torvalds 241101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 242101367c2SThomas Graf 2436723ab54SDavid S. Miller static int ip6_pkt_prohibit(struct sk_buff *skb); 2446723ab54SDavid S. Miller static int ip6_pkt_prohibit_out(struct sk_buff *skb); 2456723ab54SDavid S. Miller 246fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = { 247101367c2SThomas Graf .dst = { 248101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 249101367c2SThomas Graf .__use = 1, 2502c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 251101367c2SThomas Graf .error = -EACCES, 2529ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 2539ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 254101367c2SThomas Graf }, 255101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2564f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 257101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 258101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 259101367c2SThomas Graf }; 260101367c2SThomas Graf 261fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = { 262101367c2SThomas Graf .dst = { 263101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 264101367c2SThomas Graf .__use = 1, 2652c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 266101367c2SThomas Graf .error = -EINVAL, 267352e512cSHerbert Xu .input = dst_discard, 268352e512cSHerbert Xu .output = dst_discard, 269101367c2SThomas Graf }, 270101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2714f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 272101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 273101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 274101367c2SThomas Graf }; 275101367c2SThomas Graf 276101367c2SThomas Graf #endif 277101367c2SThomas Graf 2781da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 27997bab73fSDavid S. Miller static inline struct rt6_info *ip6_dst_alloc(struct net *net, 280957c665fSDavid S. Miller struct net_device *dev, 2818b96d22dSDavid S. Miller int flags, 2828b96d22dSDavid S. Miller struct fib6_table *table) 2831da177e4SLinus Torvalds { 28497bab73fSDavid S. Miller struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 2856f3118b5SNicolas Dichtel 0, DST_OBSOLETE_FORCE_CHK, flags); 286cf911662SDavid S. Miller 28797bab73fSDavid S. Miller if (rt) { 2888104891bSSteffen Klassert struct dst_entry *dst = &rt->dst; 2898104891bSSteffen Klassert 2908104891bSSteffen Klassert memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 2918b96d22dSDavid S. Miller rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers); 2926f3118b5SNicolas Dichtel rt->rt6i_genid = rt_genid(net); 29351ebd318SNicolas Dichtel INIT_LIST_HEAD(&rt->rt6i_siblings); 29451ebd318SNicolas Dichtel rt->rt6i_nsiblings = 0; 29597bab73fSDavid S. Miller } 296cf911662SDavid S. Miller return rt; 2971da177e4SLinus Torvalds } 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 3001da177e4SLinus Torvalds { 3011da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3021da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3031da177e4SLinus Torvalds 30497cac082SDavid S. Miller if (rt->n) 30597cac082SDavid S. Miller neigh_release(rt->n); 30697cac082SDavid S. Miller 3078e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST)) 3088e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 3098e2ec639SYan, Zheng 31038308473SDavid S. Miller if (idev) { 3111da177e4SLinus Torvalds rt->rt6i_idev = NULL; 3121da177e4SLinus Torvalds in6_dev_put(idev); 3131da177e4SLinus Torvalds } 3141716a961SGao feng 3151716a961SGao feng if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from) 3161716a961SGao feng dst_release(dst->from); 3171716a961SGao feng 31897bab73fSDavid S. Miller if (rt6_has_peer(rt)) { 31997bab73fSDavid S. Miller struct inet_peer *peer = rt6_peer_ptr(rt); 320b3419363SDavid S. Miller inet_putpeer(peer); 321b3419363SDavid S. Miller } 322b3419363SDavid S. Miller } 323b3419363SDavid S. Miller 324b3419363SDavid S. Miller void rt6_bind_peer(struct rt6_info *rt, int create) 325b3419363SDavid S. Miller { 32697bab73fSDavid S. Miller struct inet_peer_base *base; 327b3419363SDavid S. Miller struct inet_peer *peer; 328b3419363SDavid S. Miller 32997bab73fSDavid S. Miller base = inetpeer_base_ptr(rt->_rt6i_peer); 33097bab73fSDavid S. Miller if (!base) 33197bab73fSDavid S. Miller return; 33297bab73fSDavid S. Miller 33397bab73fSDavid S. Miller peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create); 3347b34ca2aSDavid S. Miller if (peer) { 33597bab73fSDavid S. Miller if (!rt6_set_peer(rt, peer)) 336b3419363SDavid S. Miller inet_putpeer(peer); 3371da177e4SLinus Torvalds } 3387b34ca2aSDavid S. Miller } 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 3411da177e4SLinus Torvalds int how) 3421da177e4SLinus Torvalds { 3431da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3441da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3455a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 346c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 3471da177e4SLinus Torvalds 34897cac082SDavid S. Miller if (dev != loopback_dev) { 34997cac082SDavid S. Miller if (idev && idev->dev == dev) { 3505a3e55d6SDenis V. Lunev struct inet6_dev *loopback_idev = 3515a3e55d6SDenis V. Lunev in6_dev_get(loopback_dev); 35238308473SDavid S. Miller if (loopback_idev) { 3531da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 3541da177e4SLinus Torvalds in6_dev_put(idev); 3551da177e4SLinus Torvalds } 3561da177e4SLinus Torvalds } 35797cac082SDavid S. Miller if (rt->n && rt->n->dev == dev) { 35897cac082SDavid S. Miller rt->n->dev = loopback_dev; 35997cac082SDavid S. Miller dev_hold(loopback_dev); 36097cac082SDavid S. Miller dev_put(dev); 36197cac082SDavid S. Miller } 36297cac082SDavid S. Miller } 3631da177e4SLinus Torvalds } 3641da177e4SLinus Torvalds 365a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt) 3661da177e4SLinus Torvalds { 3671716a961SGao feng if (rt->rt6i_flags & RTF_EXPIRES) { 3681716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 369a50feda5SEric Dumazet return true; 3701716a961SGao feng } else if (rt->dst.from) { 3713fd91fb3SLi RongQing return rt6_check_expired((struct rt6_info *) rt->dst.from); 3721716a961SGao feng } 373a50feda5SEric Dumazet return false; 3741da177e4SLinus Torvalds } 3751da177e4SLinus Torvalds 376a50feda5SEric Dumazet static bool rt6_need_strict(const struct in6_addr *daddr) 377c71099acSThomas Graf { 378a02cec21SEric Dumazet return ipv6_addr_type(daddr) & 379a02cec21SEric Dumazet (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); 380c71099acSThomas Graf } 381c71099acSThomas Graf 38251ebd318SNicolas Dichtel /* Multipath route selection: 38351ebd318SNicolas Dichtel * Hash based function using packet header and flowlabel. 38451ebd318SNicolas Dichtel * Adapted from fib_info_hashfn() 38551ebd318SNicolas Dichtel */ 38651ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count, 38751ebd318SNicolas Dichtel const struct flowi6 *fl6) 38851ebd318SNicolas Dichtel { 38951ebd318SNicolas Dichtel unsigned int val = fl6->flowi6_proto; 39051ebd318SNicolas Dichtel 391b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->daddr.s6_addr32[0]; 392b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->daddr.s6_addr32[1]; 393b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->daddr.s6_addr32[2]; 394b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->daddr.s6_addr32[3]; 39551ebd318SNicolas Dichtel 396b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->saddr.s6_addr32[0]; 397b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->saddr.s6_addr32[1]; 398b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->saddr.s6_addr32[2]; 399b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->saddr.s6_addr32[3]; 40051ebd318SNicolas Dichtel 40151ebd318SNicolas Dichtel /* Work only if this not encapsulated */ 40251ebd318SNicolas Dichtel switch (fl6->flowi6_proto) { 40351ebd318SNicolas Dichtel case IPPROTO_UDP: 40451ebd318SNicolas Dichtel case IPPROTO_TCP: 40551ebd318SNicolas Dichtel case IPPROTO_SCTP: 406b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_sport; 407b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_dport; 40851ebd318SNicolas Dichtel break; 40951ebd318SNicolas Dichtel 41051ebd318SNicolas Dichtel case IPPROTO_ICMPV6: 411b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_icmp_type; 412b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_icmp_code; 41351ebd318SNicolas Dichtel break; 41451ebd318SNicolas Dichtel } 41551ebd318SNicolas Dichtel /* RFC6438 recommands to use flowlabel */ 416b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->flowlabel; 41751ebd318SNicolas Dichtel 41851ebd318SNicolas Dichtel /* Perhaps, we need to tune, this function? */ 41951ebd318SNicolas Dichtel val = val ^ (val >> 7) ^ (val >> 12); 42051ebd318SNicolas Dichtel return val % candidate_count; 42151ebd318SNicolas Dichtel } 42251ebd318SNicolas Dichtel 42351ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match, 42451ebd318SNicolas Dichtel struct flowi6 *fl6) 42551ebd318SNicolas Dichtel { 42651ebd318SNicolas Dichtel struct rt6_info *sibling, *next_sibling; 42751ebd318SNicolas Dichtel int route_choosen; 42851ebd318SNicolas Dichtel 42951ebd318SNicolas Dichtel route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6); 43051ebd318SNicolas Dichtel /* Don't change the route, if route_choosen == 0 43151ebd318SNicolas Dichtel * (siblings does not include ourself) 43251ebd318SNicolas Dichtel */ 43351ebd318SNicolas Dichtel if (route_choosen) 43451ebd318SNicolas Dichtel list_for_each_entry_safe(sibling, next_sibling, 43551ebd318SNicolas Dichtel &match->rt6i_siblings, rt6i_siblings) { 43651ebd318SNicolas Dichtel route_choosen--; 43751ebd318SNicolas Dichtel if (route_choosen == 0) { 43851ebd318SNicolas Dichtel match = sibling; 43951ebd318SNicolas Dichtel break; 44051ebd318SNicolas Dichtel } 44151ebd318SNicolas Dichtel } 44251ebd318SNicolas Dichtel return match; 44351ebd318SNicolas Dichtel } 44451ebd318SNicolas Dichtel 4451da177e4SLinus Torvalds /* 446c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 4471da177e4SLinus Torvalds */ 4481da177e4SLinus Torvalds 4498ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 4508ed67789SDaniel Lezcano struct rt6_info *rt, 451b71d1d42SEric Dumazet const struct in6_addr *saddr, 4521da177e4SLinus Torvalds int oif, 453d420895eSYOSHIFUJI Hideaki int flags) 4541da177e4SLinus Torvalds { 4551da177e4SLinus Torvalds struct rt6_info *local = NULL; 4561da177e4SLinus Torvalds struct rt6_info *sprt; 4571da177e4SLinus Torvalds 458dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 459dd3abc4eSYOSHIFUJI Hideaki goto out; 460dd3abc4eSYOSHIFUJI Hideaki 461d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 462d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 463dd3abc4eSYOSHIFUJI Hideaki 464dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4651da177e4SLinus Torvalds if (dev->ifindex == oif) 4661da177e4SLinus Torvalds return sprt; 4671da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 46838308473SDavid S. Miller if (!sprt->rt6i_idev || 4691da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 470d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE && oif) 4711da177e4SLinus Torvalds continue; 4721da177e4SLinus Torvalds if (local && (!oif || 4731da177e4SLinus Torvalds local->rt6i_idev->dev->ifindex == oif)) 4741da177e4SLinus Torvalds continue; 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds local = sprt; 4771da177e4SLinus Torvalds } 478dd3abc4eSYOSHIFUJI Hideaki } else { 479dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 480dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 481dd3abc4eSYOSHIFUJI Hideaki return sprt; 482dd3abc4eSYOSHIFUJI Hideaki } 4831da177e4SLinus Torvalds } 4841da177e4SLinus Torvalds 485dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4861da177e4SLinus Torvalds if (local) 4871da177e4SLinus Torvalds return local; 4881da177e4SLinus Torvalds 489d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 4908ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 4911da177e4SLinus Torvalds } 492dd3abc4eSYOSHIFUJI Hideaki out: 4931da177e4SLinus Torvalds return rt; 4941da177e4SLinus Torvalds } 4951da177e4SLinus Torvalds 49627097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 49727097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 49827097255SYOSHIFUJI Hideaki { 499f2c31e32SEric Dumazet struct neighbour *neigh; 50027097255SYOSHIFUJI Hideaki /* 50127097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 50227097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 50327097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 50427097255SYOSHIFUJI Hideaki * 50527097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 50627097255SYOSHIFUJI Hideaki * to no more than one per minute. 50727097255SYOSHIFUJI Hideaki */ 50897cac082SDavid S. Miller neigh = rt ? rt->n : NULL; 50927097255SYOSHIFUJI Hideaki if (!neigh || (neigh->nud_state & NUD_VALID)) 510fdd6681dSAmerigo Wang return; 51127097255SYOSHIFUJI Hideaki read_lock_bh(&neigh->lock); 51227097255SYOSHIFUJI Hideaki if (!(neigh->nud_state & NUD_VALID) && 51352e16356SYOSHIFUJI Hideaki time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { 51427097255SYOSHIFUJI Hideaki struct in6_addr mcaddr; 51527097255SYOSHIFUJI Hideaki struct in6_addr *target; 51627097255SYOSHIFUJI Hideaki 51727097255SYOSHIFUJI Hideaki neigh->updated = jiffies; 51827097255SYOSHIFUJI Hideaki read_unlock_bh(&neigh->lock); 51927097255SYOSHIFUJI Hideaki 52027097255SYOSHIFUJI Hideaki target = (struct in6_addr *)&neigh->primary_key; 52127097255SYOSHIFUJI Hideaki addrconf_addr_solict_mult(target, &mcaddr); 522d1918542SDavid S. Miller ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL); 523f2c31e32SEric Dumazet } else { 52427097255SYOSHIFUJI Hideaki read_unlock_bh(&neigh->lock); 52527097255SYOSHIFUJI Hideaki } 526f2c31e32SEric Dumazet } 52727097255SYOSHIFUJI Hideaki #else 52827097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 52927097255SYOSHIFUJI Hideaki { 53027097255SYOSHIFUJI Hideaki } 53127097255SYOSHIFUJI Hideaki #endif 53227097255SYOSHIFUJI Hideaki 5331da177e4SLinus Torvalds /* 534554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 5351da177e4SLinus Torvalds */ 536b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 5371da177e4SLinus Torvalds { 538d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 539161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 540554cfb7eSYOSHIFUJI Hideaki return 2; 541161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 542161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 543161980f4SDavid S. Miller return 1; 544554cfb7eSYOSHIFUJI Hideaki return 0; 5451da177e4SLinus Torvalds } 5461da177e4SLinus Torvalds 547b6f99a21SDave Jones static inline int rt6_check_neigh(struct rt6_info *rt) 5481da177e4SLinus Torvalds { 549f2c31e32SEric Dumazet struct neighbour *neigh; 550398bcbebSYOSHIFUJI Hideaki int m; 551f2c31e32SEric Dumazet 55297cac082SDavid S. Miller neigh = rt->n; 5534d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 5544d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 5554d0c5911SYOSHIFUJI Hideaki m = 1; 5564d0c5911SYOSHIFUJI Hideaki else if (neigh) { 5571da177e4SLinus Torvalds read_lock_bh(&neigh->lock); 558554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 5594d0c5911SYOSHIFUJI Hideaki m = 2; 560398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 561398bcbebSYOSHIFUJI Hideaki else if (neigh->nud_state & NUD_FAILED) 562398bcbebSYOSHIFUJI Hideaki m = 0; 563398bcbebSYOSHIFUJI Hideaki #endif 564398bcbebSYOSHIFUJI Hideaki else 565ea73ee23SYOSHIFUJI Hideaki m = 1; 5661da177e4SLinus Torvalds read_unlock_bh(&neigh->lock); 567398bcbebSYOSHIFUJI Hideaki } else 568398bcbebSYOSHIFUJI Hideaki m = 0; 569554cfb7eSYOSHIFUJI Hideaki return m; 5701da177e4SLinus Torvalds } 5711da177e4SLinus Torvalds 572554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 573554cfb7eSYOSHIFUJI Hideaki int strict) 574554cfb7eSYOSHIFUJI Hideaki { 5754d0c5911SYOSHIFUJI Hideaki int m, n; 5764d0c5911SYOSHIFUJI Hideaki 5774d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 57877d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 579554cfb7eSYOSHIFUJI Hideaki return -1; 580ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 581ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 582ebacaaa0SYOSHIFUJI Hideaki #endif 5834d0c5911SYOSHIFUJI Hideaki n = rt6_check_neigh(rt); 584557e92efSYOSHIFUJI Hideaki if (!n && (strict & RT6_LOOKUP_F_REACHABLE)) 585554cfb7eSYOSHIFUJI Hideaki return -1; 586554cfb7eSYOSHIFUJI Hideaki return m; 587554cfb7eSYOSHIFUJI Hideaki } 588554cfb7eSYOSHIFUJI Hideaki 589f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 590f11e6659SDavid S. Miller int *mpri, struct rt6_info *match) 591554cfb7eSYOSHIFUJI Hideaki { 592554cfb7eSYOSHIFUJI Hideaki int m; 593554cfb7eSYOSHIFUJI Hideaki 594554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 595f11e6659SDavid S. Miller goto out; 596554cfb7eSYOSHIFUJI Hideaki 597554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 598554cfb7eSYOSHIFUJI Hideaki if (m < 0) 599f11e6659SDavid S. Miller goto out; 600554cfb7eSYOSHIFUJI Hideaki 601f11e6659SDavid S. Miller if (m > *mpri) { 602ea659e07SYOSHIFUJI Hideaki if (strict & RT6_LOOKUP_F_REACHABLE) 60327097255SYOSHIFUJI Hideaki rt6_probe(match); 604f11e6659SDavid S. Miller *mpri = m; 605554cfb7eSYOSHIFUJI Hideaki match = rt; 606ea659e07SYOSHIFUJI Hideaki } else if (strict & RT6_LOOKUP_F_REACHABLE) { 60727097255SYOSHIFUJI Hideaki rt6_probe(rt); 6081da177e4SLinus Torvalds } 609f11e6659SDavid S. Miller 610f11e6659SDavid S. Miller out: 611f11e6659SDavid S. Miller return match; 6121da177e4SLinus Torvalds } 6131da177e4SLinus Torvalds 614f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 615f11e6659SDavid S. Miller struct rt6_info *rr_head, 616f11e6659SDavid S. Miller u32 metric, int oif, int strict) 617f11e6659SDavid S. Miller { 618f11e6659SDavid S. Miller struct rt6_info *rt, *match; 619f11e6659SDavid S. Miller int mpri = -1; 620f11e6659SDavid S. Miller 621f11e6659SDavid S. Miller match = NULL; 622f11e6659SDavid S. Miller for (rt = rr_head; rt && rt->rt6i_metric == metric; 623d8d1f30bSChangli Gao rt = rt->dst.rt6_next) 624f11e6659SDavid S. Miller match = find_match(rt, oif, strict, &mpri, match); 625f11e6659SDavid S. Miller for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric; 626d8d1f30bSChangli Gao rt = rt->dst.rt6_next) 627f11e6659SDavid S. Miller match = find_match(rt, oif, strict, &mpri, match); 628f11e6659SDavid S. Miller 629f11e6659SDavid S. Miller return match; 630f11e6659SDavid S. Miller } 631f11e6659SDavid S. Miller 632f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 633f11e6659SDavid S. Miller { 634f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 6358ed67789SDaniel Lezcano struct net *net; 636f11e6659SDavid S. Miller 637f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 638f11e6659SDavid S. Miller if (!rt0) 639f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 640f11e6659SDavid S. Miller 641f11e6659SDavid S. Miller match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict); 642f11e6659SDavid S. Miller 643554cfb7eSYOSHIFUJI Hideaki if (!match && 644f11e6659SDavid S. Miller (strict & RT6_LOOKUP_F_REACHABLE)) { 645d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 646f11e6659SDavid S. Miller 647554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 648f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 649f11e6659SDavid S. Miller next = fn->leaf; 650f11e6659SDavid S. Miller 651f11e6659SDavid S. Miller if (next != rt0) 652f11e6659SDavid S. Miller fn->rr_ptr = next; 653554cfb7eSYOSHIFUJI Hideaki } 654554cfb7eSYOSHIFUJI Hideaki 655d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 656a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 6571da177e4SLinus Torvalds } 6581da177e4SLinus Torvalds 65970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 66070ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 661b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 66270ceb4f5SYOSHIFUJI Hideaki { 663c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 66470ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 66570ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 66670ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 6674bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 66870ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 66970ceb4f5SYOSHIFUJI Hideaki 67070ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 67170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 67270ceb4f5SYOSHIFUJI Hideaki } 67370ceb4f5SYOSHIFUJI Hideaki 67470ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 67570ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 67670ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 67770ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 67870ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 67970ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 68070ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 68170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 68270ceb4f5SYOSHIFUJI Hideaki } 68370ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 68470ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 68570ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 68670ceb4f5SYOSHIFUJI Hideaki } 68770ceb4f5SYOSHIFUJI Hideaki } 68870ceb4f5SYOSHIFUJI Hideaki 68970ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 69070ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 6913933fc95SJens Rosenboom return -EINVAL; 69270ceb4f5SYOSHIFUJI Hideaki 6934bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 69470ceb4f5SYOSHIFUJI Hideaki 69570ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 69670ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 69770ceb4f5SYOSHIFUJI Hideaki else { 69870ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 69970ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 70070ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 70170ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 70270ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 70370ceb4f5SYOSHIFUJI Hideaki } 70470ceb4f5SYOSHIFUJI Hideaki 705efa2cea0SDaniel Lezcano rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr, 706efa2cea0SDaniel Lezcano dev->ifindex); 70770ceb4f5SYOSHIFUJI Hideaki 70870ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 709e0a1ad73SThomas Graf ip6_del_rt(rt); 71070ceb4f5SYOSHIFUJI Hideaki rt = NULL; 71170ceb4f5SYOSHIFUJI Hideaki } 71270ceb4f5SYOSHIFUJI Hideaki 71370ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 714efa2cea0SDaniel Lezcano rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 71570ceb4f5SYOSHIFUJI Hideaki pref); 71670ceb4f5SYOSHIFUJI Hideaki else if (rt) 71770ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 71870ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 71970ceb4f5SYOSHIFUJI Hideaki 72070ceb4f5SYOSHIFUJI Hideaki if (rt) { 7211716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 7221716a961SGao feng rt6_clean_expires(rt); 7231716a961SGao feng else 7241716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 7251716a961SGao feng 72694e187c0SAmerigo Wang ip6_rt_put(rt); 72770ceb4f5SYOSHIFUJI Hideaki } 72870ceb4f5SYOSHIFUJI Hideaki return 0; 72970ceb4f5SYOSHIFUJI Hideaki } 73070ceb4f5SYOSHIFUJI Hideaki #endif 73170ceb4f5SYOSHIFUJI Hideaki 7328ed67789SDaniel Lezcano #define BACKTRACK(__net, saddr) \ 733982f56f3SYOSHIFUJI Hideaki do { \ 7348ed67789SDaniel Lezcano if (rt == __net->ipv6.ip6_null_entry) { \ 735982f56f3SYOSHIFUJI Hideaki struct fib6_node *pn; \ 736e0eda7bbSVille Nuorvala while (1) { \ 737982f56f3SYOSHIFUJI Hideaki if (fn->fn_flags & RTN_TL_ROOT) \ 738c71099acSThomas Graf goto out; \ 739982f56f3SYOSHIFUJI Hideaki pn = fn->parent; \ 740982f56f3SYOSHIFUJI Hideaki if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \ 7418bce65b9SKim Nordlund fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \ 742982f56f3SYOSHIFUJI Hideaki else \ 743982f56f3SYOSHIFUJI Hideaki fn = pn; \ 744c71099acSThomas Graf if (fn->fn_flags & RTN_RTINFO) \ 745c71099acSThomas Graf goto restart; \ 746c71099acSThomas Graf } \ 747982f56f3SYOSHIFUJI Hideaki } \ 748982f56f3SYOSHIFUJI Hideaki } while (0) 749c71099acSThomas Graf 7508ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 7518ed67789SDaniel Lezcano struct fib6_table *table, 7524c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 7531da177e4SLinus Torvalds { 7541da177e4SLinus Torvalds struct fib6_node *fn; 7551da177e4SLinus Torvalds struct rt6_info *rt; 7561da177e4SLinus Torvalds 757c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 7584c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 759c71099acSThomas Graf restart: 760c71099acSThomas Graf rt = fn->leaf; 7614c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 76251ebd318SNicolas Dichtel if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) 76351ebd318SNicolas Dichtel rt = rt6_multipath_select(rt, fl6); 7644c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 765c71099acSThomas Graf out: 766d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 767c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 7681da177e4SLinus Torvalds return rt; 769c71099acSThomas Graf 770c71099acSThomas Graf } 771c71099acSThomas Graf 772ea6e574eSFlorian Westphal struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6, 773ea6e574eSFlorian Westphal int flags) 774ea6e574eSFlorian Westphal { 775ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 776ea6e574eSFlorian Westphal } 777ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 778ea6e574eSFlorian Westphal 7799acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 7809acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 781c71099acSThomas Graf { 7824c9483b2SDavid S. Miller struct flowi6 fl6 = { 7834c9483b2SDavid S. Miller .flowi6_oif = oif, 7844c9483b2SDavid S. Miller .daddr = *daddr, 785c71099acSThomas Graf }; 786c71099acSThomas Graf struct dst_entry *dst; 78777d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 788c71099acSThomas Graf 789adaa70bbSThomas Graf if (saddr) { 7904c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 791adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 792adaa70bbSThomas Graf } 793adaa70bbSThomas Graf 7944c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 795c71099acSThomas Graf if (dst->error == 0) 796c71099acSThomas Graf return (struct rt6_info *) dst; 797c71099acSThomas Graf 798c71099acSThomas Graf dst_release(dst); 799c71099acSThomas Graf 8001da177e4SLinus Torvalds return NULL; 8011da177e4SLinus Torvalds } 8021da177e4SLinus Torvalds 8037159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 8047159039aSYOSHIFUJI Hideaki 805c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 8061da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 8071da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 8081da177e4SLinus Torvalds be destroyed. 8091da177e4SLinus Torvalds */ 8101da177e4SLinus Torvalds 81186872cb5SThomas Graf static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info) 8121da177e4SLinus Torvalds { 8131da177e4SLinus Torvalds int err; 814c71099acSThomas Graf struct fib6_table *table; 8151da177e4SLinus Torvalds 816c71099acSThomas Graf table = rt->rt6i_table; 817c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 81886872cb5SThomas Graf err = fib6_add(&table->tb6_root, rt, info); 819c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 8201da177e4SLinus Torvalds 8211da177e4SLinus Torvalds return err; 8221da177e4SLinus Torvalds } 8231da177e4SLinus Torvalds 82440e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 82540e22e8fSThomas Graf { 8264d1169c1SDenis V. Lunev struct nl_info info = { 827d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 8284d1169c1SDenis V. Lunev }; 829528c4cebSDenis V. Lunev return __ip6_ins_rt(rt, &info); 83040e22e8fSThomas Graf } 83140e22e8fSThomas Graf 8321716a961SGao feng static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, 83321efcfa0SEric Dumazet const struct in6_addr *daddr, 834b71d1d42SEric Dumazet const struct in6_addr *saddr) 8351da177e4SLinus Torvalds { 8361da177e4SLinus Torvalds struct rt6_info *rt; 8371da177e4SLinus Torvalds 8381da177e4SLinus Torvalds /* 8391da177e4SLinus Torvalds * Clone the route. 8401da177e4SLinus Torvalds */ 8411da177e4SLinus Torvalds 84221efcfa0SEric Dumazet rt = ip6_rt_copy(ort, daddr); 8431da177e4SLinus Torvalds 8441da177e4SLinus Torvalds if (rt) { 84514deae41SDavid S. Miller int attempts = !in_softirq(); 84614deae41SDavid S. Miller 84758c4fb86SYOSHIFUJI Hideaki if (!(rt->rt6i_flags & RTF_GATEWAY)) { 848bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 84921efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 85058c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 8514e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *daddr; 85258c4fb86SYOSHIFUJI Hideaki } 8531da177e4SLinus Torvalds 8541da177e4SLinus Torvalds rt->rt6i_flags |= RTF_CACHE; 8551da177e4SLinus Torvalds 8561da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 8571da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 8584e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 8591da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 8601da177e4SLinus Torvalds } 8611da177e4SLinus Torvalds #endif 8621da177e4SLinus Torvalds 86314deae41SDavid S. Miller retry: 8648ade06c6SDavid S. Miller if (rt6_bind_neighbour(rt, rt->dst.dev)) { 865d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 86614deae41SDavid S. Miller int saved_rt_min_interval = 86714deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval; 86814deae41SDavid S. Miller int saved_rt_elasticity = 86914deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity; 87014deae41SDavid S. Miller 87114deae41SDavid S. Miller if (attempts-- > 0) { 87214deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity = 1; 87314deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval = 0; 87414deae41SDavid S. Miller 87586393e52SAlexey Dobriyan ip6_dst_gc(&net->ipv6.ip6_dst_ops); 87614deae41SDavid S. Miller 87714deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity = 87814deae41SDavid S. Miller saved_rt_elasticity; 87914deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval = 88014deae41SDavid S. Miller saved_rt_min_interval; 88114deae41SDavid S. Miller goto retry; 88214deae41SDavid S. Miller } 88314deae41SDavid S. Miller 884f3213831SJoe Perches net_warn_ratelimited("Neighbour table overflow\n"); 885d8d1f30bSChangli Gao dst_free(&rt->dst); 88614deae41SDavid S. Miller return NULL; 88714deae41SDavid S. Miller } 88895a9a5baSYOSHIFUJI Hideaki } 8891da177e4SLinus Torvalds 8901da177e4SLinus Torvalds return rt; 8911da177e4SLinus Torvalds } 89295a9a5baSYOSHIFUJI Hideaki 89321efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, 89421efcfa0SEric Dumazet const struct in6_addr *daddr) 895299d9939SYOSHIFUJI Hideaki { 89621efcfa0SEric Dumazet struct rt6_info *rt = ip6_rt_copy(ort, daddr); 89721efcfa0SEric Dumazet 898299d9939SYOSHIFUJI Hideaki if (rt) { 899299d9939SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_CACHE; 90097cac082SDavid S. Miller rt->n = neigh_clone(ort->n); 901299d9939SYOSHIFUJI Hideaki } 902299d9939SYOSHIFUJI Hideaki return rt; 903299d9939SYOSHIFUJI Hideaki } 904299d9939SYOSHIFUJI Hideaki 9058ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, 9064c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 9071da177e4SLinus Torvalds { 9081da177e4SLinus Torvalds struct fib6_node *fn; 909519fbd87SYOSHIFUJI Hideaki struct rt6_info *rt, *nrt; 910c71099acSThomas Graf int strict = 0; 9111da177e4SLinus Torvalds int attempts = 3; 912519fbd87SYOSHIFUJI Hideaki int err; 91353b7997fSYOSHIFUJI Hideaki int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE; 9141da177e4SLinus Torvalds 91577d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 9161da177e4SLinus Torvalds 9171da177e4SLinus Torvalds relookup: 918c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 9191da177e4SLinus Torvalds 9208238dd06SYOSHIFUJI Hideaki restart_2: 9214c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 9221da177e4SLinus Torvalds 9231da177e4SLinus Torvalds restart: 9244acad72dSPavel Emelyanov rt = rt6_select(fn, oif, strict | reachable); 92551ebd318SNicolas Dichtel if (rt->rt6i_nsiblings && oif == 0) 92651ebd318SNicolas Dichtel rt = rt6_multipath_select(rt, fl6); 9274c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 9288ed67789SDaniel Lezcano if (rt == net->ipv6.ip6_null_entry || 9298238dd06SYOSHIFUJI Hideaki rt->rt6i_flags & RTF_CACHE) 9301da177e4SLinus Torvalds goto out; 9311da177e4SLinus Torvalds 932d8d1f30bSChangli Gao dst_hold(&rt->dst); 933c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 9341da177e4SLinus Torvalds 93597cac082SDavid S. Miller if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP)) 9364c9483b2SDavid S. Miller nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); 9377343ff31SDavid S. Miller else if (!(rt->dst.flags & DST_HOST)) 9384c9483b2SDavid S. Miller nrt = rt6_alloc_clone(rt, &fl6->daddr); 9397343ff31SDavid S. Miller else 9407343ff31SDavid S. Miller goto out2; 9411da177e4SLinus Torvalds 94294e187c0SAmerigo Wang ip6_rt_put(rt); 9438ed67789SDaniel Lezcano rt = nrt ? : net->ipv6.ip6_null_entry; 9441da177e4SLinus Torvalds 945d8d1f30bSChangli Gao dst_hold(&rt->dst); 946e40cf353SYOSHIFUJI Hideaki if (nrt) { 94740e22e8fSThomas Graf err = ip6_ins_rt(nrt); 948e40cf353SYOSHIFUJI Hideaki if (!err) 949e40cf353SYOSHIFUJI Hideaki goto out2; 950e40cf353SYOSHIFUJI Hideaki } 951e40cf353SYOSHIFUJI Hideaki 952e40cf353SYOSHIFUJI Hideaki if (--attempts <= 0) 9531da177e4SLinus Torvalds goto out2; 9541da177e4SLinus Torvalds 955519fbd87SYOSHIFUJI Hideaki /* 956c71099acSThomas Graf * Race condition! In the gap, when table->tb6_lock was 957519fbd87SYOSHIFUJI Hideaki * released someone could insert this route. Relookup. 9581da177e4SLinus Torvalds */ 95994e187c0SAmerigo Wang ip6_rt_put(rt); 9601da177e4SLinus Torvalds goto relookup; 961e40cf353SYOSHIFUJI Hideaki 962519fbd87SYOSHIFUJI Hideaki out: 9638238dd06SYOSHIFUJI Hideaki if (reachable) { 9648238dd06SYOSHIFUJI Hideaki reachable = 0; 9658238dd06SYOSHIFUJI Hideaki goto restart_2; 9668238dd06SYOSHIFUJI Hideaki } 967d8d1f30bSChangli Gao dst_hold(&rt->dst); 968c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 9691da177e4SLinus Torvalds out2: 970d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 971d8d1f30bSChangli Gao rt->dst.__use++; 972c71099acSThomas Graf 973c71099acSThomas Graf return rt; 974c71099acSThomas Graf } 975c71099acSThomas Graf 9768ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 9774c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 9784acad72dSPavel Emelyanov { 9794c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 9804acad72dSPavel Emelyanov } 9814acad72dSPavel Emelyanov 98272331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 98372331bc0SShmulik Ladkani struct net_device *dev, 98472331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 98572331bc0SShmulik Ladkani { 98672331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 98772331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 98872331bc0SShmulik Ladkani 98972331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 99072331bc0SShmulik Ladkani } 99172331bc0SShmulik Ladkani 992c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 993c71099acSThomas Graf { 994b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 995c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 996adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 9974c9483b2SDavid S. Miller struct flowi6 fl6 = { 9984c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 9994c9483b2SDavid S. Miller .daddr = iph->daddr, 10004c9483b2SDavid S. Miller .saddr = iph->saddr, 10014c9483b2SDavid S. Miller .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK, 10024c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 10034c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 1004c71099acSThomas Graf }; 1005adaa70bbSThomas Graf 100672331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 1007c71099acSThomas Graf } 1008c71099acSThomas Graf 10098ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 10104c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 1011c71099acSThomas Graf { 10124c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 1013c71099acSThomas Graf } 1014c71099acSThomas Graf 10159c7a4f9cSFlorian Westphal struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk, 10164c9483b2SDavid S. Miller struct flowi6 *fl6) 1017c71099acSThomas Graf { 1018c71099acSThomas Graf int flags = 0; 1019c71099acSThomas Graf 10201fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 10214dc27d1cSDavid McCullough 10224c9483b2SDavid S. Miller if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) 102377d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 1024c71099acSThomas Graf 10254c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 1026adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 10270c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 10280c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 1029adaa70bbSThomas Graf 10304c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 10311da177e4SLinus Torvalds } 10321da177e4SLinus Torvalds 10337159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 10341da177e4SLinus Torvalds 10352774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 103614e50e57SDavid S. Miller { 10375c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 103814e50e57SDavid S. Miller struct dst_entry *new = NULL; 103914e50e57SDavid S. Miller 1040f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 104114e50e57SDavid S. Miller if (rt) { 1042d8d1f30bSChangli Gao new = &rt->dst; 104314e50e57SDavid S. Miller 10448104891bSSteffen Klassert memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); 10458104891bSSteffen Klassert rt6_init_peer(rt, net->ipv6.peers); 10468104891bSSteffen Klassert 104714e50e57SDavid S. Miller new->__use = 1; 1048352e512cSHerbert Xu new->input = dst_discard; 1049352e512cSHerbert Xu new->output = dst_discard; 105014e50e57SDavid S. Miller 105121efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 105221efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 105321efcfa0SEric Dumazet else 1054defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 105514e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 105614e50e57SDavid S. Miller if (rt->rt6i_idev) 105714e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 105814e50e57SDavid S. Miller 10594e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 10601716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 10611716a961SGao feng rt6_clean_expires(rt); 106214e50e57SDavid S. Miller rt->rt6i_metric = 0; 106314e50e57SDavid S. Miller 106414e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 106514e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 106614e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 106714e50e57SDavid S. Miller #endif 106814e50e57SDavid S. Miller 106914e50e57SDavid S. Miller dst_free(new); 107014e50e57SDavid S. Miller } 107114e50e57SDavid S. Miller 107269ead7afSDavid S. Miller dst_release(dst_orig); 107369ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 107414e50e57SDavid S. Miller } 107514e50e57SDavid S. Miller 10761da177e4SLinus Torvalds /* 10771da177e4SLinus Torvalds * Destination cache support functions 10781da177e4SLinus Torvalds */ 10791da177e4SLinus Torvalds 10801da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 10811da177e4SLinus Torvalds { 10821da177e4SLinus Torvalds struct rt6_info *rt; 10831da177e4SLinus Torvalds 10841da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 10851da177e4SLinus Torvalds 10866f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 10876f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 10886f3118b5SNicolas Dichtel * into this function always. 10896f3118b5SNicolas Dichtel */ 10906f3118b5SNicolas Dichtel if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev))) 10916f3118b5SNicolas Dichtel return NULL; 10926f3118b5SNicolas Dichtel 1093a4477c4dSLi RongQing if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) 10941da177e4SLinus Torvalds return dst; 1095a4477c4dSLi RongQing 10961da177e4SLinus Torvalds return NULL; 10971da177e4SLinus Torvalds } 10981da177e4SLinus Torvalds 10991da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 11001da177e4SLinus Torvalds { 11011da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 11021da177e4SLinus Torvalds 11031da177e4SLinus Torvalds if (rt) { 110454c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 110554c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1106e0a1ad73SThomas Graf ip6_del_rt(rt); 110754c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 11081da177e4SLinus Torvalds } 110954c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 111054c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 111154c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 111254c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 111354c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 111454c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 11151da177e4SLinus Torvalds } 11161da177e4SLinus Torvalds 11171da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 11181da177e4SLinus Torvalds { 11191da177e4SLinus Torvalds struct rt6_info *rt; 11201da177e4SLinus Torvalds 11213ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 11221da177e4SLinus Torvalds 1123adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 11241da177e4SLinus Torvalds if (rt) { 11251716a961SGao feng if (rt->rt6i_flags & RTF_CACHE) 11261716a961SGao feng rt6_update_expires(rt, 0); 11271716a961SGao feng else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) 11281da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 11291da177e4SLinus Torvalds } 11301da177e4SLinus Torvalds } 11311da177e4SLinus Torvalds 11326700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 11336700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 11341da177e4SLinus Torvalds { 11351da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info*)dst; 11361da177e4SLinus Torvalds 113781aded24SDavid S. Miller dst_confirm(dst); 11381da177e4SLinus Torvalds if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) { 113981aded24SDavid S. Miller struct net *net = dev_net(dst->dev); 114081aded24SDavid S. Miller 11411da177e4SLinus Torvalds rt6->rt6i_flags |= RTF_MODIFIED; 11421da177e4SLinus Torvalds if (mtu < IPV6_MIN_MTU) { 1143defb3519SDavid S. Miller u32 features = dst_metric(dst, RTAX_FEATURES); 11441da177e4SLinus Torvalds mtu = IPV6_MIN_MTU; 1145defb3519SDavid S. Miller features |= RTAX_FEATURE_ALLFRAG; 1146defb3519SDavid S. Miller dst_metric_set(dst, RTAX_FEATURES, features); 11471da177e4SLinus Torvalds } 1148defb3519SDavid S. Miller dst_metric_set(dst, RTAX_MTU, mtu); 114981aded24SDavid S. Miller rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires); 11501da177e4SLinus Torvalds } 11511da177e4SLinus Torvalds } 11521da177e4SLinus Torvalds 115342ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 115442ae66c8SDavid S. Miller int oif, u32 mark) 115581aded24SDavid S. Miller { 115681aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 115781aded24SDavid S. Miller struct dst_entry *dst; 115881aded24SDavid S. Miller struct flowi6 fl6; 115981aded24SDavid S. Miller 116081aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 116181aded24SDavid S. Miller fl6.flowi6_oif = oif; 116281aded24SDavid S. Miller fl6.flowi6_mark = mark; 11633e12939aSDavid S. Miller fl6.flowi6_flags = 0; 116481aded24SDavid S. Miller fl6.daddr = iph->daddr; 116581aded24SDavid S. Miller fl6.saddr = iph->saddr; 116681aded24SDavid S. Miller fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK; 116781aded24SDavid S. Miller 116881aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 116981aded24SDavid S. Miller if (!dst->error) 11706700c270SDavid S. Miller ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu)); 117181aded24SDavid S. Miller dst_release(dst); 117281aded24SDavid S. Miller } 117381aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 117481aded24SDavid S. Miller 117581aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 117681aded24SDavid S. Miller { 117781aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 117881aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 117981aded24SDavid S. Miller } 118081aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 118181aded24SDavid S. Miller 11823a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 11833a5ad2eeSDavid S. Miller { 11843a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 11853a5ad2eeSDavid S. Miller struct dst_entry *dst; 11863a5ad2eeSDavid S. Miller struct flowi6 fl6; 11873a5ad2eeSDavid S. Miller 11883a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 11893a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 11903a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 11913a5ad2eeSDavid S. Miller fl6.flowi6_flags = 0; 11923a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 11933a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 11943a5ad2eeSDavid S. Miller fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK; 11953a5ad2eeSDavid S. Miller 11963a5ad2eeSDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 11973a5ad2eeSDavid S. Miller if (!dst->error) 11986700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 11993a5ad2eeSDavid S. Miller dst_release(dst); 12003a5ad2eeSDavid S. Miller } 12013a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 12023a5ad2eeSDavid S. Miller 12033a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 12043a5ad2eeSDavid S. Miller { 12053a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 12063a5ad2eeSDavid S. Miller } 12073a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 12083a5ad2eeSDavid S. Miller 12090dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 12101da177e4SLinus Torvalds { 12110dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 12120dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 12130dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 12140dbaee3bSDavid S. Miller 12151da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 12161da177e4SLinus Torvalds 12175578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 12185578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 12191da177e4SLinus Torvalds 12201da177e4SLinus Torvalds /* 12211da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 12221da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 12231da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 12241da177e4SLinus Torvalds * rely only on pmtu discovery" 12251da177e4SLinus Torvalds */ 12261da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 12271da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 12281da177e4SLinus Torvalds return mtu; 12291da177e4SLinus Torvalds } 12301da177e4SLinus Torvalds 1231ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1232d33e4553SDavid S. Miller { 1233d33e4553SDavid S. Miller struct inet6_dev *idev; 1234618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 1235618f9bc7SSteffen Klassert 1236618f9bc7SSteffen Klassert if (mtu) 1237618f9bc7SSteffen Klassert return mtu; 1238618f9bc7SSteffen Klassert 1239618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1240d33e4553SDavid S. Miller 1241d33e4553SDavid S. Miller rcu_read_lock(); 1242d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1243d33e4553SDavid S. Miller if (idev) 1244d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1245d33e4553SDavid S. Miller rcu_read_unlock(); 1246d33e4553SDavid S. Miller 1247d33e4553SDavid S. Miller return mtu; 1248d33e4553SDavid S. Miller } 1249d33e4553SDavid S. Miller 12503b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 12513b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 12525d0bbeebSThomas Graf 12533b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 12541da177e4SLinus Torvalds struct neighbour *neigh, 125587a11578SDavid S. Miller struct flowi6 *fl6) 12561da177e4SLinus Torvalds { 125787a11578SDavid S. Miller struct dst_entry *dst; 12581da177e4SLinus Torvalds struct rt6_info *rt; 12591da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1260c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 12611da177e4SLinus Torvalds 126238308473SDavid S. Miller if (unlikely(!idev)) 1263122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 12641da177e4SLinus Torvalds 12658b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, dev, 0, NULL); 126638308473SDavid S. Miller if (unlikely(!rt)) { 12671da177e4SLinus Torvalds in6_dev_put(idev); 126887a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 12691da177e4SLinus Torvalds goto out; 12701da177e4SLinus Torvalds } 12711da177e4SLinus Torvalds 12721da177e4SLinus Torvalds if (neigh) 12731da177e4SLinus Torvalds neigh_hold(neigh); 127414deae41SDavid S. Miller else { 1275f894cbf8SDavid S. Miller neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr); 1276b43faac6SDavid S. Miller if (IS_ERR(neigh)) { 1277252c3d84SRongQing.Li in6_dev_put(idev); 1278b43faac6SDavid S. Miller dst_free(&rt->dst); 1279b43faac6SDavid S. Miller return ERR_CAST(neigh); 1280b43faac6SDavid S. Miller } 128114deae41SDavid S. Miller } 12821da177e4SLinus Torvalds 12838e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 12848e2ec639SYan, Zheng rt->dst.output = ip6_output; 128597cac082SDavid S. Miller rt->n = neigh; 1286d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 128787a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 12888e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 12898e2ec639SYan, Zheng rt->rt6i_idev = idev; 129014edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 12911da177e4SLinus Torvalds 12923b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1293d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1294d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 12953b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 12961da177e4SLinus Torvalds 12975578689aSDaniel Lezcano fib6_force_start_gc(net); 12981da177e4SLinus Torvalds 129987a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 130087a11578SDavid S. Miller 13011da177e4SLinus Torvalds out: 130287a11578SDavid S. Miller return dst; 13031da177e4SLinus Torvalds } 13041da177e4SLinus Torvalds 13053d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 13061da177e4SLinus Torvalds { 1307e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 13083d0f24a7SStephen Hemminger int more = 0; 13091da177e4SLinus Torvalds 13103b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 13113b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 13125d0bbeebSThomas Graf 13131da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 13141da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 13151da177e4SLinus Torvalds *pprev = dst->next; 13161da177e4SLinus Torvalds dst_free(dst); 13171da177e4SLinus Torvalds } else { 13181da177e4SLinus Torvalds pprev = &dst->next; 13193d0f24a7SStephen Hemminger ++more; 13201da177e4SLinus Torvalds } 13211da177e4SLinus Torvalds } 13221da177e4SLinus Torvalds 13233b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 13245d0bbeebSThomas Graf 13253d0f24a7SStephen Hemminger return more; 13261da177e4SLinus Torvalds } 13271da177e4SLinus Torvalds 13281e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 13291e493d19SDavid S. Miller void *arg) 13301e493d19SDavid S. Miller { 13311e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 13321e493d19SDavid S. Miller 13331e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 13341e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 13351e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 13361e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 13371e493d19SDavid S. Miller if (func(rt, arg)) { 13381e493d19SDavid S. Miller *pprev = dst->next; 13391e493d19SDavid S. Miller dst_free(dst); 13401e493d19SDavid S. Miller } else { 13411e493d19SDavid S. Miller pprev = &dst->next; 13421e493d19SDavid S. Miller } 13431e493d19SDavid S. Miller } 13441e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 13451e493d19SDavid S. Miller } 13461e493d19SDavid S. Miller 1347569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 13481da177e4SLinus Torvalds { 13491da177e4SLinus Torvalds unsigned long now = jiffies; 135086393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 13517019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 13527019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 13537019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 13547019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 13557019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1356fc66f95cSEric Dumazet int entries; 13571da177e4SLinus Torvalds 1358fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 13597019b78eSDaniel Lezcano if (time_after(rt_last_gc + rt_min_interval, now) && 1360fc66f95cSEric Dumazet entries <= rt_max_size) 13611da177e4SLinus Torvalds goto out; 13621da177e4SLinus Torvalds 13636891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 13646891a346SBenjamin Thery fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net); 13656891a346SBenjamin Thery net->ipv6.ip6_rt_last_gc = now; 1366fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1367fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 13687019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 13691da177e4SLinus Torvalds out: 13707019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1371fc66f95cSEric Dumazet return entries > rt_max_size; 13721da177e4SLinus Torvalds } 13731da177e4SLinus Torvalds 13746b75d090SYOSHIFUJI Hideaki int ip6_dst_hoplimit(struct dst_entry *dst) 13751da177e4SLinus Torvalds { 13765170ae82SDavid S. Miller int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT); 1377a02e4b7dSDavid S. Miller if (hoplimit == 0) { 13786b75d090SYOSHIFUJI Hideaki struct net_device *dev = dst->dev; 1379c68f24ccSEric Dumazet struct inet6_dev *idev; 1380c68f24ccSEric Dumazet 1381c68f24ccSEric Dumazet rcu_read_lock(); 1382c68f24ccSEric Dumazet idev = __in6_dev_get(dev); 1383c68f24ccSEric Dumazet if (idev) 13841da177e4SLinus Torvalds hoplimit = idev->cnf.hop_limit; 1385c68f24ccSEric Dumazet else 138653b7997fSYOSHIFUJI Hideaki hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit; 1387c68f24ccSEric Dumazet rcu_read_unlock(); 13881da177e4SLinus Torvalds } 13891da177e4SLinus Torvalds return hoplimit; 13901da177e4SLinus Torvalds } 1391abbf46aeSDavid S. Miller EXPORT_SYMBOL(ip6_dst_hoplimit); 13921da177e4SLinus Torvalds 13931da177e4SLinus Torvalds /* 13941da177e4SLinus Torvalds * 13951da177e4SLinus Torvalds */ 13961da177e4SLinus Torvalds 139786872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg) 13981da177e4SLinus Torvalds { 13991da177e4SLinus Torvalds int err; 14005578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 14011da177e4SLinus Torvalds struct rt6_info *rt = NULL; 14021da177e4SLinus Torvalds struct net_device *dev = NULL; 14031da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1404c71099acSThomas Graf struct fib6_table *table; 14051da177e4SLinus Torvalds int addr_type; 14061da177e4SLinus Torvalds 140786872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 14081da177e4SLinus Torvalds return -EINVAL; 14091da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 141086872cb5SThomas Graf if (cfg->fc_src_len) 14111da177e4SLinus Torvalds return -EINVAL; 14121da177e4SLinus Torvalds #endif 141386872cb5SThomas Graf if (cfg->fc_ifindex) { 14141da177e4SLinus Torvalds err = -ENODEV; 14155578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 14161da177e4SLinus Torvalds if (!dev) 14171da177e4SLinus Torvalds goto out; 14181da177e4SLinus Torvalds idev = in6_dev_get(dev); 14191da177e4SLinus Torvalds if (!idev) 14201da177e4SLinus Torvalds goto out; 14211da177e4SLinus Torvalds } 14221da177e4SLinus Torvalds 142386872cb5SThomas Graf if (cfg->fc_metric == 0) 142486872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 14251da177e4SLinus Torvalds 1426c71099acSThomas Graf err = -ENOBUFS; 142738308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1428d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1429d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 143038308473SDavid S. Miller if (!table) { 1431f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1432d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1433d71314b4SMatti Vaittinen } 1434d71314b4SMatti Vaittinen } else { 1435d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1436d71314b4SMatti Vaittinen } 143738308473SDavid S. Miller 143838308473SDavid S. Miller if (!table) 1439c71099acSThomas Graf goto out; 1440c71099acSThomas Graf 14418b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table); 14421da177e4SLinus Torvalds 144338308473SDavid S. Miller if (!rt) { 14441da177e4SLinus Torvalds err = -ENOMEM; 14451da177e4SLinus Torvalds goto out; 14461da177e4SLinus Torvalds } 14471da177e4SLinus Torvalds 14481716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 14491716a961SGao feng rt6_set_expires(rt, jiffies + 14501716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 14511716a961SGao feng else 14521716a961SGao feng rt6_clean_expires(rt); 14531da177e4SLinus Torvalds 145486872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 145586872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 145686872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 145786872cb5SThomas Graf 145886872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 14591da177e4SLinus Torvalds 14601da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1461d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1462ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1463ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 14641da177e4SLinus Torvalds else 1465d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 14661da177e4SLinus Torvalds 1467d8d1f30bSChangli Gao rt->dst.output = ip6_output; 14681da177e4SLinus Torvalds 146986872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 147086872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 14711da177e4SLinus Torvalds if (rt->rt6i_dst.plen == 128) 147211d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 14731da177e4SLinus Torvalds 14748e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) { 14758e2ec639SYan, Zheng u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 14768e2ec639SYan, Zheng if (!metrics) { 14778e2ec639SYan, Zheng err = -ENOMEM; 14788e2ec639SYan, Zheng goto out; 14798e2ec639SYan, Zheng } 14808e2ec639SYan, Zheng dst_init_metrics(&rt->dst, metrics, 0); 14818e2ec639SYan, Zheng } 14821da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 148386872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 148486872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 14851da177e4SLinus Torvalds #endif 14861da177e4SLinus Torvalds 148786872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 14881da177e4SLinus Torvalds 14891da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 14901da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 14911da177e4SLinus Torvalds */ 149286872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 149338308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 149438308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 149538308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 14961da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 14975578689aSDaniel Lezcano if (dev != net->loopback_dev) { 14981da177e4SLinus Torvalds if (dev) { 14991da177e4SLinus Torvalds dev_put(dev); 15001da177e4SLinus Torvalds in6_dev_put(idev); 15011da177e4SLinus Torvalds } 15025578689aSDaniel Lezcano dev = net->loopback_dev; 15031da177e4SLinus Torvalds dev_hold(dev); 15041da177e4SLinus Torvalds idev = in6_dev_get(dev); 15051da177e4SLinus Torvalds if (!idev) { 15061da177e4SLinus Torvalds err = -ENODEV; 15071da177e4SLinus Torvalds goto out; 15081da177e4SLinus Torvalds } 15091da177e4SLinus Torvalds } 1510d8d1f30bSChangli Gao rt->dst.output = ip6_pkt_discard_out; 1511d8d1f30bSChangli Gao rt->dst.input = ip6_pkt_discard; 15121da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1513ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1514ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1515ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 1516ef2c7d7bSNicolas Dichtel break; 1517ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1518ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 1519ef2c7d7bSNicolas Dichtel break; 1520b4949ab2SNicolas Dichtel case RTN_THROW: 1521b4949ab2SNicolas Dichtel rt->dst.error = -EAGAIN; 1522b4949ab2SNicolas Dichtel break; 1523ef2c7d7bSNicolas Dichtel default: 1524ef2c7d7bSNicolas Dichtel rt->dst.error = -ENETUNREACH; 1525ef2c7d7bSNicolas Dichtel break; 1526ef2c7d7bSNicolas Dichtel } 15271da177e4SLinus Torvalds goto install_route; 15281da177e4SLinus Torvalds } 15291da177e4SLinus Torvalds 153086872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1531b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 15321da177e4SLinus Torvalds int gwa_type; 15331da177e4SLinus Torvalds 153486872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 15354e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 15361da177e4SLinus Torvalds gwa_type = ipv6_addr_type(gw_addr); 15371da177e4SLinus Torvalds 15381da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 15391da177e4SLinus Torvalds struct rt6_info *grt; 15401da177e4SLinus Torvalds 15411da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 15421da177e4SLinus Torvalds addresses as nexthop address. 15431da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 15441da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 15451da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 15461da177e4SLinus Torvalds some exceptions. --ANK 15471da177e4SLinus Torvalds */ 15481da177e4SLinus Torvalds err = -EINVAL; 15491da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 15501da177e4SLinus Torvalds goto out; 15511da177e4SLinus Torvalds 15525578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 15531da177e4SLinus Torvalds 15541da177e4SLinus Torvalds err = -EHOSTUNREACH; 155538308473SDavid S. Miller if (!grt) 15561da177e4SLinus Torvalds goto out; 15571da177e4SLinus Torvalds if (dev) { 1558d1918542SDavid S. Miller if (dev != grt->dst.dev) { 155994e187c0SAmerigo Wang ip6_rt_put(grt); 15601da177e4SLinus Torvalds goto out; 15611da177e4SLinus Torvalds } 15621da177e4SLinus Torvalds } else { 1563d1918542SDavid S. Miller dev = grt->dst.dev; 15641da177e4SLinus Torvalds idev = grt->rt6i_idev; 15651da177e4SLinus Torvalds dev_hold(dev); 15661da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 15671da177e4SLinus Torvalds } 15681da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 15691da177e4SLinus Torvalds err = 0; 157094e187c0SAmerigo Wang ip6_rt_put(grt); 15711da177e4SLinus Torvalds 15721da177e4SLinus Torvalds if (err) 15731da177e4SLinus Torvalds goto out; 15741da177e4SLinus Torvalds } 15751da177e4SLinus Torvalds err = -EINVAL; 157638308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 15771da177e4SLinus Torvalds goto out; 15781da177e4SLinus Torvalds } 15791da177e4SLinus Torvalds 15801da177e4SLinus Torvalds err = -ENODEV; 158138308473SDavid S. Miller if (!dev) 15821da177e4SLinus Torvalds goto out; 15831da177e4SLinus Torvalds 1584c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1585c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1586c3968a85SDaniel Walter err = -EINVAL; 1587c3968a85SDaniel Walter goto out; 1588c3968a85SDaniel Walter } 15894e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1590c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1591c3968a85SDaniel Walter } else 1592c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1593c3968a85SDaniel Walter 159486872cb5SThomas Graf if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) { 15958ade06c6SDavid S. Miller err = rt6_bind_neighbour(rt, dev); 1596f83c7790SDavid S. Miller if (err) 15971da177e4SLinus Torvalds goto out; 15981da177e4SLinus Torvalds } 15991da177e4SLinus Torvalds 160086872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 16011da177e4SLinus Torvalds 16021da177e4SLinus Torvalds install_route: 160386872cb5SThomas Graf if (cfg->fc_mx) { 160486872cb5SThomas Graf struct nlattr *nla; 160586872cb5SThomas Graf int remaining; 16061da177e4SLinus Torvalds 160786872cb5SThomas Graf nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 16088f4c1f9bSThomas Graf int type = nla_type(nla); 160986872cb5SThomas Graf 161086872cb5SThomas Graf if (type) { 161186872cb5SThomas Graf if (type > RTAX_MAX) { 16121da177e4SLinus Torvalds err = -EINVAL; 16131da177e4SLinus Torvalds goto out; 16141da177e4SLinus Torvalds } 161586872cb5SThomas Graf 1616defb3519SDavid S. Miller dst_metric_set(&rt->dst, type, nla_get_u32(nla)); 16171da177e4SLinus Torvalds } 16181da177e4SLinus Torvalds } 16191da177e4SLinus Torvalds } 16201da177e4SLinus Torvalds 1621d8d1f30bSChangli Gao rt->dst.dev = dev; 16221da177e4SLinus Torvalds rt->rt6i_idev = idev; 1623c71099acSThomas Graf rt->rt6i_table = table; 162463152fc0SDaniel Lezcano 1625c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 162663152fc0SDaniel Lezcano 162786872cb5SThomas Graf return __ip6_ins_rt(rt, &cfg->fc_nlinfo); 16281da177e4SLinus Torvalds 16291da177e4SLinus Torvalds out: 16301da177e4SLinus Torvalds if (dev) 16311da177e4SLinus Torvalds dev_put(dev); 16321da177e4SLinus Torvalds if (idev) 16331da177e4SLinus Torvalds in6_dev_put(idev); 16341da177e4SLinus Torvalds if (rt) 1635d8d1f30bSChangli Gao dst_free(&rt->dst); 16361da177e4SLinus Torvalds return err; 16371da177e4SLinus Torvalds } 16381da177e4SLinus Torvalds 163986872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 16401da177e4SLinus Torvalds { 16411da177e4SLinus Torvalds int err; 1642c71099acSThomas Graf struct fib6_table *table; 1643d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 16441da177e4SLinus Torvalds 16456825a26cSGao feng if (rt == net->ipv6.ip6_null_entry) { 16466825a26cSGao feng err = -ENOENT; 16476825a26cSGao feng goto out; 16486825a26cSGao feng } 16496c813a72SPatrick McHardy 1650c71099acSThomas Graf table = rt->rt6i_table; 1651c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 165286872cb5SThomas Graf err = fib6_del(rt, info); 1653c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 16541da177e4SLinus Torvalds 16556825a26cSGao feng out: 165694e187c0SAmerigo Wang ip6_rt_put(rt); 16571da177e4SLinus Torvalds return err; 16581da177e4SLinus Torvalds } 16591da177e4SLinus Torvalds 1660e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 1661e0a1ad73SThomas Graf { 16624d1169c1SDenis V. Lunev struct nl_info info = { 1663d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 16644d1169c1SDenis V. Lunev }; 1665528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 1666e0a1ad73SThomas Graf } 1667e0a1ad73SThomas Graf 166886872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 16691da177e4SLinus Torvalds { 1670c71099acSThomas Graf struct fib6_table *table; 16711da177e4SLinus Torvalds struct fib6_node *fn; 16721da177e4SLinus Torvalds struct rt6_info *rt; 16731da177e4SLinus Torvalds int err = -ESRCH; 16741da177e4SLinus Torvalds 16755578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 167638308473SDavid S. Miller if (!table) 1677c71099acSThomas Graf return err; 16781da177e4SLinus Torvalds 1679c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1680c71099acSThomas Graf 1681c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 168286872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 168386872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 16841da177e4SLinus Torvalds 16851da177e4SLinus Torvalds if (fn) { 1686d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 168786872cb5SThomas Graf if (cfg->fc_ifindex && 1688d1918542SDavid S. Miller (!rt->dst.dev || 1689d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 16901da177e4SLinus Torvalds continue; 169186872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 169286872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 16931da177e4SLinus Torvalds continue; 169486872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 16951da177e4SLinus Torvalds continue; 1696d8d1f30bSChangli Gao dst_hold(&rt->dst); 1697c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 16981da177e4SLinus Torvalds 169986872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 17001da177e4SLinus Torvalds } 17011da177e4SLinus Torvalds } 1702c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 17031da177e4SLinus Torvalds 17041da177e4SLinus Torvalds return err; 17051da177e4SLinus Torvalds } 17061da177e4SLinus Torvalds 17076700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 1708a6279458SYOSHIFUJI Hideaki { 1709e8599ff4SDavid S. Miller struct net *net = dev_net(skb->dev); 1710a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 1711e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 1712e8599ff4SDavid S. Miller const struct in6_addr *target; 1713e8599ff4SDavid S. Miller struct ndisc_options ndopts; 17146e157b6aSDavid S. Miller const struct in6_addr *dest; 17156e157b6aSDavid S. Miller struct neighbour *old_neigh; 1716e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 1717e8599ff4SDavid S. Miller struct neighbour *neigh; 1718e8599ff4SDavid S. Miller struct icmp6hdr *icmph; 17196e157b6aSDavid S. Miller int optlen, on_link; 17206e157b6aSDavid S. Miller u8 *lladdr; 1721e8599ff4SDavid S. Miller 1722e8599ff4SDavid S. Miller optlen = skb->tail - skb->transport_header; 1723e8599ff4SDavid S. Miller optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); 1724e8599ff4SDavid S. Miller 1725e8599ff4SDavid S. Miller if (optlen < 0) { 17266e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 1727e8599ff4SDavid S. Miller return; 1728e8599ff4SDavid S. Miller } 1729e8599ff4SDavid S. Miller 1730e8599ff4SDavid S. Miller icmph = icmp6_hdr(skb); 1731e8599ff4SDavid S. Miller target = (const struct in6_addr *) (icmph + 1); 1732e8599ff4SDavid S. Miller dest = target + 1; 1733e8599ff4SDavid S. Miller 1734e8599ff4SDavid S. Miller if (ipv6_addr_is_multicast(dest)) { 17356e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 1736e8599ff4SDavid S. Miller return; 1737e8599ff4SDavid S. Miller } 1738e8599ff4SDavid S. Miller 17396e157b6aSDavid S. Miller on_link = 0; 1740e8599ff4SDavid S. Miller if (ipv6_addr_equal(dest, target)) { 1741e8599ff4SDavid S. Miller on_link = 1; 1742e8599ff4SDavid S. Miller } else if (ipv6_addr_type(target) != 1743e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 17446e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 1745e8599ff4SDavid S. Miller return; 1746e8599ff4SDavid S. Miller } 1747e8599ff4SDavid S. Miller 1748e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 1749e8599ff4SDavid S. Miller if (!in6_dev) 1750e8599ff4SDavid S. Miller return; 1751e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 1752e8599ff4SDavid S. Miller return; 1753e8599ff4SDavid S. Miller 1754e8599ff4SDavid S. Miller /* RFC2461 8.1: 1755e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 1756e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 1757e8599ff4SDavid S. Miller */ 1758e8599ff4SDavid S. Miller 1759e8599ff4SDavid S. Miller if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) { 1760e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 1761e8599ff4SDavid S. Miller return; 1762e8599ff4SDavid S. Miller } 17636e157b6aSDavid S. Miller 17646e157b6aSDavid S. Miller lladdr = NULL; 1765e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 1766e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 1767e8599ff4SDavid S. Miller skb->dev); 1768e8599ff4SDavid S. Miller if (!lladdr) { 1769e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 1770e8599ff4SDavid S. Miller return; 1771e8599ff4SDavid S. Miller } 1772e8599ff4SDavid S. Miller } 1773e8599ff4SDavid S. Miller 17746e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 17756e157b6aSDavid S. Miller if (rt == net->ipv6.ip6_null_entry) { 17766e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 17776e157b6aSDavid S. Miller return; 17786e157b6aSDavid S. Miller } 17796e157b6aSDavid S. Miller 17806e157b6aSDavid S. Miller /* Redirect received -> path was valid. 17816e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 17826e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 17836e157b6aSDavid S. Miller */ 17846e157b6aSDavid S. Miller dst_confirm(&rt->dst); 17856e157b6aSDavid S. Miller 1786e8599ff4SDavid S. Miller neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1); 1787e8599ff4SDavid S. Miller if (!neigh) 1788e8599ff4SDavid S. Miller return; 1789e8599ff4SDavid S. Miller 17906e157b6aSDavid S. Miller /* Duplicate redirect: silently ignore. */ 17916e157b6aSDavid S. Miller old_neigh = rt->n; 17926e157b6aSDavid S. Miller if (neigh == old_neigh) 1793a6279458SYOSHIFUJI Hideaki goto out; 17941da177e4SLinus Torvalds 17951da177e4SLinus Torvalds /* 17961da177e4SLinus Torvalds * We have finally decided to accept it. 17971da177e4SLinus Torvalds */ 17981da177e4SLinus Torvalds 17991da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 18001da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 18011da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 18021da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 18031da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 18041da177e4SLinus Torvalds ); 18051da177e4SLinus Torvalds 180621efcfa0SEric Dumazet nrt = ip6_rt_copy(rt, dest); 180738308473SDavid S. Miller if (!nrt) 18081da177e4SLinus Torvalds goto out; 18091da177e4SLinus Torvalds 18101da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 18111da177e4SLinus Torvalds if (on_link) 18121da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 18131da177e4SLinus Torvalds 18144e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 181597cac082SDavid S. Miller nrt->n = neigh_clone(neigh); 18161da177e4SLinus Torvalds 181740e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 18181da177e4SLinus Torvalds goto out; 18191da177e4SLinus Torvalds 1820d8d1f30bSChangli Gao netevent.old = &rt->dst; 18211d248b1cSDavid S. Miller netevent.old_neigh = old_neigh; 1822d8d1f30bSChangli Gao netevent.new = &nrt->dst; 18231d248b1cSDavid S. Miller netevent.new_neigh = neigh; 18241d248b1cSDavid S. Miller netevent.daddr = dest; 18258d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 18268d71740cSTom Tucker 18271da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 18286e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 1829e0a1ad73SThomas Graf ip6_del_rt(rt); 18301da177e4SLinus Torvalds } 18311da177e4SLinus Torvalds 18321da177e4SLinus Torvalds out: 1833e8599ff4SDavid S. Miller neigh_release(neigh); 18346e157b6aSDavid S. Miller } 18356e157b6aSDavid S. Miller 18361da177e4SLinus Torvalds /* 18371da177e4SLinus Torvalds * Misc support functions 18381da177e4SLinus Torvalds */ 18391da177e4SLinus Torvalds 18401716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 184121efcfa0SEric Dumazet const struct in6_addr *dest) 18421da177e4SLinus Torvalds { 1843d1918542SDavid S. Miller struct net *net = dev_net(ort->dst.dev); 18448b96d22dSDavid S. Miller struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0, 18458b96d22dSDavid S. Miller ort->rt6i_table); 18461da177e4SLinus Torvalds 18471da177e4SLinus Torvalds if (rt) { 1848d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 1849d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 18508e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 18511da177e4SLinus Torvalds 18524e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *dest; 18538e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 1854defb3519SDavid S. Miller dst_copy_metrics(&rt->dst, &ort->dst); 1855d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 18561da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 18571da177e4SLinus Torvalds if (rt->rt6i_idev) 18581da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 1859d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 18601da177e4SLinus Torvalds 18614e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 18621716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 18631716a961SGao feng if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) == 18641716a961SGao feng (RTF_DEFAULT | RTF_ADDRCONF)) 18651716a961SGao feng rt6_set_from(rt, ort); 18661716a961SGao feng else 18671716a961SGao feng rt6_clean_expires(rt); 18681da177e4SLinus Torvalds rt->rt6i_metric = 0; 18691da177e4SLinus Torvalds 18701da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 18711da177e4SLinus Torvalds memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 18721da177e4SLinus Torvalds #endif 18730f6c6392SFlorian Westphal memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key)); 1874c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 18751da177e4SLinus Torvalds } 18761da177e4SLinus Torvalds return rt; 18771da177e4SLinus Torvalds } 18781da177e4SLinus Torvalds 187970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 1880efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 1881b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1882b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 188370ceb4f5SYOSHIFUJI Hideaki { 188470ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 188570ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 1886c71099acSThomas Graf struct fib6_table *table; 188770ceb4f5SYOSHIFUJI Hideaki 1888efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 188938308473SDavid S. Miller if (!table) 1890c71099acSThomas Graf return NULL; 1891c71099acSThomas Graf 18925744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 1893c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0); 189470ceb4f5SYOSHIFUJI Hideaki if (!fn) 189570ceb4f5SYOSHIFUJI Hideaki goto out; 189670ceb4f5SYOSHIFUJI Hideaki 1897d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1898d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 189970ceb4f5SYOSHIFUJI Hideaki continue; 190070ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 190170ceb4f5SYOSHIFUJI Hideaki continue; 190270ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 190370ceb4f5SYOSHIFUJI Hideaki continue; 1904d8d1f30bSChangli Gao dst_hold(&rt->dst); 190570ceb4f5SYOSHIFUJI Hideaki break; 190670ceb4f5SYOSHIFUJI Hideaki } 190770ceb4f5SYOSHIFUJI Hideaki out: 19085744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 190970ceb4f5SYOSHIFUJI Hideaki return rt; 191070ceb4f5SYOSHIFUJI Hideaki } 191170ceb4f5SYOSHIFUJI Hideaki 1912efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 1913b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1914b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 191595c96174SEric Dumazet unsigned int pref) 191670ceb4f5SYOSHIFUJI Hideaki { 191786872cb5SThomas Graf struct fib6_config cfg = { 191886872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 1919238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 192086872cb5SThomas Graf .fc_ifindex = ifindex, 192186872cb5SThomas Graf .fc_dst_len = prefixlen, 192286872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 192386872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 192415e47304SEric W. Biederman .fc_nlinfo.portid = 0, 1925efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 1926efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 192786872cb5SThomas Graf }; 192870ceb4f5SYOSHIFUJI Hideaki 19294e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 19304e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 193186872cb5SThomas Graf 1932e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 1933e317da96SYOSHIFUJI Hideaki if (!prefixlen) 193486872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 193570ceb4f5SYOSHIFUJI Hideaki 193686872cb5SThomas Graf ip6_route_add(&cfg); 193770ceb4f5SYOSHIFUJI Hideaki 1938efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 193970ceb4f5SYOSHIFUJI Hideaki } 194070ceb4f5SYOSHIFUJI Hideaki #endif 194170ceb4f5SYOSHIFUJI Hideaki 1942b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 19431da177e4SLinus Torvalds { 19441da177e4SLinus Torvalds struct rt6_info *rt; 1945c71099acSThomas Graf struct fib6_table *table; 19461da177e4SLinus Torvalds 1947c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 194838308473SDavid S. Miller if (!table) 1949c71099acSThomas Graf return NULL; 19501da177e4SLinus Torvalds 19515744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 1952d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) { 1953d1918542SDavid S. Miller if (dev == rt->dst.dev && 1954045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 19551da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 19561da177e4SLinus Torvalds break; 19571da177e4SLinus Torvalds } 19581da177e4SLinus Torvalds if (rt) 1959d8d1f30bSChangli Gao dst_hold(&rt->dst); 19605744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 19611da177e4SLinus Torvalds return rt; 19621da177e4SLinus Torvalds } 19631da177e4SLinus Torvalds 1964b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 1965ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 1966ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 19671da177e4SLinus Torvalds { 196886872cb5SThomas Graf struct fib6_config cfg = { 196986872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 1970238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 197186872cb5SThomas Graf .fc_ifindex = dev->ifindex, 197286872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 197386872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 197415e47304SEric W. Biederman .fc_nlinfo.portid = 0, 19755578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 1976c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 197786872cb5SThomas Graf }; 19781da177e4SLinus Torvalds 19794e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 19801da177e4SLinus Torvalds 198186872cb5SThomas Graf ip6_route_add(&cfg); 19821da177e4SLinus Torvalds 19831da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 19841da177e4SLinus Torvalds } 19851da177e4SLinus Torvalds 19867b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 19871da177e4SLinus Torvalds { 19881da177e4SLinus Torvalds struct rt6_info *rt; 1989c71099acSThomas Graf struct fib6_table *table; 1990c71099acSThomas Graf 1991c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 19927b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 199338308473SDavid S. Miller if (!table) 1994c71099acSThomas Graf return; 19951da177e4SLinus Torvalds 19961da177e4SLinus Torvalds restart: 1997c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1998d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 19991da177e4SLinus Torvalds if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { 2000d8d1f30bSChangli Gao dst_hold(&rt->dst); 2001c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2002e0a1ad73SThomas Graf ip6_del_rt(rt); 20031da177e4SLinus Torvalds goto restart; 20041da177e4SLinus Torvalds } 20051da177e4SLinus Torvalds } 2006c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 20071da177e4SLinus Torvalds } 20081da177e4SLinus Torvalds 20095578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 20105578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 201186872cb5SThomas Graf struct fib6_config *cfg) 201286872cb5SThomas Graf { 201386872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 201486872cb5SThomas Graf 201586872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 201686872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 201786872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 201886872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 201986872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 202086872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 202186872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 202286872cb5SThomas Graf 20235578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 2024f1243c2dSBenjamin Thery 20254e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 20264e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 20274e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 202886872cb5SThomas Graf } 202986872cb5SThomas Graf 20305578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 20311da177e4SLinus Torvalds { 203286872cb5SThomas Graf struct fib6_config cfg; 20331da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 20341da177e4SLinus Torvalds int err; 20351da177e4SLinus Torvalds 20361da177e4SLinus Torvalds switch(cmd) { 20371da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 20381da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 20391da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 20401da177e4SLinus Torvalds return -EPERM; 20411da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 20421da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 20431da177e4SLinus Torvalds if (err) 20441da177e4SLinus Torvalds return -EFAULT; 20451da177e4SLinus Torvalds 20465578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 204786872cb5SThomas Graf 20481da177e4SLinus Torvalds rtnl_lock(); 20491da177e4SLinus Torvalds switch (cmd) { 20501da177e4SLinus Torvalds case SIOCADDRT: 205186872cb5SThomas Graf err = ip6_route_add(&cfg); 20521da177e4SLinus Torvalds break; 20531da177e4SLinus Torvalds case SIOCDELRT: 205486872cb5SThomas Graf err = ip6_route_del(&cfg); 20551da177e4SLinus Torvalds break; 20561da177e4SLinus Torvalds default: 20571da177e4SLinus Torvalds err = -EINVAL; 20581da177e4SLinus Torvalds } 20591da177e4SLinus Torvalds rtnl_unlock(); 20601da177e4SLinus Torvalds 20611da177e4SLinus Torvalds return err; 20623ff50b79SStephen Hemminger } 20631da177e4SLinus Torvalds 20641da177e4SLinus Torvalds return -EINVAL; 20651da177e4SLinus Torvalds } 20661da177e4SLinus Torvalds 20671da177e4SLinus Torvalds /* 20681da177e4SLinus Torvalds * Drop the packet on the floor 20691da177e4SLinus Torvalds */ 20701da177e4SLinus Torvalds 2071d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 20721da177e4SLinus Torvalds { 2073612f09e8SYOSHIFUJI Hideaki int type; 2074adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2075612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2076612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 20770660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 207845bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 20793bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 20803bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2081612f09e8SYOSHIFUJI Hideaki break; 2082612f09e8SYOSHIFUJI Hideaki } 2083612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2084612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 20853bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 20863bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2087612f09e8SYOSHIFUJI Hideaki break; 2088612f09e8SYOSHIFUJI Hideaki } 20893ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 20901da177e4SLinus Torvalds kfree_skb(skb); 20911da177e4SLinus Torvalds return 0; 20921da177e4SLinus Torvalds } 20931da177e4SLinus Torvalds 20949ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 20959ce8ade0SThomas Graf { 2096612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 20979ce8ade0SThomas Graf } 20989ce8ade0SThomas Graf 209920380731SArnaldo Carvalho de Melo static int ip6_pkt_discard_out(struct sk_buff *skb) 21001da177e4SLinus Torvalds { 2101adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2102612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 21031da177e4SLinus Torvalds } 21041da177e4SLinus Torvalds 21056723ab54SDavid S. Miller #ifdef CONFIG_IPV6_MULTIPLE_TABLES 21066723ab54SDavid S. Miller 21079ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 21089ce8ade0SThomas Graf { 2109612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 21109ce8ade0SThomas Graf } 21119ce8ade0SThomas Graf 21129ce8ade0SThomas Graf static int ip6_pkt_prohibit_out(struct sk_buff *skb) 21139ce8ade0SThomas Graf { 2114adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2115612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 21169ce8ade0SThomas Graf } 21179ce8ade0SThomas Graf 21186723ab54SDavid S. Miller #endif 21196723ab54SDavid S. Miller 21201da177e4SLinus Torvalds /* 21211da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 21221da177e4SLinus Torvalds */ 21231da177e4SLinus Torvalds 21241da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 21251da177e4SLinus Torvalds const struct in6_addr *addr, 21268f031519SDavid S. Miller bool anycast) 21271da177e4SLinus Torvalds { 2128c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 21298b96d22dSDavid S. Miller struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL); 2130f83c7790SDavid S. Miller int err; 21311da177e4SLinus Torvalds 213238308473SDavid S. Miller if (!rt) { 2133f3213831SJoe Perches net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n"); 21341da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 213540385653SBen Greear } 21361da177e4SLinus Torvalds 21371da177e4SLinus Torvalds in6_dev_hold(idev); 21381da177e4SLinus Torvalds 213911d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2140d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2141d8d1f30bSChangli Gao rt->dst.output = ip6_output; 21421da177e4SLinus Torvalds rt->rt6i_idev = idev; 21431da177e4SLinus Torvalds 21441da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 214558c4fb86SYOSHIFUJI Hideaki if (anycast) 214658c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 214758c4fb86SYOSHIFUJI Hideaki else 21481da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 21498ade06c6SDavid S. Miller err = rt6_bind_neighbour(rt, rt->dst.dev); 2150f83c7790SDavid S. Miller if (err) { 2151d8d1f30bSChangli Gao dst_free(&rt->dst); 2152f83c7790SDavid S. Miller return ERR_PTR(err); 21531da177e4SLinus Torvalds } 21541da177e4SLinus Torvalds 21554e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 21561da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 21575578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 21581da177e4SLinus Torvalds 2159d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 21601da177e4SLinus Torvalds 21611da177e4SLinus Torvalds return rt; 21621da177e4SLinus Torvalds } 21631da177e4SLinus Torvalds 2164c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2165c3968a85SDaniel Walter struct rt6_info *rt, 2166b71d1d42SEric Dumazet const struct in6_addr *daddr, 2167c3968a85SDaniel Walter unsigned int prefs, 2168c3968a85SDaniel Walter struct in6_addr *saddr) 2169c3968a85SDaniel Walter { 2170c3968a85SDaniel Walter struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt); 2171c3968a85SDaniel Walter int err = 0; 2172c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) 21734e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2174c3968a85SDaniel Walter else 2175c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2176c3968a85SDaniel Walter daddr, prefs, saddr); 2177c3968a85SDaniel Walter return err; 2178c3968a85SDaniel Walter } 2179c3968a85SDaniel Walter 2180c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2181c3968a85SDaniel Walter struct arg_dev_net_ip { 2182c3968a85SDaniel Walter struct net_device *dev; 2183c3968a85SDaniel Walter struct net *net; 2184c3968a85SDaniel Walter struct in6_addr *addr; 2185c3968a85SDaniel Walter }; 2186c3968a85SDaniel Walter 2187c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2188c3968a85SDaniel Walter { 2189c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2190c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2191c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2192c3968a85SDaniel Walter 2193d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2194c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2195c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2196c3968a85SDaniel Walter /* remove prefsrc entry */ 2197c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2198c3968a85SDaniel Walter } 2199c3968a85SDaniel Walter return 0; 2200c3968a85SDaniel Walter } 2201c3968a85SDaniel Walter 2202c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2203c3968a85SDaniel Walter { 2204c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2205c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2206c3968a85SDaniel Walter .dev = ifp->idev->dev, 2207c3968a85SDaniel Walter .net = net, 2208c3968a85SDaniel Walter .addr = &ifp->addr, 2209c3968a85SDaniel Walter }; 2210c3968a85SDaniel Walter fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni); 2211c3968a85SDaniel Walter } 2212c3968a85SDaniel Walter 22138ed67789SDaniel Lezcano struct arg_dev_net { 22148ed67789SDaniel Lezcano struct net_device *dev; 22158ed67789SDaniel Lezcano struct net *net; 22168ed67789SDaniel Lezcano }; 22178ed67789SDaniel Lezcano 22181da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 22191da177e4SLinus Torvalds { 2220bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2221bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 22228ed67789SDaniel Lezcano 2223d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2224c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 22251da177e4SLinus Torvalds return -1; 2226c159d30cSDavid S. Miller 22271da177e4SLinus Torvalds return 0; 22281da177e4SLinus Torvalds } 22291da177e4SLinus Torvalds 2230f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 22311da177e4SLinus Torvalds { 22328ed67789SDaniel Lezcano struct arg_dev_net adn = { 22338ed67789SDaniel Lezcano .dev = dev, 22348ed67789SDaniel Lezcano .net = net, 22358ed67789SDaniel Lezcano }; 22368ed67789SDaniel Lezcano 22378ed67789SDaniel Lezcano fib6_clean_all(net, fib6_ifdown, 0, &adn); 22381e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 22391da177e4SLinus Torvalds } 22401da177e4SLinus Torvalds 224195c96174SEric Dumazet struct rt6_mtu_change_arg { 22421da177e4SLinus Torvalds struct net_device *dev; 224395c96174SEric Dumazet unsigned int mtu; 22441da177e4SLinus Torvalds }; 22451da177e4SLinus Torvalds 22461da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 22471da177e4SLinus Torvalds { 22481da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 22491da177e4SLinus Torvalds struct inet6_dev *idev; 22501da177e4SLinus Torvalds 22511da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 22521da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 22531da177e4SLinus Torvalds We still use this lock to block changes 22541da177e4SLinus Torvalds caused by addrconf/ndisc. 22551da177e4SLinus Torvalds */ 22561da177e4SLinus Torvalds 22571da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 225838308473SDavid S. Miller if (!idev) 22591da177e4SLinus Torvalds return 0; 22601da177e4SLinus Torvalds 22611da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 22621da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 22631da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 22641da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 22651da177e4SLinus Torvalds */ 22661da177e4SLinus Torvalds /* 22671da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 22681da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 22691da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 22701da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 22711da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 22721da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 22731da177e4SLinus Torvalds PMTU discouvery. 22741da177e4SLinus Torvalds */ 2275d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 2276d8d1f30bSChangli Gao !dst_metric_locked(&rt->dst, RTAX_MTU) && 2277d8d1f30bSChangli Gao (dst_mtu(&rt->dst) >= arg->mtu || 2278d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 2279d8d1f30bSChangli Gao dst_mtu(&rt->dst) == idev->cnf.mtu6))) { 2280defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2281566cfd8fSSimon Arlott } 22821da177e4SLinus Torvalds return 0; 22831da177e4SLinus Torvalds } 22841da177e4SLinus Torvalds 228595c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 22861da177e4SLinus Torvalds { 2287c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2288c71099acSThomas Graf .dev = dev, 2289c71099acSThomas Graf .mtu = mtu, 2290c71099acSThomas Graf }; 22911da177e4SLinus Torvalds 2292c346dca1SYOSHIFUJI Hideaki fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg); 22931da177e4SLinus Torvalds } 22941da177e4SLinus Torvalds 2295ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 22965176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 229786872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2298ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 229986872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 230086872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 230151ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 230286872cb5SThomas Graf }; 230386872cb5SThomas Graf 230486872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 230586872cb5SThomas Graf struct fib6_config *cfg) 23061da177e4SLinus Torvalds { 230786872cb5SThomas Graf struct rtmsg *rtm; 230886872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 230986872cb5SThomas Graf int err; 23101da177e4SLinus Torvalds 231186872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 231286872cb5SThomas Graf if (err < 0) 231386872cb5SThomas Graf goto errout; 23141da177e4SLinus Torvalds 231586872cb5SThomas Graf err = -EINVAL; 231686872cb5SThomas Graf rtm = nlmsg_data(nlh); 231786872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 231886872cb5SThomas Graf 231986872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 232086872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 232186872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 232286872cb5SThomas Graf cfg->fc_flags = RTF_UP; 232386872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2324ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 232586872cb5SThomas Graf 2326ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2327ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2328b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2329b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 233086872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 233186872cb5SThomas Graf 2332ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2333ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2334ab79ad14SMaciej Żenczykowski 233515e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 233686872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 23373b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 233886872cb5SThomas Graf 233986872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 234086872cb5SThomas Graf nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16); 234186872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 23421da177e4SLinus Torvalds } 234386872cb5SThomas Graf 234486872cb5SThomas Graf if (tb[RTA_DST]) { 234586872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 234686872cb5SThomas Graf 234786872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 234886872cb5SThomas Graf goto errout; 234986872cb5SThomas Graf 235086872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 23511da177e4SLinus Torvalds } 235286872cb5SThomas Graf 235386872cb5SThomas Graf if (tb[RTA_SRC]) { 235486872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 235586872cb5SThomas Graf 235686872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 235786872cb5SThomas Graf goto errout; 235886872cb5SThomas Graf 235986872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 23601da177e4SLinus Torvalds } 236186872cb5SThomas Graf 2362c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 2363c3968a85SDaniel Walter nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16); 2364c3968a85SDaniel Walter 236586872cb5SThomas Graf if (tb[RTA_OIF]) 236686872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 236786872cb5SThomas Graf 236886872cb5SThomas Graf if (tb[RTA_PRIORITY]) 236986872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 237086872cb5SThomas Graf 237186872cb5SThomas Graf if (tb[RTA_METRICS]) { 237286872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 237386872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 23741da177e4SLinus Torvalds } 237586872cb5SThomas Graf 237686872cb5SThomas Graf if (tb[RTA_TABLE]) 237786872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 237886872cb5SThomas Graf 237951ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 238051ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 238151ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 238251ebd318SNicolas Dichtel } 238351ebd318SNicolas Dichtel 238486872cb5SThomas Graf err = 0; 238586872cb5SThomas Graf errout: 238686872cb5SThomas Graf return err; 23871da177e4SLinus Torvalds } 23881da177e4SLinus Torvalds 238951ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add) 239051ebd318SNicolas Dichtel { 239151ebd318SNicolas Dichtel struct fib6_config r_cfg; 239251ebd318SNicolas Dichtel struct rtnexthop *rtnh; 239351ebd318SNicolas Dichtel int remaining; 239451ebd318SNicolas Dichtel int attrlen; 239551ebd318SNicolas Dichtel int err = 0, last_err = 0; 239651ebd318SNicolas Dichtel 239751ebd318SNicolas Dichtel beginning: 239851ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 239951ebd318SNicolas Dichtel remaining = cfg->fc_mp_len; 240051ebd318SNicolas Dichtel 240151ebd318SNicolas Dichtel /* Parse a Multipath Entry */ 240251ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 240351ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 240451ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 240551ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 240651ebd318SNicolas Dichtel 240751ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 240851ebd318SNicolas Dichtel if (attrlen > 0) { 240951ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 241051ebd318SNicolas Dichtel 241151ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 241251ebd318SNicolas Dichtel if (nla) { 241351ebd318SNicolas Dichtel nla_memcpy(&r_cfg.fc_gateway, nla, 16); 241451ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 241551ebd318SNicolas Dichtel } 241651ebd318SNicolas Dichtel } 241751ebd318SNicolas Dichtel err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg); 241851ebd318SNicolas Dichtel if (err) { 241951ebd318SNicolas Dichtel last_err = err; 242051ebd318SNicolas Dichtel /* If we are trying to remove a route, do not stop the 242151ebd318SNicolas Dichtel * loop when ip6_route_del() fails (because next hop is 242251ebd318SNicolas Dichtel * already gone), we should try to remove all next hops. 242351ebd318SNicolas Dichtel */ 242451ebd318SNicolas Dichtel if (add) { 242551ebd318SNicolas Dichtel /* If add fails, we should try to delete all 242651ebd318SNicolas Dichtel * next hops that have been already added. 242751ebd318SNicolas Dichtel */ 242851ebd318SNicolas Dichtel add = 0; 242951ebd318SNicolas Dichtel goto beginning; 243051ebd318SNicolas Dichtel } 243151ebd318SNicolas Dichtel } 24321a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 24331a72418bSNicolas Dichtel * this flag after the first nexthop (if there is a collision, 24341a72418bSNicolas Dichtel * we have already fail to add the first nexthop: 24351a72418bSNicolas Dichtel * fib6_add_rt2node() has reject it). 24361a72418bSNicolas Dichtel */ 24371a72418bSNicolas Dichtel cfg->fc_nlinfo.nlh->nlmsg_flags &= ~NLM_F_EXCL; 243851ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 243951ebd318SNicolas Dichtel } 244051ebd318SNicolas Dichtel 244151ebd318SNicolas Dichtel return last_err; 244251ebd318SNicolas Dichtel } 244351ebd318SNicolas Dichtel 2444c127ea2cSThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 24451da177e4SLinus Torvalds { 244686872cb5SThomas Graf struct fib6_config cfg; 244786872cb5SThomas Graf int err; 24481da177e4SLinus Torvalds 244986872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 245086872cb5SThomas Graf if (err < 0) 245186872cb5SThomas Graf return err; 245286872cb5SThomas Graf 245351ebd318SNicolas Dichtel if (cfg.fc_mp) 245451ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 0); 245551ebd318SNicolas Dichtel else 245686872cb5SThomas Graf return ip6_route_del(&cfg); 24571da177e4SLinus Torvalds } 24581da177e4SLinus Torvalds 2459c127ea2cSThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 24601da177e4SLinus Torvalds { 246186872cb5SThomas Graf struct fib6_config cfg; 246286872cb5SThomas Graf int err; 24631da177e4SLinus Torvalds 246486872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 246586872cb5SThomas Graf if (err < 0) 246686872cb5SThomas Graf return err; 246786872cb5SThomas Graf 246851ebd318SNicolas Dichtel if (cfg.fc_mp) 246951ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 1); 247051ebd318SNicolas Dichtel else 247186872cb5SThomas Graf return ip6_route_add(&cfg); 24721da177e4SLinus Torvalds } 24731da177e4SLinus Torvalds 2474339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void) 2475339bf98fSThomas Graf { 2476339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 2477339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 2478339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 2479339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 2480339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 2481339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 2482339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 2483339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 2484339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 24856a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 2486339bf98fSThomas Graf + nla_total_size(sizeof(struct rta_cacheinfo)); 2487339bf98fSThomas Graf } 2488339bf98fSThomas Graf 2489191cd582SBrian Haley static int rt6_fill_node(struct net *net, 2490191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 24910d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 249215e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 24937bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 24941da177e4SLinus Torvalds { 24951da177e4SLinus Torvalds struct rtmsg *rtm; 24961da177e4SLinus Torvalds struct nlmsghdr *nlh; 2497e3703b3dSThomas Graf long expires; 24989e762a4aSPatrick McHardy u32 table; 2499f2c31e32SEric Dumazet struct neighbour *n; 25001da177e4SLinus Torvalds 25011da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 25021da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 25031da177e4SLinus Torvalds /* success since this is not a prefix route */ 25041da177e4SLinus Torvalds return 1; 25051da177e4SLinus Torvalds } 25061da177e4SLinus Torvalds } 25071da177e4SLinus Torvalds 250815e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 250938308473SDavid S. Miller if (!nlh) 251026932566SPatrick McHardy return -EMSGSIZE; 25112d7202bfSThomas Graf 25122d7202bfSThomas Graf rtm = nlmsg_data(nlh); 25131da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 25141da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 25151da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 25161da177e4SLinus Torvalds rtm->rtm_tos = 0; 2517c71099acSThomas Graf if (rt->rt6i_table) 25189e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 2519c71099acSThomas Graf else 25209e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 25219e762a4aSPatrick McHardy rtm->rtm_table = table; 2522c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 2523c78679e8SDavid S. Miller goto nla_put_failure; 2524ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 2525ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 2526ef2c7d7bSNicolas Dichtel case -EINVAL: 2527ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 2528ef2c7d7bSNicolas Dichtel break; 2529ef2c7d7bSNicolas Dichtel case -EACCES: 2530ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 2531ef2c7d7bSNicolas Dichtel break; 2532b4949ab2SNicolas Dichtel case -EAGAIN: 2533b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 2534b4949ab2SNicolas Dichtel break; 2535ef2c7d7bSNicolas Dichtel default: 25361da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 2537ef2c7d7bSNicolas Dichtel break; 2538ef2c7d7bSNicolas Dichtel } 2539ef2c7d7bSNicolas Dichtel } 2540ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 2541ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 2542d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 25431da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 25441da177e4SLinus Torvalds else 25451da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 25461da177e4SLinus Torvalds rtm->rtm_flags = 0; 25471da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 25481da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 25491da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 25501da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 2551f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 2552f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 25531da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 2554f0396f60SDenis Ovsienko else 2555f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 2556f0396f60SDenis Ovsienko } 25571da177e4SLinus Torvalds 25581da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 25591da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 25601da177e4SLinus Torvalds 25611da177e4SLinus Torvalds if (dst) { 2562c78679e8SDavid S. Miller if (nla_put(skb, RTA_DST, 16, dst)) 2563c78679e8SDavid S. Miller goto nla_put_failure; 25641da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 25651da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 2566c78679e8SDavid S. Miller if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr)) 2567c78679e8SDavid S. Miller goto nla_put_failure; 25681da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 25691da177e4SLinus Torvalds if (src) { 2570c78679e8SDavid S. Miller if (nla_put(skb, RTA_SRC, 16, src)) 2571c78679e8SDavid S. Miller goto nla_put_failure; 25721da177e4SLinus Torvalds rtm->rtm_src_len = 128; 2573c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 2574c78679e8SDavid S. Miller nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr)) 2575c78679e8SDavid S. Miller goto nla_put_failure; 25761da177e4SLinus Torvalds #endif 25777bc570c8SYOSHIFUJI Hideaki if (iif) { 25787bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 25797bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 25808229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 25817bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 25827bc570c8SYOSHIFUJI Hideaki if (!nowait) { 25837bc570c8SYOSHIFUJI Hideaki if (err == 0) 25847bc570c8SYOSHIFUJI Hideaki return 0; 25857bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 25867bc570c8SYOSHIFUJI Hideaki } else { 25877bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 25887bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 25897bc570c8SYOSHIFUJI Hideaki } 25907bc570c8SYOSHIFUJI Hideaki } 25917bc570c8SYOSHIFUJI Hideaki } else 25927bc570c8SYOSHIFUJI Hideaki #endif 2593c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 2594c78679e8SDavid S. Miller goto nla_put_failure; 25957bc570c8SYOSHIFUJI Hideaki } else if (dst) { 25961da177e4SLinus Torvalds struct in6_addr saddr_buf; 2597c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 2598c78679e8SDavid S. Miller nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) 2599c78679e8SDavid S. Miller goto nla_put_failure; 2600c3968a85SDaniel Walter } 2601c3968a85SDaniel Walter 2602c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 2603c3968a85SDaniel Walter struct in6_addr saddr_buf; 26044e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 2605c78679e8SDavid S. Miller if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) 2606c78679e8SDavid S. Miller goto nla_put_failure; 26071da177e4SLinus Torvalds } 26082d7202bfSThomas Graf 2609defb3519SDavid S. Miller if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) 26102d7202bfSThomas Graf goto nla_put_failure; 26112d7202bfSThomas Graf 261297cac082SDavid S. Miller n = rt->n; 261394f826b8SEric Dumazet if (n) { 2614fdd6681dSAmerigo Wang if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) 261594f826b8SEric Dumazet goto nla_put_failure; 261694f826b8SEric Dumazet } 26172d7202bfSThomas Graf 2618c78679e8SDavid S. Miller if (rt->dst.dev && 2619c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 2620c78679e8SDavid S. Miller goto nla_put_failure; 2621c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 2622c78679e8SDavid S. Miller goto nla_put_failure; 26238253947eSLi Wei 26248253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 262569cdf8f9SYOSHIFUJI Hideaki 262687a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 2627e3703b3dSThomas Graf goto nla_put_failure; 26281da177e4SLinus Torvalds 26292d7202bfSThomas Graf return nlmsg_end(skb, nlh); 26302d7202bfSThomas Graf 26312d7202bfSThomas Graf nla_put_failure: 263226932566SPatrick McHardy nlmsg_cancel(skb, nlh); 263326932566SPatrick McHardy return -EMSGSIZE; 26341da177e4SLinus Torvalds } 26351da177e4SLinus Torvalds 26361b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 26371da177e4SLinus Torvalds { 26381da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 26391da177e4SLinus Torvalds int prefix; 26401da177e4SLinus Torvalds 26412d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 26422d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 26431da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 26441da177e4SLinus Torvalds } else 26451da177e4SLinus Torvalds prefix = 0; 26461da177e4SLinus Torvalds 2647191cd582SBrian Haley return rt6_fill_node(arg->net, 2648191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 264915e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 26507bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 26511da177e4SLinus Torvalds } 26521da177e4SLinus Torvalds 2653c127ea2cSThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) 26541da177e4SLinus Torvalds { 26553b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 2656ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 26571da177e4SLinus Torvalds struct rt6_info *rt; 2658ab364a6fSThomas Graf struct sk_buff *skb; 2659ab364a6fSThomas Graf struct rtmsg *rtm; 26604c9483b2SDavid S. Miller struct flowi6 fl6; 266172331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 2662ab364a6fSThomas Graf 2663ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 2664ab364a6fSThomas Graf if (err < 0) 2665ab364a6fSThomas Graf goto errout; 2666ab364a6fSThomas Graf 2667ab364a6fSThomas Graf err = -EINVAL; 26684c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2669ab364a6fSThomas Graf 2670ab364a6fSThomas Graf if (tb[RTA_SRC]) { 2671ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 2672ab364a6fSThomas Graf goto errout; 2673ab364a6fSThomas Graf 26744e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 2675ab364a6fSThomas Graf } 2676ab364a6fSThomas Graf 2677ab364a6fSThomas Graf if (tb[RTA_DST]) { 2678ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 2679ab364a6fSThomas Graf goto errout; 2680ab364a6fSThomas Graf 26814e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 2682ab364a6fSThomas Graf } 2683ab364a6fSThomas Graf 2684ab364a6fSThomas Graf if (tb[RTA_IIF]) 2685ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 2686ab364a6fSThomas Graf 2687ab364a6fSThomas Graf if (tb[RTA_OIF]) 268872331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 2689ab364a6fSThomas Graf 2690ab364a6fSThomas Graf if (iif) { 2691ab364a6fSThomas Graf struct net_device *dev; 269272331bc0SShmulik Ladkani int flags = 0; 269372331bc0SShmulik Ladkani 26945578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 2695ab364a6fSThomas Graf if (!dev) { 2696ab364a6fSThomas Graf err = -ENODEV; 2697ab364a6fSThomas Graf goto errout; 2698ab364a6fSThomas Graf } 269972331bc0SShmulik Ladkani 270072331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 270172331bc0SShmulik Ladkani 270272331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 270372331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 270472331bc0SShmulik Ladkani 270572331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 270672331bc0SShmulik Ladkani flags); 270772331bc0SShmulik Ladkani } else { 270872331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 270972331bc0SShmulik Ladkani 271072331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 2711ab364a6fSThomas Graf } 27121da177e4SLinus Torvalds 27131da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 271438308473SDavid S. Miller if (!skb) { 271594e187c0SAmerigo Wang ip6_rt_put(rt); 2716ab364a6fSThomas Graf err = -ENOBUFS; 2717ab364a6fSThomas Graf goto errout; 2718ab364a6fSThomas Graf } 27191da177e4SLinus Torvalds 27201da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 27211da177e4SLinus Torvalds through good chunk of routing engine. 27221da177e4SLinus Torvalds */ 2723459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 27241da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 27251da177e4SLinus Torvalds 2726d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 27271da177e4SLinus Torvalds 27284c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 272915e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 27307bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 27311da177e4SLinus Torvalds if (err < 0) { 2732ab364a6fSThomas Graf kfree_skb(skb); 2733ab364a6fSThomas Graf goto errout; 27341da177e4SLinus Torvalds } 27351da177e4SLinus Torvalds 273615e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 2737ab364a6fSThomas Graf errout: 27381da177e4SLinus Torvalds return err; 27391da177e4SLinus Torvalds } 27401da177e4SLinus Torvalds 274186872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) 27421da177e4SLinus Torvalds { 27431da177e4SLinus Torvalds struct sk_buff *skb; 27445578689aSDaniel Lezcano struct net *net = info->nl_net; 2745528c4cebSDenis V. Lunev u32 seq; 2746528c4cebSDenis V. Lunev int err; 27470d51aa80SJamal Hadi Salim 2748528c4cebSDenis V. Lunev err = -ENOBUFS; 274938308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 275086872cb5SThomas Graf 2751339bf98fSThomas Graf skb = nlmsg_new(rt6_nlmsg_size(), gfp_any()); 275238308473SDavid S. Miller if (!skb) 275321713ebcSThomas Graf goto errout; 27541da177e4SLinus Torvalds 2755191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 275615e47304SEric W. Biederman event, info->portid, seq, 0, 0, 0); 275726932566SPatrick McHardy if (err < 0) { 275826932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 275926932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 276026932566SPatrick McHardy kfree_skb(skb); 276126932566SPatrick McHardy goto errout; 276226932566SPatrick McHardy } 276315e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 27645578689aSDaniel Lezcano info->nlh, gfp_any()); 27651ce85fe4SPablo Neira Ayuso return; 276621713ebcSThomas Graf errout: 276721713ebcSThomas Graf if (err < 0) 27685578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 27691da177e4SLinus Torvalds } 27701da177e4SLinus Torvalds 27718ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 27728ed67789SDaniel Lezcano unsigned long event, void *data) 27738ed67789SDaniel Lezcano { 27748ed67789SDaniel Lezcano struct net_device *dev = (struct net_device *)data; 2775c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 27768ed67789SDaniel Lezcano 27778ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 2778d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 27798ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 27808ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 2781d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 27828ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 2783d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 27848ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 27858ed67789SDaniel Lezcano #endif 27868ed67789SDaniel Lezcano } 27878ed67789SDaniel Lezcano 27888ed67789SDaniel Lezcano return NOTIFY_OK; 27898ed67789SDaniel Lezcano } 27908ed67789SDaniel Lezcano 27911da177e4SLinus Torvalds /* 27921da177e4SLinus Torvalds * /proc 27931da177e4SLinus Torvalds */ 27941da177e4SLinus Torvalds 27951da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 27961da177e4SLinus Torvalds 27971da177e4SLinus Torvalds struct rt6_proc_arg 27981da177e4SLinus Torvalds { 27991da177e4SLinus Torvalds char *buffer; 28001da177e4SLinus Torvalds int offset; 28011da177e4SLinus Torvalds int length; 28021da177e4SLinus Torvalds int skip; 28031da177e4SLinus Torvalds int len; 28041da177e4SLinus Torvalds }; 28051da177e4SLinus Torvalds 28061da177e4SLinus Torvalds static int rt6_info_route(struct rt6_info *rt, void *p_arg) 28071da177e4SLinus Torvalds { 280833120b30SAlexey Dobriyan struct seq_file *m = p_arg; 280969cce1d1SDavid S. Miller struct neighbour *n; 28101da177e4SLinus Torvalds 28114b7a4274SHarvey Harrison seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen); 28121da177e4SLinus Torvalds 28131da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 28144b7a4274SHarvey Harrison seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen); 28151da177e4SLinus Torvalds #else 281633120b30SAlexey Dobriyan seq_puts(m, "00000000000000000000000000000000 00 "); 28171da177e4SLinus Torvalds #endif 281897cac082SDavid S. Miller n = rt->n; 281969cce1d1SDavid S. Miller if (n) { 282069cce1d1SDavid S. Miller seq_printf(m, "%pi6", n->primary_key); 28211da177e4SLinus Torvalds } else { 282233120b30SAlexey Dobriyan seq_puts(m, "00000000000000000000000000000000"); 28231da177e4SLinus Torvalds } 282433120b30SAlexey Dobriyan seq_printf(m, " %08x %08x %08x %08x %8s\n", 2825d8d1f30bSChangli Gao rt->rt6i_metric, atomic_read(&rt->dst.__refcnt), 2826d8d1f30bSChangli Gao rt->dst.__use, rt->rt6i_flags, 2827d1918542SDavid S. Miller rt->dst.dev ? rt->dst.dev->name : ""); 28281da177e4SLinus Torvalds return 0; 28291da177e4SLinus Torvalds } 28301da177e4SLinus Torvalds 283133120b30SAlexey Dobriyan static int ipv6_route_show(struct seq_file *m, void *v) 28321da177e4SLinus Torvalds { 2833f3db4851SDaniel Lezcano struct net *net = (struct net *)m->private; 283432b293a5SJosh Hunt fib6_clean_all_ro(net, rt6_info_route, 0, m); 283533120b30SAlexey Dobriyan return 0; 28361da177e4SLinus Torvalds } 28371da177e4SLinus Torvalds 283833120b30SAlexey Dobriyan static int ipv6_route_open(struct inode *inode, struct file *file) 283933120b30SAlexey Dobriyan { 2840de05c557SPavel Emelyanov return single_open_net(inode, file, ipv6_route_show); 2841f3db4851SDaniel Lezcano } 2842f3db4851SDaniel Lezcano 284333120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 284433120b30SAlexey Dobriyan .owner = THIS_MODULE, 284533120b30SAlexey Dobriyan .open = ipv6_route_open, 284633120b30SAlexey Dobriyan .read = seq_read, 284733120b30SAlexey Dobriyan .llseek = seq_lseek, 2848b6fcbdb4SPavel Emelyanov .release = single_release_net, 284933120b30SAlexey Dobriyan }; 285033120b30SAlexey Dobriyan 28511da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 28521da177e4SLinus Torvalds { 285369ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 28541da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 285569ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 285669ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 285769ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 285869ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 285969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 2860fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 286169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 28621da177e4SLinus Torvalds 28631da177e4SLinus Torvalds return 0; 28641da177e4SLinus Torvalds } 28651da177e4SLinus Torvalds 28661da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 28671da177e4SLinus Torvalds { 2868de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 286969ddb805SDaniel Lezcano } 287069ddb805SDaniel Lezcano 28719a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 28721da177e4SLinus Torvalds .owner = THIS_MODULE, 28731da177e4SLinus Torvalds .open = rt6_stats_seq_open, 28741da177e4SLinus Torvalds .read = seq_read, 28751da177e4SLinus Torvalds .llseek = seq_lseek, 2876b6fcbdb4SPavel Emelyanov .release = single_release_net, 28771da177e4SLinus Torvalds }; 28781da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 28791da177e4SLinus Torvalds 28801da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 28811da177e4SLinus Torvalds 28821da177e4SLinus Torvalds static 28838d65af78SAlexey Dobriyan int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, 28841da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 28851da177e4SLinus Torvalds { 2886c486da34SLucian Adrian Grijincu struct net *net; 2887c486da34SLucian Adrian Grijincu int delay; 2888c486da34SLucian Adrian Grijincu if (!write) 2889c486da34SLucian Adrian Grijincu return -EINVAL; 2890c486da34SLucian Adrian Grijincu 2891c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 2892c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 28938d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 28945b7c931dSDaniel Lezcano fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net); 28951da177e4SLinus Torvalds return 0; 28961da177e4SLinus Torvalds } 28971da177e4SLinus Torvalds 2898760f2d01SDaniel Lezcano ctl_table ipv6_route_table_template[] = { 28991da177e4SLinus Torvalds { 29001da177e4SLinus Torvalds .procname = "flush", 29014990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 29021da177e4SLinus Torvalds .maxlen = sizeof(int), 290389c8b3a1SDave Jones .mode = 0200, 29046d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 29051da177e4SLinus Torvalds }, 29061da177e4SLinus Torvalds { 29071da177e4SLinus Torvalds .procname = "gc_thresh", 29089a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 29091da177e4SLinus Torvalds .maxlen = sizeof(int), 29101da177e4SLinus Torvalds .mode = 0644, 29116d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 29121da177e4SLinus Torvalds }, 29131da177e4SLinus Torvalds { 29141da177e4SLinus Torvalds .procname = "max_size", 29154990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 29161da177e4SLinus Torvalds .maxlen = sizeof(int), 29171da177e4SLinus Torvalds .mode = 0644, 29186d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 29191da177e4SLinus Torvalds }, 29201da177e4SLinus Torvalds { 29211da177e4SLinus Torvalds .procname = "gc_min_interval", 29224990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 29231da177e4SLinus Torvalds .maxlen = sizeof(int), 29241da177e4SLinus Torvalds .mode = 0644, 29256d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 29261da177e4SLinus Torvalds }, 29271da177e4SLinus Torvalds { 29281da177e4SLinus Torvalds .procname = "gc_timeout", 29294990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 29301da177e4SLinus Torvalds .maxlen = sizeof(int), 29311da177e4SLinus Torvalds .mode = 0644, 29326d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 29331da177e4SLinus Torvalds }, 29341da177e4SLinus Torvalds { 29351da177e4SLinus Torvalds .procname = "gc_interval", 29364990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 29371da177e4SLinus Torvalds .maxlen = sizeof(int), 29381da177e4SLinus Torvalds .mode = 0644, 29396d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 29401da177e4SLinus Torvalds }, 29411da177e4SLinus Torvalds { 29421da177e4SLinus Torvalds .procname = "gc_elasticity", 29434990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 29441da177e4SLinus Torvalds .maxlen = sizeof(int), 29451da177e4SLinus Torvalds .mode = 0644, 2946f3d3f616SMin Zhang .proc_handler = proc_dointvec, 29471da177e4SLinus Torvalds }, 29481da177e4SLinus Torvalds { 29491da177e4SLinus Torvalds .procname = "mtu_expires", 29504990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 29511da177e4SLinus Torvalds .maxlen = sizeof(int), 29521da177e4SLinus Torvalds .mode = 0644, 29536d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 29541da177e4SLinus Torvalds }, 29551da177e4SLinus Torvalds { 29561da177e4SLinus Torvalds .procname = "min_adv_mss", 29574990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 29581da177e4SLinus Torvalds .maxlen = sizeof(int), 29591da177e4SLinus Torvalds .mode = 0644, 2960f3d3f616SMin Zhang .proc_handler = proc_dointvec, 29611da177e4SLinus Torvalds }, 29621da177e4SLinus Torvalds { 29631da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 29644990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 29651da177e4SLinus Torvalds .maxlen = sizeof(int), 29661da177e4SLinus Torvalds .mode = 0644, 29676d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 29681da177e4SLinus Torvalds }, 2969f8572d8fSEric W. Biederman { } 29701da177e4SLinus Torvalds }; 29711da177e4SLinus Torvalds 29722c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 2973760f2d01SDaniel Lezcano { 2974760f2d01SDaniel Lezcano struct ctl_table *table; 2975760f2d01SDaniel Lezcano 2976760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 2977760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 2978760f2d01SDaniel Lezcano GFP_KERNEL); 29795ee09105SYOSHIFUJI Hideaki 29805ee09105SYOSHIFUJI Hideaki if (table) { 29815ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 2982c486da34SLucian Adrian Grijincu table[0].extra1 = net; 298386393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 29845ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 29855ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 29865ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 29875ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 29885ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 29895ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 29905ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 29919c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 2992*464dc801SEric W. Biederman 2993*464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 2994*464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 2995*464dc801SEric W. Biederman table[0].procname = NULL; 29965ee09105SYOSHIFUJI Hideaki } 29975ee09105SYOSHIFUJI Hideaki 2998760f2d01SDaniel Lezcano return table; 2999760f2d01SDaniel Lezcano } 30001da177e4SLinus Torvalds #endif 30011da177e4SLinus Torvalds 30022c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 3003cdb18761SDaniel Lezcano { 3004633d424bSPavel Emelyanov int ret = -ENOMEM; 30058ed67789SDaniel Lezcano 300686393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 300786393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 3008f2fc6a54SBenjamin Thery 3009fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 3010fc66f95cSEric Dumazet goto out_ip6_dst_ops; 3011fc66f95cSEric Dumazet 30128ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 30138ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 30148ed67789SDaniel Lezcano GFP_KERNEL); 30158ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 3016fc66f95cSEric Dumazet goto out_ip6_dst_entries; 3017d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 30188ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 3019d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 302062fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 302162fa8a84SDavid S. Miller ip6_template_metrics, true); 30228ed67789SDaniel Lezcano 30238ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 30248ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 30258ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 30268ed67789SDaniel Lezcano GFP_KERNEL); 302768fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 302868fffc67SPeter Zijlstra goto out_ip6_null_entry; 3029d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 30308ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 3031d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 303262fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 303362fa8a84SDavid S. Miller ip6_template_metrics, true); 30348ed67789SDaniel Lezcano 30358ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 30368ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 30378ed67789SDaniel Lezcano GFP_KERNEL); 303868fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 303968fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 3040d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 30418ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 3042d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 304362fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 304462fa8a84SDavid S. Miller ip6_template_metrics, true); 30458ed67789SDaniel Lezcano #endif 30468ed67789SDaniel Lezcano 3047b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 3048b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 3049b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 3050b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 3051b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 3052b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 3053b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 3054b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 3055b339a47cSPeter Zijlstra 30566891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 30576891a346SBenjamin Thery 30588ed67789SDaniel Lezcano ret = 0; 30598ed67789SDaniel Lezcano out: 30608ed67789SDaniel Lezcano return ret; 3061f2fc6a54SBenjamin Thery 306268fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 306368fffc67SPeter Zijlstra out_ip6_prohibit_entry: 306468fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 306568fffc67SPeter Zijlstra out_ip6_null_entry: 306668fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 306768fffc67SPeter Zijlstra #endif 3068fc66f95cSEric Dumazet out_ip6_dst_entries: 3069fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3070f2fc6a54SBenjamin Thery out_ip6_dst_ops: 3071f2fc6a54SBenjamin Thery goto out; 3072cdb18761SDaniel Lezcano } 3073cdb18761SDaniel Lezcano 30742c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 3075cdb18761SDaniel Lezcano { 30768ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 30778ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 30788ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 30798ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 30808ed67789SDaniel Lezcano #endif 308141bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3082cdb18761SDaniel Lezcano } 3083cdb18761SDaniel Lezcano 3084d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 3085d189634eSThomas Graf { 3086d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3087d189634eSThomas Graf proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops); 3088d189634eSThomas Graf proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops); 3089d189634eSThomas Graf #endif 3090d189634eSThomas Graf return 0; 3091d189634eSThomas Graf } 3092d189634eSThomas Graf 3093d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 3094d189634eSThomas Graf { 3095d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3096d189634eSThomas Graf proc_net_remove(net, "ipv6_route"); 3097d189634eSThomas Graf proc_net_remove(net, "rt6_stats"); 3098d189634eSThomas Graf #endif 3099d189634eSThomas Graf } 3100d189634eSThomas Graf 3101cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 3102cdb18761SDaniel Lezcano .init = ip6_route_net_init, 3103cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 3104cdb18761SDaniel Lezcano }; 3105cdb18761SDaniel Lezcano 3106c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 3107c3426b47SDavid S. Miller { 3108c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 3109c3426b47SDavid S. Miller 3110c3426b47SDavid S. Miller if (!bp) 3111c3426b47SDavid S. Miller return -ENOMEM; 3112c3426b47SDavid S. Miller inet_peer_base_init(bp); 3113c3426b47SDavid S. Miller net->ipv6.peers = bp; 3114c3426b47SDavid S. Miller return 0; 3115c3426b47SDavid S. Miller } 3116c3426b47SDavid S. Miller 3117c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 3118c3426b47SDavid S. Miller { 3119c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 3120c3426b47SDavid S. Miller 3121c3426b47SDavid S. Miller net->ipv6.peers = NULL; 312256a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3123c3426b47SDavid S. Miller kfree(bp); 3124c3426b47SDavid S. Miller } 3125c3426b47SDavid S. Miller 31262b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3127c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3128c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3129c3426b47SDavid S. Miller }; 3130c3426b47SDavid S. Miller 3131d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3132d189634eSThomas Graf .init = ip6_route_net_init_late, 3133d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3134d189634eSThomas Graf }; 3135d189634eSThomas Graf 31368ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 31378ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 31388ed67789SDaniel Lezcano .priority = 0, 31398ed67789SDaniel Lezcano }; 31408ed67789SDaniel Lezcano 3141433d49c3SDaniel Lezcano int __init ip6_route_init(void) 31421da177e4SLinus Torvalds { 3143433d49c3SDaniel Lezcano int ret; 3144433d49c3SDaniel Lezcano 31459a7ec3a9SDaniel Lezcano ret = -ENOMEM; 31469a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 31479a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 31489a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 31499a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3150c19a28e1SFernando Carrijo goto out; 315114e50e57SDavid S. Miller 3152fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 31538ed67789SDaniel Lezcano if (ret) 3154bdb3289fSDaniel Lezcano goto out_kmem_cache; 3155bdb3289fSDaniel Lezcano 3156c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3157c3426b47SDavid S. Miller if (ret) 3158e8803b6cSDavid S. Miller goto out_dst_entries; 31592a0c451aSThomas Graf 31607e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 31617e52b33bSDavid S. Miller if (ret) 31627e52b33bSDavid S. Miller goto out_register_inetpeer; 3163c3426b47SDavid S. Miller 31645dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 31655dc121e9SArnaud Ebalard 31668ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 31678ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 31688ed67789SDaniel Lezcano * manually for init_net */ 3169d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 31708ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3171bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3172d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 31738ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3174d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 31758ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3176bdb3289fSDaniel Lezcano #endif 3177e8803b6cSDavid S. Miller ret = fib6_init(); 3178433d49c3SDaniel Lezcano if (ret) 31798ed67789SDaniel Lezcano goto out_register_subsys; 3180433d49c3SDaniel Lezcano 3181433d49c3SDaniel Lezcano ret = xfrm6_init(); 3182433d49c3SDaniel Lezcano if (ret) 3183e8803b6cSDavid S. Miller goto out_fib6_init; 3184c35b7e72SDaniel Lezcano 3185433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3186433d49c3SDaniel Lezcano if (ret) 3187433d49c3SDaniel Lezcano goto xfrm6_init; 31887e5449c2SDaniel Lezcano 3189d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3190d189634eSThomas Graf if (ret) 3191d189634eSThomas Graf goto fib6_rules_init; 3192d189634eSThomas Graf 3193433d49c3SDaniel Lezcano ret = -ENOBUFS; 3194c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3195c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3196c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3197d189634eSThomas Graf goto out_register_late_subsys; 3198433d49c3SDaniel Lezcano 31998ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3200cdb18761SDaniel Lezcano if (ret) 3201d189634eSThomas Graf goto out_register_late_subsys; 32028ed67789SDaniel Lezcano 3203433d49c3SDaniel Lezcano out: 3204433d49c3SDaniel Lezcano return ret; 3205433d49c3SDaniel Lezcano 3206d189634eSThomas Graf out_register_late_subsys: 3207d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3208433d49c3SDaniel Lezcano fib6_rules_init: 3209433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3210433d49c3SDaniel Lezcano xfrm6_init: 3211433d49c3SDaniel Lezcano xfrm6_fini(); 32122a0c451aSThomas Graf out_fib6_init: 32132a0c451aSThomas Graf fib6_gc_cleanup(); 32148ed67789SDaniel Lezcano out_register_subsys: 32158ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 32167e52b33bSDavid S. Miller out_register_inetpeer: 32177e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3218fc66f95cSEric Dumazet out_dst_entries: 3219fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3220433d49c3SDaniel Lezcano out_kmem_cache: 3221f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3222433d49c3SDaniel Lezcano goto out; 32231da177e4SLinus Torvalds } 32241da177e4SLinus Torvalds 32251da177e4SLinus Torvalds void ip6_route_cleanup(void) 32261da177e4SLinus Torvalds { 32278ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3228d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3229101367c2SThomas Graf fib6_rules_cleanup(); 32301da177e4SLinus Torvalds xfrm6_fini(); 32311da177e4SLinus Torvalds fib6_gc_cleanup(); 3232c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 32338ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 323441bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3235f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 32361da177e4SLinus Torvalds } 3237