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> 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds #include <asm/uaccess.h> 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 641da177e4SLinus Torvalds #include <linux/sysctl.h> 651da177e4SLinus Torvalds #endif 661da177e4SLinus Torvalds 671716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 6821efcfa0SEric Dumazet const struct in6_addr *dest); 691da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 700dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst); 71ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst); 721da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *); 731da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *); 741da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *, 751da177e4SLinus Torvalds struct net_device *dev, int how); 76569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops); 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds static int ip6_pkt_discard(struct sk_buff *skb); 791da177e4SLinus Torvalds static int ip6_pkt_discard_out(struct sk_buff *skb); 801da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb); 816700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 826700c270SDavid S. Miller struct sk_buff *skb, u32 mtu); 836700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, 846700c270SDavid S. Miller struct sk_buff *skb); 851da177e4SLinus Torvalds 8670ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 87efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 88b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 89b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 9095c96174SEric Dumazet unsigned int pref); 91efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 92b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 93b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex); 9470ceb4f5SYOSHIFUJI Hideaki #endif 9570ceb4f5SYOSHIFUJI Hideaki 9606582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) 9706582540SDavid S. Miller { 9806582540SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 9906582540SDavid S. Miller struct inet_peer *peer; 10006582540SDavid S. Miller u32 *p = NULL; 10106582540SDavid S. Miller 1028e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST)) 1038e2ec639SYan, Zheng return NULL; 1048e2ec639SYan, Zheng 105fbfe95a4SDavid S. Miller peer = rt6_get_peer_create(rt); 10606582540SDavid S. Miller if (peer) { 10706582540SDavid S. Miller u32 *old_p = __DST_METRICS_PTR(old); 10806582540SDavid S. Miller unsigned long prev, new; 10906582540SDavid S. Miller 11006582540SDavid S. Miller p = peer->metrics; 11106582540SDavid S. Miller if (inet_metrics_new(peer)) 11206582540SDavid S. Miller memcpy(p, old_p, sizeof(u32) * RTAX_MAX); 11306582540SDavid S. Miller 11406582540SDavid S. Miller new = (unsigned long) p; 11506582540SDavid S. Miller prev = cmpxchg(&dst->_metrics, old, new); 11606582540SDavid S. Miller 11706582540SDavid S. Miller if (prev != old) { 11806582540SDavid S. Miller p = __DST_METRICS_PTR(prev); 11906582540SDavid S. Miller if (prev & DST_METRICS_READ_ONLY) 12006582540SDavid S. Miller p = NULL; 12106582540SDavid S. Miller } 12206582540SDavid S. Miller } 12306582540SDavid S. Miller return p; 12406582540SDavid S. Miller } 12506582540SDavid S. Miller 126f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, 127f894cbf8SDavid S. Miller struct sk_buff *skb, 128f894cbf8SDavid S. Miller const void *daddr) 12939232973SDavid S. Miller { 13039232973SDavid S. Miller struct in6_addr *p = &rt->rt6i_gateway; 13139232973SDavid S. Miller 132a7563f34SDavid S. Miller if (!ipv6_addr_any(p)) 13339232973SDavid S. Miller return (const void *) p; 134f894cbf8SDavid S. Miller else if (skb) 135f894cbf8SDavid S. Miller return &ipv6_hdr(skb)->daddr; 13639232973SDavid S. Miller return daddr; 13739232973SDavid S. Miller } 13839232973SDavid S. Miller 139f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, 140f894cbf8SDavid S. Miller struct sk_buff *skb, 141f894cbf8SDavid S. Miller const void *daddr) 142d3aaeb38SDavid S. Miller { 14339232973SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 14439232973SDavid S. Miller struct neighbour *n; 14539232973SDavid S. Miller 146f894cbf8SDavid S. Miller daddr = choose_neigh_daddr(rt, skb, daddr); 14739232973SDavid S. Miller n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr); 148f83c7790SDavid S. Miller if (n) 149f83c7790SDavid S. Miller return n; 150f83c7790SDavid S. Miller return neigh_create(&nd_tbl, daddr, dst->dev); 151f83c7790SDavid S. Miller } 152f83c7790SDavid S. Miller 1538ade06c6SDavid S. Miller static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev) 154f83c7790SDavid S. Miller { 1558ade06c6SDavid S. Miller struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dev, &rt->rt6i_gateway); 1568ade06c6SDavid S. Miller if (!n) { 1578ade06c6SDavid S. Miller n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev); 158f83c7790SDavid S. Miller if (IS_ERR(n)) 159f83c7790SDavid S. Miller return PTR_ERR(n); 1608ade06c6SDavid S. Miller } 16197cac082SDavid S. Miller rt->n = n; 162f83c7790SDavid S. Miller 163f83c7790SDavid S. Miller return 0; 164d3aaeb38SDavid S. Miller } 165d3aaeb38SDavid S. Miller 1669a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 1671da177e4SLinus Torvalds .family = AF_INET6, 16809640e63SHarvey Harrison .protocol = cpu_to_be16(ETH_P_IPV6), 1691da177e4SLinus Torvalds .gc = ip6_dst_gc, 1701da177e4SLinus Torvalds .gc_thresh = 1024, 1711da177e4SLinus Torvalds .check = ip6_dst_check, 1720dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 173ebb762f2SSteffen Klassert .mtu = ip6_mtu, 17406582540SDavid S. Miller .cow_metrics = ipv6_cow_metrics, 1751da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 1761da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 1771da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 1781da177e4SLinus Torvalds .link_failure = ip6_link_failure, 1791da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 1806e157b6aSDavid S. Miller .redirect = rt6_do_redirect, 1811ac06e03SHerbert Xu .local_out = __ip6_local_out, 182d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 1831da177e4SLinus Torvalds }; 1841da177e4SLinus Torvalds 185ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 186ec831ea7SRoland Dreier { 187618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 188618f9bc7SSteffen Klassert 189618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 190ec831ea7SRoland Dreier } 191ec831ea7SRoland Dreier 1926700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, 1936700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 19414e50e57SDavid S. Miller { 19514e50e57SDavid S. Miller } 19614e50e57SDavid S. Miller 1976700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, 1986700c270SDavid S. Miller struct sk_buff *skb) 199b587ee3bSDavid S. Miller { 200b587ee3bSDavid S. Miller } 201b587ee3bSDavid S. Miller 2020972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, 2030972ddb2SHeld Bernhard unsigned long old) 2040972ddb2SHeld Bernhard { 2050972ddb2SHeld Bernhard return NULL; 2060972ddb2SHeld Bernhard } 2070972ddb2SHeld Bernhard 20814e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 20914e50e57SDavid S. Miller .family = AF_INET6, 21009640e63SHarvey Harrison .protocol = cpu_to_be16(ETH_P_IPV6), 21114e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 21214e50e57SDavid S. Miller .check = ip6_dst_check, 213ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 214214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 21514e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 216b587ee3bSDavid S. Miller .redirect = ip6_rt_blackhole_redirect, 2170972ddb2SHeld Bernhard .cow_metrics = ip6_rt_blackhole_cow_metrics, 218d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 21914e50e57SDavid S. Miller }; 22014e50e57SDavid S. Miller 22162fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 22262fa8a84SDavid S. Miller [RTAX_HOPLIMIT - 1] = 255, 22362fa8a84SDavid S. Miller }; 22462fa8a84SDavid S. Miller 225fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = { 2261da177e4SLinus Torvalds .dst = { 2271da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 2281da177e4SLinus Torvalds .__use = 1, 2291da177e4SLinus Torvalds .obsolete = -1, 2301da177e4SLinus Torvalds .error = -ENETUNREACH, 2311da177e4SLinus Torvalds .input = ip6_pkt_discard, 2321da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 2331da177e4SLinus Torvalds }, 2341da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2354f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 2361da177e4SLinus Torvalds .rt6i_metric = ~(u32) 0, 2371da177e4SLinus Torvalds .rt6i_ref = ATOMIC_INIT(1), 2381da177e4SLinus Torvalds }; 2391da177e4SLinus Torvalds 240101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 241101367c2SThomas Graf 2426723ab54SDavid S. Miller static int ip6_pkt_prohibit(struct sk_buff *skb); 2436723ab54SDavid S. Miller static int ip6_pkt_prohibit_out(struct sk_buff *skb); 2446723ab54SDavid S. Miller 245fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = { 246101367c2SThomas Graf .dst = { 247101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 248101367c2SThomas Graf .__use = 1, 249101367c2SThomas Graf .obsolete = -1, 250101367c2SThomas Graf .error = -EACCES, 2519ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 2529ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 253101367c2SThomas Graf }, 254101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2554f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 256101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 257101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 258101367c2SThomas Graf }; 259101367c2SThomas Graf 260fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = { 261101367c2SThomas Graf .dst = { 262101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 263101367c2SThomas Graf .__use = 1, 264101367c2SThomas Graf .obsolete = -1, 265101367c2SThomas Graf .error = -EINVAL, 266352e512cSHerbert Xu .input = dst_discard, 267352e512cSHerbert Xu .output = dst_discard, 268101367c2SThomas Graf }, 269101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2704f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 271101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 272101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 273101367c2SThomas Graf }; 274101367c2SThomas Graf 275101367c2SThomas Graf #endif 276101367c2SThomas Graf 2771da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 27897bab73fSDavid S. Miller static inline struct rt6_info *ip6_dst_alloc(struct net *net, 279957c665fSDavid S. Miller struct net_device *dev, 2808b96d22dSDavid S. Miller int flags, 2818b96d22dSDavid S. Miller struct fib6_table *table) 2821da177e4SLinus Torvalds { 28397bab73fSDavid S. Miller struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 284f5b0a874SDavid S. Miller 0, DST_OBSOLETE_NONE, flags); 285cf911662SDavid S. Miller 28697bab73fSDavid S. Miller if (rt) { 2878104891bSSteffen Klassert struct dst_entry *dst = &rt->dst; 2888104891bSSteffen Klassert 2898104891bSSteffen Klassert memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 2908b96d22dSDavid S. Miller rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers); 29197bab73fSDavid S. Miller } 292cf911662SDavid S. Miller return rt; 2931da177e4SLinus Torvalds } 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 2961da177e4SLinus Torvalds { 2971da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 2981da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 2991da177e4SLinus Torvalds 30097cac082SDavid S. Miller if (rt->n) 30197cac082SDavid S. Miller neigh_release(rt->n); 30297cac082SDavid S. Miller 3038e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST)) 3048e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 3058e2ec639SYan, Zheng 30638308473SDavid S. Miller if (idev) { 3071da177e4SLinus Torvalds rt->rt6i_idev = NULL; 3081da177e4SLinus Torvalds in6_dev_put(idev); 3091da177e4SLinus Torvalds } 3101716a961SGao feng 3111716a961SGao feng if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from) 3121716a961SGao feng dst_release(dst->from); 3131716a961SGao feng 31497bab73fSDavid S. Miller if (rt6_has_peer(rt)) { 31597bab73fSDavid S. Miller struct inet_peer *peer = rt6_peer_ptr(rt); 316b3419363SDavid S. Miller inet_putpeer(peer); 317b3419363SDavid S. Miller } 318b3419363SDavid S. Miller } 319b3419363SDavid S. Miller 3206431cbc2SDavid S. Miller static atomic_t __rt6_peer_genid = ATOMIC_INIT(0); 3216431cbc2SDavid S. Miller 3226431cbc2SDavid S. Miller static u32 rt6_peer_genid(void) 3236431cbc2SDavid S. Miller { 3246431cbc2SDavid S. Miller return atomic_read(&__rt6_peer_genid); 3256431cbc2SDavid S. Miller } 3266431cbc2SDavid S. Miller 327b3419363SDavid S. Miller void rt6_bind_peer(struct rt6_info *rt, int create) 328b3419363SDavid S. Miller { 32997bab73fSDavid S. Miller struct inet_peer_base *base; 330b3419363SDavid S. Miller struct inet_peer *peer; 331b3419363SDavid S. Miller 33297bab73fSDavid S. Miller base = inetpeer_base_ptr(rt->_rt6i_peer); 33397bab73fSDavid S. Miller if (!base) 33497bab73fSDavid S. Miller return; 33597bab73fSDavid S. Miller 33697bab73fSDavid S. Miller peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create); 3377b34ca2aSDavid S. Miller if (peer) { 33897bab73fSDavid S. Miller if (!rt6_set_peer(rt, peer)) 339b3419363SDavid S. Miller inet_putpeer(peer); 3406431cbc2SDavid S. Miller else 3416431cbc2SDavid S. Miller rt->rt6i_peer_genid = rt6_peer_genid(); 3421da177e4SLinus Torvalds } 3437b34ca2aSDavid S. Miller } 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 3461da177e4SLinus Torvalds int how) 3471da177e4SLinus Torvalds { 3481da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3491da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3505a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 351c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 3521da177e4SLinus Torvalds 35397cac082SDavid S. Miller if (dev != loopback_dev) { 35497cac082SDavid S. Miller if (idev && idev->dev == dev) { 3555a3e55d6SDenis V. Lunev struct inet6_dev *loopback_idev = 3565a3e55d6SDenis V. Lunev in6_dev_get(loopback_dev); 35738308473SDavid S. Miller if (loopback_idev) { 3581da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 3591da177e4SLinus Torvalds in6_dev_put(idev); 3601da177e4SLinus Torvalds } 3611da177e4SLinus Torvalds } 36297cac082SDavid S. Miller if (rt->n && rt->n->dev == dev) { 36397cac082SDavid S. Miller rt->n->dev = loopback_dev; 36497cac082SDavid S. Miller dev_hold(loopback_dev); 36597cac082SDavid S. Miller dev_put(dev); 36697cac082SDavid S. Miller } 36797cac082SDavid S. Miller } 3681da177e4SLinus Torvalds } 3691da177e4SLinus Torvalds 370a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt) 3711da177e4SLinus Torvalds { 3721716a961SGao feng struct rt6_info *ort = NULL; 3731716a961SGao feng 3741716a961SGao feng if (rt->rt6i_flags & RTF_EXPIRES) { 3751716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 376a50feda5SEric Dumazet return true; 3771716a961SGao feng } else if (rt->dst.from) { 3781716a961SGao feng ort = (struct rt6_info *) rt->dst.from; 3791716a961SGao feng return (ort->rt6i_flags & RTF_EXPIRES) && 3801716a961SGao feng time_after(jiffies, ort->dst.expires); 3811716a961SGao feng } 382a50feda5SEric Dumazet return false; 3831da177e4SLinus Torvalds } 3841da177e4SLinus Torvalds 385a50feda5SEric Dumazet static bool rt6_need_strict(const struct in6_addr *daddr) 386c71099acSThomas Graf { 387a02cec21SEric Dumazet return ipv6_addr_type(daddr) & 388a02cec21SEric Dumazet (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); 389c71099acSThomas Graf } 390c71099acSThomas Graf 3911da177e4SLinus Torvalds /* 392c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 3931da177e4SLinus Torvalds */ 3941da177e4SLinus Torvalds 3958ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 3968ed67789SDaniel Lezcano struct rt6_info *rt, 397b71d1d42SEric Dumazet const struct in6_addr *saddr, 3981da177e4SLinus Torvalds int oif, 399d420895eSYOSHIFUJI Hideaki int flags) 4001da177e4SLinus Torvalds { 4011da177e4SLinus Torvalds struct rt6_info *local = NULL; 4021da177e4SLinus Torvalds struct rt6_info *sprt; 4031da177e4SLinus Torvalds 404dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 405dd3abc4eSYOSHIFUJI Hideaki goto out; 406dd3abc4eSYOSHIFUJI Hideaki 407d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 408d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 409dd3abc4eSYOSHIFUJI Hideaki 410dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4111da177e4SLinus Torvalds if (dev->ifindex == oif) 4121da177e4SLinus Torvalds return sprt; 4131da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 41438308473SDavid S. Miller if (!sprt->rt6i_idev || 4151da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 416d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE && oif) 4171da177e4SLinus Torvalds continue; 4181da177e4SLinus Torvalds if (local && (!oif || 4191da177e4SLinus Torvalds local->rt6i_idev->dev->ifindex == oif)) 4201da177e4SLinus Torvalds continue; 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds local = sprt; 4231da177e4SLinus Torvalds } 424dd3abc4eSYOSHIFUJI Hideaki } else { 425dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 426dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 427dd3abc4eSYOSHIFUJI Hideaki return sprt; 428dd3abc4eSYOSHIFUJI Hideaki } 4291da177e4SLinus Torvalds } 4301da177e4SLinus Torvalds 431dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4321da177e4SLinus Torvalds if (local) 4331da177e4SLinus Torvalds return local; 4341da177e4SLinus Torvalds 435d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 4368ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 4371da177e4SLinus Torvalds } 438dd3abc4eSYOSHIFUJI Hideaki out: 4391da177e4SLinus Torvalds return rt; 4401da177e4SLinus Torvalds } 4411da177e4SLinus Torvalds 44227097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 44327097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 44427097255SYOSHIFUJI Hideaki { 445f2c31e32SEric Dumazet struct neighbour *neigh; 44627097255SYOSHIFUJI Hideaki /* 44727097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 44827097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 44927097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 45027097255SYOSHIFUJI Hideaki * 45127097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 45227097255SYOSHIFUJI Hideaki * to no more than one per minute. 45327097255SYOSHIFUJI Hideaki */ 45497cac082SDavid S. Miller neigh = rt ? rt->n : NULL; 45527097255SYOSHIFUJI Hideaki if (!neigh || (neigh->nud_state & NUD_VALID)) 456fdd6681dSAmerigo Wang return; 45727097255SYOSHIFUJI Hideaki read_lock_bh(&neigh->lock); 45827097255SYOSHIFUJI Hideaki if (!(neigh->nud_state & NUD_VALID) && 45952e16356SYOSHIFUJI Hideaki time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { 46027097255SYOSHIFUJI Hideaki struct in6_addr mcaddr; 46127097255SYOSHIFUJI Hideaki struct in6_addr *target; 46227097255SYOSHIFUJI Hideaki 46327097255SYOSHIFUJI Hideaki neigh->updated = jiffies; 46427097255SYOSHIFUJI Hideaki read_unlock_bh(&neigh->lock); 46527097255SYOSHIFUJI Hideaki 46627097255SYOSHIFUJI Hideaki target = (struct in6_addr *)&neigh->primary_key; 46727097255SYOSHIFUJI Hideaki addrconf_addr_solict_mult(target, &mcaddr); 468d1918542SDavid S. Miller ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL); 469f2c31e32SEric Dumazet } else { 47027097255SYOSHIFUJI Hideaki read_unlock_bh(&neigh->lock); 47127097255SYOSHIFUJI Hideaki } 472f2c31e32SEric Dumazet } 47327097255SYOSHIFUJI Hideaki #else 47427097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 47527097255SYOSHIFUJI Hideaki { 47627097255SYOSHIFUJI Hideaki } 47727097255SYOSHIFUJI Hideaki #endif 47827097255SYOSHIFUJI Hideaki 4791da177e4SLinus Torvalds /* 480554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 4811da177e4SLinus Torvalds */ 482b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 4831da177e4SLinus Torvalds { 484d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 485161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 486554cfb7eSYOSHIFUJI Hideaki return 2; 487161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 488161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 489161980f4SDavid S. Miller return 1; 490554cfb7eSYOSHIFUJI Hideaki return 0; 4911da177e4SLinus Torvalds } 4921da177e4SLinus Torvalds 493b6f99a21SDave Jones static inline int rt6_check_neigh(struct rt6_info *rt) 4941da177e4SLinus Torvalds { 495f2c31e32SEric Dumazet struct neighbour *neigh; 496398bcbebSYOSHIFUJI Hideaki int m; 497f2c31e32SEric Dumazet 49897cac082SDavid S. Miller neigh = rt->n; 4994d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 5004d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 5014d0c5911SYOSHIFUJI Hideaki m = 1; 5024d0c5911SYOSHIFUJI Hideaki else if (neigh) { 5031da177e4SLinus Torvalds read_lock_bh(&neigh->lock); 504554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 5054d0c5911SYOSHIFUJI Hideaki m = 2; 506398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 507398bcbebSYOSHIFUJI Hideaki else if (neigh->nud_state & NUD_FAILED) 508398bcbebSYOSHIFUJI Hideaki m = 0; 509398bcbebSYOSHIFUJI Hideaki #endif 510398bcbebSYOSHIFUJI Hideaki else 511ea73ee23SYOSHIFUJI Hideaki m = 1; 5121da177e4SLinus Torvalds read_unlock_bh(&neigh->lock); 513398bcbebSYOSHIFUJI Hideaki } else 514398bcbebSYOSHIFUJI Hideaki m = 0; 515554cfb7eSYOSHIFUJI Hideaki return m; 5161da177e4SLinus Torvalds } 5171da177e4SLinus Torvalds 518554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 519554cfb7eSYOSHIFUJI Hideaki int strict) 520554cfb7eSYOSHIFUJI Hideaki { 5214d0c5911SYOSHIFUJI Hideaki int m, n; 5224d0c5911SYOSHIFUJI Hideaki 5234d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 52477d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 525554cfb7eSYOSHIFUJI Hideaki return -1; 526ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 527ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 528ebacaaa0SYOSHIFUJI Hideaki #endif 5294d0c5911SYOSHIFUJI Hideaki n = rt6_check_neigh(rt); 530557e92efSYOSHIFUJI Hideaki if (!n && (strict & RT6_LOOKUP_F_REACHABLE)) 531554cfb7eSYOSHIFUJI Hideaki return -1; 532554cfb7eSYOSHIFUJI Hideaki return m; 533554cfb7eSYOSHIFUJI Hideaki } 534554cfb7eSYOSHIFUJI Hideaki 535f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 536f11e6659SDavid S. Miller int *mpri, struct rt6_info *match) 537554cfb7eSYOSHIFUJI Hideaki { 538554cfb7eSYOSHIFUJI Hideaki int m; 539554cfb7eSYOSHIFUJI Hideaki 540554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 541f11e6659SDavid S. Miller goto out; 542554cfb7eSYOSHIFUJI Hideaki 543554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 544554cfb7eSYOSHIFUJI Hideaki if (m < 0) 545f11e6659SDavid S. Miller goto out; 546554cfb7eSYOSHIFUJI Hideaki 547f11e6659SDavid S. Miller if (m > *mpri) { 548ea659e07SYOSHIFUJI Hideaki if (strict & RT6_LOOKUP_F_REACHABLE) 54927097255SYOSHIFUJI Hideaki rt6_probe(match); 550f11e6659SDavid S. Miller *mpri = m; 551554cfb7eSYOSHIFUJI Hideaki match = rt; 552ea659e07SYOSHIFUJI Hideaki } else if (strict & RT6_LOOKUP_F_REACHABLE) { 55327097255SYOSHIFUJI Hideaki rt6_probe(rt); 5541da177e4SLinus Torvalds } 555f11e6659SDavid S. Miller 556f11e6659SDavid S. Miller out: 557f11e6659SDavid S. Miller return match; 5581da177e4SLinus Torvalds } 5591da177e4SLinus Torvalds 560f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 561f11e6659SDavid S. Miller struct rt6_info *rr_head, 562f11e6659SDavid S. Miller u32 metric, int oif, int strict) 563f11e6659SDavid S. Miller { 564f11e6659SDavid S. Miller struct rt6_info *rt, *match; 565f11e6659SDavid S. Miller int mpri = -1; 566f11e6659SDavid S. Miller 567f11e6659SDavid S. Miller match = NULL; 568f11e6659SDavid S. Miller for (rt = rr_head; rt && rt->rt6i_metric == metric; 569d8d1f30bSChangli Gao rt = rt->dst.rt6_next) 570f11e6659SDavid S. Miller match = find_match(rt, oif, strict, &mpri, match); 571f11e6659SDavid S. Miller for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric; 572d8d1f30bSChangli Gao rt = rt->dst.rt6_next) 573f11e6659SDavid S. Miller match = find_match(rt, oif, strict, &mpri, match); 574f11e6659SDavid S. Miller 575f11e6659SDavid S. Miller return match; 576f11e6659SDavid S. Miller } 577f11e6659SDavid S. Miller 578f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 579f11e6659SDavid S. Miller { 580f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 5818ed67789SDaniel Lezcano struct net *net; 582f11e6659SDavid S. Miller 583f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 584f11e6659SDavid S. Miller if (!rt0) 585f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 586f11e6659SDavid S. Miller 587f11e6659SDavid S. Miller match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict); 588f11e6659SDavid S. Miller 589554cfb7eSYOSHIFUJI Hideaki if (!match && 590f11e6659SDavid S. Miller (strict & RT6_LOOKUP_F_REACHABLE)) { 591d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 592f11e6659SDavid S. Miller 593554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 594f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 595f11e6659SDavid S. Miller next = fn->leaf; 596f11e6659SDavid S. Miller 597f11e6659SDavid S. Miller if (next != rt0) 598f11e6659SDavid S. Miller fn->rr_ptr = next; 599554cfb7eSYOSHIFUJI Hideaki } 600554cfb7eSYOSHIFUJI Hideaki 601d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 602a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 6031da177e4SLinus Torvalds } 6041da177e4SLinus Torvalds 60570ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 60670ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 607b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 60870ceb4f5SYOSHIFUJI Hideaki { 609c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 61070ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 61170ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 61270ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 6134bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 61470ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 61570ceb4f5SYOSHIFUJI Hideaki 61670ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 61770ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 61870ceb4f5SYOSHIFUJI Hideaki } 61970ceb4f5SYOSHIFUJI Hideaki 62070ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 62170ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 62270ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 62370ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 62470ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 62570ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 62670ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 62770ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 62870ceb4f5SYOSHIFUJI Hideaki } 62970ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 63070ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 63170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 63270ceb4f5SYOSHIFUJI Hideaki } 63370ceb4f5SYOSHIFUJI Hideaki } 63470ceb4f5SYOSHIFUJI Hideaki 63570ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 63670ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 6373933fc95SJens Rosenboom return -EINVAL; 63870ceb4f5SYOSHIFUJI Hideaki 6394bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 64070ceb4f5SYOSHIFUJI Hideaki 64170ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 64270ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 64370ceb4f5SYOSHIFUJI Hideaki else { 64470ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 64570ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 64670ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 64770ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 64870ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 64970ceb4f5SYOSHIFUJI Hideaki } 65070ceb4f5SYOSHIFUJI Hideaki 651efa2cea0SDaniel Lezcano rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr, 652efa2cea0SDaniel Lezcano dev->ifindex); 65370ceb4f5SYOSHIFUJI Hideaki 65470ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 655e0a1ad73SThomas Graf ip6_del_rt(rt); 65670ceb4f5SYOSHIFUJI Hideaki rt = NULL; 65770ceb4f5SYOSHIFUJI Hideaki } 65870ceb4f5SYOSHIFUJI Hideaki 65970ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 660efa2cea0SDaniel Lezcano rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 66170ceb4f5SYOSHIFUJI Hideaki pref); 66270ceb4f5SYOSHIFUJI Hideaki else if (rt) 66370ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 66470ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 66570ceb4f5SYOSHIFUJI Hideaki 66670ceb4f5SYOSHIFUJI Hideaki if (rt) { 6671716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 6681716a961SGao feng rt6_clean_expires(rt); 6691716a961SGao feng else 6701716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 6711716a961SGao feng 672d8d1f30bSChangli Gao dst_release(&rt->dst); 67370ceb4f5SYOSHIFUJI Hideaki } 67470ceb4f5SYOSHIFUJI Hideaki return 0; 67570ceb4f5SYOSHIFUJI Hideaki } 67670ceb4f5SYOSHIFUJI Hideaki #endif 67770ceb4f5SYOSHIFUJI Hideaki 6788ed67789SDaniel Lezcano #define BACKTRACK(__net, saddr) \ 679982f56f3SYOSHIFUJI Hideaki do { \ 6808ed67789SDaniel Lezcano if (rt == __net->ipv6.ip6_null_entry) { \ 681982f56f3SYOSHIFUJI Hideaki struct fib6_node *pn; \ 682e0eda7bbSVille Nuorvala while (1) { \ 683982f56f3SYOSHIFUJI Hideaki if (fn->fn_flags & RTN_TL_ROOT) \ 684c71099acSThomas Graf goto out; \ 685982f56f3SYOSHIFUJI Hideaki pn = fn->parent; \ 686982f56f3SYOSHIFUJI Hideaki if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \ 6878bce65b9SKim Nordlund fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \ 688982f56f3SYOSHIFUJI Hideaki else \ 689982f56f3SYOSHIFUJI Hideaki fn = pn; \ 690c71099acSThomas Graf if (fn->fn_flags & RTN_RTINFO) \ 691c71099acSThomas Graf goto restart; \ 692c71099acSThomas Graf } \ 693982f56f3SYOSHIFUJI Hideaki } \ 694982f56f3SYOSHIFUJI Hideaki } while (0) 695c71099acSThomas Graf 6968ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 6978ed67789SDaniel Lezcano struct fib6_table *table, 6984c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 6991da177e4SLinus Torvalds { 7001da177e4SLinus Torvalds struct fib6_node *fn; 7011da177e4SLinus Torvalds struct rt6_info *rt; 7021da177e4SLinus Torvalds 703c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 7044c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 705c71099acSThomas Graf restart: 706c71099acSThomas Graf rt = fn->leaf; 7074c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 7084c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 709c71099acSThomas Graf out: 710d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 711c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 7121da177e4SLinus Torvalds return rt; 713c71099acSThomas Graf 714c71099acSThomas Graf } 715c71099acSThomas Graf 716ea6e574eSFlorian Westphal struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6, 717ea6e574eSFlorian Westphal int flags) 718ea6e574eSFlorian Westphal { 719ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 720ea6e574eSFlorian Westphal } 721ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 722ea6e574eSFlorian Westphal 7239acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 7249acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 725c71099acSThomas Graf { 7264c9483b2SDavid S. Miller struct flowi6 fl6 = { 7274c9483b2SDavid S. Miller .flowi6_oif = oif, 7284c9483b2SDavid S. Miller .daddr = *daddr, 729c71099acSThomas Graf }; 730c71099acSThomas Graf struct dst_entry *dst; 73177d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 732c71099acSThomas Graf 733adaa70bbSThomas Graf if (saddr) { 7344c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 735adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 736adaa70bbSThomas Graf } 737adaa70bbSThomas Graf 7384c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 739c71099acSThomas Graf if (dst->error == 0) 740c71099acSThomas Graf return (struct rt6_info *) dst; 741c71099acSThomas Graf 742c71099acSThomas Graf dst_release(dst); 743c71099acSThomas Graf 7441da177e4SLinus Torvalds return NULL; 7451da177e4SLinus Torvalds } 7461da177e4SLinus Torvalds 7477159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 7487159039aSYOSHIFUJI Hideaki 749c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 7501da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 7511da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 7521da177e4SLinus Torvalds be destroyed. 7531da177e4SLinus Torvalds */ 7541da177e4SLinus Torvalds 75586872cb5SThomas Graf static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info) 7561da177e4SLinus Torvalds { 7571da177e4SLinus Torvalds int err; 758c71099acSThomas Graf struct fib6_table *table; 7591da177e4SLinus Torvalds 760c71099acSThomas Graf table = rt->rt6i_table; 761c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 76286872cb5SThomas Graf err = fib6_add(&table->tb6_root, rt, info); 763c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 7641da177e4SLinus Torvalds 7651da177e4SLinus Torvalds return err; 7661da177e4SLinus Torvalds } 7671da177e4SLinus Torvalds 76840e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 76940e22e8fSThomas Graf { 7704d1169c1SDenis V. Lunev struct nl_info info = { 771d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 7724d1169c1SDenis V. Lunev }; 773528c4cebSDenis V. Lunev return __ip6_ins_rt(rt, &info); 77440e22e8fSThomas Graf } 77540e22e8fSThomas Graf 7761716a961SGao feng static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, 77721efcfa0SEric Dumazet const struct in6_addr *daddr, 778b71d1d42SEric Dumazet const struct in6_addr *saddr) 7791da177e4SLinus Torvalds { 7801da177e4SLinus Torvalds struct rt6_info *rt; 7811da177e4SLinus Torvalds 7821da177e4SLinus Torvalds /* 7831da177e4SLinus Torvalds * Clone the route. 7841da177e4SLinus Torvalds */ 7851da177e4SLinus Torvalds 78621efcfa0SEric Dumazet rt = ip6_rt_copy(ort, daddr); 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds if (rt) { 78914deae41SDavid S. Miller int attempts = !in_softirq(); 79014deae41SDavid S. Miller 79158c4fb86SYOSHIFUJI Hideaki if (!(rt->rt6i_flags & RTF_GATEWAY)) { 792bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 79321efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 79458c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 7954e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *daddr; 79658c4fb86SYOSHIFUJI Hideaki } 7971da177e4SLinus Torvalds 7981da177e4SLinus Torvalds rt->rt6i_flags |= RTF_CACHE; 7991da177e4SLinus Torvalds 8001da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 8011da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 8024e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 8031da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 8041da177e4SLinus Torvalds } 8051da177e4SLinus Torvalds #endif 8061da177e4SLinus Torvalds 80714deae41SDavid S. Miller retry: 8088ade06c6SDavid S. Miller if (rt6_bind_neighbour(rt, rt->dst.dev)) { 809d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 81014deae41SDavid S. Miller int saved_rt_min_interval = 81114deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval; 81214deae41SDavid S. Miller int saved_rt_elasticity = 81314deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity; 81414deae41SDavid S. Miller 81514deae41SDavid S. Miller if (attempts-- > 0) { 81614deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity = 1; 81714deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval = 0; 81814deae41SDavid S. Miller 81986393e52SAlexey Dobriyan ip6_dst_gc(&net->ipv6.ip6_dst_ops); 82014deae41SDavid S. Miller 82114deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity = 82214deae41SDavid S. Miller saved_rt_elasticity; 82314deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval = 82414deae41SDavid S. Miller saved_rt_min_interval; 82514deae41SDavid S. Miller goto retry; 82614deae41SDavid S. Miller } 82714deae41SDavid S. Miller 828f3213831SJoe Perches net_warn_ratelimited("Neighbour table overflow\n"); 829d8d1f30bSChangli Gao dst_free(&rt->dst); 83014deae41SDavid S. Miller return NULL; 83114deae41SDavid S. Miller } 83295a9a5baSYOSHIFUJI Hideaki } 8331da177e4SLinus Torvalds 8341da177e4SLinus Torvalds return rt; 8351da177e4SLinus Torvalds } 83695a9a5baSYOSHIFUJI Hideaki 83721efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, 83821efcfa0SEric Dumazet const struct in6_addr *daddr) 839299d9939SYOSHIFUJI Hideaki { 84021efcfa0SEric Dumazet struct rt6_info *rt = ip6_rt_copy(ort, daddr); 84121efcfa0SEric Dumazet 842299d9939SYOSHIFUJI Hideaki if (rt) { 843299d9939SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_CACHE; 84497cac082SDavid S. Miller rt->n = neigh_clone(ort->n); 845299d9939SYOSHIFUJI Hideaki } 846299d9939SYOSHIFUJI Hideaki return rt; 847299d9939SYOSHIFUJI Hideaki } 848299d9939SYOSHIFUJI Hideaki 8498ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, 8504c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 8511da177e4SLinus Torvalds { 8521da177e4SLinus Torvalds struct fib6_node *fn; 853519fbd87SYOSHIFUJI Hideaki struct rt6_info *rt, *nrt; 854c71099acSThomas Graf int strict = 0; 8551da177e4SLinus Torvalds int attempts = 3; 856519fbd87SYOSHIFUJI Hideaki int err; 85753b7997fSYOSHIFUJI Hideaki int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE; 8581da177e4SLinus Torvalds 85977d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 8601da177e4SLinus Torvalds 8611da177e4SLinus Torvalds relookup: 862c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 8631da177e4SLinus Torvalds 8648238dd06SYOSHIFUJI Hideaki restart_2: 8654c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 8661da177e4SLinus Torvalds 8671da177e4SLinus Torvalds restart: 8684acad72dSPavel Emelyanov rt = rt6_select(fn, oif, strict | reachable); 8698ed67789SDaniel Lezcano 8704c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 8718ed67789SDaniel Lezcano if (rt == net->ipv6.ip6_null_entry || 8728238dd06SYOSHIFUJI Hideaki rt->rt6i_flags & RTF_CACHE) 8731da177e4SLinus Torvalds goto out; 8741da177e4SLinus Torvalds 875d8d1f30bSChangli Gao dst_hold(&rt->dst); 876c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 8771da177e4SLinus Torvalds 87897cac082SDavid S. Miller if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP)) 8794c9483b2SDavid S. Miller nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); 8807343ff31SDavid S. Miller else if (!(rt->dst.flags & DST_HOST)) 8814c9483b2SDavid S. Miller nrt = rt6_alloc_clone(rt, &fl6->daddr); 8827343ff31SDavid S. Miller else 8837343ff31SDavid S. Miller goto out2; 8841da177e4SLinus Torvalds 885d8d1f30bSChangli Gao dst_release(&rt->dst); 8868ed67789SDaniel Lezcano rt = nrt ? : net->ipv6.ip6_null_entry; 8871da177e4SLinus Torvalds 888d8d1f30bSChangli Gao dst_hold(&rt->dst); 889e40cf353SYOSHIFUJI Hideaki if (nrt) { 89040e22e8fSThomas Graf err = ip6_ins_rt(nrt); 891e40cf353SYOSHIFUJI Hideaki if (!err) 892e40cf353SYOSHIFUJI Hideaki goto out2; 893e40cf353SYOSHIFUJI Hideaki } 894e40cf353SYOSHIFUJI Hideaki 895e40cf353SYOSHIFUJI Hideaki if (--attempts <= 0) 8961da177e4SLinus Torvalds goto out2; 8971da177e4SLinus Torvalds 898519fbd87SYOSHIFUJI Hideaki /* 899c71099acSThomas Graf * Race condition! In the gap, when table->tb6_lock was 900519fbd87SYOSHIFUJI Hideaki * released someone could insert this route. Relookup. 9011da177e4SLinus Torvalds */ 902d8d1f30bSChangli Gao dst_release(&rt->dst); 9031da177e4SLinus Torvalds goto relookup; 904e40cf353SYOSHIFUJI Hideaki 905519fbd87SYOSHIFUJI Hideaki out: 9068238dd06SYOSHIFUJI Hideaki if (reachable) { 9078238dd06SYOSHIFUJI Hideaki reachable = 0; 9088238dd06SYOSHIFUJI Hideaki goto restart_2; 9098238dd06SYOSHIFUJI Hideaki } 910d8d1f30bSChangli Gao dst_hold(&rt->dst); 911c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 9121da177e4SLinus Torvalds out2: 913d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 914d8d1f30bSChangli Gao rt->dst.__use++; 915c71099acSThomas Graf 916c71099acSThomas Graf return rt; 917c71099acSThomas Graf } 918c71099acSThomas Graf 9198ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 9204c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 9214acad72dSPavel Emelyanov { 9224c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 9234acad72dSPavel Emelyanov } 9244acad72dSPavel Emelyanov 92572331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 92672331bc0SShmulik Ladkani struct net_device *dev, 92772331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 92872331bc0SShmulik Ladkani { 92972331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 93072331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 93172331bc0SShmulik Ladkani 93272331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 93372331bc0SShmulik Ladkani } 93472331bc0SShmulik Ladkani 935c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 936c71099acSThomas Graf { 937b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 938c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 939adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 9404c9483b2SDavid S. Miller struct flowi6 fl6 = { 9414c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 9424c9483b2SDavid S. Miller .daddr = iph->daddr, 9434c9483b2SDavid S. Miller .saddr = iph->saddr, 9444c9483b2SDavid S. Miller .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK, 9454c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 9464c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 947c71099acSThomas Graf }; 948adaa70bbSThomas Graf 94972331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 950c71099acSThomas Graf } 951c71099acSThomas Graf 9528ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 9534c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 954c71099acSThomas Graf { 9554c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 956c71099acSThomas Graf } 957c71099acSThomas Graf 9589c7a4f9cSFlorian Westphal struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk, 9594c9483b2SDavid S. Miller struct flowi6 *fl6) 960c71099acSThomas Graf { 961c71099acSThomas Graf int flags = 0; 962c71099acSThomas Graf 9631fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 9644dc27d1cSDavid McCullough 9654c9483b2SDavid S. Miller if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) 96677d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 967c71099acSThomas Graf 9684c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 969adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 9700c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 9710c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 972adaa70bbSThomas Graf 9734c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 9741da177e4SLinus Torvalds } 9751da177e4SLinus Torvalds 9767159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 9771da177e4SLinus Torvalds 9782774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 97914e50e57SDavid S. Miller { 9805c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 98114e50e57SDavid S. Miller struct dst_entry *new = NULL; 98214e50e57SDavid S. Miller 983f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 98414e50e57SDavid S. Miller if (rt) { 985d8d1f30bSChangli Gao new = &rt->dst; 98614e50e57SDavid S. Miller 9878104891bSSteffen Klassert memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); 9888104891bSSteffen Klassert rt6_init_peer(rt, net->ipv6.peers); 9898104891bSSteffen Klassert 99014e50e57SDavid S. Miller new->__use = 1; 991352e512cSHerbert Xu new->input = dst_discard; 992352e512cSHerbert Xu new->output = dst_discard; 99314e50e57SDavid S. Miller 99421efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 99521efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 99621efcfa0SEric Dumazet else 997defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 99814e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 99914e50e57SDavid S. Miller if (rt->rt6i_idev) 100014e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 100114e50e57SDavid S. Miller 10024e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 10031716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 10041716a961SGao feng rt6_clean_expires(rt); 100514e50e57SDavid S. Miller rt->rt6i_metric = 0; 100614e50e57SDavid S. Miller 100714e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 100814e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 100914e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 101014e50e57SDavid S. Miller #endif 101114e50e57SDavid S. Miller 101214e50e57SDavid S. Miller dst_free(new); 101314e50e57SDavid S. Miller } 101414e50e57SDavid S. Miller 101569ead7afSDavid S. Miller dst_release(dst_orig); 101669ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 101714e50e57SDavid S. Miller } 101814e50e57SDavid S. Miller 10191da177e4SLinus Torvalds /* 10201da177e4SLinus Torvalds * Destination cache support functions 10211da177e4SLinus Torvalds */ 10221da177e4SLinus Torvalds 10231da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 10241da177e4SLinus Torvalds { 10251da177e4SLinus Torvalds struct rt6_info *rt; 10261da177e4SLinus Torvalds 10271da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 10281da177e4SLinus Torvalds 10296431cbc2SDavid S. Miller if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) { 10306431cbc2SDavid S. Miller if (rt->rt6i_peer_genid != rt6_peer_genid()) { 103197bab73fSDavid S. Miller if (!rt6_has_peer(rt)) 10326431cbc2SDavid S. Miller rt6_bind_peer(rt, 0); 10336431cbc2SDavid S. Miller rt->rt6i_peer_genid = rt6_peer_genid(); 10346431cbc2SDavid S. Miller } 10351da177e4SLinus Torvalds return dst; 10366431cbc2SDavid S. Miller } 10371da177e4SLinus Torvalds return NULL; 10381da177e4SLinus Torvalds } 10391da177e4SLinus Torvalds 10401da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 10411da177e4SLinus Torvalds { 10421da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 10431da177e4SLinus Torvalds 10441da177e4SLinus Torvalds if (rt) { 104554c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 104654c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1047e0a1ad73SThomas Graf ip6_del_rt(rt); 104854c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 10491da177e4SLinus Torvalds } 105054c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 105154c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 105254c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 105354c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 105454c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 105554c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 10561da177e4SLinus Torvalds } 10571da177e4SLinus Torvalds 10581da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 10591da177e4SLinus Torvalds { 10601da177e4SLinus Torvalds struct rt6_info *rt; 10611da177e4SLinus Torvalds 10623ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 10631da177e4SLinus Torvalds 1064adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 10651da177e4SLinus Torvalds if (rt) { 10661716a961SGao feng if (rt->rt6i_flags & RTF_CACHE) 10671716a961SGao feng rt6_update_expires(rt, 0); 10681716a961SGao feng else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) 10691da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 10701da177e4SLinus Torvalds } 10711da177e4SLinus Torvalds } 10721da177e4SLinus Torvalds 10736700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 10746700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 10751da177e4SLinus Torvalds { 10761da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info*)dst; 10771da177e4SLinus Torvalds 107881aded24SDavid S. Miller dst_confirm(dst); 10791da177e4SLinus Torvalds if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) { 108081aded24SDavid S. Miller struct net *net = dev_net(dst->dev); 108181aded24SDavid S. Miller 10821da177e4SLinus Torvalds rt6->rt6i_flags |= RTF_MODIFIED; 10831da177e4SLinus Torvalds if (mtu < IPV6_MIN_MTU) { 1084defb3519SDavid S. Miller u32 features = dst_metric(dst, RTAX_FEATURES); 10851da177e4SLinus Torvalds mtu = IPV6_MIN_MTU; 1086defb3519SDavid S. Miller features |= RTAX_FEATURE_ALLFRAG; 1087defb3519SDavid S. Miller dst_metric_set(dst, RTAX_FEATURES, features); 10881da177e4SLinus Torvalds } 1089defb3519SDavid S. Miller dst_metric_set(dst, RTAX_MTU, mtu); 109081aded24SDavid S. Miller rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires); 10911da177e4SLinus Torvalds } 10921da177e4SLinus Torvalds } 10931da177e4SLinus Torvalds 109442ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 109542ae66c8SDavid S. Miller int oif, u32 mark) 109681aded24SDavid S. Miller { 109781aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 109881aded24SDavid S. Miller struct dst_entry *dst; 109981aded24SDavid S. Miller struct flowi6 fl6; 110081aded24SDavid S. Miller 110181aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 110281aded24SDavid S. Miller fl6.flowi6_oif = oif; 110381aded24SDavid S. Miller fl6.flowi6_mark = mark; 11043e12939aSDavid S. Miller fl6.flowi6_flags = 0; 110581aded24SDavid S. Miller fl6.daddr = iph->daddr; 110681aded24SDavid S. Miller fl6.saddr = iph->saddr; 110781aded24SDavid S. Miller fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK; 110881aded24SDavid S. Miller 110981aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 111081aded24SDavid S. Miller if (!dst->error) 11116700c270SDavid S. Miller ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu)); 111281aded24SDavid S. Miller dst_release(dst); 111381aded24SDavid S. Miller } 111481aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 111581aded24SDavid S. Miller 111681aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 111781aded24SDavid S. Miller { 111881aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 111981aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 112081aded24SDavid S. Miller } 112181aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 112281aded24SDavid S. Miller 11233a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 11243a5ad2eeSDavid S. Miller { 11253a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 11263a5ad2eeSDavid S. Miller struct dst_entry *dst; 11273a5ad2eeSDavid S. Miller struct flowi6 fl6; 11283a5ad2eeSDavid S. Miller 11293a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 11303a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 11313a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 11323a5ad2eeSDavid S. Miller fl6.flowi6_flags = 0; 11333a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 11343a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 11353a5ad2eeSDavid S. Miller fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK; 11363a5ad2eeSDavid S. Miller 11373a5ad2eeSDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 11383a5ad2eeSDavid S. Miller if (!dst->error) 11396700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 11403a5ad2eeSDavid S. Miller dst_release(dst); 11413a5ad2eeSDavid S. Miller } 11423a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 11433a5ad2eeSDavid S. Miller 11443a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 11453a5ad2eeSDavid S. Miller { 11463a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 11473a5ad2eeSDavid S. Miller } 11483a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 11493a5ad2eeSDavid S. Miller 11500dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 11511da177e4SLinus Torvalds { 11520dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 11530dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 11540dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 11550dbaee3bSDavid S. Miller 11561da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 11571da177e4SLinus Torvalds 11585578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 11595578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 11601da177e4SLinus Torvalds 11611da177e4SLinus Torvalds /* 11621da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 11631da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 11641da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 11651da177e4SLinus Torvalds * rely only on pmtu discovery" 11661da177e4SLinus Torvalds */ 11671da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 11681da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 11691da177e4SLinus Torvalds return mtu; 11701da177e4SLinus Torvalds } 11711da177e4SLinus Torvalds 1172ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1173d33e4553SDavid S. Miller { 1174d33e4553SDavid S. Miller struct inet6_dev *idev; 1175618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 1176618f9bc7SSteffen Klassert 1177618f9bc7SSteffen Klassert if (mtu) 1178618f9bc7SSteffen Klassert return mtu; 1179618f9bc7SSteffen Klassert 1180618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1181d33e4553SDavid S. Miller 1182d33e4553SDavid S. Miller rcu_read_lock(); 1183d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1184d33e4553SDavid S. Miller if (idev) 1185d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1186d33e4553SDavid S. Miller rcu_read_unlock(); 1187d33e4553SDavid S. Miller 1188d33e4553SDavid S. Miller return mtu; 1189d33e4553SDavid S. Miller } 1190d33e4553SDavid S. Miller 11913b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 11923b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 11935d0bbeebSThomas Graf 11943b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 11951da177e4SLinus Torvalds struct neighbour *neigh, 119687a11578SDavid S. Miller struct flowi6 *fl6) 11971da177e4SLinus Torvalds { 119887a11578SDavid S. Miller struct dst_entry *dst; 11991da177e4SLinus Torvalds struct rt6_info *rt; 12001da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1201c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 12021da177e4SLinus Torvalds 120338308473SDavid S. Miller if (unlikely(!idev)) 1204122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 12051da177e4SLinus Torvalds 12068b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, dev, 0, NULL); 120738308473SDavid S. Miller if (unlikely(!rt)) { 12081da177e4SLinus Torvalds in6_dev_put(idev); 120987a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 12101da177e4SLinus Torvalds goto out; 12111da177e4SLinus Torvalds } 12121da177e4SLinus Torvalds 12131da177e4SLinus Torvalds if (neigh) 12141da177e4SLinus Torvalds neigh_hold(neigh); 121514deae41SDavid S. Miller else { 1216f894cbf8SDavid S. Miller neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr); 1217b43faac6SDavid S. Miller if (IS_ERR(neigh)) { 1218252c3d84SRongQing.Li in6_dev_put(idev); 1219b43faac6SDavid S. Miller dst_free(&rt->dst); 1220b43faac6SDavid S. Miller return ERR_CAST(neigh); 1221b43faac6SDavid S. Miller } 122214deae41SDavid S. Miller } 12231da177e4SLinus Torvalds 12248e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 12258e2ec639SYan, Zheng rt->dst.output = ip6_output; 122697cac082SDavid S. Miller rt->n = neigh; 1227d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 122887a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 12298e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 12308e2ec639SYan, Zheng rt->rt6i_idev = idev; 12317011687fSGao feng dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255); 12321da177e4SLinus Torvalds 12333b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1234d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1235d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 12363b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 12371da177e4SLinus Torvalds 12385578689aSDaniel Lezcano fib6_force_start_gc(net); 12391da177e4SLinus Torvalds 124087a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 124187a11578SDavid S. Miller 12421da177e4SLinus Torvalds out: 124387a11578SDavid S. Miller return dst; 12441da177e4SLinus Torvalds } 12451da177e4SLinus Torvalds 12463d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 12471da177e4SLinus Torvalds { 1248e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 12493d0f24a7SStephen Hemminger int more = 0; 12501da177e4SLinus Torvalds 12513b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 12523b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 12535d0bbeebSThomas Graf 12541da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 12551da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 12561da177e4SLinus Torvalds *pprev = dst->next; 12571da177e4SLinus Torvalds dst_free(dst); 12581da177e4SLinus Torvalds } else { 12591da177e4SLinus Torvalds pprev = &dst->next; 12603d0f24a7SStephen Hemminger ++more; 12611da177e4SLinus Torvalds } 12621da177e4SLinus Torvalds } 12631da177e4SLinus Torvalds 12643b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 12655d0bbeebSThomas Graf 12663d0f24a7SStephen Hemminger return more; 12671da177e4SLinus Torvalds } 12681da177e4SLinus Torvalds 12691e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 12701e493d19SDavid S. Miller void *arg) 12711e493d19SDavid S. Miller { 12721e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 12731e493d19SDavid S. Miller 12741e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 12751e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 12761e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 12771e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 12781e493d19SDavid S. Miller if (func(rt, arg)) { 12791e493d19SDavid S. Miller *pprev = dst->next; 12801e493d19SDavid S. Miller dst_free(dst); 12811e493d19SDavid S. Miller } else { 12821e493d19SDavid S. Miller pprev = &dst->next; 12831e493d19SDavid S. Miller } 12841e493d19SDavid S. Miller } 12851e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 12861e493d19SDavid S. Miller } 12871e493d19SDavid S. Miller 1288569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 12891da177e4SLinus Torvalds { 12901da177e4SLinus Torvalds unsigned long now = jiffies; 129186393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 12927019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 12937019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 12947019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 12957019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 12967019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1297fc66f95cSEric Dumazet int entries; 12981da177e4SLinus Torvalds 1299fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 13007019b78eSDaniel Lezcano if (time_after(rt_last_gc + rt_min_interval, now) && 1301fc66f95cSEric Dumazet entries <= rt_max_size) 13021da177e4SLinus Torvalds goto out; 13031da177e4SLinus Torvalds 13046891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 13056891a346SBenjamin Thery fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net); 13066891a346SBenjamin Thery net->ipv6.ip6_rt_last_gc = now; 1307fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1308fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 13097019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 13101da177e4SLinus Torvalds out: 13117019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1312fc66f95cSEric Dumazet return entries > rt_max_size; 13131da177e4SLinus Torvalds } 13141da177e4SLinus Torvalds 13151da177e4SLinus Torvalds /* Clean host part of a prefix. Not necessary in radix tree, 13161da177e4SLinus Torvalds but results in cleaner routing tables. 13171da177e4SLinus Torvalds 13181da177e4SLinus Torvalds Remove it only when all the things will work! 13191da177e4SLinus Torvalds */ 13201da177e4SLinus Torvalds 13216b75d090SYOSHIFUJI Hideaki int ip6_dst_hoplimit(struct dst_entry *dst) 13221da177e4SLinus Torvalds { 13235170ae82SDavid S. Miller int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT); 1324a02e4b7dSDavid S. Miller if (hoplimit == 0) { 13256b75d090SYOSHIFUJI Hideaki struct net_device *dev = dst->dev; 1326c68f24ccSEric Dumazet struct inet6_dev *idev; 1327c68f24ccSEric Dumazet 1328c68f24ccSEric Dumazet rcu_read_lock(); 1329c68f24ccSEric Dumazet idev = __in6_dev_get(dev); 1330c68f24ccSEric Dumazet if (idev) 13311da177e4SLinus Torvalds hoplimit = idev->cnf.hop_limit; 1332c68f24ccSEric Dumazet else 133353b7997fSYOSHIFUJI Hideaki hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit; 1334c68f24ccSEric Dumazet rcu_read_unlock(); 13351da177e4SLinus Torvalds } 13361da177e4SLinus Torvalds return hoplimit; 13371da177e4SLinus Torvalds } 1338abbf46aeSDavid S. Miller EXPORT_SYMBOL(ip6_dst_hoplimit); 13391da177e4SLinus Torvalds 13401da177e4SLinus Torvalds /* 13411da177e4SLinus Torvalds * 13421da177e4SLinus Torvalds */ 13431da177e4SLinus Torvalds 134486872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg) 13451da177e4SLinus Torvalds { 13461da177e4SLinus Torvalds int err; 13475578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 13481da177e4SLinus Torvalds struct rt6_info *rt = NULL; 13491da177e4SLinus Torvalds struct net_device *dev = NULL; 13501da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1351c71099acSThomas Graf struct fib6_table *table; 13521da177e4SLinus Torvalds int addr_type; 13531da177e4SLinus Torvalds 135486872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 13551da177e4SLinus Torvalds return -EINVAL; 13561da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 135786872cb5SThomas Graf if (cfg->fc_src_len) 13581da177e4SLinus Torvalds return -EINVAL; 13591da177e4SLinus Torvalds #endif 136086872cb5SThomas Graf if (cfg->fc_ifindex) { 13611da177e4SLinus Torvalds err = -ENODEV; 13625578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 13631da177e4SLinus Torvalds if (!dev) 13641da177e4SLinus Torvalds goto out; 13651da177e4SLinus Torvalds idev = in6_dev_get(dev); 13661da177e4SLinus Torvalds if (!idev) 13671da177e4SLinus Torvalds goto out; 13681da177e4SLinus Torvalds } 13691da177e4SLinus Torvalds 137086872cb5SThomas Graf if (cfg->fc_metric == 0) 137186872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 13721da177e4SLinus Torvalds 1373c71099acSThomas Graf err = -ENOBUFS; 137438308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1375d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1376d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 137738308473SDavid S. Miller if (!table) { 1378f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1379d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1380d71314b4SMatti Vaittinen } 1381d71314b4SMatti Vaittinen } else { 1382d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1383d71314b4SMatti Vaittinen } 138438308473SDavid S. Miller 138538308473SDavid S. Miller if (!table) 1386c71099acSThomas Graf goto out; 1387c71099acSThomas Graf 13888b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table); 13891da177e4SLinus Torvalds 139038308473SDavid S. Miller if (!rt) { 13911da177e4SLinus Torvalds err = -ENOMEM; 13921da177e4SLinus Torvalds goto out; 13931da177e4SLinus Torvalds } 13941da177e4SLinus Torvalds 1395d8d1f30bSChangli Gao rt->dst.obsolete = -1; 13961716a961SGao feng 13971716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 13981716a961SGao feng rt6_set_expires(rt, jiffies + 13991716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 14001716a961SGao feng else 14011716a961SGao feng rt6_clean_expires(rt); 14021da177e4SLinus Torvalds 140386872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 140486872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 140586872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 140686872cb5SThomas Graf 140786872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 14081da177e4SLinus Torvalds 14091da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1410d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1411ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1412ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 14131da177e4SLinus Torvalds else 1414d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 14151da177e4SLinus Torvalds 1416d8d1f30bSChangli Gao rt->dst.output = ip6_output; 14171da177e4SLinus Torvalds 141886872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 141986872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 14201da177e4SLinus Torvalds if (rt->rt6i_dst.plen == 128) 142111d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 14221da177e4SLinus Torvalds 14238e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) { 14248e2ec639SYan, Zheng u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 14258e2ec639SYan, Zheng if (!metrics) { 14268e2ec639SYan, Zheng err = -ENOMEM; 14278e2ec639SYan, Zheng goto out; 14288e2ec639SYan, Zheng } 14298e2ec639SYan, Zheng dst_init_metrics(&rt->dst, metrics, 0); 14308e2ec639SYan, Zheng } 14311da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 143286872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 143386872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 14341da177e4SLinus Torvalds #endif 14351da177e4SLinus Torvalds 143686872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 14371da177e4SLinus Torvalds 14381da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 14391da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 14401da177e4SLinus Torvalds */ 144186872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 144238308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 144338308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 144438308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 14451da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 14465578689aSDaniel Lezcano if (dev != net->loopback_dev) { 14471da177e4SLinus Torvalds if (dev) { 14481da177e4SLinus Torvalds dev_put(dev); 14491da177e4SLinus Torvalds in6_dev_put(idev); 14501da177e4SLinus Torvalds } 14515578689aSDaniel Lezcano dev = net->loopback_dev; 14521da177e4SLinus Torvalds dev_hold(dev); 14531da177e4SLinus Torvalds idev = in6_dev_get(dev); 14541da177e4SLinus Torvalds if (!idev) { 14551da177e4SLinus Torvalds err = -ENODEV; 14561da177e4SLinus Torvalds goto out; 14571da177e4SLinus Torvalds } 14581da177e4SLinus Torvalds } 1459d8d1f30bSChangli Gao rt->dst.output = ip6_pkt_discard_out; 1460d8d1f30bSChangli Gao rt->dst.input = ip6_pkt_discard; 14611da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1462ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1463ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1464ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 1465ef2c7d7bSNicolas Dichtel break; 1466ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1467ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 1468ef2c7d7bSNicolas Dichtel break; 1469b4949ab2SNicolas Dichtel case RTN_THROW: 1470b4949ab2SNicolas Dichtel rt->dst.error = -EAGAIN; 1471b4949ab2SNicolas Dichtel break; 1472ef2c7d7bSNicolas Dichtel default: 1473ef2c7d7bSNicolas Dichtel rt->dst.error = -ENETUNREACH; 1474ef2c7d7bSNicolas Dichtel break; 1475ef2c7d7bSNicolas Dichtel } 14761da177e4SLinus Torvalds goto install_route; 14771da177e4SLinus Torvalds } 14781da177e4SLinus Torvalds 147986872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1480b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 14811da177e4SLinus Torvalds int gwa_type; 14821da177e4SLinus Torvalds 148386872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 14844e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 14851da177e4SLinus Torvalds gwa_type = ipv6_addr_type(gw_addr); 14861da177e4SLinus Torvalds 14871da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 14881da177e4SLinus Torvalds struct rt6_info *grt; 14891da177e4SLinus Torvalds 14901da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 14911da177e4SLinus Torvalds addresses as nexthop address. 14921da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 14931da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 14941da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 14951da177e4SLinus Torvalds some exceptions. --ANK 14961da177e4SLinus Torvalds */ 14971da177e4SLinus Torvalds err = -EINVAL; 14981da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 14991da177e4SLinus Torvalds goto out; 15001da177e4SLinus Torvalds 15015578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 15021da177e4SLinus Torvalds 15031da177e4SLinus Torvalds err = -EHOSTUNREACH; 150438308473SDavid S. Miller if (!grt) 15051da177e4SLinus Torvalds goto out; 15061da177e4SLinus Torvalds if (dev) { 1507d1918542SDavid S. Miller if (dev != grt->dst.dev) { 1508d8d1f30bSChangli Gao dst_release(&grt->dst); 15091da177e4SLinus Torvalds goto out; 15101da177e4SLinus Torvalds } 15111da177e4SLinus Torvalds } else { 1512d1918542SDavid S. Miller dev = grt->dst.dev; 15131da177e4SLinus Torvalds idev = grt->rt6i_idev; 15141da177e4SLinus Torvalds dev_hold(dev); 15151da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 15161da177e4SLinus Torvalds } 15171da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 15181da177e4SLinus Torvalds err = 0; 1519d8d1f30bSChangli Gao dst_release(&grt->dst); 15201da177e4SLinus Torvalds 15211da177e4SLinus Torvalds if (err) 15221da177e4SLinus Torvalds goto out; 15231da177e4SLinus Torvalds } 15241da177e4SLinus Torvalds err = -EINVAL; 152538308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 15261da177e4SLinus Torvalds goto out; 15271da177e4SLinus Torvalds } 15281da177e4SLinus Torvalds 15291da177e4SLinus Torvalds err = -ENODEV; 153038308473SDavid S. Miller if (!dev) 15311da177e4SLinus Torvalds goto out; 15321da177e4SLinus Torvalds 1533c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1534c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1535c3968a85SDaniel Walter err = -EINVAL; 1536c3968a85SDaniel Walter goto out; 1537c3968a85SDaniel Walter } 15384e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1539c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1540c3968a85SDaniel Walter } else 1541c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1542c3968a85SDaniel Walter 154386872cb5SThomas Graf if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) { 15448ade06c6SDavid S. Miller err = rt6_bind_neighbour(rt, dev); 1545f83c7790SDavid S. Miller if (err) 15461da177e4SLinus Torvalds goto out; 15471da177e4SLinus Torvalds } 15481da177e4SLinus Torvalds 154986872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 15501da177e4SLinus Torvalds 15511da177e4SLinus Torvalds install_route: 155286872cb5SThomas Graf if (cfg->fc_mx) { 155386872cb5SThomas Graf struct nlattr *nla; 155486872cb5SThomas Graf int remaining; 15551da177e4SLinus Torvalds 155686872cb5SThomas Graf nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 15578f4c1f9bSThomas Graf int type = nla_type(nla); 155886872cb5SThomas Graf 155986872cb5SThomas Graf if (type) { 156086872cb5SThomas Graf if (type > RTAX_MAX) { 15611da177e4SLinus Torvalds err = -EINVAL; 15621da177e4SLinus Torvalds goto out; 15631da177e4SLinus Torvalds } 156486872cb5SThomas Graf 1565defb3519SDavid S. Miller dst_metric_set(&rt->dst, type, nla_get_u32(nla)); 15661da177e4SLinus Torvalds } 15671da177e4SLinus Torvalds } 15681da177e4SLinus Torvalds } 15691da177e4SLinus Torvalds 1570d8d1f30bSChangli Gao rt->dst.dev = dev; 15711da177e4SLinus Torvalds rt->rt6i_idev = idev; 1572c71099acSThomas Graf rt->rt6i_table = table; 157363152fc0SDaniel Lezcano 1574c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 157563152fc0SDaniel Lezcano 157686872cb5SThomas Graf return __ip6_ins_rt(rt, &cfg->fc_nlinfo); 15771da177e4SLinus Torvalds 15781da177e4SLinus Torvalds out: 15791da177e4SLinus Torvalds if (dev) 15801da177e4SLinus Torvalds dev_put(dev); 15811da177e4SLinus Torvalds if (idev) 15821da177e4SLinus Torvalds in6_dev_put(idev); 15831da177e4SLinus Torvalds if (rt) 1584d8d1f30bSChangli Gao dst_free(&rt->dst); 15851da177e4SLinus Torvalds return err; 15861da177e4SLinus Torvalds } 15871da177e4SLinus Torvalds 158886872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 15891da177e4SLinus Torvalds { 15901da177e4SLinus Torvalds int err; 1591c71099acSThomas Graf struct fib6_table *table; 1592d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 15931da177e4SLinus Torvalds 15948ed67789SDaniel Lezcano if (rt == net->ipv6.ip6_null_entry) 15956c813a72SPatrick McHardy return -ENOENT; 15966c813a72SPatrick McHardy 1597c71099acSThomas Graf table = rt->rt6i_table; 1598c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 15991da177e4SLinus Torvalds 160086872cb5SThomas Graf err = fib6_del(rt, info); 1601d8d1f30bSChangli Gao dst_release(&rt->dst); 16021da177e4SLinus Torvalds 1603c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 16041da177e4SLinus Torvalds 16051da177e4SLinus Torvalds return err; 16061da177e4SLinus Torvalds } 16071da177e4SLinus Torvalds 1608e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 1609e0a1ad73SThomas Graf { 16104d1169c1SDenis V. Lunev struct nl_info info = { 1611d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 16124d1169c1SDenis V. Lunev }; 1613528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 1614e0a1ad73SThomas Graf } 1615e0a1ad73SThomas Graf 161686872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 16171da177e4SLinus Torvalds { 1618c71099acSThomas Graf struct fib6_table *table; 16191da177e4SLinus Torvalds struct fib6_node *fn; 16201da177e4SLinus Torvalds struct rt6_info *rt; 16211da177e4SLinus Torvalds int err = -ESRCH; 16221da177e4SLinus Torvalds 16235578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 162438308473SDavid S. Miller if (!table) 1625c71099acSThomas Graf return err; 16261da177e4SLinus Torvalds 1627c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1628c71099acSThomas Graf 1629c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 163086872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 163186872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 16321da177e4SLinus Torvalds 16331da177e4SLinus Torvalds if (fn) { 1634d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 163586872cb5SThomas Graf if (cfg->fc_ifindex && 1636d1918542SDavid S. Miller (!rt->dst.dev || 1637d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 16381da177e4SLinus Torvalds continue; 163986872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 164086872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 16411da177e4SLinus Torvalds continue; 164286872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 16431da177e4SLinus Torvalds continue; 1644d8d1f30bSChangli Gao dst_hold(&rt->dst); 1645c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 16461da177e4SLinus Torvalds 164786872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 16481da177e4SLinus Torvalds } 16491da177e4SLinus Torvalds } 1650c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 16511da177e4SLinus Torvalds 16521da177e4SLinus Torvalds return err; 16531da177e4SLinus Torvalds } 16541da177e4SLinus Torvalds 16556700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 1656a6279458SYOSHIFUJI Hideaki { 1657e8599ff4SDavid S. Miller struct net *net = dev_net(skb->dev); 1658a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 1659e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 1660e8599ff4SDavid S. Miller const struct in6_addr *target; 1661e8599ff4SDavid S. Miller struct ndisc_options ndopts; 16626e157b6aSDavid S. Miller const struct in6_addr *dest; 16636e157b6aSDavid S. Miller struct neighbour *old_neigh; 1664e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 1665e8599ff4SDavid S. Miller struct neighbour *neigh; 1666e8599ff4SDavid S. Miller struct icmp6hdr *icmph; 16676e157b6aSDavid S. Miller int optlen, on_link; 16686e157b6aSDavid S. Miller u8 *lladdr; 1669e8599ff4SDavid S. Miller 1670e8599ff4SDavid S. Miller optlen = skb->tail - skb->transport_header; 1671e8599ff4SDavid S. Miller optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); 1672e8599ff4SDavid S. Miller 1673e8599ff4SDavid S. Miller if (optlen < 0) { 16746e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 1675e8599ff4SDavid S. Miller return; 1676e8599ff4SDavid S. Miller } 1677e8599ff4SDavid S. Miller 1678e8599ff4SDavid S. Miller icmph = icmp6_hdr(skb); 1679e8599ff4SDavid S. Miller target = (const struct in6_addr *) (icmph + 1); 1680e8599ff4SDavid S. Miller dest = target + 1; 1681e8599ff4SDavid S. Miller 1682e8599ff4SDavid S. Miller if (ipv6_addr_is_multicast(dest)) { 16836e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 1684e8599ff4SDavid S. Miller return; 1685e8599ff4SDavid S. Miller } 1686e8599ff4SDavid S. Miller 16876e157b6aSDavid S. Miller on_link = 0; 1688e8599ff4SDavid S. Miller if (ipv6_addr_equal(dest, target)) { 1689e8599ff4SDavid S. Miller on_link = 1; 1690e8599ff4SDavid S. Miller } else if (ipv6_addr_type(target) != 1691e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 16926e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 1693e8599ff4SDavid S. Miller return; 1694e8599ff4SDavid S. Miller } 1695e8599ff4SDavid S. Miller 1696e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 1697e8599ff4SDavid S. Miller if (!in6_dev) 1698e8599ff4SDavid S. Miller return; 1699e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 1700e8599ff4SDavid S. Miller return; 1701e8599ff4SDavid S. Miller 1702e8599ff4SDavid S. Miller /* RFC2461 8.1: 1703e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 1704e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 1705e8599ff4SDavid S. Miller */ 1706e8599ff4SDavid S. Miller 1707e8599ff4SDavid S. Miller if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) { 1708e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 1709e8599ff4SDavid S. Miller return; 1710e8599ff4SDavid S. Miller } 17116e157b6aSDavid S. Miller 17126e157b6aSDavid S. Miller lladdr = NULL; 1713e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 1714e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 1715e8599ff4SDavid S. Miller skb->dev); 1716e8599ff4SDavid S. Miller if (!lladdr) { 1717e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 1718e8599ff4SDavid S. Miller return; 1719e8599ff4SDavid S. Miller } 1720e8599ff4SDavid S. Miller } 1721e8599ff4SDavid S. Miller 17226e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 17236e157b6aSDavid S. Miller if (rt == net->ipv6.ip6_null_entry) { 17246e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 17256e157b6aSDavid S. Miller return; 17266e157b6aSDavid S. Miller } 17276e157b6aSDavid S. Miller 17286e157b6aSDavid S. Miller /* Redirect received -> path was valid. 17296e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 17306e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 17316e157b6aSDavid S. Miller */ 17326e157b6aSDavid S. Miller dst_confirm(&rt->dst); 17336e157b6aSDavid S. Miller 1734e8599ff4SDavid S. Miller neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1); 1735e8599ff4SDavid S. Miller if (!neigh) 1736e8599ff4SDavid S. Miller return; 1737e8599ff4SDavid S. Miller 17386e157b6aSDavid S. Miller /* Duplicate redirect: silently ignore. */ 17396e157b6aSDavid S. Miller old_neigh = rt->n; 17406e157b6aSDavid S. Miller if (neigh == old_neigh) 1741a6279458SYOSHIFUJI Hideaki goto out; 17421da177e4SLinus Torvalds 17431da177e4SLinus Torvalds /* 17441da177e4SLinus Torvalds * We have finally decided to accept it. 17451da177e4SLinus Torvalds */ 17461da177e4SLinus Torvalds 17471da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 17481da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 17491da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 17501da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 17511da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 17521da177e4SLinus Torvalds ); 17531da177e4SLinus Torvalds 175421efcfa0SEric Dumazet nrt = ip6_rt_copy(rt, dest); 175538308473SDavid S. Miller if (!nrt) 17561da177e4SLinus Torvalds goto out; 17571da177e4SLinus Torvalds 17581da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 17591da177e4SLinus Torvalds if (on_link) 17601da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 17611da177e4SLinus Torvalds 17624e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 176397cac082SDavid S. Miller nrt->n = neigh_clone(neigh); 17641da177e4SLinus Torvalds 176540e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 17661da177e4SLinus Torvalds goto out; 17671da177e4SLinus Torvalds 1768d8d1f30bSChangli Gao netevent.old = &rt->dst; 17691d248b1cSDavid S. Miller netevent.old_neigh = old_neigh; 1770d8d1f30bSChangli Gao netevent.new = &nrt->dst; 17711d248b1cSDavid S. Miller netevent.new_neigh = neigh; 17721d248b1cSDavid S. Miller netevent.daddr = dest; 17738d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 17748d71740cSTom Tucker 17751da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 17766e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 1777e0a1ad73SThomas Graf ip6_del_rt(rt); 17781da177e4SLinus Torvalds } 17791da177e4SLinus Torvalds 17801da177e4SLinus Torvalds out: 1781e8599ff4SDavid S. Miller neigh_release(neigh); 17826e157b6aSDavid S. Miller } 17836e157b6aSDavid S. Miller 17841da177e4SLinus Torvalds /* 17851da177e4SLinus Torvalds * Misc support functions 17861da177e4SLinus Torvalds */ 17871da177e4SLinus Torvalds 17881716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 178921efcfa0SEric Dumazet const struct in6_addr *dest) 17901da177e4SLinus Torvalds { 1791d1918542SDavid S. Miller struct net *net = dev_net(ort->dst.dev); 17928b96d22dSDavid S. Miller struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0, 17938b96d22dSDavid S. Miller ort->rt6i_table); 17941da177e4SLinus Torvalds 17951da177e4SLinus Torvalds if (rt) { 1796d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 1797d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 17988e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 17991da177e4SLinus Torvalds 18004e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *dest; 18018e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 1802defb3519SDavid S. Miller dst_copy_metrics(&rt->dst, &ort->dst); 1803d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 18041da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 18051da177e4SLinus Torvalds if (rt->rt6i_idev) 18061da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 1807d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 18081da177e4SLinus Torvalds 18094e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 18101716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 18111716a961SGao feng if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) == 18121716a961SGao feng (RTF_DEFAULT | RTF_ADDRCONF)) 18131716a961SGao feng rt6_set_from(rt, ort); 18141716a961SGao feng else 18151716a961SGao feng rt6_clean_expires(rt); 18161da177e4SLinus Torvalds rt->rt6i_metric = 0; 18171da177e4SLinus Torvalds 18181da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 18191da177e4SLinus Torvalds memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 18201da177e4SLinus Torvalds #endif 18210f6c6392SFlorian Westphal memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key)); 1822c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 18231da177e4SLinus Torvalds } 18241da177e4SLinus Torvalds return rt; 18251da177e4SLinus Torvalds } 18261da177e4SLinus Torvalds 182770ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 1828efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 1829b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1830b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 183170ceb4f5SYOSHIFUJI Hideaki { 183270ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 183370ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 1834c71099acSThomas Graf struct fib6_table *table; 183570ceb4f5SYOSHIFUJI Hideaki 1836efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 183738308473SDavid S. Miller if (!table) 1838c71099acSThomas Graf return NULL; 1839c71099acSThomas Graf 1840*5744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 1841c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0); 184270ceb4f5SYOSHIFUJI Hideaki if (!fn) 184370ceb4f5SYOSHIFUJI Hideaki goto out; 184470ceb4f5SYOSHIFUJI Hideaki 1845d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1846d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 184770ceb4f5SYOSHIFUJI Hideaki continue; 184870ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 184970ceb4f5SYOSHIFUJI Hideaki continue; 185070ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 185170ceb4f5SYOSHIFUJI Hideaki continue; 1852d8d1f30bSChangli Gao dst_hold(&rt->dst); 185370ceb4f5SYOSHIFUJI Hideaki break; 185470ceb4f5SYOSHIFUJI Hideaki } 185570ceb4f5SYOSHIFUJI Hideaki out: 1856*5744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 185770ceb4f5SYOSHIFUJI Hideaki return rt; 185870ceb4f5SYOSHIFUJI Hideaki } 185970ceb4f5SYOSHIFUJI Hideaki 1860efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 1861b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1862b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 186395c96174SEric Dumazet unsigned int pref) 186470ceb4f5SYOSHIFUJI Hideaki { 186586872cb5SThomas Graf struct fib6_config cfg = { 186686872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 1867238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 186886872cb5SThomas Graf .fc_ifindex = ifindex, 186986872cb5SThomas Graf .fc_dst_len = prefixlen, 187086872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 187186872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 187215e47304SEric W. Biederman .fc_nlinfo.portid = 0, 1873efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 1874efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 187586872cb5SThomas Graf }; 187670ceb4f5SYOSHIFUJI Hideaki 18774e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 18784e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 187986872cb5SThomas Graf 1880e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 1881e317da96SYOSHIFUJI Hideaki if (!prefixlen) 188286872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 188370ceb4f5SYOSHIFUJI Hideaki 188486872cb5SThomas Graf ip6_route_add(&cfg); 188570ceb4f5SYOSHIFUJI Hideaki 1886efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 188770ceb4f5SYOSHIFUJI Hideaki } 188870ceb4f5SYOSHIFUJI Hideaki #endif 188970ceb4f5SYOSHIFUJI Hideaki 1890b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 18911da177e4SLinus Torvalds { 18921da177e4SLinus Torvalds struct rt6_info *rt; 1893c71099acSThomas Graf struct fib6_table *table; 18941da177e4SLinus Torvalds 1895c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 189638308473SDavid S. Miller if (!table) 1897c71099acSThomas Graf return NULL; 18981da177e4SLinus Torvalds 1899*5744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 1900d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) { 1901d1918542SDavid S. Miller if (dev == rt->dst.dev && 1902045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 19031da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 19041da177e4SLinus Torvalds break; 19051da177e4SLinus Torvalds } 19061da177e4SLinus Torvalds if (rt) 1907d8d1f30bSChangli Gao dst_hold(&rt->dst); 1908*5744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 19091da177e4SLinus Torvalds return rt; 19101da177e4SLinus Torvalds } 19111da177e4SLinus Torvalds 1912b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 1913ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 1914ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 19151da177e4SLinus Torvalds { 191686872cb5SThomas Graf struct fib6_config cfg = { 191786872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 1918238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 191986872cb5SThomas Graf .fc_ifindex = dev->ifindex, 192086872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 192186872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 192215e47304SEric W. Biederman .fc_nlinfo.portid = 0, 19235578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 1924c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 192586872cb5SThomas Graf }; 19261da177e4SLinus Torvalds 19274e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 19281da177e4SLinus Torvalds 192986872cb5SThomas Graf ip6_route_add(&cfg); 19301da177e4SLinus Torvalds 19311da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 19321da177e4SLinus Torvalds } 19331da177e4SLinus Torvalds 19347b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 19351da177e4SLinus Torvalds { 19361da177e4SLinus Torvalds struct rt6_info *rt; 1937c71099acSThomas Graf struct fib6_table *table; 1938c71099acSThomas Graf 1939c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 19407b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 194138308473SDavid S. Miller if (!table) 1942c71099acSThomas Graf return; 19431da177e4SLinus Torvalds 19441da177e4SLinus Torvalds restart: 1945c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1946d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 19471da177e4SLinus Torvalds if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { 1948d8d1f30bSChangli Gao dst_hold(&rt->dst); 1949c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 1950e0a1ad73SThomas Graf ip6_del_rt(rt); 19511da177e4SLinus Torvalds goto restart; 19521da177e4SLinus Torvalds } 19531da177e4SLinus Torvalds } 1954c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 19551da177e4SLinus Torvalds } 19561da177e4SLinus Torvalds 19575578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 19585578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 195986872cb5SThomas Graf struct fib6_config *cfg) 196086872cb5SThomas Graf { 196186872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 196286872cb5SThomas Graf 196386872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 196486872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 196586872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 196686872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 196786872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 196886872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 196986872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 197086872cb5SThomas Graf 19715578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 1972f1243c2dSBenjamin Thery 19734e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 19744e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 19754e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 197686872cb5SThomas Graf } 197786872cb5SThomas Graf 19785578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 19791da177e4SLinus Torvalds { 198086872cb5SThomas Graf struct fib6_config cfg; 19811da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 19821da177e4SLinus Torvalds int err; 19831da177e4SLinus Torvalds 19841da177e4SLinus Torvalds switch(cmd) { 19851da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 19861da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 19871da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 19881da177e4SLinus Torvalds return -EPERM; 19891da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 19901da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 19911da177e4SLinus Torvalds if (err) 19921da177e4SLinus Torvalds return -EFAULT; 19931da177e4SLinus Torvalds 19945578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 199586872cb5SThomas Graf 19961da177e4SLinus Torvalds rtnl_lock(); 19971da177e4SLinus Torvalds switch (cmd) { 19981da177e4SLinus Torvalds case SIOCADDRT: 199986872cb5SThomas Graf err = ip6_route_add(&cfg); 20001da177e4SLinus Torvalds break; 20011da177e4SLinus Torvalds case SIOCDELRT: 200286872cb5SThomas Graf err = ip6_route_del(&cfg); 20031da177e4SLinus Torvalds break; 20041da177e4SLinus Torvalds default: 20051da177e4SLinus Torvalds err = -EINVAL; 20061da177e4SLinus Torvalds } 20071da177e4SLinus Torvalds rtnl_unlock(); 20081da177e4SLinus Torvalds 20091da177e4SLinus Torvalds return err; 20103ff50b79SStephen Hemminger } 20111da177e4SLinus Torvalds 20121da177e4SLinus Torvalds return -EINVAL; 20131da177e4SLinus Torvalds } 20141da177e4SLinus Torvalds 20151da177e4SLinus Torvalds /* 20161da177e4SLinus Torvalds * Drop the packet on the floor 20171da177e4SLinus Torvalds */ 20181da177e4SLinus Torvalds 2019d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 20201da177e4SLinus Torvalds { 2021612f09e8SYOSHIFUJI Hideaki int type; 2022adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2023612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2024612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 20250660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 202645bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 20273bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 20283bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2029612f09e8SYOSHIFUJI Hideaki break; 2030612f09e8SYOSHIFUJI Hideaki } 2031612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2032612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 20333bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 20343bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2035612f09e8SYOSHIFUJI Hideaki break; 2036612f09e8SYOSHIFUJI Hideaki } 20373ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 20381da177e4SLinus Torvalds kfree_skb(skb); 20391da177e4SLinus Torvalds return 0; 20401da177e4SLinus Torvalds } 20411da177e4SLinus Torvalds 20429ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 20439ce8ade0SThomas Graf { 2044612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 20459ce8ade0SThomas Graf } 20469ce8ade0SThomas Graf 204720380731SArnaldo Carvalho de Melo static int ip6_pkt_discard_out(struct sk_buff *skb) 20481da177e4SLinus Torvalds { 2049adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2050612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 20511da177e4SLinus Torvalds } 20521da177e4SLinus Torvalds 20536723ab54SDavid S. Miller #ifdef CONFIG_IPV6_MULTIPLE_TABLES 20546723ab54SDavid S. Miller 20559ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 20569ce8ade0SThomas Graf { 2057612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 20589ce8ade0SThomas Graf } 20599ce8ade0SThomas Graf 20609ce8ade0SThomas Graf static int ip6_pkt_prohibit_out(struct sk_buff *skb) 20619ce8ade0SThomas Graf { 2062adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2063612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 20649ce8ade0SThomas Graf } 20659ce8ade0SThomas Graf 20666723ab54SDavid S. Miller #endif 20676723ab54SDavid S. Miller 20681da177e4SLinus Torvalds /* 20691da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 20701da177e4SLinus Torvalds */ 20711da177e4SLinus Torvalds 20721da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 20731da177e4SLinus Torvalds const struct in6_addr *addr, 20748f031519SDavid S. Miller bool anycast) 20751da177e4SLinus Torvalds { 2076c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 20778b96d22dSDavid S. Miller struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL); 2078f83c7790SDavid S. Miller int err; 20791da177e4SLinus Torvalds 208038308473SDavid S. Miller if (!rt) { 2081f3213831SJoe Perches net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n"); 20821da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 208340385653SBen Greear } 20841da177e4SLinus Torvalds 20851da177e4SLinus Torvalds in6_dev_hold(idev); 20861da177e4SLinus Torvalds 208711d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2088d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2089d8d1f30bSChangli Gao rt->dst.output = ip6_output; 20901da177e4SLinus Torvalds rt->rt6i_idev = idev; 2091d8d1f30bSChangli Gao rt->dst.obsolete = -1; 20921da177e4SLinus Torvalds 20931da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 209458c4fb86SYOSHIFUJI Hideaki if (anycast) 209558c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 209658c4fb86SYOSHIFUJI Hideaki else 20971da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 20988ade06c6SDavid S. Miller err = rt6_bind_neighbour(rt, rt->dst.dev); 2099f83c7790SDavid S. Miller if (err) { 2100d8d1f30bSChangli Gao dst_free(&rt->dst); 2101f83c7790SDavid S. Miller return ERR_PTR(err); 21021da177e4SLinus Torvalds } 21031da177e4SLinus Torvalds 21044e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 21051da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 21065578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 21071da177e4SLinus Torvalds 2108d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 21091da177e4SLinus Torvalds 21101da177e4SLinus Torvalds return rt; 21111da177e4SLinus Torvalds } 21121da177e4SLinus Torvalds 2113c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2114c3968a85SDaniel Walter struct rt6_info *rt, 2115b71d1d42SEric Dumazet const struct in6_addr *daddr, 2116c3968a85SDaniel Walter unsigned int prefs, 2117c3968a85SDaniel Walter struct in6_addr *saddr) 2118c3968a85SDaniel Walter { 2119c3968a85SDaniel Walter struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt); 2120c3968a85SDaniel Walter int err = 0; 2121c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) 21224e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2123c3968a85SDaniel Walter else 2124c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2125c3968a85SDaniel Walter daddr, prefs, saddr); 2126c3968a85SDaniel Walter return err; 2127c3968a85SDaniel Walter } 2128c3968a85SDaniel Walter 2129c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2130c3968a85SDaniel Walter struct arg_dev_net_ip { 2131c3968a85SDaniel Walter struct net_device *dev; 2132c3968a85SDaniel Walter struct net *net; 2133c3968a85SDaniel Walter struct in6_addr *addr; 2134c3968a85SDaniel Walter }; 2135c3968a85SDaniel Walter 2136c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2137c3968a85SDaniel Walter { 2138c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2139c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2140c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2141c3968a85SDaniel Walter 2142d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2143c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2144c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2145c3968a85SDaniel Walter /* remove prefsrc entry */ 2146c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2147c3968a85SDaniel Walter } 2148c3968a85SDaniel Walter return 0; 2149c3968a85SDaniel Walter } 2150c3968a85SDaniel Walter 2151c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2152c3968a85SDaniel Walter { 2153c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2154c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2155c3968a85SDaniel Walter .dev = ifp->idev->dev, 2156c3968a85SDaniel Walter .net = net, 2157c3968a85SDaniel Walter .addr = &ifp->addr, 2158c3968a85SDaniel Walter }; 2159c3968a85SDaniel Walter fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni); 2160c3968a85SDaniel Walter } 2161c3968a85SDaniel Walter 21628ed67789SDaniel Lezcano struct arg_dev_net { 21638ed67789SDaniel Lezcano struct net_device *dev; 21648ed67789SDaniel Lezcano struct net *net; 21658ed67789SDaniel Lezcano }; 21668ed67789SDaniel Lezcano 21671da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 21681da177e4SLinus Torvalds { 2169bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2170bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 21718ed67789SDaniel Lezcano 2172d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2173c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 21741da177e4SLinus Torvalds return -1; 2175c159d30cSDavid S. Miller 21761da177e4SLinus Torvalds return 0; 21771da177e4SLinus Torvalds } 21781da177e4SLinus Torvalds 2179f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 21801da177e4SLinus Torvalds { 21818ed67789SDaniel Lezcano struct arg_dev_net adn = { 21828ed67789SDaniel Lezcano .dev = dev, 21838ed67789SDaniel Lezcano .net = net, 21848ed67789SDaniel Lezcano }; 21858ed67789SDaniel Lezcano 21868ed67789SDaniel Lezcano fib6_clean_all(net, fib6_ifdown, 0, &adn); 21871e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 21881da177e4SLinus Torvalds } 21891da177e4SLinus Torvalds 219095c96174SEric Dumazet struct rt6_mtu_change_arg { 21911da177e4SLinus Torvalds struct net_device *dev; 219295c96174SEric Dumazet unsigned int mtu; 21931da177e4SLinus Torvalds }; 21941da177e4SLinus Torvalds 21951da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 21961da177e4SLinus Torvalds { 21971da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 21981da177e4SLinus Torvalds struct inet6_dev *idev; 21991da177e4SLinus Torvalds 22001da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 22011da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 22021da177e4SLinus Torvalds We still use this lock to block changes 22031da177e4SLinus Torvalds caused by addrconf/ndisc. 22041da177e4SLinus Torvalds */ 22051da177e4SLinus Torvalds 22061da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 220738308473SDavid S. Miller if (!idev) 22081da177e4SLinus Torvalds return 0; 22091da177e4SLinus Torvalds 22101da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 22111da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 22121da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 22131da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 22141da177e4SLinus Torvalds */ 22151da177e4SLinus Torvalds /* 22161da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 22171da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 22181da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 22191da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 22201da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 22211da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 22221da177e4SLinus Torvalds PMTU discouvery. 22231da177e4SLinus Torvalds */ 2224d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 2225d8d1f30bSChangli Gao !dst_metric_locked(&rt->dst, RTAX_MTU) && 2226d8d1f30bSChangli Gao (dst_mtu(&rt->dst) >= arg->mtu || 2227d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 2228d8d1f30bSChangli Gao dst_mtu(&rt->dst) == idev->cnf.mtu6))) { 2229defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2230566cfd8fSSimon Arlott } 22311da177e4SLinus Torvalds return 0; 22321da177e4SLinus Torvalds } 22331da177e4SLinus Torvalds 223495c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 22351da177e4SLinus Torvalds { 2236c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2237c71099acSThomas Graf .dev = dev, 2238c71099acSThomas Graf .mtu = mtu, 2239c71099acSThomas Graf }; 22401da177e4SLinus Torvalds 2241c346dca1SYOSHIFUJI Hideaki fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg); 22421da177e4SLinus Torvalds } 22431da177e4SLinus Torvalds 2244ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 22455176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 224686872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2247ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 224886872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 224986872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 225086872cb5SThomas Graf }; 225186872cb5SThomas Graf 225286872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 225386872cb5SThomas Graf struct fib6_config *cfg) 22541da177e4SLinus Torvalds { 225586872cb5SThomas Graf struct rtmsg *rtm; 225686872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 225786872cb5SThomas Graf int err; 22581da177e4SLinus Torvalds 225986872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 226086872cb5SThomas Graf if (err < 0) 226186872cb5SThomas Graf goto errout; 22621da177e4SLinus Torvalds 226386872cb5SThomas Graf err = -EINVAL; 226486872cb5SThomas Graf rtm = nlmsg_data(nlh); 226586872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 226686872cb5SThomas Graf 226786872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 226886872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 226986872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 227086872cb5SThomas Graf cfg->fc_flags = RTF_UP; 227186872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2272ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 227386872cb5SThomas Graf 2274ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2275ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2276b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2277b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 227886872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 227986872cb5SThomas Graf 2280ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2281ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2282ab79ad14SMaciej Żenczykowski 228315e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 228486872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 22853b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 228686872cb5SThomas Graf 228786872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 228886872cb5SThomas Graf nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16); 228986872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 22901da177e4SLinus Torvalds } 229186872cb5SThomas Graf 229286872cb5SThomas Graf if (tb[RTA_DST]) { 229386872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 229486872cb5SThomas Graf 229586872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 229686872cb5SThomas Graf goto errout; 229786872cb5SThomas Graf 229886872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 22991da177e4SLinus Torvalds } 230086872cb5SThomas Graf 230186872cb5SThomas Graf if (tb[RTA_SRC]) { 230286872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 230386872cb5SThomas Graf 230486872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 230586872cb5SThomas Graf goto errout; 230686872cb5SThomas Graf 230786872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 23081da177e4SLinus Torvalds } 230986872cb5SThomas Graf 2310c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 2311c3968a85SDaniel Walter nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16); 2312c3968a85SDaniel Walter 231386872cb5SThomas Graf if (tb[RTA_OIF]) 231486872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 231586872cb5SThomas Graf 231686872cb5SThomas Graf if (tb[RTA_PRIORITY]) 231786872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 231886872cb5SThomas Graf 231986872cb5SThomas Graf if (tb[RTA_METRICS]) { 232086872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 232186872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 23221da177e4SLinus Torvalds } 232386872cb5SThomas Graf 232486872cb5SThomas Graf if (tb[RTA_TABLE]) 232586872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 232686872cb5SThomas Graf 232786872cb5SThomas Graf err = 0; 232886872cb5SThomas Graf errout: 232986872cb5SThomas Graf return err; 23301da177e4SLinus Torvalds } 23311da177e4SLinus Torvalds 2332c127ea2cSThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 23331da177e4SLinus Torvalds { 233486872cb5SThomas Graf struct fib6_config cfg; 233586872cb5SThomas Graf int err; 23361da177e4SLinus Torvalds 233786872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 233886872cb5SThomas Graf if (err < 0) 233986872cb5SThomas Graf return err; 234086872cb5SThomas Graf 234186872cb5SThomas Graf return ip6_route_del(&cfg); 23421da177e4SLinus Torvalds } 23431da177e4SLinus Torvalds 2344c127ea2cSThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 23451da177e4SLinus Torvalds { 234686872cb5SThomas Graf struct fib6_config cfg; 234786872cb5SThomas Graf int err; 23481da177e4SLinus Torvalds 234986872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 235086872cb5SThomas Graf if (err < 0) 235186872cb5SThomas Graf return err; 235286872cb5SThomas Graf 235386872cb5SThomas Graf return ip6_route_add(&cfg); 23541da177e4SLinus Torvalds } 23551da177e4SLinus Torvalds 2356339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void) 2357339bf98fSThomas Graf { 2358339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 2359339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 2360339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 2361339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 2362339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 2363339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 2364339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 2365339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 2366339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 23676a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 2368339bf98fSThomas Graf + nla_total_size(sizeof(struct rta_cacheinfo)); 2369339bf98fSThomas Graf } 2370339bf98fSThomas Graf 2371191cd582SBrian Haley static int rt6_fill_node(struct net *net, 2372191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 23730d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 237415e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 23757bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 23761da177e4SLinus Torvalds { 23771da177e4SLinus Torvalds struct rtmsg *rtm; 23781da177e4SLinus Torvalds struct nlmsghdr *nlh; 2379e3703b3dSThomas Graf long expires; 23809e762a4aSPatrick McHardy u32 table; 2381f2c31e32SEric Dumazet struct neighbour *n; 23821da177e4SLinus Torvalds 23831da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 23841da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 23851da177e4SLinus Torvalds /* success since this is not a prefix route */ 23861da177e4SLinus Torvalds return 1; 23871da177e4SLinus Torvalds } 23881da177e4SLinus Torvalds } 23891da177e4SLinus Torvalds 239015e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 239138308473SDavid S. Miller if (!nlh) 239226932566SPatrick McHardy return -EMSGSIZE; 23932d7202bfSThomas Graf 23942d7202bfSThomas Graf rtm = nlmsg_data(nlh); 23951da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 23961da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 23971da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 23981da177e4SLinus Torvalds rtm->rtm_tos = 0; 2399c71099acSThomas Graf if (rt->rt6i_table) 24009e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 2401c71099acSThomas Graf else 24029e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 24039e762a4aSPatrick McHardy rtm->rtm_table = table; 2404c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 2405c78679e8SDavid S. Miller goto nla_put_failure; 2406ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 2407ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 2408ef2c7d7bSNicolas Dichtel case -EINVAL: 2409ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 2410ef2c7d7bSNicolas Dichtel break; 2411ef2c7d7bSNicolas Dichtel case -EACCES: 2412ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 2413ef2c7d7bSNicolas Dichtel break; 2414b4949ab2SNicolas Dichtel case -EAGAIN: 2415b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 2416b4949ab2SNicolas Dichtel break; 2417ef2c7d7bSNicolas Dichtel default: 24181da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 2419ef2c7d7bSNicolas Dichtel break; 2420ef2c7d7bSNicolas Dichtel } 2421ef2c7d7bSNicolas Dichtel } 2422ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 2423ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 2424d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 24251da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 24261da177e4SLinus Torvalds else 24271da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 24281da177e4SLinus Torvalds rtm->rtm_flags = 0; 24291da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 24301da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 24311da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 24321da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 2433f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 2434f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 24351da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 2436f0396f60SDenis Ovsienko else 2437f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 2438f0396f60SDenis Ovsienko } 24391da177e4SLinus Torvalds 24401da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 24411da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 24421da177e4SLinus Torvalds 24431da177e4SLinus Torvalds if (dst) { 2444c78679e8SDavid S. Miller if (nla_put(skb, RTA_DST, 16, dst)) 2445c78679e8SDavid S. Miller goto nla_put_failure; 24461da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 24471da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 2448c78679e8SDavid S. Miller if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr)) 2449c78679e8SDavid S. Miller goto nla_put_failure; 24501da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 24511da177e4SLinus Torvalds if (src) { 2452c78679e8SDavid S. Miller if (nla_put(skb, RTA_SRC, 16, src)) 2453c78679e8SDavid S. Miller goto nla_put_failure; 24541da177e4SLinus Torvalds rtm->rtm_src_len = 128; 2455c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 2456c78679e8SDavid S. Miller nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr)) 2457c78679e8SDavid S. Miller goto nla_put_failure; 24581da177e4SLinus Torvalds #endif 24597bc570c8SYOSHIFUJI Hideaki if (iif) { 24607bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 24617bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 24628229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 24637bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 24647bc570c8SYOSHIFUJI Hideaki if (!nowait) { 24657bc570c8SYOSHIFUJI Hideaki if (err == 0) 24667bc570c8SYOSHIFUJI Hideaki return 0; 24677bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 24687bc570c8SYOSHIFUJI Hideaki } else { 24697bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 24707bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 24717bc570c8SYOSHIFUJI Hideaki } 24727bc570c8SYOSHIFUJI Hideaki } 24737bc570c8SYOSHIFUJI Hideaki } else 24747bc570c8SYOSHIFUJI Hideaki #endif 2475c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 2476c78679e8SDavid S. Miller goto nla_put_failure; 24777bc570c8SYOSHIFUJI Hideaki } else if (dst) { 24781da177e4SLinus Torvalds struct in6_addr saddr_buf; 2479c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 2480c78679e8SDavid S. Miller nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) 2481c78679e8SDavid S. Miller goto nla_put_failure; 2482c3968a85SDaniel Walter } 2483c3968a85SDaniel Walter 2484c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 2485c3968a85SDaniel Walter struct in6_addr saddr_buf; 24864e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 2487c78679e8SDavid S. Miller if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) 2488c78679e8SDavid S. Miller goto nla_put_failure; 24891da177e4SLinus Torvalds } 24902d7202bfSThomas Graf 2491defb3519SDavid S. Miller if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) 24922d7202bfSThomas Graf goto nla_put_failure; 24932d7202bfSThomas Graf 249497cac082SDavid S. Miller n = rt->n; 249594f826b8SEric Dumazet if (n) { 2496fdd6681dSAmerigo Wang if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) 249794f826b8SEric Dumazet goto nla_put_failure; 249894f826b8SEric Dumazet } 24992d7202bfSThomas Graf 2500c78679e8SDavid S. Miller if (rt->dst.dev && 2501c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 2502c78679e8SDavid S. Miller goto nla_put_failure; 2503c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 2504c78679e8SDavid S. Miller goto nla_put_failure; 25058253947eSLi Wei 25068253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 250769cdf8f9SYOSHIFUJI Hideaki 250887a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 2509e3703b3dSThomas Graf goto nla_put_failure; 25101da177e4SLinus Torvalds 25112d7202bfSThomas Graf return nlmsg_end(skb, nlh); 25122d7202bfSThomas Graf 25132d7202bfSThomas Graf nla_put_failure: 251426932566SPatrick McHardy nlmsg_cancel(skb, nlh); 251526932566SPatrick McHardy return -EMSGSIZE; 25161da177e4SLinus Torvalds } 25171da177e4SLinus Torvalds 25181b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 25191da177e4SLinus Torvalds { 25201da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 25211da177e4SLinus Torvalds int prefix; 25221da177e4SLinus Torvalds 25232d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 25242d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 25251da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 25261da177e4SLinus Torvalds } else 25271da177e4SLinus Torvalds prefix = 0; 25281da177e4SLinus Torvalds 2529191cd582SBrian Haley return rt6_fill_node(arg->net, 2530191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 253115e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 25327bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 25331da177e4SLinus Torvalds } 25341da177e4SLinus Torvalds 2535c127ea2cSThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) 25361da177e4SLinus Torvalds { 25373b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 2538ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 25391da177e4SLinus Torvalds struct rt6_info *rt; 2540ab364a6fSThomas Graf struct sk_buff *skb; 2541ab364a6fSThomas Graf struct rtmsg *rtm; 25424c9483b2SDavid S. Miller struct flowi6 fl6; 254372331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 2544ab364a6fSThomas Graf 2545ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 2546ab364a6fSThomas Graf if (err < 0) 2547ab364a6fSThomas Graf goto errout; 2548ab364a6fSThomas Graf 2549ab364a6fSThomas Graf err = -EINVAL; 25504c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2551ab364a6fSThomas Graf 2552ab364a6fSThomas Graf if (tb[RTA_SRC]) { 2553ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 2554ab364a6fSThomas Graf goto errout; 2555ab364a6fSThomas Graf 25564e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 2557ab364a6fSThomas Graf } 2558ab364a6fSThomas Graf 2559ab364a6fSThomas Graf if (tb[RTA_DST]) { 2560ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 2561ab364a6fSThomas Graf goto errout; 2562ab364a6fSThomas Graf 25634e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 2564ab364a6fSThomas Graf } 2565ab364a6fSThomas Graf 2566ab364a6fSThomas Graf if (tb[RTA_IIF]) 2567ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 2568ab364a6fSThomas Graf 2569ab364a6fSThomas Graf if (tb[RTA_OIF]) 257072331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 2571ab364a6fSThomas Graf 2572ab364a6fSThomas Graf if (iif) { 2573ab364a6fSThomas Graf struct net_device *dev; 257472331bc0SShmulik Ladkani int flags = 0; 257572331bc0SShmulik Ladkani 25765578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 2577ab364a6fSThomas Graf if (!dev) { 2578ab364a6fSThomas Graf err = -ENODEV; 2579ab364a6fSThomas Graf goto errout; 2580ab364a6fSThomas Graf } 258172331bc0SShmulik Ladkani 258272331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 258372331bc0SShmulik Ladkani 258472331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 258572331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 258672331bc0SShmulik Ladkani 258772331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 258872331bc0SShmulik Ladkani flags); 258972331bc0SShmulik Ladkani } else { 259072331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 259172331bc0SShmulik Ladkani 259272331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 2593ab364a6fSThomas Graf } 25941da177e4SLinus Torvalds 25951da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 259638308473SDavid S. Miller if (!skb) { 25972173bff5SShmulik Ladkani dst_release(&rt->dst); 2598ab364a6fSThomas Graf err = -ENOBUFS; 2599ab364a6fSThomas Graf goto errout; 2600ab364a6fSThomas Graf } 26011da177e4SLinus Torvalds 26021da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 26031da177e4SLinus Torvalds through good chunk of routing engine. 26041da177e4SLinus Torvalds */ 2605459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 26061da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 26071da177e4SLinus Torvalds 2608d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 26091da177e4SLinus Torvalds 26104c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 261115e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 26127bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 26131da177e4SLinus Torvalds if (err < 0) { 2614ab364a6fSThomas Graf kfree_skb(skb); 2615ab364a6fSThomas Graf goto errout; 26161da177e4SLinus Torvalds } 26171da177e4SLinus Torvalds 261815e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 2619ab364a6fSThomas Graf errout: 26201da177e4SLinus Torvalds return err; 26211da177e4SLinus Torvalds } 26221da177e4SLinus Torvalds 262386872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) 26241da177e4SLinus Torvalds { 26251da177e4SLinus Torvalds struct sk_buff *skb; 26265578689aSDaniel Lezcano struct net *net = info->nl_net; 2627528c4cebSDenis V. Lunev u32 seq; 2628528c4cebSDenis V. Lunev int err; 26290d51aa80SJamal Hadi Salim 2630528c4cebSDenis V. Lunev err = -ENOBUFS; 263138308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 263286872cb5SThomas Graf 2633339bf98fSThomas Graf skb = nlmsg_new(rt6_nlmsg_size(), gfp_any()); 263438308473SDavid S. Miller if (!skb) 263521713ebcSThomas Graf goto errout; 26361da177e4SLinus Torvalds 2637191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 263815e47304SEric W. Biederman event, info->portid, seq, 0, 0, 0); 263926932566SPatrick McHardy if (err < 0) { 264026932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 264126932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 264226932566SPatrick McHardy kfree_skb(skb); 264326932566SPatrick McHardy goto errout; 264426932566SPatrick McHardy } 264515e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 26465578689aSDaniel Lezcano info->nlh, gfp_any()); 26471ce85fe4SPablo Neira Ayuso return; 264821713ebcSThomas Graf errout: 264921713ebcSThomas Graf if (err < 0) 26505578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 26511da177e4SLinus Torvalds } 26521da177e4SLinus Torvalds 26538ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 26548ed67789SDaniel Lezcano unsigned long event, void *data) 26558ed67789SDaniel Lezcano { 26568ed67789SDaniel Lezcano struct net_device *dev = (struct net_device *)data; 2657c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 26588ed67789SDaniel Lezcano 26598ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 2660d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 26618ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 26628ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 2663d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 26648ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 2665d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 26668ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 26678ed67789SDaniel Lezcano #endif 26688ed67789SDaniel Lezcano } 26698ed67789SDaniel Lezcano 26708ed67789SDaniel Lezcano return NOTIFY_OK; 26718ed67789SDaniel Lezcano } 26728ed67789SDaniel Lezcano 26731da177e4SLinus Torvalds /* 26741da177e4SLinus Torvalds * /proc 26751da177e4SLinus Torvalds */ 26761da177e4SLinus Torvalds 26771da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 26781da177e4SLinus Torvalds 26791da177e4SLinus Torvalds struct rt6_proc_arg 26801da177e4SLinus Torvalds { 26811da177e4SLinus Torvalds char *buffer; 26821da177e4SLinus Torvalds int offset; 26831da177e4SLinus Torvalds int length; 26841da177e4SLinus Torvalds int skip; 26851da177e4SLinus Torvalds int len; 26861da177e4SLinus Torvalds }; 26871da177e4SLinus Torvalds 26881da177e4SLinus Torvalds static int rt6_info_route(struct rt6_info *rt, void *p_arg) 26891da177e4SLinus Torvalds { 269033120b30SAlexey Dobriyan struct seq_file *m = p_arg; 269169cce1d1SDavid S. Miller struct neighbour *n; 26921da177e4SLinus Torvalds 26934b7a4274SHarvey Harrison seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen); 26941da177e4SLinus Torvalds 26951da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 26964b7a4274SHarvey Harrison seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen); 26971da177e4SLinus Torvalds #else 269833120b30SAlexey Dobriyan seq_puts(m, "00000000000000000000000000000000 00 "); 26991da177e4SLinus Torvalds #endif 270097cac082SDavid S. Miller n = rt->n; 270169cce1d1SDavid S. Miller if (n) { 270269cce1d1SDavid S. Miller seq_printf(m, "%pi6", n->primary_key); 27031da177e4SLinus Torvalds } else { 270433120b30SAlexey Dobriyan seq_puts(m, "00000000000000000000000000000000"); 27051da177e4SLinus Torvalds } 270633120b30SAlexey Dobriyan seq_printf(m, " %08x %08x %08x %08x %8s\n", 2707d8d1f30bSChangli Gao rt->rt6i_metric, atomic_read(&rt->dst.__refcnt), 2708d8d1f30bSChangli Gao rt->dst.__use, rt->rt6i_flags, 2709d1918542SDavid S. Miller rt->dst.dev ? rt->dst.dev->name : ""); 27101da177e4SLinus Torvalds return 0; 27111da177e4SLinus Torvalds } 27121da177e4SLinus Torvalds 271333120b30SAlexey Dobriyan static int ipv6_route_show(struct seq_file *m, void *v) 27141da177e4SLinus Torvalds { 2715f3db4851SDaniel Lezcano struct net *net = (struct net *)m->private; 271632b293a5SJosh Hunt fib6_clean_all_ro(net, rt6_info_route, 0, m); 271733120b30SAlexey Dobriyan return 0; 27181da177e4SLinus Torvalds } 27191da177e4SLinus Torvalds 272033120b30SAlexey Dobriyan static int ipv6_route_open(struct inode *inode, struct file *file) 272133120b30SAlexey Dobriyan { 2722de05c557SPavel Emelyanov return single_open_net(inode, file, ipv6_route_show); 2723f3db4851SDaniel Lezcano } 2724f3db4851SDaniel Lezcano 272533120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 272633120b30SAlexey Dobriyan .owner = THIS_MODULE, 272733120b30SAlexey Dobriyan .open = ipv6_route_open, 272833120b30SAlexey Dobriyan .read = seq_read, 272933120b30SAlexey Dobriyan .llseek = seq_lseek, 2730b6fcbdb4SPavel Emelyanov .release = single_release_net, 273133120b30SAlexey Dobriyan }; 273233120b30SAlexey Dobriyan 27331da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 27341da177e4SLinus Torvalds { 273569ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 27361da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 273769ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 273869ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 273969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 274069ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 274169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 2742fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 274369ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 27441da177e4SLinus Torvalds 27451da177e4SLinus Torvalds return 0; 27461da177e4SLinus Torvalds } 27471da177e4SLinus Torvalds 27481da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 27491da177e4SLinus Torvalds { 2750de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 275169ddb805SDaniel Lezcano } 275269ddb805SDaniel Lezcano 27539a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 27541da177e4SLinus Torvalds .owner = THIS_MODULE, 27551da177e4SLinus Torvalds .open = rt6_stats_seq_open, 27561da177e4SLinus Torvalds .read = seq_read, 27571da177e4SLinus Torvalds .llseek = seq_lseek, 2758b6fcbdb4SPavel Emelyanov .release = single_release_net, 27591da177e4SLinus Torvalds }; 27601da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 27611da177e4SLinus Torvalds 27621da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 27631da177e4SLinus Torvalds 27641da177e4SLinus Torvalds static 27658d65af78SAlexey Dobriyan int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, 27661da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 27671da177e4SLinus Torvalds { 2768c486da34SLucian Adrian Grijincu struct net *net; 2769c486da34SLucian Adrian Grijincu int delay; 2770c486da34SLucian Adrian Grijincu if (!write) 2771c486da34SLucian Adrian Grijincu return -EINVAL; 2772c486da34SLucian Adrian Grijincu 2773c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 2774c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 27758d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 27765b7c931dSDaniel Lezcano fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net); 27771da177e4SLinus Torvalds return 0; 27781da177e4SLinus Torvalds } 27791da177e4SLinus Torvalds 2780760f2d01SDaniel Lezcano ctl_table ipv6_route_table_template[] = { 27811da177e4SLinus Torvalds { 27821da177e4SLinus Torvalds .procname = "flush", 27834990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 27841da177e4SLinus Torvalds .maxlen = sizeof(int), 278589c8b3a1SDave Jones .mode = 0200, 27866d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 27871da177e4SLinus Torvalds }, 27881da177e4SLinus Torvalds { 27891da177e4SLinus Torvalds .procname = "gc_thresh", 27909a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 27911da177e4SLinus Torvalds .maxlen = sizeof(int), 27921da177e4SLinus Torvalds .mode = 0644, 27936d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 27941da177e4SLinus Torvalds }, 27951da177e4SLinus Torvalds { 27961da177e4SLinus Torvalds .procname = "max_size", 27974990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 27981da177e4SLinus Torvalds .maxlen = sizeof(int), 27991da177e4SLinus Torvalds .mode = 0644, 28006d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 28011da177e4SLinus Torvalds }, 28021da177e4SLinus Torvalds { 28031da177e4SLinus Torvalds .procname = "gc_min_interval", 28044990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 28051da177e4SLinus Torvalds .maxlen = sizeof(int), 28061da177e4SLinus Torvalds .mode = 0644, 28076d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28081da177e4SLinus Torvalds }, 28091da177e4SLinus Torvalds { 28101da177e4SLinus Torvalds .procname = "gc_timeout", 28114990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 28121da177e4SLinus Torvalds .maxlen = sizeof(int), 28131da177e4SLinus Torvalds .mode = 0644, 28146d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28151da177e4SLinus Torvalds }, 28161da177e4SLinus Torvalds { 28171da177e4SLinus Torvalds .procname = "gc_interval", 28184990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 28191da177e4SLinus Torvalds .maxlen = sizeof(int), 28201da177e4SLinus Torvalds .mode = 0644, 28216d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28221da177e4SLinus Torvalds }, 28231da177e4SLinus Torvalds { 28241da177e4SLinus Torvalds .procname = "gc_elasticity", 28254990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 28261da177e4SLinus Torvalds .maxlen = sizeof(int), 28271da177e4SLinus Torvalds .mode = 0644, 2828f3d3f616SMin Zhang .proc_handler = proc_dointvec, 28291da177e4SLinus Torvalds }, 28301da177e4SLinus Torvalds { 28311da177e4SLinus Torvalds .procname = "mtu_expires", 28324990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 28331da177e4SLinus Torvalds .maxlen = sizeof(int), 28341da177e4SLinus Torvalds .mode = 0644, 28356d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28361da177e4SLinus Torvalds }, 28371da177e4SLinus Torvalds { 28381da177e4SLinus Torvalds .procname = "min_adv_mss", 28394990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 28401da177e4SLinus Torvalds .maxlen = sizeof(int), 28411da177e4SLinus Torvalds .mode = 0644, 2842f3d3f616SMin Zhang .proc_handler = proc_dointvec, 28431da177e4SLinus Torvalds }, 28441da177e4SLinus Torvalds { 28451da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 28464990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 28471da177e4SLinus Torvalds .maxlen = sizeof(int), 28481da177e4SLinus Torvalds .mode = 0644, 28496d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 28501da177e4SLinus Torvalds }, 2851f8572d8fSEric W. Biederman { } 28521da177e4SLinus Torvalds }; 28531da177e4SLinus Torvalds 28542c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 2855760f2d01SDaniel Lezcano { 2856760f2d01SDaniel Lezcano struct ctl_table *table; 2857760f2d01SDaniel Lezcano 2858760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 2859760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 2860760f2d01SDaniel Lezcano GFP_KERNEL); 28615ee09105SYOSHIFUJI Hideaki 28625ee09105SYOSHIFUJI Hideaki if (table) { 28635ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 2864c486da34SLucian Adrian Grijincu table[0].extra1 = net; 286586393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 28665ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 28675ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 28685ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 28695ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 28705ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 28715ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 28725ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 28739c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 28745ee09105SYOSHIFUJI Hideaki } 28755ee09105SYOSHIFUJI Hideaki 2876760f2d01SDaniel Lezcano return table; 2877760f2d01SDaniel Lezcano } 28781da177e4SLinus Torvalds #endif 28791da177e4SLinus Torvalds 28802c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 2881cdb18761SDaniel Lezcano { 2882633d424bSPavel Emelyanov int ret = -ENOMEM; 28838ed67789SDaniel Lezcano 288486393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 288586393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 2886f2fc6a54SBenjamin Thery 2887fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 2888fc66f95cSEric Dumazet goto out_ip6_dst_ops; 2889fc66f95cSEric Dumazet 28908ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 28918ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 28928ed67789SDaniel Lezcano GFP_KERNEL); 28938ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 2894fc66f95cSEric Dumazet goto out_ip6_dst_entries; 2895d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 28968ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 2897d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 289862fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 289962fa8a84SDavid S. Miller ip6_template_metrics, true); 29008ed67789SDaniel Lezcano 29018ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 29028ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 29038ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 29048ed67789SDaniel Lezcano GFP_KERNEL); 290568fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 290668fffc67SPeter Zijlstra goto out_ip6_null_entry; 2907d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 29088ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 2909d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 291062fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 291162fa8a84SDavid S. Miller ip6_template_metrics, true); 29128ed67789SDaniel Lezcano 29138ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 29148ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 29158ed67789SDaniel Lezcano GFP_KERNEL); 291668fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 291768fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 2918d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 29198ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 2920d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 292162fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 292262fa8a84SDavid S. Miller ip6_template_metrics, true); 29238ed67789SDaniel Lezcano #endif 29248ed67789SDaniel Lezcano 2925b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 2926b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 2927b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 2928b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 2929b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 2930b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 2931b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 2932b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 2933b339a47cSPeter Zijlstra 29346891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 29356891a346SBenjamin Thery 29368ed67789SDaniel Lezcano ret = 0; 29378ed67789SDaniel Lezcano out: 29388ed67789SDaniel Lezcano return ret; 2939f2fc6a54SBenjamin Thery 294068fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 294168fffc67SPeter Zijlstra out_ip6_prohibit_entry: 294268fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 294368fffc67SPeter Zijlstra out_ip6_null_entry: 294468fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 294568fffc67SPeter Zijlstra #endif 2946fc66f95cSEric Dumazet out_ip6_dst_entries: 2947fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 2948f2fc6a54SBenjamin Thery out_ip6_dst_ops: 2949f2fc6a54SBenjamin Thery goto out; 2950cdb18761SDaniel Lezcano } 2951cdb18761SDaniel Lezcano 29522c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 2953cdb18761SDaniel Lezcano { 29548ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 29558ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 29568ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 29578ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 29588ed67789SDaniel Lezcano #endif 295941bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 2960cdb18761SDaniel Lezcano } 2961cdb18761SDaniel Lezcano 2962d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 2963d189634eSThomas Graf { 2964d189634eSThomas Graf #ifdef CONFIG_PROC_FS 2965d189634eSThomas Graf proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops); 2966d189634eSThomas Graf proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops); 2967d189634eSThomas Graf #endif 2968d189634eSThomas Graf return 0; 2969d189634eSThomas Graf } 2970d189634eSThomas Graf 2971d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 2972d189634eSThomas Graf { 2973d189634eSThomas Graf #ifdef CONFIG_PROC_FS 2974d189634eSThomas Graf proc_net_remove(net, "ipv6_route"); 2975d189634eSThomas Graf proc_net_remove(net, "rt6_stats"); 2976d189634eSThomas Graf #endif 2977d189634eSThomas Graf } 2978d189634eSThomas Graf 2979cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 2980cdb18761SDaniel Lezcano .init = ip6_route_net_init, 2981cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 2982cdb18761SDaniel Lezcano }; 2983cdb18761SDaniel Lezcano 2984c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 2985c3426b47SDavid S. Miller { 2986c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 2987c3426b47SDavid S. Miller 2988c3426b47SDavid S. Miller if (!bp) 2989c3426b47SDavid S. Miller return -ENOMEM; 2990c3426b47SDavid S. Miller inet_peer_base_init(bp); 2991c3426b47SDavid S. Miller net->ipv6.peers = bp; 2992c3426b47SDavid S. Miller return 0; 2993c3426b47SDavid S. Miller } 2994c3426b47SDavid S. Miller 2995c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 2996c3426b47SDavid S. Miller { 2997c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 2998c3426b47SDavid S. Miller 2999c3426b47SDavid S. Miller net->ipv6.peers = NULL; 300056a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3001c3426b47SDavid S. Miller kfree(bp); 3002c3426b47SDavid S. Miller } 3003c3426b47SDavid S. Miller 30042b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3005c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3006c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3007c3426b47SDavid S. Miller }; 3008c3426b47SDavid S. Miller 3009d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3010d189634eSThomas Graf .init = ip6_route_net_init_late, 3011d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3012d189634eSThomas Graf }; 3013d189634eSThomas Graf 30148ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 30158ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 30168ed67789SDaniel Lezcano .priority = 0, 30178ed67789SDaniel Lezcano }; 30188ed67789SDaniel Lezcano 3019433d49c3SDaniel Lezcano int __init ip6_route_init(void) 30201da177e4SLinus Torvalds { 3021433d49c3SDaniel Lezcano int ret; 3022433d49c3SDaniel Lezcano 30239a7ec3a9SDaniel Lezcano ret = -ENOMEM; 30249a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 30259a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 30269a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 30279a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3028c19a28e1SFernando Carrijo goto out; 302914e50e57SDavid S. Miller 3030fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 30318ed67789SDaniel Lezcano if (ret) 3032bdb3289fSDaniel Lezcano goto out_kmem_cache; 3033bdb3289fSDaniel Lezcano 3034c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3035c3426b47SDavid S. Miller if (ret) 3036e8803b6cSDavid S. Miller goto out_dst_entries; 30372a0c451aSThomas Graf 30387e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 30397e52b33bSDavid S. Miller if (ret) 30407e52b33bSDavid S. Miller goto out_register_inetpeer; 3041c3426b47SDavid S. Miller 30425dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 30435dc121e9SArnaud Ebalard 30448ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 30458ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 30468ed67789SDaniel Lezcano * manually for init_net */ 3047d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 30488ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3049bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3050d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 30518ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3052d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 30538ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3054bdb3289fSDaniel Lezcano #endif 3055e8803b6cSDavid S. Miller ret = fib6_init(); 3056433d49c3SDaniel Lezcano if (ret) 30578ed67789SDaniel Lezcano goto out_register_subsys; 3058433d49c3SDaniel Lezcano 3059433d49c3SDaniel Lezcano ret = xfrm6_init(); 3060433d49c3SDaniel Lezcano if (ret) 3061e8803b6cSDavid S. Miller goto out_fib6_init; 3062c35b7e72SDaniel Lezcano 3063433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3064433d49c3SDaniel Lezcano if (ret) 3065433d49c3SDaniel Lezcano goto xfrm6_init; 30667e5449c2SDaniel Lezcano 3067d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3068d189634eSThomas Graf if (ret) 3069d189634eSThomas Graf goto fib6_rules_init; 3070d189634eSThomas Graf 3071433d49c3SDaniel Lezcano ret = -ENOBUFS; 3072c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3073c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3074c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3075d189634eSThomas Graf goto out_register_late_subsys; 3076433d49c3SDaniel Lezcano 30778ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3078cdb18761SDaniel Lezcano if (ret) 3079d189634eSThomas Graf goto out_register_late_subsys; 30808ed67789SDaniel Lezcano 3081433d49c3SDaniel Lezcano out: 3082433d49c3SDaniel Lezcano return ret; 3083433d49c3SDaniel Lezcano 3084d189634eSThomas Graf out_register_late_subsys: 3085d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3086433d49c3SDaniel Lezcano fib6_rules_init: 3087433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3088433d49c3SDaniel Lezcano xfrm6_init: 3089433d49c3SDaniel Lezcano xfrm6_fini(); 30902a0c451aSThomas Graf out_fib6_init: 30912a0c451aSThomas Graf fib6_gc_cleanup(); 30928ed67789SDaniel Lezcano out_register_subsys: 30938ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 30947e52b33bSDavid S. Miller out_register_inetpeer: 30957e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3096fc66f95cSEric Dumazet out_dst_entries: 3097fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3098433d49c3SDaniel Lezcano out_kmem_cache: 3099f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3100433d49c3SDaniel Lezcano goto out; 31011da177e4SLinus Torvalds } 31021da177e4SLinus Torvalds 31031da177e4SLinus Torvalds void ip6_route_cleanup(void) 31041da177e4SLinus Torvalds { 31058ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3106d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3107101367c2SThomas Graf fib6_rules_cleanup(); 31081da177e4SLinus Torvalds xfrm6_fini(); 31091da177e4SLinus Torvalds fib6_gc_cleanup(); 3110c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 31118ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 311241bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3113f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 31141da177e4SLinus Torvalds } 3115