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 if (rt->rt6i_flags & RTF_EXPIRES) { 3731716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 374a50feda5SEric Dumazet return true; 3751716a961SGao feng } else if (rt->dst.from) { 376*3fd91fb3SLi RongQing return rt6_check_expired((struct rt6_info *) rt->dst.from); 3771716a961SGao feng } 378a50feda5SEric Dumazet return false; 3791da177e4SLinus Torvalds } 3801da177e4SLinus Torvalds 381a50feda5SEric Dumazet static bool rt6_need_strict(const struct in6_addr *daddr) 382c71099acSThomas Graf { 383a02cec21SEric Dumazet return ipv6_addr_type(daddr) & 384a02cec21SEric Dumazet (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); 385c71099acSThomas Graf } 386c71099acSThomas Graf 3871da177e4SLinus Torvalds /* 388c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 3891da177e4SLinus Torvalds */ 3901da177e4SLinus Torvalds 3918ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 3928ed67789SDaniel Lezcano struct rt6_info *rt, 393b71d1d42SEric Dumazet const struct in6_addr *saddr, 3941da177e4SLinus Torvalds int oif, 395d420895eSYOSHIFUJI Hideaki int flags) 3961da177e4SLinus Torvalds { 3971da177e4SLinus Torvalds struct rt6_info *local = NULL; 3981da177e4SLinus Torvalds struct rt6_info *sprt; 3991da177e4SLinus Torvalds 400dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 401dd3abc4eSYOSHIFUJI Hideaki goto out; 402dd3abc4eSYOSHIFUJI Hideaki 403d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 404d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 405dd3abc4eSYOSHIFUJI Hideaki 406dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4071da177e4SLinus Torvalds if (dev->ifindex == oif) 4081da177e4SLinus Torvalds return sprt; 4091da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 41038308473SDavid S. Miller if (!sprt->rt6i_idev || 4111da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 412d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE && oif) 4131da177e4SLinus Torvalds continue; 4141da177e4SLinus Torvalds if (local && (!oif || 4151da177e4SLinus Torvalds local->rt6i_idev->dev->ifindex == oif)) 4161da177e4SLinus Torvalds continue; 4171da177e4SLinus Torvalds } 4181da177e4SLinus Torvalds local = sprt; 4191da177e4SLinus Torvalds } 420dd3abc4eSYOSHIFUJI Hideaki } else { 421dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 422dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 423dd3abc4eSYOSHIFUJI Hideaki return sprt; 424dd3abc4eSYOSHIFUJI Hideaki } 4251da177e4SLinus Torvalds } 4261da177e4SLinus Torvalds 427dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4281da177e4SLinus Torvalds if (local) 4291da177e4SLinus Torvalds return local; 4301da177e4SLinus Torvalds 431d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 4328ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 4331da177e4SLinus Torvalds } 434dd3abc4eSYOSHIFUJI Hideaki out: 4351da177e4SLinus Torvalds return rt; 4361da177e4SLinus Torvalds } 4371da177e4SLinus Torvalds 43827097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 43927097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 44027097255SYOSHIFUJI Hideaki { 441f2c31e32SEric Dumazet struct neighbour *neigh; 44227097255SYOSHIFUJI Hideaki /* 44327097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 44427097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 44527097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 44627097255SYOSHIFUJI Hideaki * 44727097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 44827097255SYOSHIFUJI Hideaki * to no more than one per minute. 44927097255SYOSHIFUJI Hideaki */ 45097cac082SDavid S. Miller neigh = rt ? rt->n : NULL; 45127097255SYOSHIFUJI Hideaki if (!neigh || (neigh->nud_state & NUD_VALID)) 452fdd6681dSAmerigo Wang return; 45327097255SYOSHIFUJI Hideaki read_lock_bh(&neigh->lock); 45427097255SYOSHIFUJI Hideaki if (!(neigh->nud_state & NUD_VALID) && 45552e16356SYOSHIFUJI Hideaki time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { 45627097255SYOSHIFUJI Hideaki struct in6_addr mcaddr; 45727097255SYOSHIFUJI Hideaki struct in6_addr *target; 45827097255SYOSHIFUJI Hideaki 45927097255SYOSHIFUJI Hideaki neigh->updated = jiffies; 46027097255SYOSHIFUJI Hideaki read_unlock_bh(&neigh->lock); 46127097255SYOSHIFUJI Hideaki 46227097255SYOSHIFUJI Hideaki target = (struct in6_addr *)&neigh->primary_key; 46327097255SYOSHIFUJI Hideaki addrconf_addr_solict_mult(target, &mcaddr); 464d1918542SDavid S. Miller ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL); 465f2c31e32SEric Dumazet } else { 46627097255SYOSHIFUJI Hideaki read_unlock_bh(&neigh->lock); 46727097255SYOSHIFUJI Hideaki } 468f2c31e32SEric Dumazet } 46927097255SYOSHIFUJI Hideaki #else 47027097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 47127097255SYOSHIFUJI Hideaki { 47227097255SYOSHIFUJI Hideaki } 47327097255SYOSHIFUJI Hideaki #endif 47427097255SYOSHIFUJI Hideaki 4751da177e4SLinus Torvalds /* 476554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 4771da177e4SLinus Torvalds */ 478b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 4791da177e4SLinus Torvalds { 480d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 481161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 482554cfb7eSYOSHIFUJI Hideaki return 2; 483161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 484161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 485161980f4SDavid S. Miller return 1; 486554cfb7eSYOSHIFUJI Hideaki return 0; 4871da177e4SLinus Torvalds } 4881da177e4SLinus Torvalds 489b6f99a21SDave Jones static inline int rt6_check_neigh(struct rt6_info *rt) 4901da177e4SLinus Torvalds { 491f2c31e32SEric Dumazet struct neighbour *neigh; 492398bcbebSYOSHIFUJI Hideaki int m; 493f2c31e32SEric Dumazet 49497cac082SDavid S. Miller neigh = rt->n; 4954d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 4964d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 4974d0c5911SYOSHIFUJI Hideaki m = 1; 4984d0c5911SYOSHIFUJI Hideaki else if (neigh) { 4991da177e4SLinus Torvalds read_lock_bh(&neigh->lock); 500554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 5014d0c5911SYOSHIFUJI Hideaki m = 2; 502398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 503398bcbebSYOSHIFUJI Hideaki else if (neigh->nud_state & NUD_FAILED) 504398bcbebSYOSHIFUJI Hideaki m = 0; 505398bcbebSYOSHIFUJI Hideaki #endif 506398bcbebSYOSHIFUJI Hideaki else 507ea73ee23SYOSHIFUJI Hideaki m = 1; 5081da177e4SLinus Torvalds read_unlock_bh(&neigh->lock); 509398bcbebSYOSHIFUJI Hideaki } else 510398bcbebSYOSHIFUJI Hideaki m = 0; 511554cfb7eSYOSHIFUJI Hideaki return m; 5121da177e4SLinus Torvalds } 5131da177e4SLinus Torvalds 514554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 515554cfb7eSYOSHIFUJI Hideaki int strict) 516554cfb7eSYOSHIFUJI Hideaki { 5174d0c5911SYOSHIFUJI Hideaki int m, n; 5184d0c5911SYOSHIFUJI Hideaki 5194d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 52077d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 521554cfb7eSYOSHIFUJI Hideaki return -1; 522ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 523ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 524ebacaaa0SYOSHIFUJI Hideaki #endif 5254d0c5911SYOSHIFUJI Hideaki n = rt6_check_neigh(rt); 526557e92efSYOSHIFUJI Hideaki if (!n && (strict & RT6_LOOKUP_F_REACHABLE)) 527554cfb7eSYOSHIFUJI Hideaki return -1; 528554cfb7eSYOSHIFUJI Hideaki return m; 529554cfb7eSYOSHIFUJI Hideaki } 530554cfb7eSYOSHIFUJI Hideaki 531f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 532f11e6659SDavid S. Miller int *mpri, struct rt6_info *match) 533554cfb7eSYOSHIFUJI Hideaki { 534554cfb7eSYOSHIFUJI Hideaki int m; 535554cfb7eSYOSHIFUJI Hideaki 536554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 537f11e6659SDavid S. Miller goto out; 538554cfb7eSYOSHIFUJI Hideaki 539554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 540554cfb7eSYOSHIFUJI Hideaki if (m < 0) 541f11e6659SDavid S. Miller goto out; 542554cfb7eSYOSHIFUJI Hideaki 543f11e6659SDavid S. Miller if (m > *mpri) { 544ea659e07SYOSHIFUJI Hideaki if (strict & RT6_LOOKUP_F_REACHABLE) 54527097255SYOSHIFUJI Hideaki rt6_probe(match); 546f11e6659SDavid S. Miller *mpri = m; 547554cfb7eSYOSHIFUJI Hideaki match = rt; 548ea659e07SYOSHIFUJI Hideaki } else if (strict & RT6_LOOKUP_F_REACHABLE) { 54927097255SYOSHIFUJI Hideaki rt6_probe(rt); 5501da177e4SLinus Torvalds } 551f11e6659SDavid S. Miller 552f11e6659SDavid S. Miller out: 553f11e6659SDavid S. Miller return match; 5541da177e4SLinus Torvalds } 5551da177e4SLinus Torvalds 556f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 557f11e6659SDavid S. Miller struct rt6_info *rr_head, 558f11e6659SDavid S. Miller u32 metric, int oif, int strict) 559f11e6659SDavid S. Miller { 560f11e6659SDavid S. Miller struct rt6_info *rt, *match; 561f11e6659SDavid S. Miller int mpri = -1; 562f11e6659SDavid S. Miller 563f11e6659SDavid S. Miller match = NULL; 564f11e6659SDavid S. Miller for (rt = rr_head; rt && rt->rt6i_metric == metric; 565d8d1f30bSChangli Gao rt = rt->dst.rt6_next) 566f11e6659SDavid S. Miller match = find_match(rt, oif, strict, &mpri, match); 567f11e6659SDavid S. Miller for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric; 568d8d1f30bSChangli Gao rt = rt->dst.rt6_next) 569f11e6659SDavid S. Miller match = find_match(rt, oif, strict, &mpri, match); 570f11e6659SDavid S. Miller 571f11e6659SDavid S. Miller return match; 572f11e6659SDavid S. Miller } 573f11e6659SDavid S. Miller 574f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 575f11e6659SDavid S. Miller { 576f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 5778ed67789SDaniel Lezcano struct net *net; 578f11e6659SDavid S. Miller 579f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 580f11e6659SDavid S. Miller if (!rt0) 581f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 582f11e6659SDavid S. Miller 583f11e6659SDavid S. Miller match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict); 584f11e6659SDavid S. Miller 585554cfb7eSYOSHIFUJI Hideaki if (!match && 586f11e6659SDavid S. Miller (strict & RT6_LOOKUP_F_REACHABLE)) { 587d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 588f11e6659SDavid S. Miller 589554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 590f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 591f11e6659SDavid S. Miller next = fn->leaf; 592f11e6659SDavid S. Miller 593f11e6659SDavid S. Miller if (next != rt0) 594f11e6659SDavid S. Miller fn->rr_ptr = next; 595554cfb7eSYOSHIFUJI Hideaki } 596554cfb7eSYOSHIFUJI Hideaki 597d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 598a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 5991da177e4SLinus Torvalds } 6001da177e4SLinus Torvalds 60170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 60270ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 603b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 60470ceb4f5SYOSHIFUJI Hideaki { 605c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 60670ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 60770ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 60870ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 6094bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 61070ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 61170ceb4f5SYOSHIFUJI Hideaki 61270ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 61370ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 61470ceb4f5SYOSHIFUJI Hideaki } 61570ceb4f5SYOSHIFUJI Hideaki 61670ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 61770ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 61870ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 61970ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 62070ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 62170ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 62270ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 62370ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 62470ceb4f5SYOSHIFUJI Hideaki } 62570ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 62670ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 62770ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 62870ceb4f5SYOSHIFUJI Hideaki } 62970ceb4f5SYOSHIFUJI Hideaki } 63070ceb4f5SYOSHIFUJI Hideaki 63170ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 63270ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 6333933fc95SJens Rosenboom return -EINVAL; 63470ceb4f5SYOSHIFUJI Hideaki 6354bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 63670ceb4f5SYOSHIFUJI Hideaki 63770ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 63870ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 63970ceb4f5SYOSHIFUJI Hideaki else { 64070ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 64170ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 64270ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 64370ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 64470ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 64570ceb4f5SYOSHIFUJI Hideaki } 64670ceb4f5SYOSHIFUJI Hideaki 647efa2cea0SDaniel Lezcano rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr, 648efa2cea0SDaniel Lezcano dev->ifindex); 64970ceb4f5SYOSHIFUJI Hideaki 65070ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 651e0a1ad73SThomas Graf ip6_del_rt(rt); 65270ceb4f5SYOSHIFUJI Hideaki rt = NULL; 65370ceb4f5SYOSHIFUJI Hideaki } 65470ceb4f5SYOSHIFUJI Hideaki 65570ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 656efa2cea0SDaniel Lezcano rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 65770ceb4f5SYOSHIFUJI Hideaki pref); 65870ceb4f5SYOSHIFUJI Hideaki else if (rt) 65970ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 66070ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 66170ceb4f5SYOSHIFUJI Hideaki 66270ceb4f5SYOSHIFUJI Hideaki if (rt) { 6631716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 6641716a961SGao feng rt6_clean_expires(rt); 6651716a961SGao feng else 6661716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 6671716a961SGao feng 668d8d1f30bSChangli Gao dst_release(&rt->dst); 66970ceb4f5SYOSHIFUJI Hideaki } 67070ceb4f5SYOSHIFUJI Hideaki return 0; 67170ceb4f5SYOSHIFUJI Hideaki } 67270ceb4f5SYOSHIFUJI Hideaki #endif 67370ceb4f5SYOSHIFUJI Hideaki 6748ed67789SDaniel Lezcano #define BACKTRACK(__net, saddr) \ 675982f56f3SYOSHIFUJI Hideaki do { \ 6768ed67789SDaniel Lezcano if (rt == __net->ipv6.ip6_null_entry) { \ 677982f56f3SYOSHIFUJI Hideaki struct fib6_node *pn; \ 678e0eda7bbSVille Nuorvala while (1) { \ 679982f56f3SYOSHIFUJI Hideaki if (fn->fn_flags & RTN_TL_ROOT) \ 680c71099acSThomas Graf goto out; \ 681982f56f3SYOSHIFUJI Hideaki pn = fn->parent; \ 682982f56f3SYOSHIFUJI Hideaki if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \ 6838bce65b9SKim Nordlund fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \ 684982f56f3SYOSHIFUJI Hideaki else \ 685982f56f3SYOSHIFUJI Hideaki fn = pn; \ 686c71099acSThomas Graf if (fn->fn_flags & RTN_RTINFO) \ 687c71099acSThomas Graf goto restart; \ 688c71099acSThomas Graf } \ 689982f56f3SYOSHIFUJI Hideaki } \ 690982f56f3SYOSHIFUJI Hideaki } while (0) 691c71099acSThomas Graf 6928ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 6938ed67789SDaniel Lezcano struct fib6_table *table, 6944c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 6951da177e4SLinus Torvalds { 6961da177e4SLinus Torvalds struct fib6_node *fn; 6971da177e4SLinus Torvalds struct rt6_info *rt; 6981da177e4SLinus Torvalds 699c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 7004c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 701c71099acSThomas Graf restart: 702c71099acSThomas Graf rt = fn->leaf; 7034c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 7044c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 705c71099acSThomas Graf out: 706d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 707c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 7081da177e4SLinus Torvalds return rt; 709c71099acSThomas Graf 710c71099acSThomas Graf } 711c71099acSThomas Graf 712ea6e574eSFlorian Westphal struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6, 713ea6e574eSFlorian Westphal int flags) 714ea6e574eSFlorian Westphal { 715ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 716ea6e574eSFlorian Westphal } 717ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 718ea6e574eSFlorian Westphal 7199acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 7209acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 721c71099acSThomas Graf { 7224c9483b2SDavid S. Miller struct flowi6 fl6 = { 7234c9483b2SDavid S. Miller .flowi6_oif = oif, 7244c9483b2SDavid S. Miller .daddr = *daddr, 725c71099acSThomas Graf }; 726c71099acSThomas Graf struct dst_entry *dst; 72777d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 728c71099acSThomas Graf 729adaa70bbSThomas Graf if (saddr) { 7304c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 731adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 732adaa70bbSThomas Graf } 733adaa70bbSThomas Graf 7344c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 735c71099acSThomas Graf if (dst->error == 0) 736c71099acSThomas Graf return (struct rt6_info *) dst; 737c71099acSThomas Graf 738c71099acSThomas Graf dst_release(dst); 739c71099acSThomas Graf 7401da177e4SLinus Torvalds return NULL; 7411da177e4SLinus Torvalds } 7421da177e4SLinus Torvalds 7437159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 7447159039aSYOSHIFUJI Hideaki 745c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 7461da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 7471da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 7481da177e4SLinus Torvalds be destroyed. 7491da177e4SLinus Torvalds */ 7501da177e4SLinus Torvalds 75186872cb5SThomas Graf static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info) 7521da177e4SLinus Torvalds { 7531da177e4SLinus Torvalds int err; 754c71099acSThomas Graf struct fib6_table *table; 7551da177e4SLinus Torvalds 756c71099acSThomas Graf table = rt->rt6i_table; 757c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 75886872cb5SThomas Graf err = fib6_add(&table->tb6_root, rt, info); 759c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 7601da177e4SLinus Torvalds 7611da177e4SLinus Torvalds return err; 7621da177e4SLinus Torvalds } 7631da177e4SLinus Torvalds 76440e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 76540e22e8fSThomas Graf { 7664d1169c1SDenis V. Lunev struct nl_info info = { 767d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 7684d1169c1SDenis V. Lunev }; 769528c4cebSDenis V. Lunev return __ip6_ins_rt(rt, &info); 77040e22e8fSThomas Graf } 77140e22e8fSThomas Graf 7721716a961SGao feng static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, 77321efcfa0SEric Dumazet const struct in6_addr *daddr, 774b71d1d42SEric Dumazet const struct in6_addr *saddr) 7751da177e4SLinus Torvalds { 7761da177e4SLinus Torvalds struct rt6_info *rt; 7771da177e4SLinus Torvalds 7781da177e4SLinus Torvalds /* 7791da177e4SLinus Torvalds * Clone the route. 7801da177e4SLinus Torvalds */ 7811da177e4SLinus Torvalds 78221efcfa0SEric Dumazet rt = ip6_rt_copy(ort, daddr); 7831da177e4SLinus Torvalds 7841da177e4SLinus Torvalds if (rt) { 78514deae41SDavid S. Miller int attempts = !in_softirq(); 78614deae41SDavid S. Miller 78758c4fb86SYOSHIFUJI Hideaki if (!(rt->rt6i_flags & RTF_GATEWAY)) { 788bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 78921efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 79058c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 7914e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *daddr; 79258c4fb86SYOSHIFUJI Hideaki } 7931da177e4SLinus Torvalds 7941da177e4SLinus Torvalds rt->rt6i_flags |= RTF_CACHE; 7951da177e4SLinus Torvalds 7961da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 7971da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 7984e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 7991da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 8001da177e4SLinus Torvalds } 8011da177e4SLinus Torvalds #endif 8021da177e4SLinus Torvalds 80314deae41SDavid S. Miller retry: 8048ade06c6SDavid S. Miller if (rt6_bind_neighbour(rt, rt->dst.dev)) { 805d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 80614deae41SDavid S. Miller int saved_rt_min_interval = 80714deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval; 80814deae41SDavid S. Miller int saved_rt_elasticity = 80914deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity; 81014deae41SDavid S. Miller 81114deae41SDavid S. Miller if (attempts-- > 0) { 81214deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity = 1; 81314deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval = 0; 81414deae41SDavid S. Miller 81586393e52SAlexey Dobriyan ip6_dst_gc(&net->ipv6.ip6_dst_ops); 81614deae41SDavid S. Miller 81714deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity = 81814deae41SDavid S. Miller saved_rt_elasticity; 81914deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval = 82014deae41SDavid S. Miller saved_rt_min_interval; 82114deae41SDavid S. Miller goto retry; 82214deae41SDavid S. Miller } 82314deae41SDavid S. Miller 824f3213831SJoe Perches net_warn_ratelimited("Neighbour table overflow\n"); 825d8d1f30bSChangli Gao dst_free(&rt->dst); 82614deae41SDavid S. Miller return NULL; 82714deae41SDavid S. Miller } 82895a9a5baSYOSHIFUJI Hideaki } 8291da177e4SLinus Torvalds 8301da177e4SLinus Torvalds return rt; 8311da177e4SLinus Torvalds } 83295a9a5baSYOSHIFUJI Hideaki 83321efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, 83421efcfa0SEric Dumazet const struct in6_addr *daddr) 835299d9939SYOSHIFUJI Hideaki { 83621efcfa0SEric Dumazet struct rt6_info *rt = ip6_rt_copy(ort, daddr); 83721efcfa0SEric Dumazet 838299d9939SYOSHIFUJI Hideaki if (rt) { 839299d9939SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_CACHE; 84097cac082SDavid S. Miller rt->n = neigh_clone(ort->n); 841299d9939SYOSHIFUJI Hideaki } 842299d9939SYOSHIFUJI Hideaki return rt; 843299d9939SYOSHIFUJI Hideaki } 844299d9939SYOSHIFUJI Hideaki 8458ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, 8464c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 8471da177e4SLinus Torvalds { 8481da177e4SLinus Torvalds struct fib6_node *fn; 849519fbd87SYOSHIFUJI Hideaki struct rt6_info *rt, *nrt; 850c71099acSThomas Graf int strict = 0; 8511da177e4SLinus Torvalds int attempts = 3; 852519fbd87SYOSHIFUJI Hideaki int err; 85353b7997fSYOSHIFUJI Hideaki int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE; 8541da177e4SLinus Torvalds 85577d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 8561da177e4SLinus Torvalds 8571da177e4SLinus Torvalds relookup: 858c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 8591da177e4SLinus Torvalds 8608238dd06SYOSHIFUJI Hideaki restart_2: 8614c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 8621da177e4SLinus Torvalds 8631da177e4SLinus Torvalds restart: 8644acad72dSPavel Emelyanov rt = rt6_select(fn, oif, strict | reachable); 8658ed67789SDaniel Lezcano 8664c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 8678ed67789SDaniel Lezcano if (rt == net->ipv6.ip6_null_entry || 8688238dd06SYOSHIFUJI Hideaki rt->rt6i_flags & RTF_CACHE) 8691da177e4SLinus Torvalds goto out; 8701da177e4SLinus Torvalds 871d8d1f30bSChangli Gao dst_hold(&rt->dst); 872c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 8731da177e4SLinus Torvalds 87497cac082SDavid S. Miller if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP)) 8754c9483b2SDavid S. Miller nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); 8767343ff31SDavid S. Miller else if (!(rt->dst.flags & DST_HOST)) 8774c9483b2SDavid S. Miller nrt = rt6_alloc_clone(rt, &fl6->daddr); 8787343ff31SDavid S. Miller else 8797343ff31SDavid S. Miller goto out2; 8801da177e4SLinus Torvalds 881d8d1f30bSChangli Gao dst_release(&rt->dst); 8828ed67789SDaniel Lezcano rt = nrt ? : net->ipv6.ip6_null_entry; 8831da177e4SLinus Torvalds 884d8d1f30bSChangli Gao dst_hold(&rt->dst); 885e40cf353SYOSHIFUJI Hideaki if (nrt) { 88640e22e8fSThomas Graf err = ip6_ins_rt(nrt); 887e40cf353SYOSHIFUJI Hideaki if (!err) 888e40cf353SYOSHIFUJI Hideaki goto out2; 889e40cf353SYOSHIFUJI Hideaki } 890e40cf353SYOSHIFUJI Hideaki 891e40cf353SYOSHIFUJI Hideaki if (--attempts <= 0) 8921da177e4SLinus Torvalds goto out2; 8931da177e4SLinus Torvalds 894519fbd87SYOSHIFUJI Hideaki /* 895c71099acSThomas Graf * Race condition! In the gap, when table->tb6_lock was 896519fbd87SYOSHIFUJI Hideaki * released someone could insert this route. Relookup. 8971da177e4SLinus Torvalds */ 898d8d1f30bSChangli Gao dst_release(&rt->dst); 8991da177e4SLinus Torvalds goto relookup; 900e40cf353SYOSHIFUJI Hideaki 901519fbd87SYOSHIFUJI Hideaki out: 9028238dd06SYOSHIFUJI Hideaki if (reachable) { 9038238dd06SYOSHIFUJI Hideaki reachable = 0; 9048238dd06SYOSHIFUJI Hideaki goto restart_2; 9058238dd06SYOSHIFUJI Hideaki } 906d8d1f30bSChangli Gao dst_hold(&rt->dst); 907c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 9081da177e4SLinus Torvalds out2: 909d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 910d8d1f30bSChangli Gao rt->dst.__use++; 911c71099acSThomas Graf 912c71099acSThomas Graf return rt; 913c71099acSThomas Graf } 914c71099acSThomas Graf 9158ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 9164c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 9174acad72dSPavel Emelyanov { 9184c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 9194acad72dSPavel Emelyanov } 9204acad72dSPavel Emelyanov 92172331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 92272331bc0SShmulik Ladkani struct net_device *dev, 92372331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 92472331bc0SShmulik Ladkani { 92572331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 92672331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 92772331bc0SShmulik Ladkani 92872331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 92972331bc0SShmulik Ladkani } 93072331bc0SShmulik Ladkani 931c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 932c71099acSThomas Graf { 933b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 934c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 935adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 9364c9483b2SDavid S. Miller struct flowi6 fl6 = { 9374c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 9384c9483b2SDavid S. Miller .daddr = iph->daddr, 9394c9483b2SDavid S. Miller .saddr = iph->saddr, 9404c9483b2SDavid S. Miller .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK, 9414c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 9424c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 943c71099acSThomas Graf }; 944adaa70bbSThomas Graf 94572331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 946c71099acSThomas Graf } 947c71099acSThomas Graf 9488ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 9494c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 950c71099acSThomas Graf { 9514c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 952c71099acSThomas Graf } 953c71099acSThomas Graf 9549c7a4f9cSFlorian Westphal struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk, 9554c9483b2SDavid S. Miller struct flowi6 *fl6) 956c71099acSThomas Graf { 957c71099acSThomas Graf int flags = 0; 958c71099acSThomas Graf 9591fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 9604dc27d1cSDavid McCullough 9614c9483b2SDavid S. Miller if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) 96277d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 963c71099acSThomas Graf 9644c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 965adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 9660c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 9670c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 968adaa70bbSThomas Graf 9694c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 9701da177e4SLinus Torvalds } 9711da177e4SLinus Torvalds 9727159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 9731da177e4SLinus Torvalds 9742774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 97514e50e57SDavid S. Miller { 9765c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 97714e50e57SDavid S. Miller struct dst_entry *new = NULL; 97814e50e57SDavid S. Miller 979f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 98014e50e57SDavid S. Miller if (rt) { 981d8d1f30bSChangli Gao new = &rt->dst; 98214e50e57SDavid S. Miller 9838104891bSSteffen Klassert memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); 9848104891bSSteffen Klassert rt6_init_peer(rt, net->ipv6.peers); 9858104891bSSteffen Klassert 98614e50e57SDavid S. Miller new->__use = 1; 987352e512cSHerbert Xu new->input = dst_discard; 988352e512cSHerbert Xu new->output = dst_discard; 98914e50e57SDavid S. Miller 99021efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 99121efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 99221efcfa0SEric Dumazet else 993defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 99414e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 99514e50e57SDavid S. Miller if (rt->rt6i_idev) 99614e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 99714e50e57SDavid S. Miller 9984e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 9991716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 10001716a961SGao feng rt6_clean_expires(rt); 100114e50e57SDavid S. Miller rt->rt6i_metric = 0; 100214e50e57SDavid S. Miller 100314e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 100414e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 100514e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 100614e50e57SDavid S. Miller #endif 100714e50e57SDavid S. Miller 100814e50e57SDavid S. Miller dst_free(new); 100914e50e57SDavid S. Miller } 101014e50e57SDavid S. Miller 101169ead7afSDavid S. Miller dst_release(dst_orig); 101269ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 101314e50e57SDavid S. Miller } 101414e50e57SDavid S. Miller 10151da177e4SLinus Torvalds /* 10161da177e4SLinus Torvalds * Destination cache support functions 10171da177e4SLinus Torvalds */ 10181da177e4SLinus Torvalds 10191da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 10201da177e4SLinus Torvalds { 10211da177e4SLinus Torvalds struct rt6_info *rt; 10221da177e4SLinus Torvalds 10231da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 10241da177e4SLinus Torvalds 10256431cbc2SDavid S. Miller if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) { 10266431cbc2SDavid S. Miller if (rt->rt6i_peer_genid != rt6_peer_genid()) { 102797bab73fSDavid S. Miller if (!rt6_has_peer(rt)) 10286431cbc2SDavid S. Miller rt6_bind_peer(rt, 0); 10296431cbc2SDavid S. Miller rt->rt6i_peer_genid = rt6_peer_genid(); 10306431cbc2SDavid S. Miller } 10311da177e4SLinus Torvalds return dst; 10326431cbc2SDavid S. Miller } 10331da177e4SLinus Torvalds return NULL; 10341da177e4SLinus Torvalds } 10351da177e4SLinus Torvalds 10361da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 10371da177e4SLinus Torvalds { 10381da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 10391da177e4SLinus Torvalds 10401da177e4SLinus Torvalds if (rt) { 104154c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 104254c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1043e0a1ad73SThomas Graf ip6_del_rt(rt); 104454c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 10451da177e4SLinus Torvalds } 104654c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 104754c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 104854c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 104954c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 105054c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 105154c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 10521da177e4SLinus Torvalds } 10531da177e4SLinus Torvalds 10541da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 10551da177e4SLinus Torvalds { 10561da177e4SLinus Torvalds struct rt6_info *rt; 10571da177e4SLinus Torvalds 10583ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 10591da177e4SLinus Torvalds 1060adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 10611da177e4SLinus Torvalds if (rt) { 10621716a961SGao feng if (rt->rt6i_flags & RTF_CACHE) 10631716a961SGao feng rt6_update_expires(rt, 0); 10641716a961SGao feng else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) 10651da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 10661da177e4SLinus Torvalds } 10671da177e4SLinus Torvalds } 10681da177e4SLinus Torvalds 10696700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 10706700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 10711da177e4SLinus Torvalds { 10721da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info*)dst; 10731da177e4SLinus Torvalds 107481aded24SDavid S. Miller dst_confirm(dst); 10751da177e4SLinus Torvalds if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) { 107681aded24SDavid S. Miller struct net *net = dev_net(dst->dev); 107781aded24SDavid S. Miller 10781da177e4SLinus Torvalds rt6->rt6i_flags |= RTF_MODIFIED; 10791da177e4SLinus Torvalds if (mtu < IPV6_MIN_MTU) { 1080defb3519SDavid S. Miller u32 features = dst_metric(dst, RTAX_FEATURES); 10811da177e4SLinus Torvalds mtu = IPV6_MIN_MTU; 1082defb3519SDavid S. Miller features |= RTAX_FEATURE_ALLFRAG; 1083defb3519SDavid S. Miller dst_metric_set(dst, RTAX_FEATURES, features); 10841da177e4SLinus Torvalds } 1085defb3519SDavid S. Miller dst_metric_set(dst, RTAX_MTU, mtu); 108681aded24SDavid S. Miller rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires); 10871da177e4SLinus Torvalds } 10881da177e4SLinus Torvalds } 10891da177e4SLinus Torvalds 109042ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 109142ae66c8SDavid S. Miller int oif, u32 mark) 109281aded24SDavid S. Miller { 109381aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 109481aded24SDavid S. Miller struct dst_entry *dst; 109581aded24SDavid S. Miller struct flowi6 fl6; 109681aded24SDavid S. Miller 109781aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 109881aded24SDavid S. Miller fl6.flowi6_oif = oif; 109981aded24SDavid S. Miller fl6.flowi6_mark = mark; 11003e12939aSDavid S. Miller fl6.flowi6_flags = 0; 110181aded24SDavid S. Miller fl6.daddr = iph->daddr; 110281aded24SDavid S. Miller fl6.saddr = iph->saddr; 110381aded24SDavid S. Miller fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK; 110481aded24SDavid S. Miller 110581aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 110681aded24SDavid S. Miller if (!dst->error) 11076700c270SDavid S. Miller ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu)); 110881aded24SDavid S. Miller dst_release(dst); 110981aded24SDavid S. Miller } 111081aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 111181aded24SDavid S. Miller 111281aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 111381aded24SDavid S. Miller { 111481aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 111581aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 111681aded24SDavid S. Miller } 111781aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 111881aded24SDavid S. Miller 11193a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 11203a5ad2eeSDavid S. Miller { 11213a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 11223a5ad2eeSDavid S. Miller struct dst_entry *dst; 11233a5ad2eeSDavid S. Miller struct flowi6 fl6; 11243a5ad2eeSDavid S. Miller 11253a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 11263a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 11273a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 11283a5ad2eeSDavid S. Miller fl6.flowi6_flags = 0; 11293a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 11303a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 11313a5ad2eeSDavid S. Miller fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK; 11323a5ad2eeSDavid S. Miller 11333a5ad2eeSDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 11343a5ad2eeSDavid S. Miller if (!dst->error) 11356700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 11363a5ad2eeSDavid S. Miller dst_release(dst); 11373a5ad2eeSDavid S. Miller } 11383a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 11393a5ad2eeSDavid S. Miller 11403a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 11413a5ad2eeSDavid S. Miller { 11423a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 11433a5ad2eeSDavid S. Miller } 11443a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 11453a5ad2eeSDavid S. Miller 11460dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 11471da177e4SLinus Torvalds { 11480dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 11490dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 11500dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 11510dbaee3bSDavid S. Miller 11521da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 11531da177e4SLinus Torvalds 11545578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 11555578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 11561da177e4SLinus Torvalds 11571da177e4SLinus Torvalds /* 11581da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 11591da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 11601da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 11611da177e4SLinus Torvalds * rely only on pmtu discovery" 11621da177e4SLinus Torvalds */ 11631da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 11641da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 11651da177e4SLinus Torvalds return mtu; 11661da177e4SLinus Torvalds } 11671da177e4SLinus Torvalds 1168ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1169d33e4553SDavid S. Miller { 1170d33e4553SDavid S. Miller struct inet6_dev *idev; 1171618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 1172618f9bc7SSteffen Klassert 1173618f9bc7SSteffen Klassert if (mtu) 1174618f9bc7SSteffen Klassert return mtu; 1175618f9bc7SSteffen Klassert 1176618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1177d33e4553SDavid S. Miller 1178d33e4553SDavid S. Miller rcu_read_lock(); 1179d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1180d33e4553SDavid S. Miller if (idev) 1181d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1182d33e4553SDavid S. Miller rcu_read_unlock(); 1183d33e4553SDavid S. Miller 1184d33e4553SDavid S. Miller return mtu; 1185d33e4553SDavid S. Miller } 1186d33e4553SDavid S. Miller 11873b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 11883b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 11895d0bbeebSThomas Graf 11903b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 11911da177e4SLinus Torvalds struct neighbour *neigh, 119287a11578SDavid S. Miller struct flowi6 *fl6) 11931da177e4SLinus Torvalds { 119487a11578SDavid S. Miller struct dst_entry *dst; 11951da177e4SLinus Torvalds struct rt6_info *rt; 11961da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1197c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 11981da177e4SLinus Torvalds 119938308473SDavid S. Miller if (unlikely(!idev)) 1200122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 12011da177e4SLinus Torvalds 12028b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, dev, 0, NULL); 120338308473SDavid S. Miller if (unlikely(!rt)) { 12041da177e4SLinus Torvalds in6_dev_put(idev); 120587a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 12061da177e4SLinus Torvalds goto out; 12071da177e4SLinus Torvalds } 12081da177e4SLinus Torvalds 12091da177e4SLinus Torvalds if (neigh) 12101da177e4SLinus Torvalds neigh_hold(neigh); 121114deae41SDavid S. Miller else { 1212f894cbf8SDavid S. Miller neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr); 1213b43faac6SDavid S. Miller if (IS_ERR(neigh)) { 1214252c3d84SRongQing.Li in6_dev_put(idev); 1215b43faac6SDavid S. Miller dst_free(&rt->dst); 1216b43faac6SDavid S. Miller return ERR_CAST(neigh); 1217b43faac6SDavid S. Miller } 121814deae41SDavid S. Miller } 12191da177e4SLinus Torvalds 12208e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 12218e2ec639SYan, Zheng rt->dst.output = ip6_output; 122297cac082SDavid S. Miller rt->n = neigh; 1223d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 122487a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 12258e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 12268e2ec639SYan, Zheng rt->rt6i_idev = idev; 12277011687fSGao feng dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255); 12281da177e4SLinus Torvalds 12293b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1230d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1231d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 12323b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 12331da177e4SLinus Torvalds 12345578689aSDaniel Lezcano fib6_force_start_gc(net); 12351da177e4SLinus Torvalds 123687a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 123787a11578SDavid S. Miller 12381da177e4SLinus Torvalds out: 123987a11578SDavid S. Miller return dst; 12401da177e4SLinus Torvalds } 12411da177e4SLinus Torvalds 12423d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 12431da177e4SLinus Torvalds { 1244e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 12453d0f24a7SStephen Hemminger int more = 0; 12461da177e4SLinus Torvalds 12473b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 12483b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 12495d0bbeebSThomas Graf 12501da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 12511da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 12521da177e4SLinus Torvalds *pprev = dst->next; 12531da177e4SLinus Torvalds dst_free(dst); 12541da177e4SLinus Torvalds } else { 12551da177e4SLinus Torvalds pprev = &dst->next; 12563d0f24a7SStephen Hemminger ++more; 12571da177e4SLinus Torvalds } 12581da177e4SLinus Torvalds } 12591da177e4SLinus Torvalds 12603b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 12615d0bbeebSThomas Graf 12623d0f24a7SStephen Hemminger return more; 12631da177e4SLinus Torvalds } 12641da177e4SLinus Torvalds 12651e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 12661e493d19SDavid S. Miller void *arg) 12671e493d19SDavid S. Miller { 12681e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 12691e493d19SDavid S. Miller 12701e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 12711e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 12721e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 12731e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 12741e493d19SDavid S. Miller if (func(rt, arg)) { 12751e493d19SDavid S. Miller *pprev = dst->next; 12761e493d19SDavid S. Miller dst_free(dst); 12771e493d19SDavid S. Miller } else { 12781e493d19SDavid S. Miller pprev = &dst->next; 12791e493d19SDavid S. Miller } 12801e493d19SDavid S. Miller } 12811e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 12821e493d19SDavid S. Miller } 12831e493d19SDavid S. Miller 1284569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 12851da177e4SLinus Torvalds { 12861da177e4SLinus Torvalds unsigned long now = jiffies; 128786393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 12887019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 12897019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 12907019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 12917019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 12927019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1293fc66f95cSEric Dumazet int entries; 12941da177e4SLinus Torvalds 1295fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 12967019b78eSDaniel Lezcano if (time_after(rt_last_gc + rt_min_interval, now) && 1297fc66f95cSEric Dumazet entries <= rt_max_size) 12981da177e4SLinus Torvalds goto out; 12991da177e4SLinus Torvalds 13006891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 13016891a346SBenjamin Thery fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net); 13026891a346SBenjamin Thery net->ipv6.ip6_rt_last_gc = now; 1303fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1304fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 13057019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 13061da177e4SLinus Torvalds out: 13077019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1308fc66f95cSEric Dumazet return entries > rt_max_size; 13091da177e4SLinus Torvalds } 13101da177e4SLinus Torvalds 13111da177e4SLinus Torvalds /* Clean host part of a prefix. Not necessary in radix tree, 13121da177e4SLinus Torvalds but results in cleaner routing tables. 13131da177e4SLinus Torvalds 13141da177e4SLinus Torvalds Remove it only when all the things will work! 13151da177e4SLinus Torvalds */ 13161da177e4SLinus Torvalds 13176b75d090SYOSHIFUJI Hideaki int ip6_dst_hoplimit(struct dst_entry *dst) 13181da177e4SLinus Torvalds { 13195170ae82SDavid S. Miller int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT); 1320a02e4b7dSDavid S. Miller if (hoplimit == 0) { 13216b75d090SYOSHIFUJI Hideaki struct net_device *dev = dst->dev; 1322c68f24ccSEric Dumazet struct inet6_dev *idev; 1323c68f24ccSEric Dumazet 1324c68f24ccSEric Dumazet rcu_read_lock(); 1325c68f24ccSEric Dumazet idev = __in6_dev_get(dev); 1326c68f24ccSEric Dumazet if (idev) 13271da177e4SLinus Torvalds hoplimit = idev->cnf.hop_limit; 1328c68f24ccSEric Dumazet else 132953b7997fSYOSHIFUJI Hideaki hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit; 1330c68f24ccSEric Dumazet rcu_read_unlock(); 13311da177e4SLinus Torvalds } 13321da177e4SLinus Torvalds return hoplimit; 13331da177e4SLinus Torvalds } 1334abbf46aeSDavid S. Miller EXPORT_SYMBOL(ip6_dst_hoplimit); 13351da177e4SLinus Torvalds 13361da177e4SLinus Torvalds /* 13371da177e4SLinus Torvalds * 13381da177e4SLinus Torvalds */ 13391da177e4SLinus Torvalds 134086872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg) 13411da177e4SLinus Torvalds { 13421da177e4SLinus Torvalds int err; 13435578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 13441da177e4SLinus Torvalds struct rt6_info *rt = NULL; 13451da177e4SLinus Torvalds struct net_device *dev = NULL; 13461da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1347c71099acSThomas Graf struct fib6_table *table; 13481da177e4SLinus Torvalds int addr_type; 13491da177e4SLinus Torvalds 135086872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 13511da177e4SLinus Torvalds return -EINVAL; 13521da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 135386872cb5SThomas Graf if (cfg->fc_src_len) 13541da177e4SLinus Torvalds return -EINVAL; 13551da177e4SLinus Torvalds #endif 135686872cb5SThomas Graf if (cfg->fc_ifindex) { 13571da177e4SLinus Torvalds err = -ENODEV; 13585578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 13591da177e4SLinus Torvalds if (!dev) 13601da177e4SLinus Torvalds goto out; 13611da177e4SLinus Torvalds idev = in6_dev_get(dev); 13621da177e4SLinus Torvalds if (!idev) 13631da177e4SLinus Torvalds goto out; 13641da177e4SLinus Torvalds } 13651da177e4SLinus Torvalds 136686872cb5SThomas Graf if (cfg->fc_metric == 0) 136786872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 13681da177e4SLinus Torvalds 1369c71099acSThomas Graf err = -ENOBUFS; 137038308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1371d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1372d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 137338308473SDavid S. Miller if (!table) { 1374f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1375d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1376d71314b4SMatti Vaittinen } 1377d71314b4SMatti Vaittinen } else { 1378d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1379d71314b4SMatti Vaittinen } 138038308473SDavid S. Miller 138138308473SDavid S. Miller if (!table) 1382c71099acSThomas Graf goto out; 1383c71099acSThomas Graf 13848b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table); 13851da177e4SLinus Torvalds 138638308473SDavid S. Miller if (!rt) { 13871da177e4SLinus Torvalds err = -ENOMEM; 13881da177e4SLinus Torvalds goto out; 13891da177e4SLinus Torvalds } 13901da177e4SLinus Torvalds 1391d8d1f30bSChangli Gao rt->dst.obsolete = -1; 13921716a961SGao feng 13931716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 13941716a961SGao feng rt6_set_expires(rt, jiffies + 13951716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 13961716a961SGao feng else 13971716a961SGao feng rt6_clean_expires(rt); 13981da177e4SLinus Torvalds 139986872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 140086872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 140186872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 140286872cb5SThomas Graf 140386872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 14041da177e4SLinus Torvalds 14051da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1406d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1407ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1408ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 14091da177e4SLinus Torvalds else 1410d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 14111da177e4SLinus Torvalds 1412d8d1f30bSChangli Gao rt->dst.output = ip6_output; 14131da177e4SLinus Torvalds 141486872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 141586872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 14161da177e4SLinus Torvalds if (rt->rt6i_dst.plen == 128) 141711d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 14181da177e4SLinus Torvalds 14198e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) { 14208e2ec639SYan, Zheng u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 14218e2ec639SYan, Zheng if (!metrics) { 14228e2ec639SYan, Zheng err = -ENOMEM; 14238e2ec639SYan, Zheng goto out; 14248e2ec639SYan, Zheng } 14258e2ec639SYan, Zheng dst_init_metrics(&rt->dst, metrics, 0); 14268e2ec639SYan, Zheng } 14271da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 142886872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 142986872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 14301da177e4SLinus Torvalds #endif 14311da177e4SLinus Torvalds 143286872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 14331da177e4SLinus Torvalds 14341da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 14351da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 14361da177e4SLinus Torvalds */ 143786872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 143838308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 143938308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 144038308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 14411da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 14425578689aSDaniel Lezcano if (dev != net->loopback_dev) { 14431da177e4SLinus Torvalds if (dev) { 14441da177e4SLinus Torvalds dev_put(dev); 14451da177e4SLinus Torvalds in6_dev_put(idev); 14461da177e4SLinus Torvalds } 14475578689aSDaniel Lezcano dev = net->loopback_dev; 14481da177e4SLinus Torvalds dev_hold(dev); 14491da177e4SLinus Torvalds idev = in6_dev_get(dev); 14501da177e4SLinus Torvalds if (!idev) { 14511da177e4SLinus Torvalds err = -ENODEV; 14521da177e4SLinus Torvalds goto out; 14531da177e4SLinus Torvalds } 14541da177e4SLinus Torvalds } 1455d8d1f30bSChangli Gao rt->dst.output = ip6_pkt_discard_out; 1456d8d1f30bSChangli Gao rt->dst.input = ip6_pkt_discard; 14571da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1458ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1459ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1460ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 1461ef2c7d7bSNicolas Dichtel break; 1462ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1463ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 1464ef2c7d7bSNicolas Dichtel break; 1465b4949ab2SNicolas Dichtel case RTN_THROW: 1466b4949ab2SNicolas Dichtel rt->dst.error = -EAGAIN; 1467b4949ab2SNicolas Dichtel break; 1468ef2c7d7bSNicolas Dichtel default: 1469ef2c7d7bSNicolas Dichtel rt->dst.error = -ENETUNREACH; 1470ef2c7d7bSNicolas Dichtel break; 1471ef2c7d7bSNicolas Dichtel } 14721da177e4SLinus Torvalds goto install_route; 14731da177e4SLinus Torvalds } 14741da177e4SLinus Torvalds 147586872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1476b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 14771da177e4SLinus Torvalds int gwa_type; 14781da177e4SLinus Torvalds 147986872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 14804e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 14811da177e4SLinus Torvalds gwa_type = ipv6_addr_type(gw_addr); 14821da177e4SLinus Torvalds 14831da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 14841da177e4SLinus Torvalds struct rt6_info *grt; 14851da177e4SLinus Torvalds 14861da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 14871da177e4SLinus Torvalds addresses as nexthop address. 14881da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 14891da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 14901da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 14911da177e4SLinus Torvalds some exceptions. --ANK 14921da177e4SLinus Torvalds */ 14931da177e4SLinus Torvalds err = -EINVAL; 14941da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 14951da177e4SLinus Torvalds goto out; 14961da177e4SLinus Torvalds 14975578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 14981da177e4SLinus Torvalds 14991da177e4SLinus Torvalds err = -EHOSTUNREACH; 150038308473SDavid S. Miller if (!grt) 15011da177e4SLinus Torvalds goto out; 15021da177e4SLinus Torvalds if (dev) { 1503d1918542SDavid S. Miller if (dev != grt->dst.dev) { 1504d8d1f30bSChangli Gao dst_release(&grt->dst); 15051da177e4SLinus Torvalds goto out; 15061da177e4SLinus Torvalds } 15071da177e4SLinus Torvalds } else { 1508d1918542SDavid S. Miller dev = grt->dst.dev; 15091da177e4SLinus Torvalds idev = grt->rt6i_idev; 15101da177e4SLinus Torvalds dev_hold(dev); 15111da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 15121da177e4SLinus Torvalds } 15131da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 15141da177e4SLinus Torvalds err = 0; 1515d8d1f30bSChangli Gao dst_release(&grt->dst); 15161da177e4SLinus Torvalds 15171da177e4SLinus Torvalds if (err) 15181da177e4SLinus Torvalds goto out; 15191da177e4SLinus Torvalds } 15201da177e4SLinus Torvalds err = -EINVAL; 152138308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 15221da177e4SLinus Torvalds goto out; 15231da177e4SLinus Torvalds } 15241da177e4SLinus Torvalds 15251da177e4SLinus Torvalds err = -ENODEV; 152638308473SDavid S. Miller if (!dev) 15271da177e4SLinus Torvalds goto out; 15281da177e4SLinus Torvalds 1529c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1530c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1531c3968a85SDaniel Walter err = -EINVAL; 1532c3968a85SDaniel Walter goto out; 1533c3968a85SDaniel Walter } 15344e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1535c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1536c3968a85SDaniel Walter } else 1537c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1538c3968a85SDaniel Walter 153986872cb5SThomas Graf if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) { 15408ade06c6SDavid S. Miller err = rt6_bind_neighbour(rt, dev); 1541f83c7790SDavid S. Miller if (err) 15421da177e4SLinus Torvalds goto out; 15431da177e4SLinus Torvalds } 15441da177e4SLinus Torvalds 154586872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 15461da177e4SLinus Torvalds 15471da177e4SLinus Torvalds install_route: 154886872cb5SThomas Graf if (cfg->fc_mx) { 154986872cb5SThomas Graf struct nlattr *nla; 155086872cb5SThomas Graf int remaining; 15511da177e4SLinus Torvalds 155286872cb5SThomas Graf nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 15538f4c1f9bSThomas Graf int type = nla_type(nla); 155486872cb5SThomas Graf 155586872cb5SThomas Graf if (type) { 155686872cb5SThomas Graf if (type > RTAX_MAX) { 15571da177e4SLinus Torvalds err = -EINVAL; 15581da177e4SLinus Torvalds goto out; 15591da177e4SLinus Torvalds } 156086872cb5SThomas Graf 1561defb3519SDavid S. Miller dst_metric_set(&rt->dst, type, nla_get_u32(nla)); 15621da177e4SLinus Torvalds } 15631da177e4SLinus Torvalds } 15641da177e4SLinus Torvalds } 15651da177e4SLinus Torvalds 1566d8d1f30bSChangli Gao rt->dst.dev = dev; 15671da177e4SLinus Torvalds rt->rt6i_idev = idev; 1568c71099acSThomas Graf rt->rt6i_table = table; 156963152fc0SDaniel Lezcano 1570c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 157163152fc0SDaniel Lezcano 157286872cb5SThomas Graf return __ip6_ins_rt(rt, &cfg->fc_nlinfo); 15731da177e4SLinus Torvalds 15741da177e4SLinus Torvalds out: 15751da177e4SLinus Torvalds if (dev) 15761da177e4SLinus Torvalds dev_put(dev); 15771da177e4SLinus Torvalds if (idev) 15781da177e4SLinus Torvalds in6_dev_put(idev); 15791da177e4SLinus Torvalds if (rt) 1580d8d1f30bSChangli Gao dst_free(&rt->dst); 15811da177e4SLinus Torvalds return err; 15821da177e4SLinus Torvalds } 15831da177e4SLinus Torvalds 158486872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 15851da177e4SLinus Torvalds { 15861da177e4SLinus Torvalds int err; 1587c71099acSThomas Graf struct fib6_table *table; 1588d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 15891da177e4SLinus Torvalds 15908ed67789SDaniel Lezcano if (rt == net->ipv6.ip6_null_entry) 15916c813a72SPatrick McHardy return -ENOENT; 15926c813a72SPatrick McHardy 1593c71099acSThomas Graf table = rt->rt6i_table; 1594c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 15951da177e4SLinus Torvalds 159686872cb5SThomas Graf err = fib6_del(rt, info); 1597d8d1f30bSChangli Gao dst_release(&rt->dst); 15981da177e4SLinus Torvalds 1599c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 16001da177e4SLinus Torvalds 16011da177e4SLinus Torvalds return err; 16021da177e4SLinus Torvalds } 16031da177e4SLinus Torvalds 1604e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 1605e0a1ad73SThomas Graf { 16064d1169c1SDenis V. Lunev struct nl_info info = { 1607d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 16084d1169c1SDenis V. Lunev }; 1609528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 1610e0a1ad73SThomas Graf } 1611e0a1ad73SThomas Graf 161286872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 16131da177e4SLinus Torvalds { 1614c71099acSThomas Graf struct fib6_table *table; 16151da177e4SLinus Torvalds struct fib6_node *fn; 16161da177e4SLinus Torvalds struct rt6_info *rt; 16171da177e4SLinus Torvalds int err = -ESRCH; 16181da177e4SLinus Torvalds 16195578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 162038308473SDavid S. Miller if (!table) 1621c71099acSThomas Graf return err; 16221da177e4SLinus Torvalds 1623c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1624c71099acSThomas Graf 1625c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 162686872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 162786872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 16281da177e4SLinus Torvalds 16291da177e4SLinus Torvalds if (fn) { 1630d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 163186872cb5SThomas Graf if (cfg->fc_ifindex && 1632d1918542SDavid S. Miller (!rt->dst.dev || 1633d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 16341da177e4SLinus Torvalds continue; 163586872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 163686872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 16371da177e4SLinus Torvalds continue; 163886872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 16391da177e4SLinus Torvalds continue; 1640d8d1f30bSChangli Gao dst_hold(&rt->dst); 1641c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 16421da177e4SLinus Torvalds 164386872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 16441da177e4SLinus Torvalds } 16451da177e4SLinus Torvalds } 1646c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 16471da177e4SLinus Torvalds 16481da177e4SLinus Torvalds return err; 16491da177e4SLinus Torvalds } 16501da177e4SLinus Torvalds 16516700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 1652a6279458SYOSHIFUJI Hideaki { 1653e8599ff4SDavid S. Miller struct net *net = dev_net(skb->dev); 1654a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 1655e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 1656e8599ff4SDavid S. Miller const struct in6_addr *target; 1657e8599ff4SDavid S. Miller struct ndisc_options ndopts; 16586e157b6aSDavid S. Miller const struct in6_addr *dest; 16596e157b6aSDavid S. Miller struct neighbour *old_neigh; 1660e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 1661e8599ff4SDavid S. Miller struct neighbour *neigh; 1662e8599ff4SDavid S. Miller struct icmp6hdr *icmph; 16636e157b6aSDavid S. Miller int optlen, on_link; 16646e157b6aSDavid S. Miller u8 *lladdr; 1665e8599ff4SDavid S. Miller 1666e8599ff4SDavid S. Miller optlen = skb->tail - skb->transport_header; 1667e8599ff4SDavid S. Miller optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); 1668e8599ff4SDavid S. Miller 1669e8599ff4SDavid S. Miller if (optlen < 0) { 16706e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 1671e8599ff4SDavid S. Miller return; 1672e8599ff4SDavid S. Miller } 1673e8599ff4SDavid S. Miller 1674e8599ff4SDavid S. Miller icmph = icmp6_hdr(skb); 1675e8599ff4SDavid S. Miller target = (const struct in6_addr *) (icmph + 1); 1676e8599ff4SDavid S. Miller dest = target + 1; 1677e8599ff4SDavid S. Miller 1678e8599ff4SDavid S. Miller if (ipv6_addr_is_multicast(dest)) { 16796e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 1680e8599ff4SDavid S. Miller return; 1681e8599ff4SDavid S. Miller } 1682e8599ff4SDavid S. Miller 16836e157b6aSDavid S. Miller on_link = 0; 1684e8599ff4SDavid S. Miller if (ipv6_addr_equal(dest, target)) { 1685e8599ff4SDavid S. Miller on_link = 1; 1686e8599ff4SDavid S. Miller } else if (ipv6_addr_type(target) != 1687e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 16886e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 1689e8599ff4SDavid S. Miller return; 1690e8599ff4SDavid S. Miller } 1691e8599ff4SDavid S. Miller 1692e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 1693e8599ff4SDavid S. Miller if (!in6_dev) 1694e8599ff4SDavid S. Miller return; 1695e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 1696e8599ff4SDavid S. Miller return; 1697e8599ff4SDavid S. Miller 1698e8599ff4SDavid S. Miller /* RFC2461 8.1: 1699e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 1700e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 1701e8599ff4SDavid S. Miller */ 1702e8599ff4SDavid S. Miller 1703e8599ff4SDavid S. Miller if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) { 1704e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 1705e8599ff4SDavid S. Miller return; 1706e8599ff4SDavid S. Miller } 17076e157b6aSDavid S. Miller 17086e157b6aSDavid S. Miller lladdr = NULL; 1709e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 1710e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 1711e8599ff4SDavid S. Miller skb->dev); 1712e8599ff4SDavid S. Miller if (!lladdr) { 1713e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 1714e8599ff4SDavid S. Miller return; 1715e8599ff4SDavid S. Miller } 1716e8599ff4SDavid S. Miller } 1717e8599ff4SDavid S. Miller 17186e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 17196e157b6aSDavid S. Miller if (rt == net->ipv6.ip6_null_entry) { 17206e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 17216e157b6aSDavid S. Miller return; 17226e157b6aSDavid S. Miller } 17236e157b6aSDavid S. Miller 17246e157b6aSDavid S. Miller /* Redirect received -> path was valid. 17256e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 17266e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 17276e157b6aSDavid S. Miller */ 17286e157b6aSDavid S. Miller dst_confirm(&rt->dst); 17296e157b6aSDavid S. Miller 1730e8599ff4SDavid S. Miller neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1); 1731e8599ff4SDavid S. Miller if (!neigh) 1732e8599ff4SDavid S. Miller return; 1733e8599ff4SDavid S. Miller 17346e157b6aSDavid S. Miller /* Duplicate redirect: silently ignore. */ 17356e157b6aSDavid S. Miller old_neigh = rt->n; 17366e157b6aSDavid S. Miller if (neigh == old_neigh) 1737a6279458SYOSHIFUJI Hideaki goto out; 17381da177e4SLinus Torvalds 17391da177e4SLinus Torvalds /* 17401da177e4SLinus Torvalds * We have finally decided to accept it. 17411da177e4SLinus Torvalds */ 17421da177e4SLinus Torvalds 17431da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 17441da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 17451da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 17461da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 17471da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 17481da177e4SLinus Torvalds ); 17491da177e4SLinus Torvalds 175021efcfa0SEric Dumazet nrt = ip6_rt_copy(rt, dest); 175138308473SDavid S. Miller if (!nrt) 17521da177e4SLinus Torvalds goto out; 17531da177e4SLinus Torvalds 17541da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 17551da177e4SLinus Torvalds if (on_link) 17561da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 17571da177e4SLinus Torvalds 17584e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 175997cac082SDavid S. Miller nrt->n = neigh_clone(neigh); 17601da177e4SLinus Torvalds 176140e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 17621da177e4SLinus Torvalds goto out; 17631da177e4SLinus Torvalds 1764d8d1f30bSChangli Gao netevent.old = &rt->dst; 17651d248b1cSDavid S. Miller netevent.old_neigh = old_neigh; 1766d8d1f30bSChangli Gao netevent.new = &nrt->dst; 17671d248b1cSDavid S. Miller netevent.new_neigh = neigh; 17681d248b1cSDavid S. Miller netevent.daddr = dest; 17698d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 17708d71740cSTom Tucker 17711da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 17726e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 1773e0a1ad73SThomas Graf ip6_del_rt(rt); 17741da177e4SLinus Torvalds } 17751da177e4SLinus Torvalds 17761da177e4SLinus Torvalds out: 1777e8599ff4SDavid S. Miller neigh_release(neigh); 17786e157b6aSDavid S. Miller } 17796e157b6aSDavid S. Miller 17801da177e4SLinus Torvalds /* 17811da177e4SLinus Torvalds * Misc support functions 17821da177e4SLinus Torvalds */ 17831da177e4SLinus Torvalds 17841716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 178521efcfa0SEric Dumazet const struct in6_addr *dest) 17861da177e4SLinus Torvalds { 1787d1918542SDavid S. Miller struct net *net = dev_net(ort->dst.dev); 17888b96d22dSDavid S. Miller struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0, 17898b96d22dSDavid S. Miller ort->rt6i_table); 17901da177e4SLinus Torvalds 17911da177e4SLinus Torvalds if (rt) { 1792d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 1793d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 17948e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 17951da177e4SLinus Torvalds 17964e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *dest; 17978e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 1798defb3519SDavid S. Miller dst_copy_metrics(&rt->dst, &ort->dst); 1799d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 18001da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 18011da177e4SLinus Torvalds if (rt->rt6i_idev) 18021da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 1803d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 18041da177e4SLinus Torvalds 18054e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 18061716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 18071716a961SGao feng if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) == 18081716a961SGao feng (RTF_DEFAULT | RTF_ADDRCONF)) 18091716a961SGao feng rt6_set_from(rt, ort); 18101716a961SGao feng else 18111716a961SGao feng rt6_clean_expires(rt); 18121da177e4SLinus Torvalds rt->rt6i_metric = 0; 18131da177e4SLinus Torvalds 18141da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 18151da177e4SLinus Torvalds memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 18161da177e4SLinus Torvalds #endif 18170f6c6392SFlorian Westphal memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key)); 1818c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 18191da177e4SLinus Torvalds } 18201da177e4SLinus Torvalds return rt; 18211da177e4SLinus Torvalds } 18221da177e4SLinus Torvalds 182370ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 1824efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 1825b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1826b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 182770ceb4f5SYOSHIFUJI Hideaki { 182870ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 182970ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 1830c71099acSThomas Graf struct fib6_table *table; 183170ceb4f5SYOSHIFUJI Hideaki 1832efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 183338308473SDavid S. Miller if (!table) 1834c71099acSThomas Graf return NULL; 1835c71099acSThomas Graf 18365744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 1837c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0); 183870ceb4f5SYOSHIFUJI Hideaki if (!fn) 183970ceb4f5SYOSHIFUJI Hideaki goto out; 184070ceb4f5SYOSHIFUJI Hideaki 1841d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1842d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 184370ceb4f5SYOSHIFUJI Hideaki continue; 184470ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 184570ceb4f5SYOSHIFUJI Hideaki continue; 184670ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 184770ceb4f5SYOSHIFUJI Hideaki continue; 1848d8d1f30bSChangli Gao dst_hold(&rt->dst); 184970ceb4f5SYOSHIFUJI Hideaki break; 185070ceb4f5SYOSHIFUJI Hideaki } 185170ceb4f5SYOSHIFUJI Hideaki out: 18525744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 185370ceb4f5SYOSHIFUJI Hideaki return rt; 185470ceb4f5SYOSHIFUJI Hideaki } 185570ceb4f5SYOSHIFUJI Hideaki 1856efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 1857b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1858b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 185995c96174SEric Dumazet unsigned int pref) 186070ceb4f5SYOSHIFUJI Hideaki { 186186872cb5SThomas Graf struct fib6_config cfg = { 186286872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 1863238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 186486872cb5SThomas Graf .fc_ifindex = ifindex, 186586872cb5SThomas Graf .fc_dst_len = prefixlen, 186686872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 186786872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 186815e47304SEric W. Biederman .fc_nlinfo.portid = 0, 1869efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 1870efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 187186872cb5SThomas Graf }; 187270ceb4f5SYOSHIFUJI Hideaki 18734e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 18744e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 187586872cb5SThomas Graf 1876e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 1877e317da96SYOSHIFUJI Hideaki if (!prefixlen) 187886872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 187970ceb4f5SYOSHIFUJI Hideaki 188086872cb5SThomas Graf ip6_route_add(&cfg); 188170ceb4f5SYOSHIFUJI Hideaki 1882efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 188370ceb4f5SYOSHIFUJI Hideaki } 188470ceb4f5SYOSHIFUJI Hideaki #endif 188570ceb4f5SYOSHIFUJI Hideaki 1886b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 18871da177e4SLinus Torvalds { 18881da177e4SLinus Torvalds struct rt6_info *rt; 1889c71099acSThomas Graf struct fib6_table *table; 18901da177e4SLinus Torvalds 1891c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 189238308473SDavid S. Miller if (!table) 1893c71099acSThomas Graf return NULL; 18941da177e4SLinus Torvalds 18955744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 1896d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) { 1897d1918542SDavid S. Miller if (dev == rt->dst.dev && 1898045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 18991da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 19001da177e4SLinus Torvalds break; 19011da177e4SLinus Torvalds } 19021da177e4SLinus Torvalds if (rt) 1903d8d1f30bSChangli Gao dst_hold(&rt->dst); 19045744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 19051da177e4SLinus Torvalds return rt; 19061da177e4SLinus Torvalds } 19071da177e4SLinus Torvalds 1908b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 1909ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 1910ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 19111da177e4SLinus Torvalds { 191286872cb5SThomas Graf struct fib6_config cfg = { 191386872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 1914238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 191586872cb5SThomas Graf .fc_ifindex = dev->ifindex, 191686872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 191786872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 191815e47304SEric W. Biederman .fc_nlinfo.portid = 0, 19195578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 1920c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 192186872cb5SThomas Graf }; 19221da177e4SLinus Torvalds 19234e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 19241da177e4SLinus Torvalds 192586872cb5SThomas Graf ip6_route_add(&cfg); 19261da177e4SLinus Torvalds 19271da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 19281da177e4SLinus Torvalds } 19291da177e4SLinus Torvalds 19307b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 19311da177e4SLinus Torvalds { 19321da177e4SLinus Torvalds struct rt6_info *rt; 1933c71099acSThomas Graf struct fib6_table *table; 1934c71099acSThomas Graf 1935c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 19367b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 193738308473SDavid S. Miller if (!table) 1938c71099acSThomas Graf return; 19391da177e4SLinus Torvalds 19401da177e4SLinus Torvalds restart: 1941c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1942d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 19431da177e4SLinus Torvalds if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { 1944d8d1f30bSChangli Gao dst_hold(&rt->dst); 1945c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 1946e0a1ad73SThomas Graf ip6_del_rt(rt); 19471da177e4SLinus Torvalds goto restart; 19481da177e4SLinus Torvalds } 19491da177e4SLinus Torvalds } 1950c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 19511da177e4SLinus Torvalds } 19521da177e4SLinus Torvalds 19535578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 19545578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 195586872cb5SThomas Graf struct fib6_config *cfg) 195686872cb5SThomas Graf { 195786872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 195886872cb5SThomas Graf 195986872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 196086872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 196186872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 196286872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 196386872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 196486872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 196586872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 196686872cb5SThomas Graf 19675578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 1968f1243c2dSBenjamin Thery 19694e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 19704e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 19714e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 197286872cb5SThomas Graf } 197386872cb5SThomas Graf 19745578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 19751da177e4SLinus Torvalds { 197686872cb5SThomas Graf struct fib6_config cfg; 19771da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 19781da177e4SLinus Torvalds int err; 19791da177e4SLinus Torvalds 19801da177e4SLinus Torvalds switch(cmd) { 19811da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 19821da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 19831da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 19841da177e4SLinus Torvalds return -EPERM; 19851da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 19861da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 19871da177e4SLinus Torvalds if (err) 19881da177e4SLinus Torvalds return -EFAULT; 19891da177e4SLinus Torvalds 19905578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 199186872cb5SThomas Graf 19921da177e4SLinus Torvalds rtnl_lock(); 19931da177e4SLinus Torvalds switch (cmd) { 19941da177e4SLinus Torvalds case SIOCADDRT: 199586872cb5SThomas Graf err = ip6_route_add(&cfg); 19961da177e4SLinus Torvalds break; 19971da177e4SLinus Torvalds case SIOCDELRT: 199886872cb5SThomas Graf err = ip6_route_del(&cfg); 19991da177e4SLinus Torvalds break; 20001da177e4SLinus Torvalds default: 20011da177e4SLinus Torvalds err = -EINVAL; 20021da177e4SLinus Torvalds } 20031da177e4SLinus Torvalds rtnl_unlock(); 20041da177e4SLinus Torvalds 20051da177e4SLinus Torvalds return err; 20063ff50b79SStephen Hemminger } 20071da177e4SLinus Torvalds 20081da177e4SLinus Torvalds return -EINVAL; 20091da177e4SLinus Torvalds } 20101da177e4SLinus Torvalds 20111da177e4SLinus Torvalds /* 20121da177e4SLinus Torvalds * Drop the packet on the floor 20131da177e4SLinus Torvalds */ 20141da177e4SLinus Torvalds 2015d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 20161da177e4SLinus Torvalds { 2017612f09e8SYOSHIFUJI Hideaki int type; 2018adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2019612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2020612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 20210660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 202245bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 20233bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 20243bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2025612f09e8SYOSHIFUJI Hideaki break; 2026612f09e8SYOSHIFUJI Hideaki } 2027612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2028612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 20293bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 20303bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2031612f09e8SYOSHIFUJI Hideaki break; 2032612f09e8SYOSHIFUJI Hideaki } 20333ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 20341da177e4SLinus Torvalds kfree_skb(skb); 20351da177e4SLinus Torvalds return 0; 20361da177e4SLinus Torvalds } 20371da177e4SLinus Torvalds 20389ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 20399ce8ade0SThomas Graf { 2040612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 20419ce8ade0SThomas Graf } 20429ce8ade0SThomas Graf 204320380731SArnaldo Carvalho de Melo static int ip6_pkt_discard_out(struct sk_buff *skb) 20441da177e4SLinus Torvalds { 2045adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2046612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 20471da177e4SLinus Torvalds } 20481da177e4SLinus Torvalds 20496723ab54SDavid S. Miller #ifdef CONFIG_IPV6_MULTIPLE_TABLES 20506723ab54SDavid S. Miller 20519ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 20529ce8ade0SThomas Graf { 2053612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 20549ce8ade0SThomas Graf } 20559ce8ade0SThomas Graf 20569ce8ade0SThomas Graf static int ip6_pkt_prohibit_out(struct sk_buff *skb) 20579ce8ade0SThomas Graf { 2058adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2059612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 20609ce8ade0SThomas Graf } 20619ce8ade0SThomas Graf 20626723ab54SDavid S. Miller #endif 20636723ab54SDavid S. Miller 20641da177e4SLinus Torvalds /* 20651da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 20661da177e4SLinus Torvalds */ 20671da177e4SLinus Torvalds 20681da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 20691da177e4SLinus Torvalds const struct in6_addr *addr, 20708f031519SDavid S. Miller bool anycast) 20711da177e4SLinus Torvalds { 2072c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 20738b96d22dSDavid S. Miller struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL); 2074f83c7790SDavid S. Miller int err; 20751da177e4SLinus Torvalds 207638308473SDavid S. Miller if (!rt) { 2077f3213831SJoe Perches net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n"); 20781da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 207940385653SBen Greear } 20801da177e4SLinus Torvalds 20811da177e4SLinus Torvalds in6_dev_hold(idev); 20821da177e4SLinus Torvalds 208311d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2084d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2085d8d1f30bSChangli Gao rt->dst.output = ip6_output; 20861da177e4SLinus Torvalds rt->rt6i_idev = idev; 2087d8d1f30bSChangli Gao rt->dst.obsolete = -1; 20881da177e4SLinus Torvalds 20891da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 209058c4fb86SYOSHIFUJI Hideaki if (anycast) 209158c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 209258c4fb86SYOSHIFUJI Hideaki else 20931da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 20948ade06c6SDavid S. Miller err = rt6_bind_neighbour(rt, rt->dst.dev); 2095f83c7790SDavid S. Miller if (err) { 2096d8d1f30bSChangli Gao dst_free(&rt->dst); 2097f83c7790SDavid S. Miller return ERR_PTR(err); 20981da177e4SLinus Torvalds } 20991da177e4SLinus Torvalds 21004e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 21011da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 21025578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 21031da177e4SLinus Torvalds 2104d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 21051da177e4SLinus Torvalds 21061da177e4SLinus Torvalds return rt; 21071da177e4SLinus Torvalds } 21081da177e4SLinus Torvalds 2109c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2110c3968a85SDaniel Walter struct rt6_info *rt, 2111b71d1d42SEric Dumazet const struct in6_addr *daddr, 2112c3968a85SDaniel Walter unsigned int prefs, 2113c3968a85SDaniel Walter struct in6_addr *saddr) 2114c3968a85SDaniel Walter { 2115c3968a85SDaniel Walter struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt); 2116c3968a85SDaniel Walter int err = 0; 2117c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) 21184e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2119c3968a85SDaniel Walter else 2120c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2121c3968a85SDaniel Walter daddr, prefs, saddr); 2122c3968a85SDaniel Walter return err; 2123c3968a85SDaniel Walter } 2124c3968a85SDaniel Walter 2125c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2126c3968a85SDaniel Walter struct arg_dev_net_ip { 2127c3968a85SDaniel Walter struct net_device *dev; 2128c3968a85SDaniel Walter struct net *net; 2129c3968a85SDaniel Walter struct in6_addr *addr; 2130c3968a85SDaniel Walter }; 2131c3968a85SDaniel Walter 2132c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2133c3968a85SDaniel Walter { 2134c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2135c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2136c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2137c3968a85SDaniel Walter 2138d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2139c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2140c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2141c3968a85SDaniel Walter /* remove prefsrc entry */ 2142c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2143c3968a85SDaniel Walter } 2144c3968a85SDaniel Walter return 0; 2145c3968a85SDaniel Walter } 2146c3968a85SDaniel Walter 2147c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2148c3968a85SDaniel Walter { 2149c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2150c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2151c3968a85SDaniel Walter .dev = ifp->idev->dev, 2152c3968a85SDaniel Walter .net = net, 2153c3968a85SDaniel Walter .addr = &ifp->addr, 2154c3968a85SDaniel Walter }; 2155c3968a85SDaniel Walter fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni); 2156c3968a85SDaniel Walter } 2157c3968a85SDaniel Walter 21588ed67789SDaniel Lezcano struct arg_dev_net { 21598ed67789SDaniel Lezcano struct net_device *dev; 21608ed67789SDaniel Lezcano struct net *net; 21618ed67789SDaniel Lezcano }; 21628ed67789SDaniel Lezcano 21631da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 21641da177e4SLinus Torvalds { 2165bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2166bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 21678ed67789SDaniel Lezcano 2168d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2169c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 21701da177e4SLinus Torvalds return -1; 2171c159d30cSDavid S. Miller 21721da177e4SLinus Torvalds return 0; 21731da177e4SLinus Torvalds } 21741da177e4SLinus Torvalds 2175f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 21761da177e4SLinus Torvalds { 21778ed67789SDaniel Lezcano struct arg_dev_net adn = { 21788ed67789SDaniel Lezcano .dev = dev, 21798ed67789SDaniel Lezcano .net = net, 21808ed67789SDaniel Lezcano }; 21818ed67789SDaniel Lezcano 21828ed67789SDaniel Lezcano fib6_clean_all(net, fib6_ifdown, 0, &adn); 21831e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 21841da177e4SLinus Torvalds } 21851da177e4SLinus Torvalds 218695c96174SEric Dumazet struct rt6_mtu_change_arg { 21871da177e4SLinus Torvalds struct net_device *dev; 218895c96174SEric Dumazet unsigned int mtu; 21891da177e4SLinus Torvalds }; 21901da177e4SLinus Torvalds 21911da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 21921da177e4SLinus Torvalds { 21931da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 21941da177e4SLinus Torvalds struct inet6_dev *idev; 21951da177e4SLinus Torvalds 21961da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 21971da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 21981da177e4SLinus Torvalds We still use this lock to block changes 21991da177e4SLinus Torvalds caused by addrconf/ndisc. 22001da177e4SLinus Torvalds */ 22011da177e4SLinus Torvalds 22021da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 220338308473SDavid S. Miller if (!idev) 22041da177e4SLinus Torvalds return 0; 22051da177e4SLinus Torvalds 22061da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 22071da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 22081da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 22091da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 22101da177e4SLinus Torvalds */ 22111da177e4SLinus Torvalds /* 22121da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 22131da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 22141da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 22151da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 22161da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 22171da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 22181da177e4SLinus Torvalds PMTU discouvery. 22191da177e4SLinus Torvalds */ 2220d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 2221d8d1f30bSChangli Gao !dst_metric_locked(&rt->dst, RTAX_MTU) && 2222d8d1f30bSChangli Gao (dst_mtu(&rt->dst) >= arg->mtu || 2223d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 2224d8d1f30bSChangli Gao dst_mtu(&rt->dst) == idev->cnf.mtu6))) { 2225defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2226566cfd8fSSimon Arlott } 22271da177e4SLinus Torvalds return 0; 22281da177e4SLinus Torvalds } 22291da177e4SLinus Torvalds 223095c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 22311da177e4SLinus Torvalds { 2232c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2233c71099acSThomas Graf .dev = dev, 2234c71099acSThomas Graf .mtu = mtu, 2235c71099acSThomas Graf }; 22361da177e4SLinus Torvalds 2237c346dca1SYOSHIFUJI Hideaki fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg); 22381da177e4SLinus Torvalds } 22391da177e4SLinus Torvalds 2240ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 22415176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 224286872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2243ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 224486872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 224586872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 224686872cb5SThomas Graf }; 224786872cb5SThomas Graf 224886872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 224986872cb5SThomas Graf struct fib6_config *cfg) 22501da177e4SLinus Torvalds { 225186872cb5SThomas Graf struct rtmsg *rtm; 225286872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 225386872cb5SThomas Graf int err; 22541da177e4SLinus Torvalds 225586872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 225686872cb5SThomas Graf if (err < 0) 225786872cb5SThomas Graf goto errout; 22581da177e4SLinus Torvalds 225986872cb5SThomas Graf err = -EINVAL; 226086872cb5SThomas Graf rtm = nlmsg_data(nlh); 226186872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 226286872cb5SThomas Graf 226386872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 226486872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 226586872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 226686872cb5SThomas Graf cfg->fc_flags = RTF_UP; 226786872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2268ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 226986872cb5SThomas Graf 2270ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2271ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2272b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2273b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 227486872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 227586872cb5SThomas Graf 2276ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2277ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2278ab79ad14SMaciej Żenczykowski 227915e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 228086872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 22813b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 228286872cb5SThomas Graf 228386872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 228486872cb5SThomas Graf nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16); 228586872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 22861da177e4SLinus Torvalds } 228786872cb5SThomas Graf 228886872cb5SThomas Graf if (tb[RTA_DST]) { 228986872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 229086872cb5SThomas Graf 229186872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 229286872cb5SThomas Graf goto errout; 229386872cb5SThomas Graf 229486872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 22951da177e4SLinus Torvalds } 229686872cb5SThomas Graf 229786872cb5SThomas Graf if (tb[RTA_SRC]) { 229886872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 229986872cb5SThomas Graf 230086872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 230186872cb5SThomas Graf goto errout; 230286872cb5SThomas Graf 230386872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 23041da177e4SLinus Torvalds } 230586872cb5SThomas Graf 2306c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 2307c3968a85SDaniel Walter nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16); 2308c3968a85SDaniel Walter 230986872cb5SThomas Graf if (tb[RTA_OIF]) 231086872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 231186872cb5SThomas Graf 231286872cb5SThomas Graf if (tb[RTA_PRIORITY]) 231386872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 231486872cb5SThomas Graf 231586872cb5SThomas Graf if (tb[RTA_METRICS]) { 231686872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 231786872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 23181da177e4SLinus Torvalds } 231986872cb5SThomas Graf 232086872cb5SThomas Graf if (tb[RTA_TABLE]) 232186872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 232286872cb5SThomas Graf 232386872cb5SThomas Graf err = 0; 232486872cb5SThomas Graf errout: 232586872cb5SThomas Graf return err; 23261da177e4SLinus Torvalds } 23271da177e4SLinus Torvalds 2328c127ea2cSThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 23291da177e4SLinus Torvalds { 233086872cb5SThomas Graf struct fib6_config cfg; 233186872cb5SThomas Graf int err; 23321da177e4SLinus Torvalds 233386872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 233486872cb5SThomas Graf if (err < 0) 233586872cb5SThomas Graf return err; 233686872cb5SThomas Graf 233786872cb5SThomas Graf return ip6_route_del(&cfg); 23381da177e4SLinus Torvalds } 23391da177e4SLinus Torvalds 2340c127ea2cSThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 23411da177e4SLinus Torvalds { 234286872cb5SThomas Graf struct fib6_config cfg; 234386872cb5SThomas Graf int err; 23441da177e4SLinus Torvalds 234586872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 234686872cb5SThomas Graf if (err < 0) 234786872cb5SThomas Graf return err; 234886872cb5SThomas Graf 234986872cb5SThomas Graf return ip6_route_add(&cfg); 23501da177e4SLinus Torvalds } 23511da177e4SLinus Torvalds 2352339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void) 2353339bf98fSThomas Graf { 2354339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 2355339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 2356339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 2357339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 2358339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 2359339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 2360339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 2361339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 2362339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 23636a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 2364339bf98fSThomas Graf + nla_total_size(sizeof(struct rta_cacheinfo)); 2365339bf98fSThomas Graf } 2366339bf98fSThomas Graf 2367191cd582SBrian Haley static int rt6_fill_node(struct net *net, 2368191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 23690d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 237015e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 23717bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 23721da177e4SLinus Torvalds { 23731da177e4SLinus Torvalds struct rtmsg *rtm; 23741da177e4SLinus Torvalds struct nlmsghdr *nlh; 2375e3703b3dSThomas Graf long expires; 23769e762a4aSPatrick McHardy u32 table; 2377f2c31e32SEric Dumazet struct neighbour *n; 23781da177e4SLinus Torvalds 23791da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 23801da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 23811da177e4SLinus Torvalds /* success since this is not a prefix route */ 23821da177e4SLinus Torvalds return 1; 23831da177e4SLinus Torvalds } 23841da177e4SLinus Torvalds } 23851da177e4SLinus Torvalds 238615e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 238738308473SDavid S. Miller if (!nlh) 238826932566SPatrick McHardy return -EMSGSIZE; 23892d7202bfSThomas Graf 23902d7202bfSThomas Graf rtm = nlmsg_data(nlh); 23911da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 23921da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 23931da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 23941da177e4SLinus Torvalds rtm->rtm_tos = 0; 2395c71099acSThomas Graf if (rt->rt6i_table) 23969e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 2397c71099acSThomas Graf else 23989e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 23999e762a4aSPatrick McHardy rtm->rtm_table = table; 2400c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 2401c78679e8SDavid S. Miller goto nla_put_failure; 2402ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 2403ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 2404ef2c7d7bSNicolas Dichtel case -EINVAL: 2405ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 2406ef2c7d7bSNicolas Dichtel break; 2407ef2c7d7bSNicolas Dichtel case -EACCES: 2408ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 2409ef2c7d7bSNicolas Dichtel break; 2410b4949ab2SNicolas Dichtel case -EAGAIN: 2411b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 2412b4949ab2SNicolas Dichtel break; 2413ef2c7d7bSNicolas Dichtel default: 24141da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 2415ef2c7d7bSNicolas Dichtel break; 2416ef2c7d7bSNicolas Dichtel } 2417ef2c7d7bSNicolas Dichtel } 2418ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 2419ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 2420d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 24211da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 24221da177e4SLinus Torvalds else 24231da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 24241da177e4SLinus Torvalds rtm->rtm_flags = 0; 24251da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 24261da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 24271da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 24281da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 2429f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 2430f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 24311da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 2432f0396f60SDenis Ovsienko else 2433f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 2434f0396f60SDenis Ovsienko } 24351da177e4SLinus Torvalds 24361da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 24371da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 24381da177e4SLinus Torvalds 24391da177e4SLinus Torvalds if (dst) { 2440c78679e8SDavid S. Miller if (nla_put(skb, RTA_DST, 16, dst)) 2441c78679e8SDavid S. Miller goto nla_put_failure; 24421da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 24431da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 2444c78679e8SDavid S. Miller if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr)) 2445c78679e8SDavid S. Miller goto nla_put_failure; 24461da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 24471da177e4SLinus Torvalds if (src) { 2448c78679e8SDavid S. Miller if (nla_put(skb, RTA_SRC, 16, src)) 2449c78679e8SDavid S. Miller goto nla_put_failure; 24501da177e4SLinus Torvalds rtm->rtm_src_len = 128; 2451c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 2452c78679e8SDavid S. Miller nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr)) 2453c78679e8SDavid S. Miller goto nla_put_failure; 24541da177e4SLinus Torvalds #endif 24557bc570c8SYOSHIFUJI Hideaki if (iif) { 24567bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 24577bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 24588229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 24597bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 24607bc570c8SYOSHIFUJI Hideaki if (!nowait) { 24617bc570c8SYOSHIFUJI Hideaki if (err == 0) 24627bc570c8SYOSHIFUJI Hideaki return 0; 24637bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 24647bc570c8SYOSHIFUJI Hideaki } else { 24657bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 24667bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 24677bc570c8SYOSHIFUJI Hideaki } 24687bc570c8SYOSHIFUJI Hideaki } 24697bc570c8SYOSHIFUJI Hideaki } else 24707bc570c8SYOSHIFUJI Hideaki #endif 2471c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 2472c78679e8SDavid S. Miller goto nla_put_failure; 24737bc570c8SYOSHIFUJI Hideaki } else if (dst) { 24741da177e4SLinus Torvalds struct in6_addr saddr_buf; 2475c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 2476c78679e8SDavid S. Miller nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) 2477c78679e8SDavid S. Miller goto nla_put_failure; 2478c3968a85SDaniel Walter } 2479c3968a85SDaniel Walter 2480c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 2481c3968a85SDaniel Walter struct in6_addr saddr_buf; 24824e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 2483c78679e8SDavid S. Miller if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) 2484c78679e8SDavid S. Miller goto nla_put_failure; 24851da177e4SLinus Torvalds } 24862d7202bfSThomas Graf 2487defb3519SDavid S. Miller if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) 24882d7202bfSThomas Graf goto nla_put_failure; 24892d7202bfSThomas Graf 249097cac082SDavid S. Miller n = rt->n; 249194f826b8SEric Dumazet if (n) { 2492fdd6681dSAmerigo Wang if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) 249394f826b8SEric Dumazet goto nla_put_failure; 249494f826b8SEric Dumazet } 24952d7202bfSThomas Graf 2496c78679e8SDavid S. Miller if (rt->dst.dev && 2497c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 2498c78679e8SDavid S. Miller goto nla_put_failure; 2499c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 2500c78679e8SDavid S. Miller goto nla_put_failure; 25018253947eSLi Wei 25028253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 250369cdf8f9SYOSHIFUJI Hideaki 250487a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 2505e3703b3dSThomas Graf goto nla_put_failure; 25061da177e4SLinus Torvalds 25072d7202bfSThomas Graf return nlmsg_end(skb, nlh); 25082d7202bfSThomas Graf 25092d7202bfSThomas Graf nla_put_failure: 251026932566SPatrick McHardy nlmsg_cancel(skb, nlh); 251126932566SPatrick McHardy return -EMSGSIZE; 25121da177e4SLinus Torvalds } 25131da177e4SLinus Torvalds 25141b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 25151da177e4SLinus Torvalds { 25161da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 25171da177e4SLinus Torvalds int prefix; 25181da177e4SLinus Torvalds 25192d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 25202d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 25211da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 25221da177e4SLinus Torvalds } else 25231da177e4SLinus Torvalds prefix = 0; 25241da177e4SLinus Torvalds 2525191cd582SBrian Haley return rt6_fill_node(arg->net, 2526191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 252715e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 25287bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 25291da177e4SLinus Torvalds } 25301da177e4SLinus Torvalds 2531c127ea2cSThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) 25321da177e4SLinus Torvalds { 25333b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 2534ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 25351da177e4SLinus Torvalds struct rt6_info *rt; 2536ab364a6fSThomas Graf struct sk_buff *skb; 2537ab364a6fSThomas Graf struct rtmsg *rtm; 25384c9483b2SDavid S. Miller struct flowi6 fl6; 253972331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 2540ab364a6fSThomas Graf 2541ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 2542ab364a6fSThomas Graf if (err < 0) 2543ab364a6fSThomas Graf goto errout; 2544ab364a6fSThomas Graf 2545ab364a6fSThomas Graf err = -EINVAL; 25464c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2547ab364a6fSThomas Graf 2548ab364a6fSThomas Graf if (tb[RTA_SRC]) { 2549ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 2550ab364a6fSThomas Graf goto errout; 2551ab364a6fSThomas Graf 25524e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 2553ab364a6fSThomas Graf } 2554ab364a6fSThomas Graf 2555ab364a6fSThomas Graf if (tb[RTA_DST]) { 2556ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 2557ab364a6fSThomas Graf goto errout; 2558ab364a6fSThomas Graf 25594e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 2560ab364a6fSThomas Graf } 2561ab364a6fSThomas Graf 2562ab364a6fSThomas Graf if (tb[RTA_IIF]) 2563ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 2564ab364a6fSThomas Graf 2565ab364a6fSThomas Graf if (tb[RTA_OIF]) 256672331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 2567ab364a6fSThomas Graf 2568ab364a6fSThomas Graf if (iif) { 2569ab364a6fSThomas Graf struct net_device *dev; 257072331bc0SShmulik Ladkani int flags = 0; 257172331bc0SShmulik Ladkani 25725578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 2573ab364a6fSThomas Graf if (!dev) { 2574ab364a6fSThomas Graf err = -ENODEV; 2575ab364a6fSThomas Graf goto errout; 2576ab364a6fSThomas Graf } 257772331bc0SShmulik Ladkani 257872331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 257972331bc0SShmulik Ladkani 258072331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 258172331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 258272331bc0SShmulik Ladkani 258372331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 258472331bc0SShmulik Ladkani flags); 258572331bc0SShmulik Ladkani } else { 258672331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 258772331bc0SShmulik Ladkani 258872331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 2589ab364a6fSThomas Graf } 25901da177e4SLinus Torvalds 25911da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 259238308473SDavid S. Miller if (!skb) { 25932173bff5SShmulik Ladkani dst_release(&rt->dst); 2594ab364a6fSThomas Graf err = -ENOBUFS; 2595ab364a6fSThomas Graf goto errout; 2596ab364a6fSThomas Graf } 25971da177e4SLinus Torvalds 25981da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 25991da177e4SLinus Torvalds through good chunk of routing engine. 26001da177e4SLinus Torvalds */ 2601459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 26021da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 26031da177e4SLinus Torvalds 2604d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 26051da177e4SLinus Torvalds 26064c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 260715e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 26087bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 26091da177e4SLinus Torvalds if (err < 0) { 2610ab364a6fSThomas Graf kfree_skb(skb); 2611ab364a6fSThomas Graf goto errout; 26121da177e4SLinus Torvalds } 26131da177e4SLinus Torvalds 261415e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 2615ab364a6fSThomas Graf errout: 26161da177e4SLinus Torvalds return err; 26171da177e4SLinus Torvalds } 26181da177e4SLinus Torvalds 261986872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) 26201da177e4SLinus Torvalds { 26211da177e4SLinus Torvalds struct sk_buff *skb; 26225578689aSDaniel Lezcano struct net *net = info->nl_net; 2623528c4cebSDenis V. Lunev u32 seq; 2624528c4cebSDenis V. Lunev int err; 26250d51aa80SJamal Hadi Salim 2626528c4cebSDenis V. Lunev err = -ENOBUFS; 262738308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 262886872cb5SThomas Graf 2629339bf98fSThomas Graf skb = nlmsg_new(rt6_nlmsg_size(), gfp_any()); 263038308473SDavid S. Miller if (!skb) 263121713ebcSThomas Graf goto errout; 26321da177e4SLinus Torvalds 2633191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 263415e47304SEric W. Biederman event, info->portid, seq, 0, 0, 0); 263526932566SPatrick McHardy if (err < 0) { 263626932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 263726932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 263826932566SPatrick McHardy kfree_skb(skb); 263926932566SPatrick McHardy goto errout; 264026932566SPatrick McHardy } 264115e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 26425578689aSDaniel Lezcano info->nlh, gfp_any()); 26431ce85fe4SPablo Neira Ayuso return; 264421713ebcSThomas Graf errout: 264521713ebcSThomas Graf if (err < 0) 26465578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 26471da177e4SLinus Torvalds } 26481da177e4SLinus Torvalds 26498ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 26508ed67789SDaniel Lezcano unsigned long event, void *data) 26518ed67789SDaniel Lezcano { 26528ed67789SDaniel Lezcano struct net_device *dev = (struct net_device *)data; 2653c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 26548ed67789SDaniel Lezcano 26558ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 2656d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 26578ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 26588ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 2659d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 26608ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 2661d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 26628ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 26638ed67789SDaniel Lezcano #endif 26648ed67789SDaniel Lezcano } 26658ed67789SDaniel Lezcano 26668ed67789SDaniel Lezcano return NOTIFY_OK; 26678ed67789SDaniel Lezcano } 26688ed67789SDaniel Lezcano 26691da177e4SLinus Torvalds /* 26701da177e4SLinus Torvalds * /proc 26711da177e4SLinus Torvalds */ 26721da177e4SLinus Torvalds 26731da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 26741da177e4SLinus Torvalds 26751da177e4SLinus Torvalds struct rt6_proc_arg 26761da177e4SLinus Torvalds { 26771da177e4SLinus Torvalds char *buffer; 26781da177e4SLinus Torvalds int offset; 26791da177e4SLinus Torvalds int length; 26801da177e4SLinus Torvalds int skip; 26811da177e4SLinus Torvalds int len; 26821da177e4SLinus Torvalds }; 26831da177e4SLinus Torvalds 26841da177e4SLinus Torvalds static int rt6_info_route(struct rt6_info *rt, void *p_arg) 26851da177e4SLinus Torvalds { 268633120b30SAlexey Dobriyan struct seq_file *m = p_arg; 268769cce1d1SDavid S. Miller struct neighbour *n; 26881da177e4SLinus Torvalds 26894b7a4274SHarvey Harrison seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen); 26901da177e4SLinus Torvalds 26911da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 26924b7a4274SHarvey Harrison seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen); 26931da177e4SLinus Torvalds #else 269433120b30SAlexey Dobriyan seq_puts(m, "00000000000000000000000000000000 00 "); 26951da177e4SLinus Torvalds #endif 269697cac082SDavid S. Miller n = rt->n; 269769cce1d1SDavid S. Miller if (n) { 269869cce1d1SDavid S. Miller seq_printf(m, "%pi6", n->primary_key); 26991da177e4SLinus Torvalds } else { 270033120b30SAlexey Dobriyan seq_puts(m, "00000000000000000000000000000000"); 27011da177e4SLinus Torvalds } 270233120b30SAlexey Dobriyan seq_printf(m, " %08x %08x %08x %08x %8s\n", 2703d8d1f30bSChangli Gao rt->rt6i_metric, atomic_read(&rt->dst.__refcnt), 2704d8d1f30bSChangli Gao rt->dst.__use, rt->rt6i_flags, 2705d1918542SDavid S. Miller rt->dst.dev ? rt->dst.dev->name : ""); 27061da177e4SLinus Torvalds return 0; 27071da177e4SLinus Torvalds } 27081da177e4SLinus Torvalds 270933120b30SAlexey Dobriyan static int ipv6_route_show(struct seq_file *m, void *v) 27101da177e4SLinus Torvalds { 2711f3db4851SDaniel Lezcano struct net *net = (struct net *)m->private; 271232b293a5SJosh Hunt fib6_clean_all_ro(net, rt6_info_route, 0, m); 271333120b30SAlexey Dobriyan return 0; 27141da177e4SLinus Torvalds } 27151da177e4SLinus Torvalds 271633120b30SAlexey Dobriyan static int ipv6_route_open(struct inode *inode, struct file *file) 271733120b30SAlexey Dobriyan { 2718de05c557SPavel Emelyanov return single_open_net(inode, file, ipv6_route_show); 2719f3db4851SDaniel Lezcano } 2720f3db4851SDaniel Lezcano 272133120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 272233120b30SAlexey Dobriyan .owner = THIS_MODULE, 272333120b30SAlexey Dobriyan .open = ipv6_route_open, 272433120b30SAlexey Dobriyan .read = seq_read, 272533120b30SAlexey Dobriyan .llseek = seq_lseek, 2726b6fcbdb4SPavel Emelyanov .release = single_release_net, 272733120b30SAlexey Dobriyan }; 272833120b30SAlexey Dobriyan 27291da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 27301da177e4SLinus Torvalds { 273169ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 27321da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 273369ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 273469ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 273569ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 273669ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 273769ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 2738fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 273969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 27401da177e4SLinus Torvalds 27411da177e4SLinus Torvalds return 0; 27421da177e4SLinus Torvalds } 27431da177e4SLinus Torvalds 27441da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 27451da177e4SLinus Torvalds { 2746de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 274769ddb805SDaniel Lezcano } 274869ddb805SDaniel Lezcano 27499a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 27501da177e4SLinus Torvalds .owner = THIS_MODULE, 27511da177e4SLinus Torvalds .open = rt6_stats_seq_open, 27521da177e4SLinus Torvalds .read = seq_read, 27531da177e4SLinus Torvalds .llseek = seq_lseek, 2754b6fcbdb4SPavel Emelyanov .release = single_release_net, 27551da177e4SLinus Torvalds }; 27561da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 27571da177e4SLinus Torvalds 27581da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 27591da177e4SLinus Torvalds 27601da177e4SLinus Torvalds static 27618d65af78SAlexey Dobriyan int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, 27621da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 27631da177e4SLinus Torvalds { 2764c486da34SLucian Adrian Grijincu struct net *net; 2765c486da34SLucian Adrian Grijincu int delay; 2766c486da34SLucian Adrian Grijincu if (!write) 2767c486da34SLucian Adrian Grijincu return -EINVAL; 2768c486da34SLucian Adrian Grijincu 2769c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 2770c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 27718d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 27725b7c931dSDaniel Lezcano fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net); 27731da177e4SLinus Torvalds return 0; 27741da177e4SLinus Torvalds } 27751da177e4SLinus Torvalds 2776760f2d01SDaniel Lezcano ctl_table ipv6_route_table_template[] = { 27771da177e4SLinus Torvalds { 27781da177e4SLinus Torvalds .procname = "flush", 27794990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 27801da177e4SLinus Torvalds .maxlen = sizeof(int), 278189c8b3a1SDave Jones .mode = 0200, 27826d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 27831da177e4SLinus Torvalds }, 27841da177e4SLinus Torvalds { 27851da177e4SLinus Torvalds .procname = "gc_thresh", 27869a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 27871da177e4SLinus Torvalds .maxlen = sizeof(int), 27881da177e4SLinus Torvalds .mode = 0644, 27896d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 27901da177e4SLinus Torvalds }, 27911da177e4SLinus Torvalds { 27921da177e4SLinus Torvalds .procname = "max_size", 27934990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 27941da177e4SLinus Torvalds .maxlen = sizeof(int), 27951da177e4SLinus Torvalds .mode = 0644, 27966d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 27971da177e4SLinus Torvalds }, 27981da177e4SLinus Torvalds { 27991da177e4SLinus Torvalds .procname = "gc_min_interval", 28004990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 28011da177e4SLinus Torvalds .maxlen = sizeof(int), 28021da177e4SLinus Torvalds .mode = 0644, 28036d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28041da177e4SLinus Torvalds }, 28051da177e4SLinus Torvalds { 28061da177e4SLinus Torvalds .procname = "gc_timeout", 28074990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 28081da177e4SLinus Torvalds .maxlen = sizeof(int), 28091da177e4SLinus Torvalds .mode = 0644, 28106d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28111da177e4SLinus Torvalds }, 28121da177e4SLinus Torvalds { 28131da177e4SLinus Torvalds .procname = "gc_interval", 28144990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 28151da177e4SLinus Torvalds .maxlen = sizeof(int), 28161da177e4SLinus Torvalds .mode = 0644, 28176d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28181da177e4SLinus Torvalds }, 28191da177e4SLinus Torvalds { 28201da177e4SLinus Torvalds .procname = "gc_elasticity", 28214990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 28221da177e4SLinus Torvalds .maxlen = sizeof(int), 28231da177e4SLinus Torvalds .mode = 0644, 2824f3d3f616SMin Zhang .proc_handler = proc_dointvec, 28251da177e4SLinus Torvalds }, 28261da177e4SLinus Torvalds { 28271da177e4SLinus Torvalds .procname = "mtu_expires", 28284990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 28291da177e4SLinus Torvalds .maxlen = sizeof(int), 28301da177e4SLinus Torvalds .mode = 0644, 28316d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28321da177e4SLinus Torvalds }, 28331da177e4SLinus Torvalds { 28341da177e4SLinus Torvalds .procname = "min_adv_mss", 28354990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 28361da177e4SLinus Torvalds .maxlen = sizeof(int), 28371da177e4SLinus Torvalds .mode = 0644, 2838f3d3f616SMin Zhang .proc_handler = proc_dointvec, 28391da177e4SLinus Torvalds }, 28401da177e4SLinus Torvalds { 28411da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 28424990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 28431da177e4SLinus Torvalds .maxlen = sizeof(int), 28441da177e4SLinus Torvalds .mode = 0644, 28456d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 28461da177e4SLinus Torvalds }, 2847f8572d8fSEric W. Biederman { } 28481da177e4SLinus Torvalds }; 28491da177e4SLinus Torvalds 28502c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 2851760f2d01SDaniel Lezcano { 2852760f2d01SDaniel Lezcano struct ctl_table *table; 2853760f2d01SDaniel Lezcano 2854760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 2855760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 2856760f2d01SDaniel Lezcano GFP_KERNEL); 28575ee09105SYOSHIFUJI Hideaki 28585ee09105SYOSHIFUJI Hideaki if (table) { 28595ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 2860c486da34SLucian Adrian Grijincu table[0].extra1 = net; 286186393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 28625ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 28635ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 28645ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 28655ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 28665ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 28675ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 28685ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 28699c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 28705ee09105SYOSHIFUJI Hideaki } 28715ee09105SYOSHIFUJI Hideaki 2872760f2d01SDaniel Lezcano return table; 2873760f2d01SDaniel Lezcano } 28741da177e4SLinus Torvalds #endif 28751da177e4SLinus Torvalds 28762c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 2877cdb18761SDaniel Lezcano { 2878633d424bSPavel Emelyanov int ret = -ENOMEM; 28798ed67789SDaniel Lezcano 288086393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 288186393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 2882f2fc6a54SBenjamin Thery 2883fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 2884fc66f95cSEric Dumazet goto out_ip6_dst_ops; 2885fc66f95cSEric Dumazet 28868ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 28878ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 28888ed67789SDaniel Lezcano GFP_KERNEL); 28898ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 2890fc66f95cSEric Dumazet goto out_ip6_dst_entries; 2891d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 28928ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 2893d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 289462fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 289562fa8a84SDavid S. Miller ip6_template_metrics, true); 28968ed67789SDaniel Lezcano 28978ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 28988ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 28998ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 29008ed67789SDaniel Lezcano GFP_KERNEL); 290168fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 290268fffc67SPeter Zijlstra goto out_ip6_null_entry; 2903d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 29048ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 2905d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 290662fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 290762fa8a84SDavid S. Miller ip6_template_metrics, true); 29088ed67789SDaniel Lezcano 29098ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 29108ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 29118ed67789SDaniel Lezcano GFP_KERNEL); 291268fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 291368fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 2914d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 29158ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 2916d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 291762fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 291862fa8a84SDavid S. Miller ip6_template_metrics, true); 29198ed67789SDaniel Lezcano #endif 29208ed67789SDaniel Lezcano 2921b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 2922b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 2923b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 2924b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 2925b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 2926b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 2927b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 2928b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 2929b339a47cSPeter Zijlstra 29306891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 29316891a346SBenjamin Thery 29328ed67789SDaniel Lezcano ret = 0; 29338ed67789SDaniel Lezcano out: 29348ed67789SDaniel Lezcano return ret; 2935f2fc6a54SBenjamin Thery 293668fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 293768fffc67SPeter Zijlstra out_ip6_prohibit_entry: 293868fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 293968fffc67SPeter Zijlstra out_ip6_null_entry: 294068fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 294168fffc67SPeter Zijlstra #endif 2942fc66f95cSEric Dumazet out_ip6_dst_entries: 2943fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 2944f2fc6a54SBenjamin Thery out_ip6_dst_ops: 2945f2fc6a54SBenjamin Thery goto out; 2946cdb18761SDaniel Lezcano } 2947cdb18761SDaniel Lezcano 29482c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 2949cdb18761SDaniel Lezcano { 29508ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 29518ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 29528ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 29538ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 29548ed67789SDaniel Lezcano #endif 295541bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 2956cdb18761SDaniel Lezcano } 2957cdb18761SDaniel Lezcano 2958d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 2959d189634eSThomas Graf { 2960d189634eSThomas Graf #ifdef CONFIG_PROC_FS 2961d189634eSThomas Graf proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops); 2962d189634eSThomas Graf proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops); 2963d189634eSThomas Graf #endif 2964d189634eSThomas Graf return 0; 2965d189634eSThomas Graf } 2966d189634eSThomas Graf 2967d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 2968d189634eSThomas Graf { 2969d189634eSThomas Graf #ifdef CONFIG_PROC_FS 2970d189634eSThomas Graf proc_net_remove(net, "ipv6_route"); 2971d189634eSThomas Graf proc_net_remove(net, "rt6_stats"); 2972d189634eSThomas Graf #endif 2973d189634eSThomas Graf } 2974d189634eSThomas Graf 2975cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 2976cdb18761SDaniel Lezcano .init = ip6_route_net_init, 2977cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 2978cdb18761SDaniel Lezcano }; 2979cdb18761SDaniel Lezcano 2980c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 2981c3426b47SDavid S. Miller { 2982c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 2983c3426b47SDavid S. Miller 2984c3426b47SDavid S. Miller if (!bp) 2985c3426b47SDavid S. Miller return -ENOMEM; 2986c3426b47SDavid S. Miller inet_peer_base_init(bp); 2987c3426b47SDavid S. Miller net->ipv6.peers = bp; 2988c3426b47SDavid S. Miller return 0; 2989c3426b47SDavid S. Miller } 2990c3426b47SDavid S. Miller 2991c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 2992c3426b47SDavid S. Miller { 2993c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 2994c3426b47SDavid S. Miller 2995c3426b47SDavid S. Miller net->ipv6.peers = NULL; 299656a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 2997c3426b47SDavid S. Miller kfree(bp); 2998c3426b47SDavid S. Miller } 2999c3426b47SDavid S. Miller 30002b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3001c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3002c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3003c3426b47SDavid S. Miller }; 3004c3426b47SDavid S. Miller 3005d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3006d189634eSThomas Graf .init = ip6_route_net_init_late, 3007d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3008d189634eSThomas Graf }; 3009d189634eSThomas Graf 30108ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 30118ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 30128ed67789SDaniel Lezcano .priority = 0, 30138ed67789SDaniel Lezcano }; 30148ed67789SDaniel Lezcano 3015433d49c3SDaniel Lezcano int __init ip6_route_init(void) 30161da177e4SLinus Torvalds { 3017433d49c3SDaniel Lezcano int ret; 3018433d49c3SDaniel Lezcano 30199a7ec3a9SDaniel Lezcano ret = -ENOMEM; 30209a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 30219a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 30229a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 30239a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3024c19a28e1SFernando Carrijo goto out; 302514e50e57SDavid S. Miller 3026fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 30278ed67789SDaniel Lezcano if (ret) 3028bdb3289fSDaniel Lezcano goto out_kmem_cache; 3029bdb3289fSDaniel Lezcano 3030c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3031c3426b47SDavid S. Miller if (ret) 3032e8803b6cSDavid S. Miller goto out_dst_entries; 30332a0c451aSThomas Graf 30347e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 30357e52b33bSDavid S. Miller if (ret) 30367e52b33bSDavid S. Miller goto out_register_inetpeer; 3037c3426b47SDavid S. Miller 30385dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 30395dc121e9SArnaud Ebalard 30408ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 30418ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 30428ed67789SDaniel Lezcano * manually for init_net */ 3043d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 30448ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3045bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3046d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 30478ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3048d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 30498ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3050bdb3289fSDaniel Lezcano #endif 3051e8803b6cSDavid S. Miller ret = fib6_init(); 3052433d49c3SDaniel Lezcano if (ret) 30538ed67789SDaniel Lezcano goto out_register_subsys; 3054433d49c3SDaniel Lezcano 3055433d49c3SDaniel Lezcano ret = xfrm6_init(); 3056433d49c3SDaniel Lezcano if (ret) 3057e8803b6cSDavid S. Miller goto out_fib6_init; 3058c35b7e72SDaniel Lezcano 3059433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3060433d49c3SDaniel Lezcano if (ret) 3061433d49c3SDaniel Lezcano goto xfrm6_init; 30627e5449c2SDaniel Lezcano 3063d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3064d189634eSThomas Graf if (ret) 3065d189634eSThomas Graf goto fib6_rules_init; 3066d189634eSThomas Graf 3067433d49c3SDaniel Lezcano ret = -ENOBUFS; 3068c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3069c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3070c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3071d189634eSThomas Graf goto out_register_late_subsys; 3072433d49c3SDaniel Lezcano 30738ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3074cdb18761SDaniel Lezcano if (ret) 3075d189634eSThomas Graf goto out_register_late_subsys; 30768ed67789SDaniel Lezcano 3077433d49c3SDaniel Lezcano out: 3078433d49c3SDaniel Lezcano return ret; 3079433d49c3SDaniel Lezcano 3080d189634eSThomas Graf out_register_late_subsys: 3081d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3082433d49c3SDaniel Lezcano fib6_rules_init: 3083433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3084433d49c3SDaniel Lezcano xfrm6_init: 3085433d49c3SDaniel Lezcano xfrm6_fini(); 30862a0c451aSThomas Graf out_fib6_init: 30872a0c451aSThomas Graf fib6_gc_cleanup(); 30888ed67789SDaniel Lezcano out_register_subsys: 30898ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 30907e52b33bSDavid S. Miller out_register_inetpeer: 30917e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3092fc66f95cSEric Dumazet out_dst_entries: 3093fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3094433d49c3SDaniel Lezcano out_kmem_cache: 3095f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3096433d49c3SDaniel Lezcano goto out; 30971da177e4SLinus Torvalds } 30981da177e4SLinus Torvalds 30991da177e4SLinus Torvalds void ip6_route_cleanup(void) 31001da177e4SLinus Torvalds { 31018ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3102d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3103101367c2SThomas Graf fib6_rules_cleanup(); 31041da177e4SLinus Torvalds xfrm6_fini(); 31051da177e4SLinus Torvalds fib6_gc_cleanup(); 3106c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 31078ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 310841bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3109f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 31101da177e4SLinus Torvalds } 3111