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 225bdb3289fSDaniel Lezcano static 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 245280a34c8SAdrian Bunk static 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 260bdb3289fSDaniel Lezcano static 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, 284*6f3118b5SNicolas Dichtel 0, DST_OBSOLETE_FORCE_CHK, 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); 291*6f3118b5SNicolas Dichtel rt->rt6i_genid = rt_genid(net); 29297bab73fSDavid S. Miller } 293cf911662SDavid S. Miller return rt; 2941da177e4SLinus Torvalds } 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 2971da177e4SLinus Torvalds { 2981da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 2991da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3001da177e4SLinus Torvalds 30197cac082SDavid S. Miller if (rt->n) 30297cac082SDavid S. Miller neigh_release(rt->n); 30397cac082SDavid S. Miller 3048e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST)) 3058e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 3068e2ec639SYan, Zheng 30738308473SDavid S. Miller if (idev) { 3081da177e4SLinus Torvalds rt->rt6i_idev = NULL; 3091da177e4SLinus Torvalds in6_dev_put(idev); 3101da177e4SLinus Torvalds } 3111716a961SGao feng 3121716a961SGao feng if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from) 3131716a961SGao feng dst_release(dst->from); 3141716a961SGao feng 31597bab73fSDavid S. Miller if (rt6_has_peer(rt)) { 31697bab73fSDavid S. Miller struct inet_peer *peer = rt6_peer_ptr(rt); 317b3419363SDavid S. Miller inet_putpeer(peer); 318b3419363SDavid S. Miller } 319b3419363SDavid S. Miller } 320b3419363SDavid S. Miller 3216431cbc2SDavid S. Miller static atomic_t __rt6_peer_genid = ATOMIC_INIT(0); 3226431cbc2SDavid S. Miller 3236431cbc2SDavid S. Miller static u32 rt6_peer_genid(void) 3246431cbc2SDavid S. Miller { 3256431cbc2SDavid S. Miller return atomic_read(&__rt6_peer_genid); 3266431cbc2SDavid S. Miller } 3276431cbc2SDavid S. Miller 328b3419363SDavid S. Miller void rt6_bind_peer(struct rt6_info *rt, int create) 329b3419363SDavid S. Miller { 33097bab73fSDavid S. Miller struct inet_peer_base *base; 331b3419363SDavid S. Miller struct inet_peer *peer; 332b3419363SDavid S. Miller 33397bab73fSDavid S. Miller base = inetpeer_base_ptr(rt->_rt6i_peer); 33497bab73fSDavid S. Miller if (!base) 33597bab73fSDavid S. Miller return; 33697bab73fSDavid S. Miller 33797bab73fSDavid S. Miller peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create); 3387b34ca2aSDavid S. Miller if (peer) { 33997bab73fSDavid S. Miller if (!rt6_set_peer(rt, peer)) 340b3419363SDavid S. Miller inet_putpeer(peer); 3416431cbc2SDavid S. Miller else 3426431cbc2SDavid S. Miller rt->rt6i_peer_genid = rt6_peer_genid(); 3431da177e4SLinus Torvalds } 3447b34ca2aSDavid S. Miller } 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 3471da177e4SLinus Torvalds int how) 3481da177e4SLinus Torvalds { 3491da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3501da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3515a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 352c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 3531da177e4SLinus Torvalds 35497cac082SDavid S. Miller if (dev != loopback_dev) { 35597cac082SDavid S. Miller if (idev && idev->dev == dev) { 3565a3e55d6SDenis V. Lunev struct inet6_dev *loopback_idev = 3575a3e55d6SDenis V. Lunev in6_dev_get(loopback_dev); 35838308473SDavid S. Miller if (loopback_idev) { 3591da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 3601da177e4SLinus Torvalds in6_dev_put(idev); 3611da177e4SLinus Torvalds } 3621da177e4SLinus Torvalds } 36397cac082SDavid S. Miller if (rt->n && rt->n->dev == dev) { 36497cac082SDavid S. Miller rt->n->dev = loopback_dev; 36597cac082SDavid S. Miller dev_hold(loopback_dev); 36697cac082SDavid S. Miller dev_put(dev); 36797cac082SDavid S. Miller } 36897cac082SDavid S. Miller } 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds 371a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt) 3721da177e4SLinus Torvalds { 3731716a961SGao feng struct rt6_info *ort = NULL; 3741716a961SGao feng 3751716a961SGao feng if (rt->rt6i_flags & RTF_EXPIRES) { 3761716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 377a50feda5SEric Dumazet return true; 3781716a961SGao feng } else if (rt->dst.from) { 3791716a961SGao feng ort = (struct rt6_info *) rt->dst.from; 3801716a961SGao feng return (ort->rt6i_flags & RTF_EXPIRES) && 3811716a961SGao feng time_after(jiffies, ort->dst.expires); 3821716a961SGao feng } 383a50feda5SEric Dumazet return false; 3841da177e4SLinus Torvalds } 3851da177e4SLinus Torvalds 386a50feda5SEric Dumazet static bool rt6_need_strict(const struct in6_addr *daddr) 387c71099acSThomas Graf { 388a02cec21SEric Dumazet return ipv6_addr_type(daddr) & 389a02cec21SEric Dumazet (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); 390c71099acSThomas Graf } 391c71099acSThomas Graf 3921da177e4SLinus Torvalds /* 393c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 3941da177e4SLinus Torvalds */ 3951da177e4SLinus Torvalds 3968ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 3978ed67789SDaniel Lezcano struct rt6_info *rt, 398b71d1d42SEric Dumazet const struct in6_addr *saddr, 3991da177e4SLinus Torvalds int oif, 400d420895eSYOSHIFUJI Hideaki int flags) 4011da177e4SLinus Torvalds { 4021da177e4SLinus Torvalds struct rt6_info *local = NULL; 4031da177e4SLinus Torvalds struct rt6_info *sprt; 4041da177e4SLinus Torvalds 405dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 406dd3abc4eSYOSHIFUJI Hideaki goto out; 407dd3abc4eSYOSHIFUJI Hideaki 408d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 409d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 410dd3abc4eSYOSHIFUJI Hideaki 411dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4121da177e4SLinus Torvalds if (dev->ifindex == oif) 4131da177e4SLinus Torvalds return sprt; 4141da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 41538308473SDavid S. Miller if (!sprt->rt6i_idev || 4161da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 417d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE && oif) 4181da177e4SLinus Torvalds continue; 4191da177e4SLinus Torvalds if (local && (!oif || 4201da177e4SLinus Torvalds local->rt6i_idev->dev->ifindex == oif)) 4211da177e4SLinus Torvalds continue; 4221da177e4SLinus Torvalds } 4231da177e4SLinus Torvalds local = sprt; 4241da177e4SLinus Torvalds } 425dd3abc4eSYOSHIFUJI Hideaki } else { 426dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 427dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 428dd3abc4eSYOSHIFUJI Hideaki return sprt; 429dd3abc4eSYOSHIFUJI Hideaki } 4301da177e4SLinus Torvalds } 4311da177e4SLinus Torvalds 432dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4331da177e4SLinus Torvalds if (local) 4341da177e4SLinus Torvalds return local; 4351da177e4SLinus Torvalds 436d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 4378ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 4381da177e4SLinus Torvalds } 439dd3abc4eSYOSHIFUJI Hideaki out: 4401da177e4SLinus Torvalds return rt; 4411da177e4SLinus Torvalds } 4421da177e4SLinus Torvalds 44327097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 44427097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 44527097255SYOSHIFUJI Hideaki { 446f2c31e32SEric Dumazet struct neighbour *neigh; 44727097255SYOSHIFUJI Hideaki /* 44827097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 44927097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 45027097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 45127097255SYOSHIFUJI Hideaki * 45227097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 45327097255SYOSHIFUJI Hideaki * to no more than one per minute. 45427097255SYOSHIFUJI Hideaki */ 455f2c31e32SEric Dumazet rcu_read_lock(); 45697cac082SDavid S. Miller neigh = rt ? rt->n : NULL; 45727097255SYOSHIFUJI Hideaki if (!neigh || (neigh->nud_state & NUD_VALID)) 458f2c31e32SEric Dumazet goto out; 45927097255SYOSHIFUJI Hideaki read_lock_bh(&neigh->lock); 46027097255SYOSHIFUJI Hideaki if (!(neigh->nud_state & NUD_VALID) && 46152e16356SYOSHIFUJI Hideaki time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { 46227097255SYOSHIFUJI Hideaki struct in6_addr mcaddr; 46327097255SYOSHIFUJI Hideaki struct in6_addr *target; 46427097255SYOSHIFUJI Hideaki 46527097255SYOSHIFUJI Hideaki neigh->updated = jiffies; 46627097255SYOSHIFUJI Hideaki read_unlock_bh(&neigh->lock); 46727097255SYOSHIFUJI Hideaki 46827097255SYOSHIFUJI Hideaki target = (struct in6_addr *)&neigh->primary_key; 46927097255SYOSHIFUJI Hideaki addrconf_addr_solict_mult(target, &mcaddr); 470d1918542SDavid S. Miller ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL); 471f2c31e32SEric Dumazet } else { 47227097255SYOSHIFUJI Hideaki read_unlock_bh(&neigh->lock); 47327097255SYOSHIFUJI Hideaki } 474f2c31e32SEric Dumazet out: 475f2c31e32SEric Dumazet rcu_read_unlock(); 476f2c31e32SEric Dumazet } 47727097255SYOSHIFUJI Hideaki #else 47827097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 47927097255SYOSHIFUJI Hideaki { 48027097255SYOSHIFUJI Hideaki } 48127097255SYOSHIFUJI Hideaki #endif 48227097255SYOSHIFUJI Hideaki 4831da177e4SLinus Torvalds /* 484554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 4851da177e4SLinus Torvalds */ 486b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 4871da177e4SLinus Torvalds { 488d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 489161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 490554cfb7eSYOSHIFUJI Hideaki return 2; 491161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 492161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 493161980f4SDavid S. Miller return 1; 494554cfb7eSYOSHIFUJI Hideaki return 0; 4951da177e4SLinus Torvalds } 4961da177e4SLinus Torvalds 497b6f99a21SDave Jones static inline int rt6_check_neigh(struct rt6_info *rt) 4981da177e4SLinus Torvalds { 499f2c31e32SEric Dumazet struct neighbour *neigh; 500398bcbebSYOSHIFUJI Hideaki int m; 501f2c31e32SEric Dumazet 502f2c31e32SEric Dumazet rcu_read_lock(); 50397cac082SDavid S. Miller neigh = rt->n; 5044d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 5054d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 5064d0c5911SYOSHIFUJI Hideaki m = 1; 5074d0c5911SYOSHIFUJI Hideaki else if (neigh) { 5081da177e4SLinus Torvalds read_lock_bh(&neigh->lock); 509554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 5104d0c5911SYOSHIFUJI Hideaki m = 2; 511398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 512398bcbebSYOSHIFUJI Hideaki else if (neigh->nud_state & NUD_FAILED) 513398bcbebSYOSHIFUJI Hideaki m = 0; 514398bcbebSYOSHIFUJI Hideaki #endif 515398bcbebSYOSHIFUJI Hideaki else 516ea73ee23SYOSHIFUJI Hideaki m = 1; 5171da177e4SLinus Torvalds read_unlock_bh(&neigh->lock); 518398bcbebSYOSHIFUJI Hideaki } else 519398bcbebSYOSHIFUJI Hideaki m = 0; 520f2c31e32SEric Dumazet rcu_read_unlock(); 521554cfb7eSYOSHIFUJI Hideaki return m; 5221da177e4SLinus Torvalds } 5231da177e4SLinus Torvalds 524554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 525554cfb7eSYOSHIFUJI Hideaki int strict) 526554cfb7eSYOSHIFUJI Hideaki { 5274d0c5911SYOSHIFUJI Hideaki int m, n; 5284d0c5911SYOSHIFUJI Hideaki 5294d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 53077d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 531554cfb7eSYOSHIFUJI Hideaki return -1; 532ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 533ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 534ebacaaa0SYOSHIFUJI Hideaki #endif 5354d0c5911SYOSHIFUJI Hideaki n = rt6_check_neigh(rt); 536557e92efSYOSHIFUJI Hideaki if (!n && (strict & RT6_LOOKUP_F_REACHABLE)) 537554cfb7eSYOSHIFUJI Hideaki return -1; 538554cfb7eSYOSHIFUJI Hideaki return m; 539554cfb7eSYOSHIFUJI Hideaki } 540554cfb7eSYOSHIFUJI Hideaki 541f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 542f11e6659SDavid S. Miller int *mpri, struct rt6_info *match) 543554cfb7eSYOSHIFUJI Hideaki { 544554cfb7eSYOSHIFUJI Hideaki int m; 545554cfb7eSYOSHIFUJI Hideaki 546554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 547f11e6659SDavid S. Miller goto out; 548554cfb7eSYOSHIFUJI Hideaki 549554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 550554cfb7eSYOSHIFUJI Hideaki if (m < 0) 551f11e6659SDavid S. Miller goto out; 552554cfb7eSYOSHIFUJI Hideaki 553f11e6659SDavid S. Miller if (m > *mpri) { 554ea659e07SYOSHIFUJI Hideaki if (strict & RT6_LOOKUP_F_REACHABLE) 55527097255SYOSHIFUJI Hideaki rt6_probe(match); 556f11e6659SDavid S. Miller *mpri = m; 557554cfb7eSYOSHIFUJI Hideaki match = rt; 558ea659e07SYOSHIFUJI Hideaki } else if (strict & RT6_LOOKUP_F_REACHABLE) { 55927097255SYOSHIFUJI Hideaki rt6_probe(rt); 5601da177e4SLinus Torvalds } 561f11e6659SDavid S. Miller 562f11e6659SDavid S. Miller out: 563f11e6659SDavid S. Miller return match; 5641da177e4SLinus Torvalds } 5651da177e4SLinus Torvalds 566f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 567f11e6659SDavid S. Miller struct rt6_info *rr_head, 568f11e6659SDavid S. Miller u32 metric, int oif, int strict) 569f11e6659SDavid S. Miller { 570f11e6659SDavid S. Miller struct rt6_info *rt, *match; 571f11e6659SDavid S. Miller int mpri = -1; 572f11e6659SDavid S. Miller 573f11e6659SDavid S. Miller match = NULL; 574f11e6659SDavid S. Miller for (rt = rr_head; rt && rt->rt6i_metric == metric; 575d8d1f30bSChangli Gao rt = rt->dst.rt6_next) 576f11e6659SDavid S. Miller match = find_match(rt, oif, strict, &mpri, match); 577f11e6659SDavid S. Miller for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric; 578d8d1f30bSChangli Gao rt = rt->dst.rt6_next) 579f11e6659SDavid S. Miller match = find_match(rt, oif, strict, &mpri, match); 580f11e6659SDavid S. Miller 581f11e6659SDavid S. Miller return match; 582f11e6659SDavid S. Miller } 583f11e6659SDavid S. Miller 584f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 585f11e6659SDavid S. Miller { 586f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 5878ed67789SDaniel Lezcano struct net *net; 588f11e6659SDavid S. Miller 589f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 590f11e6659SDavid S. Miller if (!rt0) 591f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 592f11e6659SDavid S. Miller 593f11e6659SDavid S. Miller match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict); 594f11e6659SDavid S. Miller 595554cfb7eSYOSHIFUJI Hideaki if (!match && 596f11e6659SDavid S. Miller (strict & RT6_LOOKUP_F_REACHABLE)) { 597d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 598f11e6659SDavid S. Miller 599554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 600f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 601f11e6659SDavid S. Miller next = fn->leaf; 602f11e6659SDavid S. Miller 603f11e6659SDavid S. Miller if (next != rt0) 604f11e6659SDavid S. Miller fn->rr_ptr = next; 605554cfb7eSYOSHIFUJI Hideaki } 606554cfb7eSYOSHIFUJI Hideaki 607d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 608a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 6091da177e4SLinus Torvalds } 6101da177e4SLinus Torvalds 61170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 61270ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 613b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 61470ceb4f5SYOSHIFUJI Hideaki { 615c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 61670ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 61770ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 61870ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 6194bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 62070ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 62170ceb4f5SYOSHIFUJI Hideaki 62270ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 62370ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 62470ceb4f5SYOSHIFUJI Hideaki } 62570ceb4f5SYOSHIFUJI Hideaki 62670ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 62770ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 62870ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 62970ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 63070ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 63170ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 63270ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 63370ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 63470ceb4f5SYOSHIFUJI Hideaki } 63570ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 63670ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 63770ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 63870ceb4f5SYOSHIFUJI Hideaki } 63970ceb4f5SYOSHIFUJI Hideaki } 64070ceb4f5SYOSHIFUJI Hideaki 64170ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 64270ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 6433933fc95SJens Rosenboom return -EINVAL; 64470ceb4f5SYOSHIFUJI Hideaki 6454bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 64670ceb4f5SYOSHIFUJI Hideaki 64770ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 64870ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 64970ceb4f5SYOSHIFUJI Hideaki else { 65070ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 65170ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 65270ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 65370ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 65470ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 65570ceb4f5SYOSHIFUJI Hideaki } 65670ceb4f5SYOSHIFUJI Hideaki 657efa2cea0SDaniel Lezcano rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr, 658efa2cea0SDaniel Lezcano dev->ifindex); 65970ceb4f5SYOSHIFUJI Hideaki 66070ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 661e0a1ad73SThomas Graf ip6_del_rt(rt); 66270ceb4f5SYOSHIFUJI Hideaki rt = NULL; 66370ceb4f5SYOSHIFUJI Hideaki } 66470ceb4f5SYOSHIFUJI Hideaki 66570ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 666efa2cea0SDaniel Lezcano rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 66770ceb4f5SYOSHIFUJI Hideaki pref); 66870ceb4f5SYOSHIFUJI Hideaki else if (rt) 66970ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 67070ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 67170ceb4f5SYOSHIFUJI Hideaki 67270ceb4f5SYOSHIFUJI Hideaki if (rt) { 6731716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 6741716a961SGao feng rt6_clean_expires(rt); 6751716a961SGao feng else 6761716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 6771716a961SGao feng 678d8d1f30bSChangli Gao dst_release(&rt->dst); 67970ceb4f5SYOSHIFUJI Hideaki } 68070ceb4f5SYOSHIFUJI Hideaki return 0; 68170ceb4f5SYOSHIFUJI Hideaki } 68270ceb4f5SYOSHIFUJI Hideaki #endif 68370ceb4f5SYOSHIFUJI Hideaki 6848ed67789SDaniel Lezcano #define BACKTRACK(__net, saddr) \ 685982f56f3SYOSHIFUJI Hideaki do { \ 6868ed67789SDaniel Lezcano if (rt == __net->ipv6.ip6_null_entry) { \ 687982f56f3SYOSHIFUJI Hideaki struct fib6_node *pn; \ 688e0eda7bbSVille Nuorvala while (1) { \ 689982f56f3SYOSHIFUJI Hideaki if (fn->fn_flags & RTN_TL_ROOT) \ 690c71099acSThomas Graf goto out; \ 691982f56f3SYOSHIFUJI Hideaki pn = fn->parent; \ 692982f56f3SYOSHIFUJI Hideaki if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \ 6938bce65b9SKim Nordlund fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \ 694982f56f3SYOSHIFUJI Hideaki else \ 695982f56f3SYOSHIFUJI Hideaki fn = pn; \ 696c71099acSThomas Graf if (fn->fn_flags & RTN_RTINFO) \ 697c71099acSThomas Graf goto restart; \ 698c71099acSThomas Graf } \ 699982f56f3SYOSHIFUJI Hideaki } \ 700982f56f3SYOSHIFUJI Hideaki } while (0) 701c71099acSThomas Graf 7028ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 7038ed67789SDaniel Lezcano struct fib6_table *table, 7044c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 7051da177e4SLinus Torvalds { 7061da177e4SLinus Torvalds struct fib6_node *fn; 7071da177e4SLinus Torvalds struct rt6_info *rt; 7081da177e4SLinus Torvalds 709c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 7104c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 711c71099acSThomas Graf restart: 712c71099acSThomas Graf rt = fn->leaf; 7134c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 7144c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 715c71099acSThomas Graf out: 716d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 717c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 7181da177e4SLinus Torvalds return rt; 719c71099acSThomas Graf 720c71099acSThomas Graf } 721c71099acSThomas Graf 722ea6e574eSFlorian Westphal struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6, 723ea6e574eSFlorian Westphal int flags) 724ea6e574eSFlorian Westphal { 725ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 726ea6e574eSFlorian Westphal } 727ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 728ea6e574eSFlorian Westphal 7299acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 7309acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 731c71099acSThomas Graf { 7324c9483b2SDavid S. Miller struct flowi6 fl6 = { 7334c9483b2SDavid S. Miller .flowi6_oif = oif, 7344c9483b2SDavid S. Miller .daddr = *daddr, 735c71099acSThomas Graf }; 736c71099acSThomas Graf struct dst_entry *dst; 73777d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 738c71099acSThomas Graf 739adaa70bbSThomas Graf if (saddr) { 7404c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 741adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 742adaa70bbSThomas Graf } 743adaa70bbSThomas Graf 7444c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 745c71099acSThomas Graf if (dst->error == 0) 746c71099acSThomas Graf return (struct rt6_info *) dst; 747c71099acSThomas Graf 748c71099acSThomas Graf dst_release(dst); 749c71099acSThomas Graf 7501da177e4SLinus Torvalds return NULL; 7511da177e4SLinus Torvalds } 7521da177e4SLinus Torvalds 7537159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 7547159039aSYOSHIFUJI Hideaki 755c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 7561da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 7571da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 7581da177e4SLinus Torvalds be destroyed. 7591da177e4SLinus Torvalds */ 7601da177e4SLinus Torvalds 76186872cb5SThomas Graf static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info) 7621da177e4SLinus Torvalds { 7631da177e4SLinus Torvalds int err; 764c71099acSThomas Graf struct fib6_table *table; 7651da177e4SLinus Torvalds 766c71099acSThomas Graf table = rt->rt6i_table; 767c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 76886872cb5SThomas Graf err = fib6_add(&table->tb6_root, rt, info); 769c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 7701da177e4SLinus Torvalds 7711da177e4SLinus Torvalds return err; 7721da177e4SLinus Torvalds } 7731da177e4SLinus Torvalds 77440e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 77540e22e8fSThomas Graf { 7764d1169c1SDenis V. Lunev struct nl_info info = { 777d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 7784d1169c1SDenis V. Lunev }; 779528c4cebSDenis V. Lunev return __ip6_ins_rt(rt, &info); 78040e22e8fSThomas Graf } 78140e22e8fSThomas Graf 7821716a961SGao feng static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, 78321efcfa0SEric Dumazet const struct in6_addr *daddr, 784b71d1d42SEric Dumazet const struct in6_addr *saddr) 7851da177e4SLinus Torvalds { 7861da177e4SLinus Torvalds struct rt6_info *rt; 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds /* 7891da177e4SLinus Torvalds * Clone the route. 7901da177e4SLinus Torvalds */ 7911da177e4SLinus Torvalds 79221efcfa0SEric Dumazet rt = ip6_rt_copy(ort, daddr); 7931da177e4SLinus Torvalds 7941da177e4SLinus Torvalds if (rt) { 79514deae41SDavid S. Miller int attempts = !in_softirq(); 79614deae41SDavid S. Miller 79758c4fb86SYOSHIFUJI Hideaki if (!(rt->rt6i_flags & RTF_GATEWAY)) { 798bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 79921efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 80058c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 8014e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *daddr; 80258c4fb86SYOSHIFUJI Hideaki } 8031da177e4SLinus Torvalds 8041da177e4SLinus Torvalds rt->rt6i_flags |= RTF_CACHE; 8051da177e4SLinus Torvalds 8061da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 8071da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 8084e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 8091da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 8101da177e4SLinus Torvalds } 8111da177e4SLinus Torvalds #endif 8121da177e4SLinus Torvalds 81314deae41SDavid S. Miller retry: 8148ade06c6SDavid S. Miller if (rt6_bind_neighbour(rt, rt->dst.dev)) { 815d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 81614deae41SDavid S. Miller int saved_rt_min_interval = 81714deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval; 81814deae41SDavid S. Miller int saved_rt_elasticity = 81914deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity; 82014deae41SDavid S. Miller 82114deae41SDavid S. Miller if (attempts-- > 0) { 82214deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity = 1; 82314deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval = 0; 82414deae41SDavid S. Miller 82586393e52SAlexey Dobriyan ip6_dst_gc(&net->ipv6.ip6_dst_ops); 82614deae41SDavid S. Miller 82714deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity = 82814deae41SDavid S. Miller saved_rt_elasticity; 82914deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval = 83014deae41SDavid S. Miller saved_rt_min_interval; 83114deae41SDavid S. Miller goto retry; 83214deae41SDavid S. Miller } 83314deae41SDavid S. Miller 834f3213831SJoe Perches net_warn_ratelimited("Neighbour table overflow\n"); 835d8d1f30bSChangli Gao dst_free(&rt->dst); 83614deae41SDavid S. Miller return NULL; 83714deae41SDavid S. Miller } 83895a9a5baSYOSHIFUJI Hideaki } 8391da177e4SLinus Torvalds 8401da177e4SLinus Torvalds return rt; 8411da177e4SLinus Torvalds } 84295a9a5baSYOSHIFUJI Hideaki 84321efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, 84421efcfa0SEric Dumazet const struct in6_addr *daddr) 845299d9939SYOSHIFUJI Hideaki { 84621efcfa0SEric Dumazet struct rt6_info *rt = ip6_rt_copy(ort, daddr); 84721efcfa0SEric Dumazet 848299d9939SYOSHIFUJI Hideaki if (rt) { 849299d9939SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_CACHE; 85097cac082SDavid S. Miller rt->n = neigh_clone(ort->n); 851299d9939SYOSHIFUJI Hideaki } 852299d9939SYOSHIFUJI Hideaki return rt; 853299d9939SYOSHIFUJI Hideaki } 854299d9939SYOSHIFUJI Hideaki 8558ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, 8564c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 8571da177e4SLinus Torvalds { 8581da177e4SLinus Torvalds struct fib6_node *fn; 859519fbd87SYOSHIFUJI Hideaki struct rt6_info *rt, *nrt; 860c71099acSThomas Graf int strict = 0; 8611da177e4SLinus Torvalds int attempts = 3; 862519fbd87SYOSHIFUJI Hideaki int err; 86353b7997fSYOSHIFUJI Hideaki int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE; 8641da177e4SLinus Torvalds 86577d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 8661da177e4SLinus Torvalds 8671da177e4SLinus Torvalds relookup: 868c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 8691da177e4SLinus Torvalds 8708238dd06SYOSHIFUJI Hideaki restart_2: 8714c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 8721da177e4SLinus Torvalds 8731da177e4SLinus Torvalds restart: 8744acad72dSPavel Emelyanov rt = rt6_select(fn, oif, strict | reachable); 8758ed67789SDaniel Lezcano 8764c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 8778ed67789SDaniel Lezcano if (rt == net->ipv6.ip6_null_entry || 8788238dd06SYOSHIFUJI Hideaki rt->rt6i_flags & RTF_CACHE) 8791da177e4SLinus Torvalds goto out; 8801da177e4SLinus Torvalds 881d8d1f30bSChangli Gao dst_hold(&rt->dst); 882c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 8831da177e4SLinus Torvalds 88497cac082SDavid S. Miller if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP)) 8854c9483b2SDavid S. Miller nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); 8867343ff31SDavid S. Miller else if (!(rt->dst.flags & DST_HOST)) 8874c9483b2SDavid S. Miller nrt = rt6_alloc_clone(rt, &fl6->daddr); 8887343ff31SDavid S. Miller else 8897343ff31SDavid S. Miller goto out2; 8901da177e4SLinus Torvalds 891d8d1f30bSChangli Gao dst_release(&rt->dst); 8928ed67789SDaniel Lezcano rt = nrt ? : net->ipv6.ip6_null_entry; 8931da177e4SLinus Torvalds 894d8d1f30bSChangli Gao dst_hold(&rt->dst); 895e40cf353SYOSHIFUJI Hideaki if (nrt) { 89640e22e8fSThomas Graf err = ip6_ins_rt(nrt); 897e40cf353SYOSHIFUJI Hideaki if (!err) 898e40cf353SYOSHIFUJI Hideaki goto out2; 899e40cf353SYOSHIFUJI Hideaki } 900e40cf353SYOSHIFUJI Hideaki 901e40cf353SYOSHIFUJI Hideaki if (--attempts <= 0) 9021da177e4SLinus Torvalds goto out2; 9031da177e4SLinus Torvalds 904519fbd87SYOSHIFUJI Hideaki /* 905c71099acSThomas Graf * Race condition! In the gap, when table->tb6_lock was 906519fbd87SYOSHIFUJI Hideaki * released someone could insert this route. Relookup. 9071da177e4SLinus Torvalds */ 908d8d1f30bSChangli Gao dst_release(&rt->dst); 9091da177e4SLinus Torvalds goto relookup; 910e40cf353SYOSHIFUJI Hideaki 911519fbd87SYOSHIFUJI Hideaki out: 9128238dd06SYOSHIFUJI Hideaki if (reachable) { 9138238dd06SYOSHIFUJI Hideaki reachable = 0; 9148238dd06SYOSHIFUJI Hideaki goto restart_2; 9158238dd06SYOSHIFUJI Hideaki } 916d8d1f30bSChangli Gao dst_hold(&rt->dst); 917c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 9181da177e4SLinus Torvalds out2: 919d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 920d8d1f30bSChangli Gao rt->dst.__use++; 921c71099acSThomas Graf 922c71099acSThomas Graf return rt; 923c71099acSThomas Graf } 924c71099acSThomas Graf 9258ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 9264c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 9274acad72dSPavel Emelyanov { 9284c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 9294acad72dSPavel Emelyanov } 9304acad72dSPavel Emelyanov 93172331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 93272331bc0SShmulik Ladkani struct net_device *dev, 93372331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 93472331bc0SShmulik Ladkani { 93572331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 93672331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 93772331bc0SShmulik Ladkani 93872331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 93972331bc0SShmulik Ladkani } 94072331bc0SShmulik Ladkani 941c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 942c71099acSThomas Graf { 943b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 944c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 945adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 9464c9483b2SDavid S. Miller struct flowi6 fl6 = { 9474c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 9484c9483b2SDavid S. Miller .daddr = iph->daddr, 9494c9483b2SDavid S. Miller .saddr = iph->saddr, 9504c9483b2SDavid S. Miller .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK, 9514c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 9524c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 953c71099acSThomas Graf }; 954adaa70bbSThomas Graf 95572331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 956c71099acSThomas Graf } 957c71099acSThomas Graf 9588ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 9594c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 960c71099acSThomas Graf { 9614c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 962c71099acSThomas Graf } 963c71099acSThomas Graf 9649c7a4f9cSFlorian Westphal struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk, 9654c9483b2SDavid S. Miller struct flowi6 *fl6) 966c71099acSThomas Graf { 967c71099acSThomas Graf int flags = 0; 968c71099acSThomas Graf 9694dc27d1cSDavid McCullough fl6->flowi6_iif = net->loopback_dev->ifindex; 9704dc27d1cSDavid McCullough 9714c9483b2SDavid S. Miller if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) 97277d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 973c71099acSThomas Graf 9744c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 975adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 9760c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 9770c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 978adaa70bbSThomas Graf 9794c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 9801da177e4SLinus Torvalds } 9811da177e4SLinus Torvalds 9827159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 9831da177e4SLinus Torvalds 9842774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 98514e50e57SDavid S. Miller { 9865c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 98714e50e57SDavid S. Miller struct dst_entry *new = NULL; 98814e50e57SDavid S. Miller 989f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 99014e50e57SDavid S. Miller if (rt) { 991d8d1f30bSChangli Gao new = &rt->dst; 99214e50e57SDavid S. Miller 9938104891bSSteffen Klassert memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); 9948104891bSSteffen Klassert rt6_init_peer(rt, net->ipv6.peers); 9958104891bSSteffen Klassert 99614e50e57SDavid S. Miller new->__use = 1; 997352e512cSHerbert Xu new->input = dst_discard; 998352e512cSHerbert Xu new->output = dst_discard; 99914e50e57SDavid S. Miller 100021efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 100121efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 100221efcfa0SEric Dumazet else 1003defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 100414e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 100514e50e57SDavid S. Miller if (rt->rt6i_idev) 100614e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 100714e50e57SDavid S. Miller 10084e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 10091716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 10101716a961SGao feng rt6_clean_expires(rt); 101114e50e57SDavid S. Miller rt->rt6i_metric = 0; 101214e50e57SDavid S. Miller 101314e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 101414e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 101514e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 101614e50e57SDavid S. Miller #endif 101714e50e57SDavid S. Miller 101814e50e57SDavid S. Miller dst_free(new); 101914e50e57SDavid S. Miller } 102014e50e57SDavid S. Miller 102169ead7afSDavid S. Miller dst_release(dst_orig); 102269ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 102314e50e57SDavid S. Miller } 102414e50e57SDavid S. Miller 10251da177e4SLinus Torvalds /* 10261da177e4SLinus Torvalds * Destination cache support functions 10271da177e4SLinus Torvalds */ 10281da177e4SLinus Torvalds 10291da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 10301da177e4SLinus Torvalds { 10311da177e4SLinus Torvalds struct rt6_info *rt; 10321da177e4SLinus Torvalds 10331da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 10341da177e4SLinus Torvalds 1035*6f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 1036*6f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 1037*6f3118b5SNicolas Dichtel * into this function always. 1038*6f3118b5SNicolas Dichtel */ 1039*6f3118b5SNicolas Dichtel if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev))) 1040*6f3118b5SNicolas Dichtel return NULL; 1041*6f3118b5SNicolas Dichtel 10426431cbc2SDavid S. Miller if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) { 10436431cbc2SDavid S. Miller if (rt->rt6i_peer_genid != rt6_peer_genid()) { 104497bab73fSDavid S. Miller if (!rt6_has_peer(rt)) 10456431cbc2SDavid S. Miller rt6_bind_peer(rt, 0); 10466431cbc2SDavid S. Miller rt->rt6i_peer_genid = rt6_peer_genid(); 10476431cbc2SDavid S. Miller } 10481da177e4SLinus Torvalds return dst; 10496431cbc2SDavid S. Miller } 10501da177e4SLinus Torvalds return NULL; 10511da177e4SLinus Torvalds } 10521da177e4SLinus Torvalds 10531da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 10541da177e4SLinus Torvalds { 10551da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 10561da177e4SLinus Torvalds 10571da177e4SLinus Torvalds if (rt) { 105854c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 105954c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1060e0a1ad73SThomas Graf ip6_del_rt(rt); 106154c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 10621da177e4SLinus Torvalds } 106354c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 106454c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 106554c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 106654c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 106754c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 106854c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 10691da177e4SLinus Torvalds } 10701da177e4SLinus Torvalds 10711da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 10721da177e4SLinus Torvalds { 10731da177e4SLinus Torvalds struct rt6_info *rt; 10741da177e4SLinus Torvalds 10753ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 10761da177e4SLinus Torvalds 1077adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 10781da177e4SLinus Torvalds if (rt) { 10791716a961SGao feng if (rt->rt6i_flags & RTF_CACHE) 10801716a961SGao feng rt6_update_expires(rt, 0); 10811716a961SGao feng else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) 10821da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 10831da177e4SLinus Torvalds } 10841da177e4SLinus Torvalds } 10851da177e4SLinus Torvalds 10866700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 10876700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 10881da177e4SLinus Torvalds { 10891da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info*)dst; 10901da177e4SLinus Torvalds 109181aded24SDavid S. Miller dst_confirm(dst); 10921da177e4SLinus Torvalds if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) { 109381aded24SDavid S. Miller struct net *net = dev_net(dst->dev); 109481aded24SDavid S. Miller 10951da177e4SLinus Torvalds rt6->rt6i_flags |= RTF_MODIFIED; 10961da177e4SLinus Torvalds if (mtu < IPV6_MIN_MTU) { 1097defb3519SDavid S. Miller u32 features = dst_metric(dst, RTAX_FEATURES); 10981da177e4SLinus Torvalds mtu = IPV6_MIN_MTU; 1099defb3519SDavid S. Miller features |= RTAX_FEATURE_ALLFRAG; 1100defb3519SDavid S. Miller dst_metric_set(dst, RTAX_FEATURES, features); 11011da177e4SLinus Torvalds } 1102defb3519SDavid S. Miller dst_metric_set(dst, RTAX_MTU, mtu); 110381aded24SDavid S. Miller rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires); 11041da177e4SLinus Torvalds } 11051da177e4SLinus Torvalds } 11061da177e4SLinus Torvalds 110742ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 110842ae66c8SDavid S. Miller int oif, u32 mark) 110981aded24SDavid S. Miller { 111081aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 111181aded24SDavid S. Miller struct dst_entry *dst; 111281aded24SDavid S. Miller struct flowi6 fl6; 111381aded24SDavid S. Miller 111481aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 111581aded24SDavid S. Miller fl6.flowi6_oif = oif; 111681aded24SDavid S. Miller fl6.flowi6_mark = mark; 11173e12939aSDavid S. Miller fl6.flowi6_flags = 0; 111881aded24SDavid S. Miller fl6.daddr = iph->daddr; 111981aded24SDavid S. Miller fl6.saddr = iph->saddr; 112081aded24SDavid S. Miller fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK; 112181aded24SDavid S. Miller 112281aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 112381aded24SDavid S. Miller if (!dst->error) 11246700c270SDavid S. Miller ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu)); 112581aded24SDavid S. Miller dst_release(dst); 112681aded24SDavid S. Miller } 112781aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 112881aded24SDavid S. Miller 112981aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 113081aded24SDavid S. Miller { 113181aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 113281aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 113381aded24SDavid S. Miller } 113481aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 113581aded24SDavid S. Miller 11363a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 11373a5ad2eeSDavid S. Miller { 11383a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 11393a5ad2eeSDavid S. Miller struct dst_entry *dst; 11403a5ad2eeSDavid S. Miller struct flowi6 fl6; 11413a5ad2eeSDavid S. Miller 11423a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 11433a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 11443a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 11453a5ad2eeSDavid S. Miller fl6.flowi6_flags = 0; 11463a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 11473a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 11483a5ad2eeSDavid S. Miller fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK; 11493a5ad2eeSDavid S. Miller 11503a5ad2eeSDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 11513a5ad2eeSDavid S. Miller if (!dst->error) 11526700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 11533a5ad2eeSDavid S. Miller dst_release(dst); 11543a5ad2eeSDavid S. Miller } 11553a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 11563a5ad2eeSDavid S. Miller 11573a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 11583a5ad2eeSDavid S. Miller { 11593a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 11603a5ad2eeSDavid S. Miller } 11613a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 11623a5ad2eeSDavid S. Miller 11630dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 11641da177e4SLinus Torvalds { 11650dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 11660dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 11670dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 11680dbaee3bSDavid S. Miller 11691da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 11701da177e4SLinus Torvalds 11715578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 11725578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 11731da177e4SLinus Torvalds 11741da177e4SLinus Torvalds /* 11751da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 11761da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 11771da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 11781da177e4SLinus Torvalds * rely only on pmtu discovery" 11791da177e4SLinus Torvalds */ 11801da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 11811da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 11821da177e4SLinus Torvalds return mtu; 11831da177e4SLinus Torvalds } 11841da177e4SLinus Torvalds 1185ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1186d33e4553SDavid S. Miller { 1187d33e4553SDavid S. Miller struct inet6_dev *idev; 1188618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 1189618f9bc7SSteffen Klassert 1190618f9bc7SSteffen Klassert if (mtu) 1191618f9bc7SSteffen Klassert return mtu; 1192618f9bc7SSteffen Klassert 1193618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1194d33e4553SDavid S. Miller 1195d33e4553SDavid S. Miller rcu_read_lock(); 1196d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1197d33e4553SDavid S. Miller if (idev) 1198d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1199d33e4553SDavid S. Miller rcu_read_unlock(); 1200d33e4553SDavid S. Miller 1201d33e4553SDavid S. Miller return mtu; 1202d33e4553SDavid S. Miller } 1203d33e4553SDavid S. Miller 12043b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 12053b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 12065d0bbeebSThomas Graf 12073b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 12081da177e4SLinus Torvalds struct neighbour *neigh, 120987a11578SDavid S. Miller struct flowi6 *fl6) 12101da177e4SLinus Torvalds { 121187a11578SDavid S. Miller struct dst_entry *dst; 12121da177e4SLinus Torvalds struct rt6_info *rt; 12131da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1214c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 12151da177e4SLinus Torvalds 121638308473SDavid S. Miller if (unlikely(!idev)) 1217122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 12181da177e4SLinus Torvalds 12198b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, dev, 0, NULL); 122038308473SDavid S. Miller if (unlikely(!rt)) { 12211da177e4SLinus Torvalds in6_dev_put(idev); 122287a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 12231da177e4SLinus Torvalds goto out; 12241da177e4SLinus Torvalds } 12251da177e4SLinus Torvalds 12261da177e4SLinus Torvalds if (neigh) 12271da177e4SLinus Torvalds neigh_hold(neigh); 122814deae41SDavid S. Miller else { 1229f894cbf8SDavid S. Miller neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr); 1230b43faac6SDavid S. Miller if (IS_ERR(neigh)) { 1231252c3d84SRongQing.Li in6_dev_put(idev); 1232b43faac6SDavid S. Miller dst_free(&rt->dst); 1233b43faac6SDavid S. Miller return ERR_CAST(neigh); 1234b43faac6SDavid S. Miller } 123514deae41SDavid S. Miller } 12361da177e4SLinus Torvalds 12378e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 12388e2ec639SYan, Zheng rt->dst.output = ip6_output; 123997cac082SDavid S. Miller rt->n = neigh; 1240d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 124187a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 12428e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 12438e2ec639SYan, Zheng rt->rt6i_idev = idev; 12447011687fSGao feng dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255); 12451da177e4SLinus Torvalds 12463b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1247d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1248d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 12493b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 12501da177e4SLinus Torvalds 12515578689aSDaniel Lezcano fib6_force_start_gc(net); 12521da177e4SLinus Torvalds 125387a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 125487a11578SDavid S. Miller 12551da177e4SLinus Torvalds out: 125687a11578SDavid S. Miller return dst; 12571da177e4SLinus Torvalds } 12581da177e4SLinus Torvalds 12593d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 12601da177e4SLinus Torvalds { 1261e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 12623d0f24a7SStephen Hemminger int more = 0; 12631da177e4SLinus Torvalds 12643b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 12653b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 12665d0bbeebSThomas Graf 12671da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 12681da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 12691da177e4SLinus Torvalds *pprev = dst->next; 12701da177e4SLinus Torvalds dst_free(dst); 12711da177e4SLinus Torvalds } else { 12721da177e4SLinus Torvalds pprev = &dst->next; 12733d0f24a7SStephen Hemminger ++more; 12741da177e4SLinus Torvalds } 12751da177e4SLinus Torvalds } 12761da177e4SLinus Torvalds 12773b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 12785d0bbeebSThomas Graf 12793d0f24a7SStephen Hemminger return more; 12801da177e4SLinus Torvalds } 12811da177e4SLinus Torvalds 12821e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 12831e493d19SDavid S. Miller void *arg) 12841e493d19SDavid S. Miller { 12851e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 12861e493d19SDavid S. Miller 12871e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 12881e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 12891e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 12901e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 12911e493d19SDavid S. Miller if (func(rt, arg)) { 12921e493d19SDavid S. Miller *pprev = dst->next; 12931e493d19SDavid S. Miller dst_free(dst); 12941e493d19SDavid S. Miller } else { 12951e493d19SDavid S. Miller pprev = &dst->next; 12961e493d19SDavid S. Miller } 12971e493d19SDavid S. Miller } 12981e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 12991e493d19SDavid S. Miller } 13001e493d19SDavid S. Miller 1301569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 13021da177e4SLinus Torvalds { 13031da177e4SLinus Torvalds unsigned long now = jiffies; 130486393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 13057019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 13067019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 13077019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 13087019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 13097019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1310fc66f95cSEric Dumazet int entries; 13111da177e4SLinus Torvalds 1312fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 13137019b78eSDaniel Lezcano if (time_after(rt_last_gc + rt_min_interval, now) && 1314fc66f95cSEric Dumazet entries <= rt_max_size) 13151da177e4SLinus Torvalds goto out; 13161da177e4SLinus Torvalds 13176891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 13186891a346SBenjamin Thery fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net); 13196891a346SBenjamin Thery net->ipv6.ip6_rt_last_gc = now; 1320fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1321fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 13227019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 13231da177e4SLinus Torvalds out: 13247019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1325fc66f95cSEric Dumazet return entries > rt_max_size; 13261da177e4SLinus Torvalds } 13271da177e4SLinus Torvalds 13281da177e4SLinus Torvalds /* Clean host part of a prefix. Not necessary in radix tree, 13291da177e4SLinus Torvalds but results in cleaner routing tables. 13301da177e4SLinus Torvalds 13311da177e4SLinus Torvalds Remove it only when all the things will work! 13321da177e4SLinus Torvalds */ 13331da177e4SLinus Torvalds 13346b75d090SYOSHIFUJI Hideaki int ip6_dst_hoplimit(struct dst_entry *dst) 13351da177e4SLinus Torvalds { 13365170ae82SDavid S. Miller int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT); 1337a02e4b7dSDavid S. Miller if (hoplimit == 0) { 13386b75d090SYOSHIFUJI Hideaki struct net_device *dev = dst->dev; 1339c68f24ccSEric Dumazet struct inet6_dev *idev; 1340c68f24ccSEric Dumazet 1341c68f24ccSEric Dumazet rcu_read_lock(); 1342c68f24ccSEric Dumazet idev = __in6_dev_get(dev); 1343c68f24ccSEric Dumazet if (idev) 13441da177e4SLinus Torvalds hoplimit = idev->cnf.hop_limit; 1345c68f24ccSEric Dumazet else 134653b7997fSYOSHIFUJI Hideaki hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit; 1347c68f24ccSEric Dumazet rcu_read_unlock(); 13481da177e4SLinus Torvalds } 13491da177e4SLinus Torvalds return hoplimit; 13501da177e4SLinus Torvalds } 1351abbf46aeSDavid S. Miller EXPORT_SYMBOL(ip6_dst_hoplimit); 13521da177e4SLinus Torvalds 13531da177e4SLinus Torvalds /* 13541da177e4SLinus Torvalds * 13551da177e4SLinus Torvalds */ 13561da177e4SLinus Torvalds 135786872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg) 13581da177e4SLinus Torvalds { 13591da177e4SLinus Torvalds int err; 13605578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 13611da177e4SLinus Torvalds struct rt6_info *rt = NULL; 13621da177e4SLinus Torvalds struct net_device *dev = NULL; 13631da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1364c71099acSThomas Graf struct fib6_table *table; 13651da177e4SLinus Torvalds int addr_type; 13661da177e4SLinus Torvalds 136786872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 13681da177e4SLinus Torvalds return -EINVAL; 13691da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 137086872cb5SThomas Graf if (cfg->fc_src_len) 13711da177e4SLinus Torvalds return -EINVAL; 13721da177e4SLinus Torvalds #endif 137386872cb5SThomas Graf if (cfg->fc_ifindex) { 13741da177e4SLinus Torvalds err = -ENODEV; 13755578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 13761da177e4SLinus Torvalds if (!dev) 13771da177e4SLinus Torvalds goto out; 13781da177e4SLinus Torvalds idev = in6_dev_get(dev); 13791da177e4SLinus Torvalds if (!idev) 13801da177e4SLinus Torvalds goto out; 13811da177e4SLinus Torvalds } 13821da177e4SLinus Torvalds 138386872cb5SThomas Graf if (cfg->fc_metric == 0) 138486872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 13851da177e4SLinus Torvalds 1386c71099acSThomas Graf err = -ENOBUFS; 138738308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1388d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1389d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 139038308473SDavid S. Miller if (!table) { 1391f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1392d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1393d71314b4SMatti Vaittinen } 1394d71314b4SMatti Vaittinen } else { 1395d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1396d71314b4SMatti Vaittinen } 139738308473SDavid S. Miller 139838308473SDavid S. Miller if (!table) 1399c71099acSThomas Graf goto out; 1400c71099acSThomas Graf 14018b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table); 14021da177e4SLinus Torvalds 140338308473SDavid S. Miller if (!rt) { 14041da177e4SLinus Torvalds err = -ENOMEM; 14051da177e4SLinus Torvalds goto out; 14061da177e4SLinus Torvalds } 14071da177e4SLinus Torvalds 14081716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 14091716a961SGao feng rt6_set_expires(rt, jiffies + 14101716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 14111716a961SGao feng else 14121716a961SGao feng rt6_clean_expires(rt); 14131da177e4SLinus Torvalds 141486872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 141586872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 141686872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 141786872cb5SThomas Graf 141886872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 14191da177e4SLinus Torvalds 14201da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1421d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1422ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1423ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 14241da177e4SLinus Torvalds else 1425d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 14261da177e4SLinus Torvalds 1427d8d1f30bSChangli Gao rt->dst.output = ip6_output; 14281da177e4SLinus Torvalds 142986872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 143086872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 14311da177e4SLinus Torvalds if (rt->rt6i_dst.plen == 128) 143211d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 14331da177e4SLinus Torvalds 14348e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) { 14358e2ec639SYan, Zheng u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 14368e2ec639SYan, Zheng if (!metrics) { 14378e2ec639SYan, Zheng err = -ENOMEM; 14388e2ec639SYan, Zheng goto out; 14398e2ec639SYan, Zheng } 14408e2ec639SYan, Zheng dst_init_metrics(&rt->dst, metrics, 0); 14418e2ec639SYan, Zheng } 14421da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 144386872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 144486872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 14451da177e4SLinus Torvalds #endif 14461da177e4SLinus Torvalds 144786872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 14481da177e4SLinus Torvalds 14491da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 14501da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 14511da177e4SLinus Torvalds */ 145286872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 145338308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 145438308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 145538308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 14561da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 14575578689aSDaniel Lezcano if (dev != net->loopback_dev) { 14581da177e4SLinus Torvalds if (dev) { 14591da177e4SLinus Torvalds dev_put(dev); 14601da177e4SLinus Torvalds in6_dev_put(idev); 14611da177e4SLinus Torvalds } 14625578689aSDaniel Lezcano dev = net->loopback_dev; 14631da177e4SLinus Torvalds dev_hold(dev); 14641da177e4SLinus Torvalds idev = in6_dev_get(dev); 14651da177e4SLinus Torvalds if (!idev) { 14661da177e4SLinus Torvalds err = -ENODEV; 14671da177e4SLinus Torvalds goto out; 14681da177e4SLinus Torvalds } 14691da177e4SLinus Torvalds } 1470d8d1f30bSChangli Gao rt->dst.output = ip6_pkt_discard_out; 1471d8d1f30bSChangli Gao rt->dst.input = ip6_pkt_discard; 1472d8d1f30bSChangli Gao rt->dst.error = -ENETUNREACH; 14731da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 14741da177e4SLinus Torvalds goto install_route; 14751da177e4SLinus Torvalds } 14761da177e4SLinus Torvalds 147786872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1478b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 14791da177e4SLinus Torvalds int gwa_type; 14801da177e4SLinus Torvalds 148186872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 14824e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 14831da177e4SLinus Torvalds gwa_type = ipv6_addr_type(gw_addr); 14841da177e4SLinus Torvalds 14851da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 14861da177e4SLinus Torvalds struct rt6_info *grt; 14871da177e4SLinus Torvalds 14881da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 14891da177e4SLinus Torvalds addresses as nexthop address. 14901da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 14911da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 14921da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 14931da177e4SLinus Torvalds some exceptions. --ANK 14941da177e4SLinus Torvalds */ 14951da177e4SLinus Torvalds err = -EINVAL; 14961da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 14971da177e4SLinus Torvalds goto out; 14981da177e4SLinus Torvalds 14995578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 15001da177e4SLinus Torvalds 15011da177e4SLinus Torvalds err = -EHOSTUNREACH; 150238308473SDavid S. Miller if (!grt) 15031da177e4SLinus Torvalds goto out; 15041da177e4SLinus Torvalds if (dev) { 1505d1918542SDavid S. Miller if (dev != grt->dst.dev) { 1506d8d1f30bSChangli Gao dst_release(&grt->dst); 15071da177e4SLinus Torvalds goto out; 15081da177e4SLinus Torvalds } 15091da177e4SLinus Torvalds } else { 1510d1918542SDavid S. Miller dev = grt->dst.dev; 15111da177e4SLinus Torvalds idev = grt->rt6i_idev; 15121da177e4SLinus Torvalds dev_hold(dev); 15131da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 15141da177e4SLinus Torvalds } 15151da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 15161da177e4SLinus Torvalds err = 0; 1517d8d1f30bSChangli Gao dst_release(&grt->dst); 15181da177e4SLinus Torvalds 15191da177e4SLinus Torvalds if (err) 15201da177e4SLinus Torvalds goto out; 15211da177e4SLinus Torvalds } 15221da177e4SLinus Torvalds err = -EINVAL; 152338308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 15241da177e4SLinus Torvalds goto out; 15251da177e4SLinus Torvalds } 15261da177e4SLinus Torvalds 15271da177e4SLinus Torvalds err = -ENODEV; 152838308473SDavid S. Miller if (!dev) 15291da177e4SLinus Torvalds goto out; 15301da177e4SLinus Torvalds 1531c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1532c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1533c3968a85SDaniel Walter err = -EINVAL; 1534c3968a85SDaniel Walter goto out; 1535c3968a85SDaniel Walter } 15364e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1537c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1538c3968a85SDaniel Walter } else 1539c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1540c3968a85SDaniel Walter 154186872cb5SThomas Graf if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) { 15428ade06c6SDavid S. Miller err = rt6_bind_neighbour(rt, dev); 1543f83c7790SDavid S. Miller if (err) 15441da177e4SLinus Torvalds goto out; 15451da177e4SLinus Torvalds } 15461da177e4SLinus Torvalds 154786872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 15481da177e4SLinus Torvalds 15491da177e4SLinus Torvalds install_route: 155086872cb5SThomas Graf if (cfg->fc_mx) { 155186872cb5SThomas Graf struct nlattr *nla; 155286872cb5SThomas Graf int remaining; 15531da177e4SLinus Torvalds 155486872cb5SThomas Graf nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 15558f4c1f9bSThomas Graf int type = nla_type(nla); 155686872cb5SThomas Graf 155786872cb5SThomas Graf if (type) { 155886872cb5SThomas Graf if (type > RTAX_MAX) { 15591da177e4SLinus Torvalds err = -EINVAL; 15601da177e4SLinus Torvalds goto out; 15611da177e4SLinus Torvalds } 156286872cb5SThomas Graf 1563defb3519SDavid S. Miller dst_metric_set(&rt->dst, type, nla_get_u32(nla)); 15641da177e4SLinus Torvalds } 15651da177e4SLinus Torvalds } 15661da177e4SLinus Torvalds } 15671da177e4SLinus Torvalds 1568d8d1f30bSChangli Gao rt->dst.dev = dev; 15691da177e4SLinus Torvalds rt->rt6i_idev = idev; 1570c71099acSThomas Graf rt->rt6i_table = table; 157163152fc0SDaniel Lezcano 1572c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 157363152fc0SDaniel Lezcano 157486872cb5SThomas Graf return __ip6_ins_rt(rt, &cfg->fc_nlinfo); 15751da177e4SLinus Torvalds 15761da177e4SLinus Torvalds out: 15771da177e4SLinus Torvalds if (dev) 15781da177e4SLinus Torvalds dev_put(dev); 15791da177e4SLinus Torvalds if (idev) 15801da177e4SLinus Torvalds in6_dev_put(idev); 15811da177e4SLinus Torvalds if (rt) 1582d8d1f30bSChangli Gao dst_free(&rt->dst); 15831da177e4SLinus Torvalds return err; 15841da177e4SLinus Torvalds } 15851da177e4SLinus Torvalds 158686872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 15871da177e4SLinus Torvalds { 15881da177e4SLinus Torvalds int err; 1589c71099acSThomas Graf struct fib6_table *table; 1590d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 15911da177e4SLinus Torvalds 15928ed67789SDaniel Lezcano if (rt == net->ipv6.ip6_null_entry) 15936c813a72SPatrick McHardy return -ENOENT; 15946c813a72SPatrick McHardy 1595c71099acSThomas Graf table = rt->rt6i_table; 1596c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 15971da177e4SLinus Torvalds 159886872cb5SThomas Graf err = fib6_del(rt, info); 1599d8d1f30bSChangli Gao dst_release(&rt->dst); 16001da177e4SLinus Torvalds 1601c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 16021da177e4SLinus Torvalds 16031da177e4SLinus Torvalds return err; 16041da177e4SLinus Torvalds } 16051da177e4SLinus Torvalds 1606e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 1607e0a1ad73SThomas Graf { 16084d1169c1SDenis V. Lunev struct nl_info info = { 1609d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 16104d1169c1SDenis V. Lunev }; 1611528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 1612e0a1ad73SThomas Graf } 1613e0a1ad73SThomas Graf 161486872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 16151da177e4SLinus Torvalds { 1616c71099acSThomas Graf struct fib6_table *table; 16171da177e4SLinus Torvalds struct fib6_node *fn; 16181da177e4SLinus Torvalds struct rt6_info *rt; 16191da177e4SLinus Torvalds int err = -ESRCH; 16201da177e4SLinus Torvalds 16215578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 162238308473SDavid S. Miller if (!table) 1623c71099acSThomas Graf return err; 16241da177e4SLinus Torvalds 1625c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1626c71099acSThomas Graf 1627c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 162886872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 162986872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 16301da177e4SLinus Torvalds 16311da177e4SLinus Torvalds if (fn) { 1632d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 163386872cb5SThomas Graf if (cfg->fc_ifindex && 1634d1918542SDavid S. Miller (!rt->dst.dev || 1635d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 16361da177e4SLinus Torvalds continue; 163786872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 163886872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 16391da177e4SLinus Torvalds continue; 164086872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 16411da177e4SLinus Torvalds continue; 1642d8d1f30bSChangli Gao dst_hold(&rt->dst); 1643c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 16441da177e4SLinus Torvalds 164586872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 16461da177e4SLinus Torvalds } 16471da177e4SLinus Torvalds } 1648c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 16491da177e4SLinus Torvalds 16501da177e4SLinus Torvalds return err; 16511da177e4SLinus Torvalds } 16521da177e4SLinus Torvalds 16536700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 1654a6279458SYOSHIFUJI Hideaki { 1655e8599ff4SDavid S. Miller struct net *net = dev_net(skb->dev); 1656a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 1657e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 1658e8599ff4SDavid S. Miller const struct in6_addr *target; 1659e8599ff4SDavid S. Miller struct ndisc_options ndopts; 16606e157b6aSDavid S. Miller const struct in6_addr *dest; 16616e157b6aSDavid S. Miller struct neighbour *old_neigh; 1662e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 1663e8599ff4SDavid S. Miller struct neighbour *neigh; 1664e8599ff4SDavid S. Miller struct icmp6hdr *icmph; 16656e157b6aSDavid S. Miller int optlen, on_link; 16666e157b6aSDavid S. Miller u8 *lladdr; 1667e8599ff4SDavid S. Miller 1668e8599ff4SDavid S. Miller optlen = skb->tail - skb->transport_header; 1669e8599ff4SDavid S. Miller optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); 1670e8599ff4SDavid S. Miller 1671e8599ff4SDavid S. Miller if (optlen < 0) { 16726e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 1673e8599ff4SDavid S. Miller return; 1674e8599ff4SDavid S. Miller } 1675e8599ff4SDavid S. Miller 1676e8599ff4SDavid S. Miller icmph = icmp6_hdr(skb); 1677e8599ff4SDavid S. Miller target = (const struct in6_addr *) (icmph + 1); 1678e8599ff4SDavid S. Miller dest = target + 1; 1679e8599ff4SDavid S. Miller 1680e8599ff4SDavid S. Miller if (ipv6_addr_is_multicast(dest)) { 16816e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 1682e8599ff4SDavid S. Miller return; 1683e8599ff4SDavid S. Miller } 1684e8599ff4SDavid S. Miller 16856e157b6aSDavid S. Miller on_link = 0; 1686e8599ff4SDavid S. Miller if (ipv6_addr_equal(dest, target)) { 1687e8599ff4SDavid S. Miller on_link = 1; 1688e8599ff4SDavid S. Miller } else if (ipv6_addr_type(target) != 1689e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 16906e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 1691e8599ff4SDavid S. Miller return; 1692e8599ff4SDavid S. Miller } 1693e8599ff4SDavid S. Miller 1694e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 1695e8599ff4SDavid S. Miller if (!in6_dev) 1696e8599ff4SDavid S. Miller return; 1697e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 1698e8599ff4SDavid S. Miller return; 1699e8599ff4SDavid S. Miller 1700e8599ff4SDavid S. Miller /* RFC2461 8.1: 1701e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 1702e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 1703e8599ff4SDavid S. Miller */ 1704e8599ff4SDavid S. Miller 1705e8599ff4SDavid S. Miller if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) { 1706e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 1707e8599ff4SDavid S. Miller return; 1708e8599ff4SDavid S. Miller } 17096e157b6aSDavid S. Miller 17106e157b6aSDavid S. Miller lladdr = NULL; 1711e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 1712e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 1713e8599ff4SDavid S. Miller skb->dev); 1714e8599ff4SDavid S. Miller if (!lladdr) { 1715e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 1716e8599ff4SDavid S. Miller return; 1717e8599ff4SDavid S. Miller } 1718e8599ff4SDavid S. Miller } 1719e8599ff4SDavid S. Miller 17206e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 17216e157b6aSDavid S. Miller if (rt == net->ipv6.ip6_null_entry) { 17226e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 17236e157b6aSDavid S. Miller return; 17246e157b6aSDavid S. Miller } 17256e157b6aSDavid S. Miller 17266e157b6aSDavid S. Miller /* Redirect received -> path was valid. 17276e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 17286e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 17296e157b6aSDavid S. Miller */ 17306e157b6aSDavid S. Miller dst_confirm(&rt->dst); 17316e157b6aSDavid S. Miller 1732e8599ff4SDavid S. Miller neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1); 1733e8599ff4SDavid S. Miller if (!neigh) 1734e8599ff4SDavid S. Miller return; 1735e8599ff4SDavid S. Miller 17366e157b6aSDavid S. Miller /* Duplicate redirect: silently ignore. */ 17376e157b6aSDavid S. Miller old_neigh = rt->n; 17386e157b6aSDavid S. Miller if (neigh == old_neigh) 1739a6279458SYOSHIFUJI Hideaki goto out; 17401da177e4SLinus Torvalds 17411da177e4SLinus Torvalds /* 17421da177e4SLinus Torvalds * We have finally decided to accept it. 17431da177e4SLinus Torvalds */ 17441da177e4SLinus Torvalds 17451da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 17461da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 17471da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 17481da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 17491da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 17501da177e4SLinus Torvalds ); 17511da177e4SLinus Torvalds 175221efcfa0SEric Dumazet nrt = ip6_rt_copy(rt, dest); 175338308473SDavid S. Miller if (!nrt) 17541da177e4SLinus Torvalds goto out; 17551da177e4SLinus Torvalds 17561da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 17571da177e4SLinus Torvalds if (on_link) 17581da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 17591da177e4SLinus Torvalds 17604e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 176197cac082SDavid S. Miller nrt->n = neigh_clone(neigh); 17621da177e4SLinus Torvalds 176340e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 17641da177e4SLinus Torvalds goto out; 17651da177e4SLinus Torvalds 1766d8d1f30bSChangli Gao netevent.old = &rt->dst; 17671d248b1cSDavid S. Miller netevent.old_neigh = old_neigh; 1768d8d1f30bSChangli Gao netevent.new = &nrt->dst; 17691d248b1cSDavid S. Miller netevent.new_neigh = neigh; 17701d248b1cSDavid S. Miller netevent.daddr = dest; 17718d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 17728d71740cSTom Tucker 17731da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 17746e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 1775e0a1ad73SThomas Graf ip6_del_rt(rt); 17761da177e4SLinus Torvalds } 17771da177e4SLinus Torvalds 17781da177e4SLinus Torvalds out: 1779e8599ff4SDavid S. Miller neigh_release(neigh); 17806e157b6aSDavid S. Miller } 17816e157b6aSDavid S. Miller 17821da177e4SLinus Torvalds /* 17831da177e4SLinus Torvalds * Misc support functions 17841da177e4SLinus Torvalds */ 17851da177e4SLinus Torvalds 17861716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 178721efcfa0SEric Dumazet const struct in6_addr *dest) 17881da177e4SLinus Torvalds { 1789d1918542SDavid S. Miller struct net *net = dev_net(ort->dst.dev); 17908b96d22dSDavid S. Miller struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0, 17918b96d22dSDavid S. Miller ort->rt6i_table); 17921da177e4SLinus Torvalds 17931da177e4SLinus Torvalds if (rt) { 1794d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 1795d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 17968e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 17971da177e4SLinus Torvalds 17984e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *dest; 17998e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 1800defb3519SDavid S. Miller dst_copy_metrics(&rt->dst, &ort->dst); 1801d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 18021da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 18031da177e4SLinus Torvalds if (rt->rt6i_idev) 18041da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 1805d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 18061da177e4SLinus Torvalds 18074e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 18081716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 18091716a961SGao feng if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) == 18101716a961SGao feng (RTF_DEFAULT | RTF_ADDRCONF)) 18111716a961SGao feng rt6_set_from(rt, ort); 18121716a961SGao feng else 18131716a961SGao feng rt6_clean_expires(rt); 18141da177e4SLinus Torvalds rt->rt6i_metric = 0; 18151da177e4SLinus Torvalds 18161da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 18171da177e4SLinus Torvalds memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 18181da177e4SLinus Torvalds #endif 18190f6c6392SFlorian Westphal memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key)); 1820c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 18211da177e4SLinus Torvalds } 18221da177e4SLinus Torvalds return rt; 18231da177e4SLinus Torvalds } 18241da177e4SLinus Torvalds 182570ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 1826efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 1827b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1828b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 182970ceb4f5SYOSHIFUJI Hideaki { 183070ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 183170ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 1832c71099acSThomas Graf struct fib6_table *table; 183370ceb4f5SYOSHIFUJI Hideaki 1834efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 183538308473SDavid S. Miller if (!table) 1836c71099acSThomas Graf return NULL; 1837c71099acSThomas Graf 1838c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 1839c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0); 184070ceb4f5SYOSHIFUJI Hideaki if (!fn) 184170ceb4f5SYOSHIFUJI Hideaki goto out; 184270ceb4f5SYOSHIFUJI Hideaki 1843d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1844d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 184570ceb4f5SYOSHIFUJI Hideaki continue; 184670ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 184770ceb4f5SYOSHIFUJI Hideaki continue; 184870ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 184970ceb4f5SYOSHIFUJI Hideaki continue; 1850d8d1f30bSChangli Gao dst_hold(&rt->dst); 185170ceb4f5SYOSHIFUJI Hideaki break; 185270ceb4f5SYOSHIFUJI Hideaki } 185370ceb4f5SYOSHIFUJI Hideaki out: 1854c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 185570ceb4f5SYOSHIFUJI Hideaki return rt; 185670ceb4f5SYOSHIFUJI Hideaki } 185770ceb4f5SYOSHIFUJI Hideaki 1858efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 1859b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1860b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 186195c96174SEric Dumazet unsigned int pref) 186270ceb4f5SYOSHIFUJI Hideaki { 186386872cb5SThomas Graf struct fib6_config cfg = { 186486872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 1865238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 186686872cb5SThomas Graf .fc_ifindex = ifindex, 186786872cb5SThomas Graf .fc_dst_len = prefixlen, 186886872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 186986872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 1870efa2cea0SDaniel Lezcano .fc_nlinfo.pid = 0, 1871efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 1872efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 187386872cb5SThomas Graf }; 187470ceb4f5SYOSHIFUJI Hideaki 18754e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 18764e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 187786872cb5SThomas Graf 1878e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 1879e317da96SYOSHIFUJI Hideaki if (!prefixlen) 188086872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 188170ceb4f5SYOSHIFUJI Hideaki 188286872cb5SThomas Graf ip6_route_add(&cfg); 188370ceb4f5SYOSHIFUJI Hideaki 1884efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 188570ceb4f5SYOSHIFUJI Hideaki } 188670ceb4f5SYOSHIFUJI Hideaki #endif 188770ceb4f5SYOSHIFUJI Hideaki 1888b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 18891da177e4SLinus Torvalds { 18901da177e4SLinus Torvalds struct rt6_info *rt; 1891c71099acSThomas Graf struct fib6_table *table; 18921da177e4SLinus Torvalds 1893c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 189438308473SDavid S. Miller if (!table) 1895c71099acSThomas Graf return NULL; 18961da177e4SLinus Torvalds 1897c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 1898d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) { 1899d1918542SDavid S. Miller if (dev == rt->dst.dev && 1900045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 19011da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 19021da177e4SLinus Torvalds break; 19031da177e4SLinus Torvalds } 19041da177e4SLinus Torvalds if (rt) 1905d8d1f30bSChangli Gao dst_hold(&rt->dst); 1906c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 19071da177e4SLinus Torvalds return rt; 19081da177e4SLinus Torvalds } 19091da177e4SLinus Torvalds 1910b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 1911ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 1912ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 19131da177e4SLinus Torvalds { 191486872cb5SThomas Graf struct fib6_config cfg = { 191586872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 1916238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 191786872cb5SThomas Graf .fc_ifindex = dev->ifindex, 191886872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 191986872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 19205578689aSDaniel Lezcano .fc_nlinfo.pid = 0, 19215578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 1922c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 192386872cb5SThomas Graf }; 19241da177e4SLinus Torvalds 19254e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 19261da177e4SLinus Torvalds 192786872cb5SThomas Graf ip6_route_add(&cfg); 19281da177e4SLinus Torvalds 19291da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 19301da177e4SLinus Torvalds } 19311da177e4SLinus Torvalds 19327b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 19331da177e4SLinus Torvalds { 19341da177e4SLinus Torvalds struct rt6_info *rt; 1935c71099acSThomas Graf struct fib6_table *table; 1936c71099acSThomas Graf 1937c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 19387b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 193938308473SDavid S. Miller if (!table) 1940c71099acSThomas Graf return; 19411da177e4SLinus Torvalds 19421da177e4SLinus Torvalds restart: 1943c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1944d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 19451da177e4SLinus Torvalds if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { 1946d8d1f30bSChangli Gao dst_hold(&rt->dst); 1947c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 1948e0a1ad73SThomas Graf ip6_del_rt(rt); 19491da177e4SLinus Torvalds goto restart; 19501da177e4SLinus Torvalds } 19511da177e4SLinus Torvalds } 1952c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 19531da177e4SLinus Torvalds } 19541da177e4SLinus Torvalds 19555578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 19565578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 195786872cb5SThomas Graf struct fib6_config *cfg) 195886872cb5SThomas Graf { 195986872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 196086872cb5SThomas Graf 196186872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 196286872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 196386872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 196486872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 196586872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 196686872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 196786872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 196886872cb5SThomas Graf 19695578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 1970f1243c2dSBenjamin Thery 19714e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 19724e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 19734e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 197486872cb5SThomas Graf } 197586872cb5SThomas Graf 19765578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 19771da177e4SLinus Torvalds { 197886872cb5SThomas Graf struct fib6_config cfg; 19791da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 19801da177e4SLinus Torvalds int err; 19811da177e4SLinus Torvalds 19821da177e4SLinus Torvalds switch(cmd) { 19831da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 19841da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 19851da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 19861da177e4SLinus Torvalds return -EPERM; 19871da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 19881da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 19891da177e4SLinus Torvalds if (err) 19901da177e4SLinus Torvalds return -EFAULT; 19911da177e4SLinus Torvalds 19925578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 199386872cb5SThomas Graf 19941da177e4SLinus Torvalds rtnl_lock(); 19951da177e4SLinus Torvalds switch (cmd) { 19961da177e4SLinus Torvalds case SIOCADDRT: 199786872cb5SThomas Graf err = ip6_route_add(&cfg); 19981da177e4SLinus Torvalds break; 19991da177e4SLinus Torvalds case SIOCDELRT: 200086872cb5SThomas Graf err = ip6_route_del(&cfg); 20011da177e4SLinus Torvalds break; 20021da177e4SLinus Torvalds default: 20031da177e4SLinus Torvalds err = -EINVAL; 20041da177e4SLinus Torvalds } 20051da177e4SLinus Torvalds rtnl_unlock(); 20061da177e4SLinus Torvalds 20071da177e4SLinus Torvalds return err; 20083ff50b79SStephen Hemminger } 20091da177e4SLinus Torvalds 20101da177e4SLinus Torvalds return -EINVAL; 20111da177e4SLinus Torvalds } 20121da177e4SLinus Torvalds 20131da177e4SLinus Torvalds /* 20141da177e4SLinus Torvalds * Drop the packet on the floor 20151da177e4SLinus Torvalds */ 20161da177e4SLinus Torvalds 2017d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 20181da177e4SLinus Torvalds { 2019612f09e8SYOSHIFUJI Hideaki int type; 2020adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2021612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2022612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 20230660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 202445bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 20253bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 20263bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2027612f09e8SYOSHIFUJI Hideaki break; 2028612f09e8SYOSHIFUJI Hideaki } 2029612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2030612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 20313bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 20323bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2033612f09e8SYOSHIFUJI Hideaki break; 2034612f09e8SYOSHIFUJI Hideaki } 20353ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 20361da177e4SLinus Torvalds kfree_skb(skb); 20371da177e4SLinus Torvalds return 0; 20381da177e4SLinus Torvalds } 20391da177e4SLinus Torvalds 20409ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 20419ce8ade0SThomas Graf { 2042612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 20439ce8ade0SThomas Graf } 20449ce8ade0SThomas Graf 204520380731SArnaldo Carvalho de Melo static int ip6_pkt_discard_out(struct sk_buff *skb) 20461da177e4SLinus Torvalds { 2047adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2048612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 20491da177e4SLinus Torvalds } 20501da177e4SLinus Torvalds 20516723ab54SDavid S. Miller #ifdef CONFIG_IPV6_MULTIPLE_TABLES 20526723ab54SDavid S. Miller 20539ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 20549ce8ade0SThomas Graf { 2055612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 20569ce8ade0SThomas Graf } 20579ce8ade0SThomas Graf 20589ce8ade0SThomas Graf static int ip6_pkt_prohibit_out(struct sk_buff *skb) 20599ce8ade0SThomas Graf { 2060adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2061612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 20629ce8ade0SThomas Graf } 20639ce8ade0SThomas Graf 20646723ab54SDavid S. Miller #endif 20656723ab54SDavid S. Miller 20661da177e4SLinus Torvalds /* 20671da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 20681da177e4SLinus Torvalds */ 20691da177e4SLinus Torvalds 20701da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 20711da177e4SLinus Torvalds const struct in6_addr *addr, 20728f031519SDavid S. Miller bool anycast) 20731da177e4SLinus Torvalds { 2074c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 20758b96d22dSDavid S. Miller struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0, NULL); 2076f83c7790SDavid S. Miller int err; 20771da177e4SLinus Torvalds 207838308473SDavid S. Miller if (!rt) { 2079f3213831SJoe Perches net_warn_ratelimited("Maximum number of routes reached, consider increasing route/max_size\n"); 20801da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 208140385653SBen Greear } 20821da177e4SLinus Torvalds 20831da177e4SLinus Torvalds in6_dev_hold(idev); 20841da177e4SLinus Torvalds 208511d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2086d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2087d8d1f30bSChangli Gao rt->dst.output = ip6_output; 20881da177e4SLinus Torvalds rt->rt6i_idev = idev; 20891da177e4SLinus Torvalds 20901da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 209158c4fb86SYOSHIFUJI Hideaki if (anycast) 209258c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 209358c4fb86SYOSHIFUJI Hideaki else 20941da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 20958ade06c6SDavid S. Miller err = rt6_bind_neighbour(rt, rt->dst.dev); 2096f83c7790SDavid S. Miller if (err) { 2097d8d1f30bSChangli Gao dst_free(&rt->dst); 2098f83c7790SDavid S. Miller return ERR_PTR(err); 20991da177e4SLinus Torvalds } 21001da177e4SLinus Torvalds 21014e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 21021da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 21035578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 21041da177e4SLinus Torvalds 2105d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 21061da177e4SLinus Torvalds 21071da177e4SLinus Torvalds return rt; 21081da177e4SLinus Torvalds } 21091da177e4SLinus Torvalds 2110c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2111c3968a85SDaniel Walter struct rt6_info *rt, 2112b71d1d42SEric Dumazet const struct in6_addr *daddr, 2113c3968a85SDaniel Walter unsigned int prefs, 2114c3968a85SDaniel Walter struct in6_addr *saddr) 2115c3968a85SDaniel Walter { 2116c3968a85SDaniel Walter struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt); 2117c3968a85SDaniel Walter int err = 0; 2118c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) 21194e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2120c3968a85SDaniel Walter else 2121c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2122c3968a85SDaniel Walter daddr, prefs, saddr); 2123c3968a85SDaniel Walter return err; 2124c3968a85SDaniel Walter } 2125c3968a85SDaniel Walter 2126c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2127c3968a85SDaniel Walter struct arg_dev_net_ip { 2128c3968a85SDaniel Walter struct net_device *dev; 2129c3968a85SDaniel Walter struct net *net; 2130c3968a85SDaniel Walter struct in6_addr *addr; 2131c3968a85SDaniel Walter }; 2132c3968a85SDaniel Walter 2133c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2134c3968a85SDaniel Walter { 2135c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2136c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2137c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2138c3968a85SDaniel Walter 2139d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2140c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2141c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2142c3968a85SDaniel Walter /* remove prefsrc entry */ 2143c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2144c3968a85SDaniel Walter } 2145c3968a85SDaniel Walter return 0; 2146c3968a85SDaniel Walter } 2147c3968a85SDaniel Walter 2148c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2149c3968a85SDaniel Walter { 2150c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2151c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2152c3968a85SDaniel Walter .dev = ifp->idev->dev, 2153c3968a85SDaniel Walter .net = net, 2154c3968a85SDaniel Walter .addr = &ifp->addr, 2155c3968a85SDaniel Walter }; 2156c3968a85SDaniel Walter fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni); 2157c3968a85SDaniel Walter } 2158c3968a85SDaniel Walter 21598ed67789SDaniel Lezcano struct arg_dev_net { 21608ed67789SDaniel Lezcano struct net_device *dev; 21618ed67789SDaniel Lezcano struct net *net; 21628ed67789SDaniel Lezcano }; 21638ed67789SDaniel Lezcano 21641da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 21651da177e4SLinus Torvalds { 2166bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2167bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 21688ed67789SDaniel Lezcano 2169d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2170c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 21711da177e4SLinus Torvalds return -1; 2172c159d30cSDavid S. Miller 21731da177e4SLinus Torvalds return 0; 21741da177e4SLinus Torvalds } 21751da177e4SLinus Torvalds 2176f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 21771da177e4SLinus Torvalds { 21788ed67789SDaniel Lezcano struct arg_dev_net adn = { 21798ed67789SDaniel Lezcano .dev = dev, 21808ed67789SDaniel Lezcano .net = net, 21818ed67789SDaniel Lezcano }; 21828ed67789SDaniel Lezcano 21838ed67789SDaniel Lezcano fib6_clean_all(net, fib6_ifdown, 0, &adn); 21841e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 21851da177e4SLinus Torvalds } 21861da177e4SLinus Torvalds 218795c96174SEric Dumazet struct rt6_mtu_change_arg { 21881da177e4SLinus Torvalds struct net_device *dev; 218995c96174SEric Dumazet unsigned int mtu; 21901da177e4SLinus Torvalds }; 21911da177e4SLinus Torvalds 21921da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 21931da177e4SLinus Torvalds { 21941da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 21951da177e4SLinus Torvalds struct inet6_dev *idev; 21961da177e4SLinus Torvalds 21971da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 21981da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 21991da177e4SLinus Torvalds We still use this lock to block changes 22001da177e4SLinus Torvalds caused by addrconf/ndisc. 22011da177e4SLinus Torvalds */ 22021da177e4SLinus Torvalds 22031da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 220438308473SDavid S. Miller if (!idev) 22051da177e4SLinus Torvalds return 0; 22061da177e4SLinus Torvalds 22071da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 22081da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 22091da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 22101da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 22111da177e4SLinus Torvalds */ 22121da177e4SLinus Torvalds /* 22131da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 22141da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 22151da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 22161da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 22171da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 22181da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 22191da177e4SLinus Torvalds PMTU discouvery. 22201da177e4SLinus Torvalds */ 2221d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 2222d8d1f30bSChangli Gao !dst_metric_locked(&rt->dst, RTAX_MTU) && 2223d8d1f30bSChangli Gao (dst_mtu(&rt->dst) >= arg->mtu || 2224d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 2225d8d1f30bSChangli Gao dst_mtu(&rt->dst) == idev->cnf.mtu6))) { 2226defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2227566cfd8fSSimon Arlott } 22281da177e4SLinus Torvalds return 0; 22291da177e4SLinus Torvalds } 22301da177e4SLinus Torvalds 223195c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 22321da177e4SLinus Torvalds { 2233c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2234c71099acSThomas Graf .dev = dev, 2235c71099acSThomas Graf .mtu = mtu, 2236c71099acSThomas Graf }; 22371da177e4SLinus Torvalds 2238c346dca1SYOSHIFUJI Hideaki fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg); 22391da177e4SLinus Torvalds } 22401da177e4SLinus Torvalds 2241ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 22425176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 224386872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2244ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 224586872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 224686872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 224786872cb5SThomas Graf }; 224886872cb5SThomas Graf 224986872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 225086872cb5SThomas Graf struct fib6_config *cfg) 22511da177e4SLinus Torvalds { 225286872cb5SThomas Graf struct rtmsg *rtm; 225386872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 225486872cb5SThomas Graf int err; 22551da177e4SLinus Torvalds 225686872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 225786872cb5SThomas Graf if (err < 0) 225886872cb5SThomas Graf goto errout; 22591da177e4SLinus Torvalds 226086872cb5SThomas Graf err = -EINVAL; 226186872cb5SThomas Graf rtm = nlmsg_data(nlh); 226286872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 226386872cb5SThomas Graf 226486872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 226586872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 226686872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 226786872cb5SThomas Graf cfg->fc_flags = RTF_UP; 226886872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 226986872cb5SThomas Graf 227086872cb5SThomas Graf if (rtm->rtm_type == RTN_UNREACHABLE) 227186872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 227286872cb5SThomas Graf 2273ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2274ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2275ab79ad14SMaciej Żenczykowski 227686872cb5SThomas Graf cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid; 227786872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 22783b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 227986872cb5SThomas Graf 228086872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 228186872cb5SThomas Graf nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16); 228286872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 22831da177e4SLinus Torvalds } 228486872cb5SThomas Graf 228586872cb5SThomas Graf if (tb[RTA_DST]) { 228686872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 228786872cb5SThomas Graf 228886872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 228986872cb5SThomas Graf goto errout; 229086872cb5SThomas Graf 229186872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 22921da177e4SLinus Torvalds } 229386872cb5SThomas Graf 229486872cb5SThomas Graf if (tb[RTA_SRC]) { 229586872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 229686872cb5SThomas Graf 229786872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 229886872cb5SThomas Graf goto errout; 229986872cb5SThomas Graf 230086872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 23011da177e4SLinus Torvalds } 230286872cb5SThomas Graf 2303c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 2304c3968a85SDaniel Walter nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16); 2305c3968a85SDaniel Walter 230686872cb5SThomas Graf if (tb[RTA_OIF]) 230786872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 230886872cb5SThomas Graf 230986872cb5SThomas Graf if (tb[RTA_PRIORITY]) 231086872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 231186872cb5SThomas Graf 231286872cb5SThomas Graf if (tb[RTA_METRICS]) { 231386872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 231486872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 23151da177e4SLinus Torvalds } 231686872cb5SThomas Graf 231786872cb5SThomas Graf if (tb[RTA_TABLE]) 231886872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 231986872cb5SThomas Graf 232086872cb5SThomas Graf err = 0; 232186872cb5SThomas Graf errout: 232286872cb5SThomas Graf return err; 23231da177e4SLinus Torvalds } 23241da177e4SLinus Torvalds 2325c127ea2cSThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 23261da177e4SLinus Torvalds { 232786872cb5SThomas Graf struct fib6_config cfg; 232886872cb5SThomas Graf int err; 23291da177e4SLinus Torvalds 233086872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 233186872cb5SThomas Graf if (err < 0) 233286872cb5SThomas Graf return err; 233386872cb5SThomas Graf 233486872cb5SThomas Graf return ip6_route_del(&cfg); 23351da177e4SLinus Torvalds } 23361da177e4SLinus Torvalds 2337c127ea2cSThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 23381da177e4SLinus Torvalds { 233986872cb5SThomas Graf struct fib6_config cfg; 234086872cb5SThomas Graf int err; 23411da177e4SLinus Torvalds 234286872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 234386872cb5SThomas Graf if (err < 0) 234486872cb5SThomas Graf return err; 234586872cb5SThomas Graf 234686872cb5SThomas Graf return ip6_route_add(&cfg); 23471da177e4SLinus Torvalds } 23481da177e4SLinus Torvalds 2349339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void) 2350339bf98fSThomas Graf { 2351339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 2352339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 2353339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 2354339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 2355339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 2356339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 2357339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 2358339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 2359339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 23606a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 2361339bf98fSThomas Graf + nla_total_size(sizeof(struct rta_cacheinfo)); 2362339bf98fSThomas Graf } 2363339bf98fSThomas Graf 2364191cd582SBrian Haley static int rt6_fill_node(struct net *net, 2365191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 23660d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 23670d51aa80SJamal Hadi Salim int iif, int type, u32 pid, u32 seq, 23687bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 23691da177e4SLinus Torvalds { 23701da177e4SLinus Torvalds struct rtmsg *rtm; 23711da177e4SLinus Torvalds struct nlmsghdr *nlh; 2372e3703b3dSThomas Graf long expires; 23739e762a4aSPatrick McHardy u32 table; 2374f2c31e32SEric Dumazet struct neighbour *n; 23751da177e4SLinus Torvalds 23761da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 23771da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 23781da177e4SLinus Torvalds /* success since this is not a prefix route */ 23791da177e4SLinus Torvalds return 1; 23801da177e4SLinus Torvalds } 23811da177e4SLinus Torvalds } 23821da177e4SLinus Torvalds 23832d7202bfSThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags); 238438308473SDavid S. Miller if (!nlh) 238526932566SPatrick McHardy return -EMSGSIZE; 23862d7202bfSThomas Graf 23872d7202bfSThomas Graf rtm = nlmsg_data(nlh); 23881da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 23891da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 23901da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 23911da177e4SLinus Torvalds rtm->rtm_tos = 0; 2392c71099acSThomas Graf if (rt->rt6i_table) 23939e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 2394c71099acSThomas Graf else 23959e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 23969e762a4aSPatrick McHardy rtm->rtm_table = table; 2397c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 2398c78679e8SDavid S. Miller goto nla_put_failure; 23991da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_REJECT) 24001da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 2401ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 2402ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 2403d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 24041da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 24051da177e4SLinus Torvalds else 24061da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 24071da177e4SLinus Torvalds rtm->rtm_flags = 0; 24081da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 24091da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 24101da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 24111da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 2412f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 2413f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 24141da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 2415f0396f60SDenis Ovsienko else 2416f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 2417f0396f60SDenis Ovsienko } 24181da177e4SLinus Torvalds 24191da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 24201da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 24211da177e4SLinus Torvalds 24221da177e4SLinus Torvalds if (dst) { 2423c78679e8SDavid S. Miller if (nla_put(skb, RTA_DST, 16, dst)) 2424c78679e8SDavid S. Miller goto nla_put_failure; 24251da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 24261da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 2427c78679e8SDavid S. Miller if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr)) 2428c78679e8SDavid S. Miller goto nla_put_failure; 24291da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 24301da177e4SLinus Torvalds if (src) { 2431c78679e8SDavid S. Miller if (nla_put(skb, RTA_SRC, 16, src)) 2432c78679e8SDavid S. Miller goto nla_put_failure; 24331da177e4SLinus Torvalds rtm->rtm_src_len = 128; 2434c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 2435c78679e8SDavid S. Miller nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr)) 2436c78679e8SDavid S. Miller goto nla_put_failure; 24371da177e4SLinus Torvalds #endif 24387bc570c8SYOSHIFUJI Hideaki if (iif) { 24397bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 24407bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 24418229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 24427bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 24437bc570c8SYOSHIFUJI Hideaki if (!nowait) { 24447bc570c8SYOSHIFUJI Hideaki if (err == 0) 24457bc570c8SYOSHIFUJI Hideaki return 0; 24467bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 24477bc570c8SYOSHIFUJI Hideaki } else { 24487bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 24497bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 24507bc570c8SYOSHIFUJI Hideaki } 24517bc570c8SYOSHIFUJI Hideaki } 24527bc570c8SYOSHIFUJI Hideaki } else 24537bc570c8SYOSHIFUJI Hideaki #endif 2454c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 2455c78679e8SDavid S. Miller goto nla_put_failure; 24567bc570c8SYOSHIFUJI Hideaki } else if (dst) { 24571da177e4SLinus Torvalds struct in6_addr saddr_buf; 2458c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 2459c78679e8SDavid S. Miller nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) 2460c78679e8SDavid S. Miller goto nla_put_failure; 2461c3968a85SDaniel Walter } 2462c3968a85SDaniel Walter 2463c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 2464c3968a85SDaniel Walter struct in6_addr saddr_buf; 24654e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 2466c78679e8SDavid S. Miller if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) 2467c78679e8SDavid S. Miller goto nla_put_failure; 24681da177e4SLinus Torvalds } 24692d7202bfSThomas Graf 2470defb3519SDavid S. Miller if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) 24712d7202bfSThomas Graf goto nla_put_failure; 24722d7202bfSThomas Graf 2473f2c31e32SEric Dumazet rcu_read_lock(); 247497cac082SDavid S. Miller n = rt->n; 247594f826b8SEric Dumazet if (n) { 247694f826b8SEric Dumazet if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) { 247794f826b8SEric Dumazet rcu_read_unlock(); 247894f826b8SEric Dumazet goto nla_put_failure; 247994f826b8SEric Dumazet } 248094f826b8SEric Dumazet } 2481f2c31e32SEric Dumazet rcu_read_unlock(); 24822d7202bfSThomas Graf 2483c78679e8SDavid S. Miller if (rt->dst.dev && 2484c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 2485c78679e8SDavid S. Miller goto nla_put_failure; 2486c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 2487c78679e8SDavid S. Miller goto nla_put_failure; 24888253947eSLi Wei 24898253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 249069cdf8f9SYOSHIFUJI Hideaki 249187a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 2492e3703b3dSThomas Graf goto nla_put_failure; 24931da177e4SLinus Torvalds 24942d7202bfSThomas Graf return nlmsg_end(skb, nlh); 24952d7202bfSThomas Graf 24962d7202bfSThomas Graf nla_put_failure: 249726932566SPatrick McHardy nlmsg_cancel(skb, nlh); 249826932566SPatrick McHardy return -EMSGSIZE; 24991da177e4SLinus Torvalds } 25001da177e4SLinus Torvalds 25011b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 25021da177e4SLinus Torvalds { 25031da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 25041da177e4SLinus Torvalds int prefix; 25051da177e4SLinus Torvalds 25062d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 25072d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 25081da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 25091da177e4SLinus Torvalds } else 25101da177e4SLinus Torvalds prefix = 0; 25111da177e4SLinus Torvalds 2512191cd582SBrian Haley return rt6_fill_node(arg->net, 2513191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 25141da177e4SLinus Torvalds NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq, 25157bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 25161da177e4SLinus Torvalds } 25171da177e4SLinus Torvalds 2518c127ea2cSThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) 25191da177e4SLinus Torvalds { 25203b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 2521ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 25221da177e4SLinus Torvalds struct rt6_info *rt; 2523ab364a6fSThomas Graf struct sk_buff *skb; 2524ab364a6fSThomas Graf struct rtmsg *rtm; 25254c9483b2SDavid S. Miller struct flowi6 fl6; 252672331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 2527ab364a6fSThomas Graf 2528ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 2529ab364a6fSThomas Graf if (err < 0) 2530ab364a6fSThomas Graf goto errout; 2531ab364a6fSThomas Graf 2532ab364a6fSThomas Graf err = -EINVAL; 25334c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2534ab364a6fSThomas Graf 2535ab364a6fSThomas Graf if (tb[RTA_SRC]) { 2536ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 2537ab364a6fSThomas Graf goto errout; 2538ab364a6fSThomas Graf 25394e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 2540ab364a6fSThomas Graf } 2541ab364a6fSThomas Graf 2542ab364a6fSThomas Graf if (tb[RTA_DST]) { 2543ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 2544ab364a6fSThomas Graf goto errout; 2545ab364a6fSThomas Graf 25464e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 2547ab364a6fSThomas Graf } 2548ab364a6fSThomas Graf 2549ab364a6fSThomas Graf if (tb[RTA_IIF]) 2550ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 2551ab364a6fSThomas Graf 2552ab364a6fSThomas Graf if (tb[RTA_OIF]) 255372331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 2554ab364a6fSThomas Graf 2555ab364a6fSThomas Graf if (iif) { 2556ab364a6fSThomas Graf struct net_device *dev; 255772331bc0SShmulik Ladkani int flags = 0; 255872331bc0SShmulik Ladkani 25595578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 2560ab364a6fSThomas Graf if (!dev) { 2561ab364a6fSThomas Graf err = -ENODEV; 2562ab364a6fSThomas Graf goto errout; 2563ab364a6fSThomas Graf } 256472331bc0SShmulik Ladkani 256572331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 256672331bc0SShmulik Ladkani 256772331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 256872331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 256972331bc0SShmulik Ladkani 257072331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 257172331bc0SShmulik Ladkani flags); 257272331bc0SShmulik Ladkani } else { 257372331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 257472331bc0SShmulik Ladkani 257572331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 2576ab364a6fSThomas Graf } 25771da177e4SLinus Torvalds 25781da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 257938308473SDavid S. Miller if (!skb) { 25802173bff5SShmulik Ladkani dst_release(&rt->dst); 2581ab364a6fSThomas Graf err = -ENOBUFS; 2582ab364a6fSThomas Graf goto errout; 2583ab364a6fSThomas Graf } 25841da177e4SLinus Torvalds 25851da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 25861da177e4SLinus Torvalds through good chunk of routing engine. 25871da177e4SLinus Torvalds */ 2588459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 25891da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 25901da177e4SLinus Torvalds 2591d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 25921da177e4SLinus Torvalds 25934c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 25941da177e4SLinus Torvalds RTM_NEWROUTE, NETLINK_CB(in_skb).pid, 25957bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 25961da177e4SLinus Torvalds if (err < 0) { 2597ab364a6fSThomas Graf kfree_skb(skb); 2598ab364a6fSThomas Graf goto errout; 25991da177e4SLinus Torvalds } 26001da177e4SLinus Torvalds 26015578689aSDaniel Lezcano err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid); 2602ab364a6fSThomas Graf errout: 26031da177e4SLinus Torvalds return err; 26041da177e4SLinus Torvalds } 26051da177e4SLinus Torvalds 260686872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) 26071da177e4SLinus Torvalds { 26081da177e4SLinus Torvalds struct sk_buff *skb; 26095578689aSDaniel Lezcano struct net *net = info->nl_net; 2610528c4cebSDenis V. Lunev u32 seq; 2611528c4cebSDenis V. Lunev int err; 26120d51aa80SJamal Hadi Salim 2613528c4cebSDenis V. Lunev err = -ENOBUFS; 261438308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 261586872cb5SThomas Graf 2616339bf98fSThomas Graf skb = nlmsg_new(rt6_nlmsg_size(), gfp_any()); 261738308473SDavid S. Miller if (!skb) 261821713ebcSThomas Graf goto errout; 26191da177e4SLinus Torvalds 2620191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 26217bc570c8SYOSHIFUJI Hideaki event, info->pid, seq, 0, 0, 0); 262226932566SPatrick McHardy if (err < 0) { 262326932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 262426932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 262526932566SPatrick McHardy kfree_skb(skb); 262626932566SPatrick McHardy goto errout; 262726932566SPatrick McHardy } 26281ce85fe4SPablo Neira Ayuso rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE, 26295578689aSDaniel Lezcano info->nlh, gfp_any()); 26301ce85fe4SPablo Neira Ayuso return; 263121713ebcSThomas Graf errout: 263221713ebcSThomas Graf if (err < 0) 26335578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 26341da177e4SLinus Torvalds } 26351da177e4SLinus Torvalds 26368ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 26378ed67789SDaniel Lezcano unsigned long event, void *data) 26388ed67789SDaniel Lezcano { 26398ed67789SDaniel Lezcano struct net_device *dev = (struct net_device *)data; 2640c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 26418ed67789SDaniel Lezcano 26428ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 2643d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 26448ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 26458ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 2646d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 26478ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 2648d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 26498ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 26508ed67789SDaniel Lezcano #endif 26518ed67789SDaniel Lezcano } 26528ed67789SDaniel Lezcano 26538ed67789SDaniel Lezcano return NOTIFY_OK; 26548ed67789SDaniel Lezcano } 26558ed67789SDaniel Lezcano 26561da177e4SLinus Torvalds /* 26571da177e4SLinus Torvalds * /proc 26581da177e4SLinus Torvalds */ 26591da177e4SLinus Torvalds 26601da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 26611da177e4SLinus Torvalds 26621da177e4SLinus Torvalds struct rt6_proc_arg 26631da177e4SLinus Torvalds { 26641da177e4SLinus Torvalds char *buffer; 26651da177e4SLinus Torvalds int offset; 26661da177e4SLinus Torvalds int length; 26671da177e4SLinus Torvalds int skip; 26681da177e4SLinus Torvalds int len; 26691da177e4SLinus Torvalds }; 26701da177e4SLinus Torvalds 26711da177e4SLinus Torvalds static int rt6_info_route(struct rt6_info *rt, void *p_arg) 26721da177e4SLinus Torvalds { 267333120b30SAlexey Dobriyan struct seq_file *m = p_arg; 267469cce1d1SDavid S. Miller struct neighbour *n; 26751da177e4SLinus Torvalds 26764b7a4274SHarvey Harrison seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen); 26771da177e4SLinus Torvalds 26781da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 26794b7a4274SHarvey Harrison seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen); 26801da177e4SLinus Torvalds #else 268133120b30SAlexey Dobriyan seq_puts(m, "00000000000000000000000000000000 00 "); 26821da177e4SLinus Torvalds #endif 2683f2c31e32SEric Dumazet rcu_read_lock(); 268497cac082SDavid S. Miller n = rt->n; 268569cce1d1SDavid S. Miller if (n) { 268669cce1d1SDavid S. Miller seq_printf(m, "%pi6", n->primary_key); 26871da177e4SLinus Torvalds } else { 268833120b30SAlexey Dobriyan seq_puts(m, "00000000000000000000000000000000"); 26891da177e4SLinus Torvalds } 2690f2c31e32SEric Dumazet rcu_read_unlock(); 269133120b30SAlexey Dobriyan seq_printf(m, " %08x %08x %08x %08x %8s\n", 2692d8d1f30bSChangli Gao rt->rt6i_metric, atomic_read(&rt->dst.__refcnt), 2693d8d1f30bSChangli Gao rt->dst.__use, rt->rt6i_flags, 2694d1918542SDavid S. Miller rt->dst.dev ? rt->dst.dev->name : ""); 26951da177e4SLinus Torvalds return 0; 26961da177e4SLinus Torvalds } 26971da177e4SLinus Torvalds 269833120b30SAlexey Dobriyan static int ipv6_route_show(struct seq_file *m, void *v) 26991da177e4SLinus Torvalds { 2700f3db4851SDaniel Lezcano struct net *net = (struct net *)m->private; 270132b293a5SJosh Hunt fib6_clean_all_ro(net, rt6_info_route, 0, m); 270233120b30SAlexey Dobriyan return 0; 27031da177e4SLinus Torvalds } 27041da177e4SLinus Torvalds 270533120b30SAlexey Dobriyan static int ipv6_route_open(struct inode *inode, struct file *file) 270633120b30SAlexey Dobriyan { 2707de05c557SPavel Emelyanov return single_open_net(inode, file, ipv6_route_show); 2708f3db4851SDaniel Lezcano } 2709f3db4851SDaniel Lezcano 271033120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 271133120b30SAlexey Dobriyan .owner = THIS_MODULE, 271233120b30SAlexey Dobriyan .open = ipv6_route_open, 271333120b30SAlexey Dobriyan .read = seq_read, 271433120b30SAlexey Dobriyan .llseek = seq_lseek, 2715b6fcbdb4SPavel Emelyanov .release = single_release_net, 271633120b30SAlexey Dobriyan }; 271733120b30SAlexey Dobriyan 27181da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 27191da177e4SLinus Torvalds { 272069ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 27211da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 272269ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 272369ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 272469ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 272569ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 272669ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 2727fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 272869ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 27291da177e4SLinus Torvalds 27301da177e4SLinus Torvalds return 0; 27311da177e4SLinus Torvalds } 27321da177e4SLinus Torvalds 27331da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 27341da177e4SLinus Torvalds { 2735de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 273669ddb805SDaniel Lezcano } 273769ddb805SDaniel Lezcano 27389a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 27391da177e4SLinus Torvalds .owner = THIS_MODULE, 27401da177e4SLinus Torvalds .open = rt6_stats_seq_open, 27411da177e4SLinus Torvalds .read = seq_read, 27421da177e4SLinus Torvalds .llseek = seq_lseek, 2743b6fcbdb4SPavel Emelyanov .release = single_release_net, 27441da177e4SLinus Torvalds }; 27451da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 27461da177e4SLinus Torvalds 27471da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 27481da177e4SLinus Torvalds 27491da177e4SLinus Torvalds static 27508d65af78SAlexey Dobriyan int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, 27511da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 27521da177e4SLinus Torvalds { 2753c486da34SLucian Adrian Grijincu struct net *net; 2754c486da34SLucian Adrian Grijincu int delay; 2755c486da34SLucian Adrian Grijincu if (!write) 2756c486da34SLucian Adrian Grijincu return -EINVAL; 2757c486da34SLucian Adrian Grijincu 2758c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 2759c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 27608d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 27615b7c931dSDaniel Lezcano fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net); 27621da177e4SLinus Torvalds return 0; 27631da177e4SLinus Torvalds } 27641da177e4SLinus Torvalds 2765760f2d01SDaniel Lezcano ctl_table ipv6_route_table_template[] = { 27661da177e4SLinus Torvalds { 27671da177e4SLinus Torvalds .procname = "flush", 27684990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 27691da177e4SLinus Torvalds .maxlen = sizeof(int), 277089c8b3a1SDave Jones .mode = 0200, 27716d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 27721da177e4SLinus Torvalds }, 27731da177e4SLinus Torvalds { 27741da177e4SLinus Torvalds .procname = "gc_thresh", 27759a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 27761da177e4SLinus Torvalds .maxlen = sizeof(int), 27771da177e4SLinus Torvalds .mode = 0644, 27786d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 27791da177e4SLinus Torvalds }, 27801da177e4SLinus Torvalds { 27811da177e4SLinus Torvalds .procname = "max_size", 27824990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 27831da177e4SLinus Torvalds .maxlen = sizeof(int), 27841da177e4SLinus Torvalds .mode = 0644, 27856d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 27861da177e4SLinus Torvalds }, 27871da177e4SLinus Torvalds { 27881da177e4SLinus Torvalds .procname = "gc_min_interval", 27894990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 27901da177e4SLinus Torvalds .maxlen = sizeof(int), 27911da177e4SLinus Torvalds .mode = 0644, 27926d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 27931da177e4SLinus Torvalds }, 27941da177e4SLinus Torvalds { 27951da177e4SLinus Torvalds .procname = "gc_timeout", 27964990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 27971da177e4SLinus Torvalds .maxlen = sizeof(int), 27981da177e4SLinus Torvalds .mode = 0644, 27996d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28001da177e4SLinus Torvalds }, 28011da177e4SLinus Torvalds { 28021da177e4SLinus Torvalds .procname = "gc_interval", 28034990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 28041da177e4SLinus Torvalds .maxlen = sizeof(int), 28051da177e4SLinus Torvalds .mode = 0644, 28066d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28071da177e4SLinus Torvalds }, 28081da177e4SLinus Torvalds { 28091da177e4SLinus Torvalds .procname = "gc_elasticity", 28104990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 28111da177e4SLinus Torvalds .maxlen = sizeof(int), 28121da177e4SLinus Torvalds .mode = 0644, 2813f3d3f616SMin Zhang .proc_handler = proc_dointvec, 28141da177e4SLinus Torvalds }, 28151da177e4SLinus Torvalds { 28161da177e4SLinus Torvalds .procname = "mtu_expires", 28174990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 28181da177e4SLinus Torvalds .maxlen = sizeof(int), 28191da177e4SLinus Torvalds .mode = 0644, 28206d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28211da177e4SLinus Torvalds }, 28221da177e4SLinus Torvalds { 28231da177e4SLinus Torvalds .procname = "min_adv_mss", 28244990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 28251da177e4SLinus Torvalds .maxlen = sizeof(int), 28261da177e4SLinus Torvalds .mode = 0644, 2827f3d3f616SMin Zhang .proc_handler = proc_dointvec, 28281da177e4SLinus Torvalds }, 28291da177e4SLinus Torvalds { 28301da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 28314990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 28321da177e4SLinus Torvalds .maxlen = sizeof(int), 28331da177e4SLinus Torvalds .mode = 0644, 28346d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 28351da177e4SLinus Torvalds }, 2836f8572d8fSEric W. Biederman { } 28371da177e4SLinus Torvalds }; 28381da177e4SLinus Torvalds 28392c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 2840760f2d01SDaniel Lezcano { 2841760f2d01SDaniel Lezcano struct ctl_table *table; 2842760f2d01SDaniel Lezcano 2843760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 2844760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 2845760f2d01SDaniel Lezcano GFP_KERNEL); 28465ee09105SYOSHIFUJI Hideaki 28475ee09105SYOSHIFUJI Hideaki if (table) { 28485ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 2849c486da34SLucian Adrian Grijincu table[0].extra1 = net; 285086393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 28515ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 28525ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 28535ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 28545ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 28555ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 28565ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 28575ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 28589c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 28595ee09105SYOSHIFUJI Hideaki } 28605ee09105SYOSHIFUJI Hideaki 2861760f2d01SDaniel Lezcano return table; 2862760f2d01SDaniel Lezcano } 28631da177e4SLinus Torvalds #endif 28641da177e4SLinus Torvalds 28652c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 2866cdb18761SDaniel Lezcano { 2867633d424bSPavel Emelyanov int ret = -ENOMEM; 28688ed67789SDaniel Lezcano 286986393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 287086393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 2871f2fc6a54SBenjamin Thery 2872fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 2873fc66f95cSEric Dumazet goto out_ip6_dst_ops; 2874fc66f95cSEric Dumazet 28758ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 28768ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 28778ed67789SDaniel Lezcano GFP_KERNEL); 28788ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 2879fc66f95cSEric Dumazet goto out_ip6_dst_entries; 2880d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 28818ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 2882d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 288362fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 288462fa8a84SDavid S. Miller ip6_template_metrics, true); 28858ed67789SDaniel Lezcano 28868ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 28878ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 28888ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 28898ed67789SDaniel Lezcano GFP_KERNEL); 289068fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 289168fffc67SPeter Zijlstra goto out_ip6_null_entry; 2892d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 28938ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 2894d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 289562fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 289662fa8a84SDavid S. Miller ip6_template_metrics, true); 28978ed67789SDaniel Lezcano 28988ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 28998ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 29008ed67789SDaniel Lezcano GFP_KERNEL); 290168fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 290268fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 2903d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 29048ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 2905d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 290662fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 290762fa8a84SDavid S. Miller ip6_template_metrics, true); 29088ed67789SDaniel Lezcano #endif 29098ed67789SDaniel Lezcano 2910b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 2911b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 2912b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 2913b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 2914b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 2915b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 2916b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 2917b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 2918b339a47cSPeter Zijlstra 29196891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 29206891a346SBenjamin Thery 29218ed67789SDaniel Lezcano ret = 0; 29228ed67789SDaniel Lezcano out: 29238ed67789SDaniel Lezcano return ret; 2924f2fc6a54SBenjamin Thery 292568fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 292668fffc67SPeter Zijlstra out_ip6_prohibit_entry: 292768fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 292868fffc67SPeter Zijlstra out_ip6_null_entry: 292968fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 293068fffc67SPeter Zijlstra #endif 2931fc66f95cSEric Dumazet out_ip6_dst_entries: 2932fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 2933f2fc6a54SBenjamin Thery out_ip6_dst_ops: 2934f2fc6a54SBenjamin Thery goto out; 2935cdb18761SDaniel Lezcano } 2936cdb18761SDaniel Lezcano 29372c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 2938cdb18761SDaniel Lezcano { 29398ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 29408ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 29418ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 29428ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 29438ed67789SDaniel Lezcano #endif 294441bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 2945cdb18761SDaniel Lezcano } 2946cdb18761SDaniel Lezcano 2947d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 2948d189634eSThomas Graf { 2949d189634eSThomas Graf #ifdef CONFIG_PROC_FS 2950d189634eSThomas Graf proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops); 2951d189634eSThomas Graf proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops); 2952d189634eSThomas Graf #endif 2953d189634eSThomas Graf return 0; 2954d189634eSThomas Graf } 2955d189634eSThomas Graf 2956d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 2957d189634eSThomas Graf { 2958d189634eSThomas Graf #ifdef CONFIG_PROC_FS 2959d189634eSThomas Graf proc_net_remove(net, "ipv6_route"); 2960d189634eSThomas Graf proc_net_remove(net, "rt6_stats"); 2961d189634eSThomas Graf #endif 2962d189634eSThomas Graf } 2963d189634eSThomas Graf 2964cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 2965cdb18761SDaniel Lezcano .init = ip6_route_net_init, 2966cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 2967cdb18761SDaniel Lezcano }; 2968cdb18761SDaniel Lezcano 2969c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 2970c3426b47SDavid S. Miller { 2971c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 2972c3426b47SDavid S. Miller 2973c3426b47SDavid S. Miller if (!bp) 2974c3426b47SDavid S. Miller return -ENOMEM; 2975c3426b47SDavid S. Miller inet_peer_base_init(bp); 2976c3426b47SDavid S. Miller net->ipv6.peers = bp; 2977c3426b47SDavid S. Miller return 0; 2978c3426b47SDavid S. Miller } 2979c3426b47SDavid S. Miller 2980c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 2981c3426b47SDavid S. Miller { 2982c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 2983c3426b47SDavid S. Miller 2984c3426b47SDavid S. Miller net->ipv6.peers = NULL; 298556a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 2986c3426b47SDavid S. Miller kfree(bp); 2987c3426b47SDavid S. Miller } 2988c3426b47SDavid S. Miller 29892b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 2990c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 2991c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 2992c3426b47SDavid S. Miller }; 2993c3426b47SDavid S. Miller 2994d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 2995d189634eSThomas Graf .init = ip6_route_net_init_late, 2996d189634eSThomas Graf .exit = ip6_route_net_exit_late, 2997d189634eSThomas Graf }; 2998d189634eSThomas Graf 29998ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 30008ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 30018ed67789SDaniel Lezcano .priority = 0, 30028ed67789SDaniel Lezcano }; 30038ed67789SDaniel Lezcano 3004433d49c3SDaniel Lezcano int __init ip6_route_init(void) 30051da177e4SLinus Torvalds { 3006433d49c3SDaniel Lezcano int ret; 3007433d49c3SDaniel Lezcano 30089a7ec3a9SDaniel Lezcano ret = -ENOMEM; 30099a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 30109a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 30119a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 30129a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3013c19a28e1SFernando Carrijo goto out; 301414e50e57SDavid S. Miller 3015fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 30168ed67789SDaniel Lezcano if (ret) 3017bdb3289fSDaniel Lezcano goto out_kmem_cache; 3018bdb3289fSDaniel Lezcano 3019c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3020c3426b47SDavid S. Miller if (ret) 3021e8803b6cSDavid S. Miller goto out_dst_entries; 30222a0c451aSThomas Graf 30237e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 30247e52b33bSDavid S. Miller if (ret) 30257e52b33bSDavid S. Miller goto out_register_inetpeer; 3026c3426b47SDavid S. Miller 30275dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 30285dc121e9SArnaud Ebalard 30298ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 30308ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 30318ed67789SDaniel Lezcano * manually for init_net */ 3032d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 30338ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3034bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3035d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 30368ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3037d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 30388ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3039bdb3289fSDaniel Lezcano #endif 3040e8803b6cSDavid S. Miller ret = fib6_init(); 3041433d49c3SDaniel Lezcano if (ret) 30428ed67789SDaniel Lezcano goto out_register_subsys; 3043433d49c3SDaniel Lezcano 3044433d49c3SDaniel Lezcano ret = xfrm6_init(); 3045433d49c3SDaniel Lezcano if (ret) 3046e8803b6cSDavid S. Miller goto out_fib6_init; 3047c35b7e72SDaniel Lezcano 3048433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3049433d49c3SDaniel Lezcano if (ret) 3050433d49c3SDaniel Lezcano goto xfrm6_init; 30517e5449c2SDaniel Lezcano 3052d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3053d189634eSThomas Graf if (ret) 3054d189634eSThomas Graf goto fib6_rules_init; 3055d189634eSThomas Graf 3056433d49c3SDaniel Lezcano ret = -ENOBUFS; 3057c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3058c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3059c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3060d189634eSThomas Graf goto out_register_late_subsys; 3061433d49c3SDaniel Lezcano 30628ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3063cdb18761SDaniel Lezcano if (ret) 3064d189634eSThomas Graf goto out_register_late_subsys; 30658ed67789SDaniel Lezcano 3066433d49c3SDaniel Lezcano out: 3067433d49c3SDaniel Lezcano return ret; 3068433d49c3SDaniel Lezcano 3069d189634eSThomas Graf out_register_late_subsys: 3070d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3071433d49c3SDaniel Lezcano fib6_rules_init: 3072433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3073433d49c3SDaniel Lezcano xfrm6_init: 3074433d49c3SDaniel Lezcano xfrm6_fini(); 30752a0c451aSThomas Graf out_fib6_init: 30762a0c451aSThomas Graf fib6_gc_cleanup(); 30778ed67789SDaniel Lezcano out_register_subsys: 30788ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 30797e52b33bSDavid S. Miller out_register_inetpeer: 30807e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3081fc66f95cSEric Dumazet out_dst_entries: 3082fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3083433d49c3SDaniel Lezcano out_kmem_cache: 3084f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3085433d49c3SDaniel Lezcano goto out; 30861da177e4SLinus Torvalds } 30871da177e4SLinus Torvalds 30881da177e4SLinus Torvalds void ip6_route_cleanup(void) 30891da177e4SLinus Torvalds { 30908ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3091d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3092101367c2SThomas Graf fib6_rules_cleanup(); 30931da177e4SLinus Torvalds xfrm6_fini(); 30941da177e4SLinus Torvalds fib6_gc_cleanup(); 3095c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 30968ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 309741bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3098f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 30991da177e4SLinus Torvalds } 3100