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 274fc268d2SRandy Dunlap #include <linux/capability.h> 281da177e4SLinus Torvalds #include <linux/errno.h> 29bc3b2d7fSPaul Gortmaker #include <linux/export.h> 301da177e4SLinus Torvalds #include <linux/types.h> 311da177e4SLinus Torvalds #include <linux/times.h> 321da177e4SLinus Torvalds #include <linux/socket.h> 331da177e4SLinus Torvalds #include <linux/sockios.h> 341da177e4SLinus Torvalds #include <linux/net.h> 351da177e4SLinus Torvalds #include <linux/route.h> 361da177e4SLinus Torvalds #include <linux/netdevice.h> 371da177e4SLinus Torvalds #include <linux/in6.h> 387bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h> 391da177e4SLinus Torvalds #include <linux/init.h> 401da177e4SLinus Torvalds #include <linux/if_arp.h> 411da177e4SLinus Torvalds #include <linux/proc_fs.h> 421da177e4SLinus Torvalds #include <linux/seq_file.h> 435b7c931dSDaniel Lezcano #include <linux/nsproxy.h> 445a0e3ad6STejun Heo #include <linux/slab.h> 45457c4cbcSEric W. Biederman #include <net/net_namespace.h> 461da177e4SLinus Torvalds #include <net/snmp.h> 471da177e4SLinus Torvalds #include <net/ipv6.h> 481da177e4SLinus Torvalds #include <net/ip6_fib.h> 491da177e4SLinus Torvalds #include <net/ip6_route.h> 501da177e4SLinus Torvalds #include <net/ndisc.h> 511da177e4SLinus Torvalds #include <net/addrconf.h> 521da177e4SLinus Torvalds #include <net/tcp.h> 531da177e4SLinus Torvalds #include <linux/rtnetlink.h> 541da177e4SLinus Torvalds #include <net/dst.h> 551da177e4SLinus Torvalds #include <net/xfrm.h> 568d71740cSTom Tucker #include <net/netevent.h> 5721713ebcSThomas Graf #include <net/netlink.h> 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds #include <asm/uaccess.h> 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 621da177e4SLinus Torvalds #include <linux/sysctl.h> 631da177e4SLinus Torvalds #endif 641da177e4SLinus Torvalds 6521efcfa0SEric Dumazet static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort, 6621efcfa0SEric Dumazet const struct in6_addr *dest); 671da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 680dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst); 69ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst); 701da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *); 711da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *); 721da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *, 731da177e4SLinus Torvalds struct net_device *dev, int how); 74569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops); 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds static int ip6_pkt_discard(struct sk_buff *skb); 771da177e4SLinus Torvalds static int ip6_pkt_discard_out(struct sk_buff *skb); 781da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb); 791da177e4SLinus Torvalds static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu); 801da177e4SLinus Torvalds 8170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 82efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 83b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 84b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 8570ceb4f5SYOSHIFUJI Hideaki unsigned pref); 86efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 87b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 88b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex); 8970ceb4f5SYOSHIFUJI Hideaki #endif 9070ceb4f5SYOSHIFUJI Hideaki 9106582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) 9206582540SDavid S. Miller { 9306582540SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 9406582540SDavid S. Miller struct inet_peer *peer; 9506582540SDavid S. Miller u32 *p = NULL; 9606582540SDavid S. Miller 978e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST)) 988e2ec639SYan, Zheng return NULL; 998e2ec639SYan, Zheng 10006582540SDavid S. Miller if (!rt->rt6i_peer) 10106582540SDavid S. Miller rt6_bind_peer(rt, 1); 10206582540SDavid S. Miller 10306582540SDavid S. Miller peer = rt->rt6i_peer; 10406582540SDavid S. Miller if (peer) { 10506582540SDavid S. Miller u32 *old_p = __DST_METRICS_PTR(old); 10606582540SDavid S. Miller unsigned long prev, new; 10706582540SDavid S. Miller 10806582540SDavid S. Miller p = peer->metrics; 10906582540SDavid S. Miller if (inet_metrics_new(peer)) 11006582540SDavid S. Miller memcpy(p, old_p, sizeof(u32) * RTAX_MAX); 11106582540SDavid S. Miller 11206582540SDavid S. Miller new = (unsigned long) p; 11306582540SDavid S. Miller prev = cmpxchg(&dst->_metrics, old, new); 11406582540SDavid S. Miller 11506582540SDavid S. Miller if (prev != old) { 11606582540SDavid S. Miller p = __DST_METRICS_PTR(prev); 11706582540SDavid S. Miller if (prev & DST_METRICS_READ_ONLY) 11806582540SDavid S. Miller p = NULL; 11906582540SDavid S. Miller } 12006582540SDavid S. Miller } 12106582540SDavid S. Miller return p; 12206582540SDavid S. Miller } 12306582540SDavid S. Miller 124*39232973SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, const void *daddr) 125*39232973SDavid S. Miller { 126*39232973SDavid S. Miller struct in6_addr *p = &rt->rt6i_gateway; 127*39232973SDavid S. Miller 128*39232973SDavid S. Miller if (p->s6_addr32[0] | p->s6_addr32[1] | 129*39232973SDavid S. Miller p->s6_addr32[2] | p->s6_addr32[3]) 130*39232973SDavid S. Miller return (const void *) p; 131*39232973SDavid S. Miller return daddr; 132*39232973SDavid S. Miller } 133*39232973SDavid S. Miller 134d3aaeb38SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, const void *daddr) 135d3aaeb38SDavid S. Miller { 136*39232973SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 137*39232973SDavid S. Miller struct neighbour *n; 138*39232973SDavid S. Miller 139*39232973SDavid S. Miller daddr = choose_neigh_daddr(rt, daddr); 140*39232973SDavid S. Miller n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr); 141f83c7790SDavid S. Miller if (n) 142f83c7790SDavid S. Miller return n; 143f83c7790SDavid S. Miller return neigh_create(&nd_tbl, daddr, dst->dev); 144f83c7790SDavid S. Miller } 145f83c7790SDavid S. Miller 1468ade06c6SDavid S. Miller static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev) 147f83c7790SDavid S. Miller { 1488ade06c6SDavid S. Miller struct neighbour *n = __ipv6_neigh_lookup(&nd_tbl, dev, &rt->rt6i_gateway); 1498ade06c6SDavid S. Miller if (!n) { 1508ade06c6SDavid S. Miller n = neigh_create(&nd_tbl, &rt->rt6i_gateway, dev); 151f83c7790SDavid S. Miller if (IS_ERR(n)) 152f83c7790SDavid S. Miller return PTR_ERR(n); 1538ade06c6SDavid S. Miller } 154f83c7790SDavid S. Miller dst_set_neighbour(&rt->dst, n); 155f83c7790SDavid S. Miller 156f83c7790SDavid S. Miller return 0; 157d3aaeb38SDavid S. Miller } 158d3aaeb38SDavid S. Miller 1599a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 1601da177e4SLinus Torvalds .family = AF_INET6, 16109640e63SHarvey Harrison .protocol = cpu_to_be16(ETH_P_IPV6), 1621da177e4SLinus Torvalds .gc = ip6_dst_gc, 1631da177e4SLinus Torvalds .gc_thresh = 1024, 1641da177e4SLinus Torvalds .check = ip6_dst_check, 1650dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 166ebb762f2SSteffen Klassert .mtu = ip6_mtu, 16706582540SDavid S. Miller .cow_metrics = ipv6_cow_metrics, 1681da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 1691da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 1701da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 1711da177e4SLinus Torvalds .link_failure = ip6_link_failure, 1721da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 1731ac06e03SHerbert Xu .local_out = __ip6_local_out, 174d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 1751da177e4SLinus Torvalds }; 1761da177e4SLinus Torvalds 177ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 178ec831ea7SRoland Dreier { 179618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 180618f9bc7SSteffen Klassert 181618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 182ec831ea7SRoland Dreier } 183ec831ea7SRoland Dreier 18414e50e57SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, u32 mtu) 18514e50e57SDavid S. Miller { 18614e50e57SDavid S. Miller } 18714e50e57SDavid S. Miller 1880972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, 1890972ddb2SHeld Bernhard unsigned long old) 1900972ddb2SHeld Bernhard { 1910972ddb2SHeld Bernhard return NULL; 1920972ddb2SHeld Bernhard } 1930972ddb2SHeld Bernhard 19414e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 19514e50e57SDavid S. Miller .family = AF_INET6, 19609640e63SHarvey Harrison .protocol = cpu_to_be16(ETH_P_IPV6), 19714e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 19814e50e57SDavid S. Miller .check = ip6_dst_check, 199ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 200214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 20114e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 2020972ddb2SHeld Bernhard .cow_metrics = ip6_rt_blackhole_cow_metrics, 203d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 20414e50e57SDavid S. Miller }; 20514e50e57SDavid S. Miller 20662fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 20762fa8a84SDavid S. Miller [RTAX_HOPLIMIT - 1] = 255, 20862fa8a84SDavid S. Miller }; 20962fa8a84SDavid S. Miller 210bdb3289fSDaniel Lezcano static struct rt6_info ip6_null_entry_template = { 2111da177e4SLinus Torvalds .dst = { 2121da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 2131da177e4SLinus Torvalds .__use = 1, 2141da177e4SLinus Torvalds .obsolete = -1, 2151da177e4SLinus Torvalds .error = -ENETUNREACH, 2161da177e4SLinus Torvalds .input = ip6_pkt_discard, 2171da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 2181da177e4SLinus Torvalds }, 2191da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2204f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 2211da177e4SLinus Torvalds .rt6i_metric = ~(u32) 0, 2221da177e4SLinus Torvalds .rt6i_ref = ATOMIC_INIT(1), 2231da177e4SLinus Torvalds }; 2241da177e4SLinus Torvalds 225101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 226101367c2SThomas Graf 2276723ab54SDavid S. Miller static int ip6_pkt_prohibit(struct sk_buff *skb); 2286723ab54SDavid S. Miller static int ip6_pkt_prohibit_out(struct sk_buff *skb); 2296723ab54SDavid S. Miller 230280a34c8SAdrian Bunk static struct rt6_info ip6_prohibit_entry_template = { 231101367c2SThomas Graf .dst = { 232101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 233101367c2SThomas Graf .__use = 1, 234101367c2SThomas Graf .obsolete = -1, 235101367c2SThomas Graf .error = -EACCES, 2369ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 2379ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 238101367c2SThomas Graf }, 239101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2404f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 241101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 242101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 243101367c2SThomas Graf }; 244101367c2SThomas Graf 245bdb3289fSDaniel Lezcano static struct rt6_info ip6_blk_hole_entry_template = { 246101367c2SThomas Graf .dst = { 247101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 248101367c2SThomas Graf .__use = 1, 249101367c2SThomas Graf .obsolete = -1, 250101367c2SThomas Graf .error = -EINVAL, 251352e512cSHerbert Xu .input = dst_discard, 252352e512cSHerbert Xu .output = dst_discard, 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 260101367c2SThomas Graf #endif 261101367c2SThomas Graf 2621da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 2635c1e6aa3SDavid S. Miller static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops, 264957c665fSDavid S. Miller struct net_device *dev, 265957c665fSDavid S. Miller int flags) 2661da177e4SLinus Torvalds { 267957c665fSDavid S. Miller struct rt6_info *rt = dst_alloc(ops, dev, 0, 0, flags); 268cf911662SDavid S. Miller 26938308473SDavid S. Miller if (rt) 270fbe58186SMadalin Bucur memset(&rt->rt6i_table, 0, 271fbe58186SMadalin Bucur sizeof(*rt) - sizeof(struct dst_entry)); 272cf911662SDavid S. Miller 273cf911662SDavid S. Miller return rt; 2741da177e4SLinus Torvalds } 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 2771da177e4SLinus Torvalds { 2781da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 2791da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 280b3419363SDavid S. Miller struct inet_peer *peer = rt->rt6i_peer; 2811da177e4SLinus Torvalds 2828e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST)) 2838e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 2848e2ec639SYan, Zheng 28538308473SDavid S. Miller if (idev) { 2861da177e4SLinus Torvalds rt->rt6i_idev = NULL; 2871da177e4SLinus Torvalds in6_dev_put(idev); 2881da177e4SLinus Torvalds } 289b3419363SDavid S. Miller if (peer) { 290b3419363SDavid S. Miller rt->rt6i_peer = NULL; 291b3419363SDavid S. Miller inet_putpeer(peer); 292b3419363SDavid S. Miller } 293b3419363SDavid S. Miller } 294b3419363SDavid S. Miller 2956431cbc2SDavid S. Miller static atomic_t __rt6_peer_genid = ATOMIC_INIT(0); 2966431cbc2SDavid S. Miller 2976431cbc2SDavid S. Miller static u32 rt6_peer_genid(void) 2986431cbc2SDavid S. Miller { 2996431cbc2SDavid S. Miller return atomic_read(&__rt6_peer_genid); 3006431cbc2SDavid S. Miller } 3016431cbc2SDavid S. Miller 302b3419363SDavid S. Miller void rt6_bind_peer(struct rt6_info *rt, int create) 303b3419363SDavid S. Miller { 304b3419363SDavid S. Miller struct inet_peer *peer; 305b3419363SDavid S. Miller 306b3419363SDavid S. Miller peer = inet_getpeer_v6(&rt->rt6i_dst.addr, create); 307b3419363SDavid S. Miller if (peer && cmpxchg(&rt->rt6i_peer, NULL, peer) != NULL) 308b3419363SDavid S. Miller inet_putpeer(peer); 3096431cbc2SDavid S. Miller else 3106431cbc2SDavid S. Miller rt->rt6i_peer_genid = rt6_peer_genid(); 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 3141da177e4SLinus Torvalds int how) 3151da177e4SLinus Torvalds { 3161da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3171da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3185a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 319c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 3201da177e4SLinus Torvalds 32138308473SDavid S. Miller if (dev != loopback_dev && idev && idev->dev == dev) { 3225a3e55d6SDenis V. Lunev struct inet6_dev *loopback_idev = 3235a3e55d6SDenis V. Lunev in6_dev_get(loopback_dev); 32438308473SDavid S. Miller if (loopback_idev) { 3251da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 3261da177e4SLinus Torvalds in6_dev_put(idev); 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds } 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds static __inline__ int rt6_check_expired(const struct rt6_info *rt) 3321da177e4SLinus Torvalds { 333a02cec21SEric Dumazet return (rt->rt6i_flags & RTF_EXPIRES) && 334d1918542SDavid S. Miller time_after(jiffies, rt->dst.expires); 3351da177e4SLinus Torvalds } 3361da177e4SLinus Torvalds 337b71d1d42SEric Dumazet static inline int rt6_need_strict(const struct in6_addr *daddr) 338c71099acSThomas Graf { 339a02cec21SEric Dumazet return ipv6_addr_type(daddr) & 340a02cec21SEric Dumazet (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); 341c71099acSThomas Graf } 342c71099acSThomas Graf 3431da177e4SLinus Torvalds /* 344c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 3451da177e4SLinus Torvalds */ 3461da177e4SLinus Torvalds 3478ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 3488ed67789SDaniel Lezcano struct rt6_info *rt, 349b71d1d42SEric Dumazet const struct in6_addr *saddr, 3501da177e4SLinus Torvalds int oif, 351d420895eSYOSHIFUJI Hideaki int flags) 3521da177e4SLinus Torvalds { 3531da177e4SLinus Torvalds struct rt6_info *local = NULL; 3541da177e4SLinus Torvalds struct rt6_info *sprt; 3551da177e4SLinus Torvalds 356dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 357dd3abc4eSYOSHIFUJI Hideaki goto out; 358dd3abc4eSYOSHIFUJI Hideaki 359d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 360d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 361dd3abc4eSYOSHIFUJI Hideaki 362dd3abc4eSYOSHIFUJI Hideaki if (oif) { 3631da177e4SLinus Torvalds if (dev->ifindex == oif) 3641da177e4SLinus Torvalds return sprt; 3651da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 36638308473SDavid S. Miller if (!sprt->rt6i_idev || 3671da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 368d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE && oif) 3691da177e4SLinus Torvalds continue; 3701da177e4SLinus Torvalds if (local && (!oif || 3711da177e4SLinus Torvalds local->rt6i_idev->dev->ifindex == oif)) 3721da177e4SLinus Torvalds continue; 3731da177e4SLinus Torvalds } 3741da177e4SLinus Torvalds local = sprt; 3751da177e4SLinus Torvalds } 376dd3abc4eSYOSHIFUJI Hideaki } else { 377dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 378dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 379dd3abc4eSYOSHIFUJI Hideaki return sprt; 380dd3abc4eSYOSHIFUJI Hideaki } 3811da177e4SLinus Torvalds } 3821da177e4SLinus Torvalds 383dd3abc4eSYOSHIFUJI Hideaki if (oif) { 3841da177e4SLinus Torvalds if (local) 3851da177e4SLinus Torvalds return local; 3861da177e4SLinus Torvalds 387d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 3888ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 3891da177e4SLinus Torvalds } 390dd3abc4eSYOSHIFUJI Hideaki out: 3911da177e4SLinus Torvalds return rt; 3921da177e4SLinus Torvalds } 3931da177e4SLinus Torvalds 39427097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 39527097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 39627097255SYOSHIFUJI Hideaki { 397f2c31e32SEric Dumazet struct neighbour *neigh; 39827097255SYOSHIFUJI Hideaki /* 39927097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 40027097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 40127097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 40227097255SYOSHIFUJI Hideaki * 40327097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 40427097255SYOSHIFUJI Hideaki * to no more than one per minute. 40527097255SYOSHIFUJI Hideaki */ 406f2c31e32SEric Dumazet rcu_read_lock(); 40727217455SDavid Miller neigh = rt ? dst_get_neighbour_noref(&rt->dst) : NULL; 40827097255SYOSHIFUJI Hideaki if (!neigh || (neigh->nud_state & NUD_VALID)) 409f2c31e32SEric Dumazet goto out; 41027097255SYOSHIFUJI Hideaki read_lock_bh(&neigh->lock); 41127097255SYOSHIFUJI Hideaki if (!(neigh->nud_state & NUD_VALID) && 41252e16356SYOSHIFUJI Hideaki time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { 41327097255SYOSHIFUJI Hideaki struct in6_addr mcaddr; 41427097255SYOSHIFUJI Hideaki struct in6_addr *target; 41527097255SYOSHIFUJI Hideaki 41627097255SYOSHIFUJI Hideaki neigh->updated = jiffies; 41727097255SYOSHIFUJI Hideaki read_unlock_bh(&neigh->lock); 41827097255SYOSHIFUJI Hideaki 41927097255SYOSHIFUJI Hideaki target = (struct in6_addr *)&neigh->primary_key; 42027097255SYOSHIFUJI Hideaki addrconf_addr_solict_mult(target, &mcaddr); 421d1918542SDavid S. Miller ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL); 422f2c31e32SEric Dumazet } else { 42327097255SYOSHIFUJI Hideaki read_unlock_bh(&neigh->lock); 42427097255SYOSHIFUJI Hideaki } 425f2c31e32SEric Dumazet out: 426f2c31e32SEric Dumazet rcu_read_unlock(); 427f2c31e32SEric Dumazet } 42827097255SYOSHIFUJI Hideaki #else 42927097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 43027097255SYOSHIFUJI Hideaki { 43127097255SYOSHIFUJI Hideaki } 43227097255SYOSHIFUJI Hideaki #endif 43327097255SYOSHIFUJI Hideaki 4341da177e4SLinus Torvalds /* 435554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 4361da177e4SLinus Torvalds */ 437b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 4381da177e4SLinus Torvalds { 439d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 440161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 441554cfb7eSYOSHIFUJI Hideaki return 2; 442161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 443161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 444161980f4SDavid S. Miller return 1; 445554cfb7eSYOSHIFUJI Hideaki return 0; 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds 448b6f99a21SDave Jones static inline int rt6_check_neigh(struct rt6_info *rt) 4491da177e4SLinus Torvalds { 450f2c31e32SEric Dumazet struct neighbour *neigh; 451398bcbebSYOSHIFUJI Hideaki int m; 452f2c31e32SEric Dumazet 453f2c31e32SEric Dumazet rcu_read_lock(); 45427217455SDavid Miller neigh = dst_get_neighbour_noref(&rt->dst); 4554d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 4564d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 4574d0c5911SYOSHIFUJI Hideaki m = 1; 4584d0c5911SYOSHIFUJI Hideaki else if (neigh) { 4591da177e4SLinus Torvalds read_lock_bh(&neigh->lock); 460554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 4614d0c5911SYOSHIFUJI Hideaki m = 2; 462398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 463398bcbebSYOSHIFUJI Hideaki else if (neigh->nud_state & NUD_FAILED) 464398bcbebSYOSHIFUJI Hideaki m = 0; 465398bcbebSYOSHIFUJI Hideaki #endif 466398bcbebSYOSHIFUJI Hideaki else 467ea73ee23SYOSHIFUJI Hideaki m = 1; 4681da177e4SLinus Torvalds read_unlock_bh(&neigh->lock); 469398bcbebSYOSHIFUJI Hideaki } else 470398bcbebSYOSHIFUJI Hideaki m = 0; 471f2c31e32SEric Dumazet rcu_read_unlock(); 472554cfb7eSYOSHIFUJI Hideaki return m; 4731da177e4SLinus Torvalds } 4741da177e4SLinus Torvalds 475554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 476554cfb7eSYOSHIFUJI Hideaki int strict) 477554cfb7eSYOSHIFUJI Hideaki { 4784d0c5911SYOSHIFUJI Hideaki int m, n; 4794d0c5911SYOSHIFUJI Hideaki 4804d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 48177d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 482554cfb7eSYOSHIFUJI Hideaki return -1; 483ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 484ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 485ebacaaa0SYOSHIFUJI Hideaki #endif 4864d0c5911SYOSHIFUJI Hideaki n = rt6_check_neigh(rt); 487557e92efSYOSHIFUJI Hideaki if (!n && (strict & RT6_LOOKUP_F_REACHABLE)) 488554cfb7eSYOSHIFUJI Hideaki return -1; 489554cfb7eSYOSHIFUJI Hideaki return m; 490554cfb7eSYOSHIFUJI Hideaki } 491554cfb7eSYOSHIFUJI Hideaki 492f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 493f11e6659SDavid S. Miller int *mpri, struct rt6_info *match) 494554cfb7eSYOSHIFUJI Hideaki { 495554cfb7eSYOSHIFUJI Hideaki int m; 496554cfb7eSYOSHIFUJI Hideaki 497554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 498f11e6659SDavid S. Miller goto out; 499554cfb7eSYOSHIFUJI Hideaki 500554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 501554cfb7eSYOSHIFUJI Hideaki if (m < 0) 502f11e6659SDavid S. Miller goto out; 503554cfb7eSYOSHIFUJI Hideaki 504f11e6659SDavid S. Miller if (m > *mpri) { 505ea659e07SYOSHIFUJI Hideaki if (strict & RT6_LOOKUP_F_REACHABLE) 50627097255SYOSHIFUJI Hideaki rt6_probe(match); 507f11e6659SDavid S. Miller *mpri = m; 508554cfb7eSYOSHIFUJI Hideaki match = rt; 509ea659e07SYOSHIFUJI Hideaki } else if (strict & RT6_LOOKUP_F_REACHABLE) { 51027097255SYOSHIFUJI Hideaki rt6_probe(rt); 5111da177e4SLinus Torvalds } 512f11e6659SDavid S. Miller 513f11e6659SDavid S. Miller out: 514f11e6659SDavid S. Miller return match; 5151da177e4SLinus Torvalds } 5161da177e4SLinus Torvalds 517f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 518f11e6659SDavid S. Miller struct rt6_info *rr_head, 519f11e6659SDavid S. Miller u32 metric, int oif, int strict) 520f11e6659SDavid S. Miller { 521f11e6659SDavid S. Miller struct rt6_info *rt, *match; 522f11e6659SDavid S. Miller int mpri = -1; 523f11e6659SDavid S. Miller 524f11e6659SDavid S. Miller match = NULL; 525f11e6659SDavid S. Miller for (rt = rr_head; rt && rt->rt6i_metric == metric; 526d8d1f30bSChangli Gao rt = rt->dst.rt6_next) 527f11e6659SDavid S. Miller match = find_match(rt, oif, strict, &mpri, match); 528f11e6659SDavid S. Miller for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric; 529d8d1f30bSChangli Gao rt = rt->dst.rt6_next) 530f11e6659SDavid S. Miller match = find_match(rt, oif, strict, &mpri, match); 531f11e6659SDavid S. Miller 532f11e6659SDavid S. Miller return match; 533f11e6659SDavid S. Miller } 534f11e6659SDavid S. Miller 535f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 536f11e6659SDavid S. Miller { 537f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 5388ed67789SDaniel Lezcano struct net *net; 539f11e6659SDavid S. Miller 540f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 541f11e6659SDavid S. Miller if (!rt0) 542f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 543f11e6659SDavid S. Miller 544f11e6659SDavid S. Miller match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict); 545f11e6659SDavid S. Miller 546554cfb7eSYOSHIFUJI Hideaki if (!match && 547f11e6659SDavid S. Miller (strict & RT6_LOOKUP_F_REACHABLE)) { 548d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 549f11e6659SDavid S. Miller 550554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 551f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 552f11e6659SDavid S. Miller next = fn->leaf; 553f11e6659SDavid S. Miller 554f11e6659SDavid S. Miller if (next != rt0) 555f11e6659SDavid S. Miller fn->rr_ptr = next; 556554cfb7eSYOSHIFUJI Hideaki } 557554cfb7eSYOSHIFUJI Hideaki 558d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 559a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 5601da177e4SLinus Torvalds } 5611da177e4SLinus Torvalds 56270ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 56370ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 564b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 56570ceb4f5SYOSHIFUJI Hideaki { 566c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 56770ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 56870ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 56970ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 5704bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 57170ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 57270ceb4f5SYOSHIFUJI Hideaki 57370ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 57470ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 57570ceb4f5SYOSHIFUJI Hideaki } 57670ceb4f5SYOSHIFUJI Hideaki 57770ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 57870ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 57970ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 58070ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 58170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 58270ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 58370ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 58470ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 58570ceb4f5SYOSHIFUJI Hideaki } 58670ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 58770ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 58870ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 58970ceb4f5SYOSHIFUJI Hideaki } 59070ceb4f5SYOSHIFUJI Hideaki } 59170ceb4f5SYOSHIFUJI Hideaki 59270ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 59370ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 5943933fc95SJens Rosenboom return -EINVAL; 59570ceb4f5SYOSHIFUJI Hideaki 5964bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 59770ceb4f5SYOSHIFUJI Hideaki 59870ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 59970ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 60070ceb4f5SYOSHIFUJI Hideaki else { 60170ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 60270ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 60370ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 60470ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 60570ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 60670ceb4f5SYOSHIFUJI Hideaki } 60770ceb4f5SYOSHIFUJI Hideaki 608efa2cea0SDaniel Lezcano rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr, 609efa2cea0SDaniel Lezcano dev->ifindex); 61070ceb4f5SYOSHIFUJI Hideaki 61170ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 612e0a1ad73SThomas Graf ip6_del_rt(rt); 61370ceb4f5SYOSHIFUJI Hideaki rt = NULL; 61470ceb4f5SYOSHIFUJI Hideaki } 61570ceb4f5SYOSHIFUJI Hideaki 61670ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 617efa2cea0SDaniel Lezcano rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 61870ceb4f5SYOSHIFUJI Hideaki pref); 61970ceb4f5SYOSHIFUJI Hideaki else if (rt) 62070ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 62170ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 62270ceb4f5SYOSHIFUJI Hideaki 62370ceb4f5SYOSHIFUJI Hideaki if (rt) { 6244bed72e4SYOSHIFUJI Hideaki if (!addrconf_finite_timeout(lifetime)) { 62570ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags &= ~RTF_EXPIRES; 62670ceb4f5SYOSHIFUJI Hideaki } else { 627d1918542SDavid S. Miller rt->dst.expires = jiffies + HZ * lifetime; 62870ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_EXPIRES; 62970ceb4f5SYOSHIFUJI Hideaki } 630d8d1f30bSChangli Gao dst_release(&rt->dst); 63170ceb4f5SYOSHIFUJI Hideaki } 63270ceb4f5SYOSHIFUJI Hideaki return 0; 63370ceb4f5SYOSHIFUJI Hideaki } 63470ceb4f5SYOSHIFUJI Hideaki #endif 63570ceb4f5SYOSHIFUJI Hideaki 6368ed67789SDaniel Lezcano #define BACKTRACK(__net, saddr) \ 637982f56f3SYOSHIFUJI Hideaki do { \ 6388ed67789SDaniel Lezcano if (rt == __net->ipv6.ip6_null_entry) { \ 639982f56f3SYOSHIFUJI Hideaki struct fib6_node *pn; \ 640e0eda7bbSVille Nuorvala while (1) { \ 641982f56f3SYOSHIFUJI Hideaki if (fn->fn_flags & RTN_TL_ROOT) \ 642c71099acSThomas Graf goto out; \ 643982f56f3SYOSHIFUJI Hideaki pn = fn->parent; \ 644982f56f3SYOSHIFUJI Hideaki if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \ 6458bce65b9SKim Nordlund fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \ 646982f56f3SYOSHIFUJI Hideaki else \ 647982f56f3SYOSHIFUJI Hideaki fn = pn; \ 648c71099acSThomas Graf if (fn->fn_flags & RTN_RTINFO) \ 649c71099acSThomas Graf goto restart; \ 650c71099acSThomas Graf } \ 651982f56f3SYOSHIFUJI Hideaki } \ 652982f56f3SYOSHIFUJI Hideaki } while (0) 653c71099acSThomas Graf 6548ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 6558ed67789SDaniel Lezcano struct fib6_table *table, 6564c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 6571da177e4SLinus Torvalds { 6581da177e4SLinus Torvalds struct fib6_node *fn; 6591da177e4SLinus Torvalds struct rt6_info *rt; 6601da177e4SLinus Torvalds 661c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 6624c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 663c71099acSThomas Graf restart: 664c71099acSThomas Graf rt = fn->leaf; 6654c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 6664c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 667c71099acSThomas Graf out: 668d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 669c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 6701da177e4SLinus Torvalds return rt; 671c71099acSThomas Graf 672c71099acSThomas Graf } 673c71099acSThomas Graf 674ea6e574eSFlorian Westphal struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6, 675ea6e574eSFlorian Westphal int flags) 676ea6e574eSFlorian Westphal { 677ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 678ea6e574eSFlorian Westphal } 679ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 680ea6e574eSFlorian Westphal 6819acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 6829acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 683c71099acSThomas Graf { 6844c9483b2SDavid S. Miller struct flowi6 fl6 = { 6854c9483b2SDavid S. Miller .flowi6_oif = oif, 6864c9483b2SDavid S. Miller .daddr = *daddr, 687c71099acSThomas Graf }; 688c71099acSThomas Graf struct dst_entry *dst; 68977d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 690c71099acSThomas Graf 691adaa70bbSThomas Graf if (saddr) { 6924c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 693adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 694adaa70bbSThomas Graf } 695adaa70bbSThomas Graf 6964c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 697c71099acSThomas Graf if (dst->error == 0) 698c71099acSThomas Graf return (struct rt6_info *) dst; 699c71099acSThomas Graf 700c71099acSThomas Graf dst_release(dst); 701c71099acSThomas Graf 7021da177e4SLinus Torvalds return NULL; 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds 7057159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 7067159039aSYOSHIFUJI Hideaki 707c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 7081da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 7091da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 7101da177e4SLinus Torvalds be destroyed. 7111da177e4SLinus Torvalds */ 7121da177e4SLinus Torvalds 71386872cb5SThomas Graf static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info) 7141da177e4SLinus Torvalds { 7151da177e4SLinus Torvalds int err; 716c71099acSThomas Graf struct fib6_table *table; 7171da177e4SLinus Torvalds 718c71099acSThomas Graf table = rt->rt6i_table; 719c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 72086872cb5SThomas Graf err = fib6_add(&table->tb6_root, rt, info); 721c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 7221da177e4SLinus Torvalds 7231da177e4SLinus Torvalds return err; 7241da177e4SLinus Torvalds } 7251da177e4SLinus Torvalds 72640e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 72740e22e8fSThomas Graf { 7284d1169c1SDenis V. Lunev struct nl_info info = { 729d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 7304d1169c1SDenis V. Lunev }; 731528c4cebSDenis V. Lunev return __ip6_ins_rt(rt, &info); 73240e22e8fSThomas Graf } 73340e22e8fSThomas Graf 73421efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_cow(const struct rt6_info *ort, 73521efcfa0SEric Dumazet const struct in6_addr *daddr, 736b71d1d42SEric Dumazet const struct in6_addr *saddr) 7371da177e4SLinus Torvalds { 7381da177e4SLinus Torvalds struct rt6_info *rt; 7391da177e4SLinus Torvalds 7401da177e4SLinus Torvalds /* 7411da177e4SLinus Torvalds * Clone the route. 7421da177e4SLinus Torvalds */ 7431da177e4SLinus Torvalds 74421efcfa0SEric Dumazet rt = ip6_rt_copy(ort, daddr); 7451da177e4SLinus Torvalds 7461da177e4SLinus Torvalds if (rt) { 74714deae41SDavid S. Miller int attempts = !in_softirq(); 74814deae41SDavid S. Miller 74958c4fb86SYOSHIFUJI Hideaki if (!(rt->rt6i_flags & RTF_GATEWAY)) { 750bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 75121efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 75258c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 7534e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *daddr; 75458c4fb86SYOSHIFUJI Hideaki } 7551da177e4SLinus Torvalds 7561da177e4SLinus Torvalds rt->rt6i_flags |= RTF_CACHE; 7571da177e4SLinus Torvalds 7581da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 7591da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 7604e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 7611da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 7621da177e4SLinus Torvalds } 7631da177e4SLinus Torvalds #endif 7641da177e4SLinus Torvalds 76514deae41SDavid S. Miller retry: 7668ade06c6SDavid S. Miller if (rt6_bind_neighbour(rt, rt->dst.dev)) { 767d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 76814deae41SDavid S. Miller int saved_rt_min_interval = 76914deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval; 77014deae41SDavid S. Miller int saved_rt_elasticity = 77114deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity; 77214deae41SDavid S. Miller 77314deae41SDavid S. Miller if (attempts-- > 0) { 77414deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity = 1; 77514deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval = 0; 77614deae41SDavid S. Miller 77786393e52SAlexey Dobriyan ip6_dst_gc(&net->ipv6.ip6_dst_ops); 77814deae41SDavid S. Miller 77914deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_elasticity = 78014deae41SDavid S. Miller saved_rt_elasticity; 78114deae41SDavid S. Miller net->ipv6.sysctl.ip6_rt_gc_min_interval = 78214deae41SDavid S. Miller saved_rt_min_interval; 78314deae41SDavid S. Miller goto retry; 78414deae41SDavid S. Miller } 78514deae41SDavid S. Miller 78614deae41SDavid S. Miller if (net_ratelimit()) 78714deae41SDavid S. Miller printk(KERN_WARNING 7887e1b33e5SUlrich Weber "ipv6: Neighbour table overflow.\n"); 789d8d1f30bSChangli Gao dst_free(&rt->dst); 79014deae41SDavid S. Miller return NULL; 79114deae41SDavid S. Miller } 79295a9a5baSYOSHIFUJI Hideaki } 7931da177e4SLinus Torvalds 7941da177e4SLinus Torvalds return rt; 7951da177e4SLinus Torvalds } 79695a9a5baSYOSHIFUJI Hideaki 79721efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, 79821efcfa0SEric Dumazet const struct in6_addr *daddr) 799299d9939SYOSHIFUJI Hideaki { 80021efcfa0SEric Dumazet struct rt6_info *rt = ip6_rt_copy(ort, daddr); 80121efcfa0SEric Dumazet 802299d9939SYOSHIFUJI Hideaki if (rt) { 803299d9939SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_CACHE; 80427217455SDavid Miller dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_noref_raw(&ort->dst))); 805299d9939SYOSHIFUJI Hideaki } 806299d9939SYOSHIFUJI Hideaki return rt; 807299d9939SYOSHIFUJI Hideaki } 808299d9939SYOSHIFUJI Hideaki 8098ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, 8104c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 8111da177e4SLinus Torvalds { 8121da177e4SLinus Torvalds struct fib6_node *fn; 813519fbd87SYOSHIFUJI Hideaki struct rt6_info *rt, *nrt; 814c71099acSThomas Graf int strict = 0; 8151da177e4SLinus Torvalds int attempts = 3; 816519fbd87SYOSHIFUJI Hideaki int err; 81753b7997fSYOSHIFUJI Hideaki int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE; 8181da177e4SLinus Torvalds 81977d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 8201da177e4SLinus Torvalds 8211da177e4SLinus Torvalds relookup: 822c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 8231da177e4SLinus Torvalds 8248238dd06SYOSHIFUJI Hideaki restart_2: 8254c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 8261da177e4SLinus Torvalds 8271da177e4SLinus Torvalds restart: 8284acad72dSPavel Emelyanov rt = rt6_select(fn, oif, strict | reachable); 8298ed67789SDaniel Lezcano 8304c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 8318ed67789SDaniel Lezcano if (rt == net->ipv6.ip6_null_entry || 8328238dd06SYOSHIFUJI Hideaki rt->rt6i_flags & RTF_CACHE) 8331da177e4SLinus Torvalds goto out; 8341da177e4SLinus Torvalds 835d8d1f30bSChangli Gao dst_hold(&rt->dst); 836c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 8371da177e4SLinus Torvalds 83827217455SDavid Miller if (!dst_get_neighbour_noref_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP)) 8394c9483b2SDavid S. Miller nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); 8407343ff31SDavid S. Miller else if (!(rt->dst.flags & DST_HOST)) 8414c9483b2SDavid S. Miller nrt = rt6_alloc_clone(rt, &fl6->daddr); 8427343ff31SDavid S. Miller else 8437343ff31SDavid S. Miller goto out2; 8441da177e4SLinus Torvalds 845d8d1f30bSChangli Gao dst_release(&rt->dst); 8468ed67789SDaniel Lezcano rt = nrt ? : net->ipv6.ip6_null_entry; 8471da177e4SLinus Torvalds 848d8d1f30bSChangli Gao dst_hold(&rt->dst); 849e40cf353SYOSHIFUJI Hideaki if (nrt) { 85040e22e8fSThomas Graf err = ip6_ins_rt(nrt); 851e40cf353SYOSHIFUJI Hideaki if (!err) 852e40cf353SYOSHIFUJI Hideaki goto out2; 853e40cf353SYOSHIFUJI Hideaki } 854e40cf353SYOSHIFUJI Hideaki 855e40cf353SYOSHIFUJI Hideaki if (--attempts <= 0) 8561da177e4SLinus Torvalds goto out2; 8571da177e4SLinus Torvalds 858519fbd87SYOSHIFUJI Hideaki /* 859c71099acSThomas Graf * Race condition! In the gap, when table->tb6_lock was 860519fbd87SYOSHIFUJI Hideaki * released someone could insert this route. Relookup. 8611da177e4SLinus Torvalds */ 862d8d1f30bSChangli Gao dst_release(&rt->dst); 8631da177e4SLinus Torvalds goto relookup; 864e40cf353SYOSHIFUJI Hideaki 865519fbd87SYOSHIFUJI Hideaki out: 8668238dd06SYOSHIFUJI Hideaki if (reachable) { 8678238dd06SYOSHIFUJI Hideaki reachable = 0; 8688238dd06SYOSHIFUJI Hideaki goto restart_2; 8698238dd06SYOSHIFUJI Hideaki } 870d8d1f30bSChangli Gao dst_hold(&rt->dst); 871c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 8721da177e4SLinus Torvalds out2: 873d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 874d8d1f30bSChangli Gao rt->dst.__use++; 875c71099acSThomas Graf 876c71099acSThomas Graf return rt; 877c71099acSThomas Graf } 878c71099acSThomas Graf 8798ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 8804c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 8814acad72dSPavel Emelyanov { 8824c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 8834acad72dSPavel Emelyanov } 8844acad72dSPavel Emelyanov 885c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 886c71099acSThomas Graf { 887b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 888c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 889adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 8904c9483b2SDavid S. Miller struct flowi6 fl6 = { 8914c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 8924c9483b2SDavid S. Miller .daddr = iph->daddr, 8934c9483b2SDavid S. Miller .saddr = iph->saddr, 8944c9483b2SDavid S. Miller .flowlabel = (* (__be32 *) iph) & IPV6_FLOWINFO_MASK, 8954c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 8964c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 897c71099acSThomas Graf }; 898adaa70bbSThomas Graf 8991d6e55f1SThomas Goff if (rt6_need_strict(&iph->daddr) && skb->dev->type != ARPHRD_PIMREG) 900adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_IFACE; 901c71099acSThomas Graf 9024c9483b2SDavid S. Miller skb_dst_set(skb, fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_input)); 903c71099acSThomas Graf } 904c71099acSThomas Graf 9058ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 9064c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 907c71099acSThomas Graf { 9084c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 909c71099acSThomas Graf } 910c71099acSThomas Graf 9119c7a4f9cSFlorian Westphal struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk, 9124c9483b2SDavid S. Miller struct flowi6 *fl6) 913c71099acSThomas Graf { 914c71099acSThomas Graf int flags = 0; 915c71099acSThomas Graf 9164c9483b2SDavid S. Miller if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) 91777d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 918c71099acSThomas Graf 9194c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 920adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 9210c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 9220c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 923adaa70bbSThomas Graf 9244c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 9251da177e4SLinus Torvalds } 9261da177e4SLinus Torvalds 9277159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 9281da177e4SLinus Torvalds 9292774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 93014e50e57SDavid S. Miller { 9315c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 93214e50e57SDavid S. Miller struct dst_entry *new = NULL; 93314e50e57SDavid S. Miller 9345c1e6aa3SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0); 93514e50e57SDavid S. Miller if (rt) { 936cf911662SDavid S. Miller memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry)); 937cf911662SDavid S. Miller 938d8d1f30bSChangli Gao new = &rt->dst; 93914e50e57SDavid S. Miller 94014e50e57SDavid S. Miller new->__use = 1; 941352e512cSHerbert Xu new->input = dst_discard; 942352e512cSHerbert Xu new->output = dst_discard; 94314e50e57SDavid S. Miller 94421efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 94521efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 94621efcfa0SEric Dumazet else 947defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 94814e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 94914e50e57SDavid S. Miller if (rt->rt6i_idev) 95014e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 951d1918542SDavid S. Miller rt->dst.expires = 0; 95214e50e57SDavid S. Miller 9534e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 95414e50e57SDavid S. Miller rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES; 95514e50e57SDavid S. Miller rt->rt6i_metric = 0; 95614e50e57SDavid S. Miller 95714e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 95814e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 95914e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 96014e50e57SDavid S. Miller #endif 96114e50e57SDavid S. Miller 96214e50e57SDavid S. Miller dst_free(new); 96314e50e57SDavid S. Miller } 96414e50e57SDavid S. Miller 96569ead7afSDavid S. Miller dst_release(dst_orig); 96669ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 96714e50e57SDavid S. Miller } 96814e50e57SDavid S. Miller 9691da177e4SLinus Torvalds /* 9701da177e4SLinus Torvalds * Destination cache support functions 9711da177e4SLinus Torvalds */ 9721da177e4SLinus Torvalds 9731da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 9741da177e4SLinus Torvalds { 9751da177e4SLinus Torvalds struct rt6_info *rt; 9761da177e4SLinus Torvalds 9771da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 9781da177e4SLinus Torvalds 9796431cbc2SDavid S. Miller if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) { 9806431cbc2SDavid S. Miller if (rt->rt6i_peer_genid != rt6_peer_genid()) { 9816431cbc2SDavid S. Miller if (!rt->rt6i_peer) 9826431cbc2SDavid S. Miller rt6_bind_peer(rt, 0); 9836431cbc2SDavid S. Miller rt->rt6i_peer_genid = rt6_peer_genid(); 9846431cbc2SDavid S. Miller } 9851da177e4SLinus Torvalds return dst; 9866431cbc2SDavid S. Miller } 9871da177e4SLinus Torvalds return NULL; 9881da177e4SLinus Torvalds } 9891da177e4SLinus Torvalds 9901da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 9911da177e4SLinus Torvalds { 9921da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 9931da177e4SLinus Torvalds 9941da177e4SLinus Torvalds if (rt) { 99554c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 99654c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 997e0a1ad73SThomas Graf ip6_del_rt(rt); 99854c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 9991da177e4SLinus Torvalds } 100054c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 100154c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 100254c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 100354c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 100454c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 100554c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 10061da177e4SLinus Torvalds } 10071da177e4SLinus Torvalds 10081da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 10091da177e4SLinus Torvalds { 10101da177e4SLinus Torvalds struct rt6_info *rt; 10111da177e4SLinus Torvalds 10123ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 10131da177e4SLinus Torvalds 1014adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 10151da177e4SLinus Torvalds if (rt) { 10161da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 1017d8d1f30bSChangli Gao dst_set_expires(&rt->dst, 0); 10181da177e4SLinus Torvalds rt->rt6i_flags |= RTF_EXPIRES; 10191da177e4SLinus Torvalds } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) 10201da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 10211da177e4SLinus Torvalds } 10221da177e4SLinus Torvalds } 10231da177e4SLinus Torvalds 10241da177e4SLinus Torvalds static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu) 10251da177e4SLinus Torvalds { 10261da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info*)dst; 10271da177e4SLinus Torvalds 10281da177e4SLinus Torvalds if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) { 10291da177e4SLinus Torvalds rt6->rt6i_flags |= RTF_MODIFIED; 10301da177e4SLinus Torvalds if (mtu < IPV6_MIN_MTU) { 1031defb3519SDavid S. Miller u32 features = dst_metric(dst, RTAX_FEATURES); 10321da177e4SLinus Torvalds mtu = IPV6_MIN_MTU; 1033defb3519SDavid S. Miller features |= RTAX_FEATURE_ALLFRAG; 1034defb3519SDavid S. Miller dst_metric_set(dst, RTAX_FEATURES, features); 10351da177e4SLinus Torvalds } 1036defb3519SDavid S. Miller dst_metric_set(dst, RTAX_MTU, mtu); 10371da177e4SLinus Torvalds } 10381da177e4SLinus Torvalds } 10391da177e4SLinus Torvalds 10400dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 10411da177e4SLinus Torvalds { 10420dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 10430dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 10440dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 10450dbaee3bSDavid S. Miller 10461da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 10471da177e4SLinus Torvalds 10485578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 10495578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 10501da177e4SLinus Torvalds 10511da177e4SLinus Torvalds /* 10521da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 10531da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 10541da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 10551da177e4SLinus Torvalds * rely only on pmtu discovery" 10561da177e4SLinus Torvalds */ 10571da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 10581da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 10591da177e4SLinus Torvalds return mtu; 10601da177e4SLinus Torvalds } 10611da177e4SLinus Torvalds 1062ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1063d33e4553SDavid S. Miller { 1064d33e4553SDavid S. Miller struct inet6_dev *idev; 1065618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 1066618f9bc7SSteffen Klassert 1067618f9bc7SSteffen Klassert if (mtu) 1068618f9bc7SSteffen Klassert return mtu; 1069618f9bc7SSteffen Klassert 1070618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1071d33e4553SDavid S. Miller 1072d33e4553SDavid S. Miller rcu_read_lock(); 1073d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1074d33e4553SDavid S. Miller if (idev) 1075d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1076d33e4553SDavid S. Miller rcu_read_unlock(); 1077d33e4553SDavid S. Miller 1078d33e4553SDavid S. Miller return mtu; 1079d33e4553SDavid S. Miller } 1080d33e4553SDavid S. Miller 10813b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 10823b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 10835d0bbeebSThomas Graf 10843b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 10851da177e4SLinus Torvalds struct neighbour *neigh, 108687a11578SDavid S. Miller struct flowi6 *fl6) 10871da177e4SLinus Torvalds { 108887a11578SDavid S. Miller struct dst_entry *dst; 10891da177e4SLinus Torvalds struct rt6_info *rt; 10901da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1091c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 10921da177e4SLinus Torvalds 109338308473SDavid S. Miller if (unlikely(!idev)) 10941da177e4SLinus Torvalds return NULL; 10951da177e4SLinus Torvalds 1096957c665fSDavid S. Miller rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, dev, 0); 109738308473SDavid S. Miller if (unlikely(!rt)) { 10981da177e4SLinus Torvalds in6_dev_put(idev); 109987a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 11001da177e4SLinus Torvalds goto out; 11011da177e4SLinus Torvalds } 11021da177e4SLinus Torvalds 11031da177e4SLinus Torvalds if (neigh) 11041da177e4SLinus Torvalds neigh_hold(neigh); 110514deae41SDavid S. Miller else { 1106f83c7790SDavid S. Miller neigh = ip6_neigh_lookup(&rt->dst, &fl6->daddr); 1107b43faac6SDavid S. Miller if (IS_ERR(neigh)) { 1108252c3d84SRongQing.Li in6_dev_put(idev); 1109b43faac6SDavid S. Miller dst_free(&rt->dst); 1110b43faac6SDavid S. Miller return ERR_CAST(neigh); 1111b43faac6SDavid S. Miller } 111214deae41SDavid S. Miller } 11131da177e4SLinus Torvalds 11148e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 11158e2ec639SYan, Zheng rt->dst.output = ip6_output; 111669cce1d1SDavid S. Miller dst_set_neighbour(&rt->dst, neigh); 1117d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 111887a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 11198e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 11208e2ec639SYan, Zheng rt->rt6i_idev = idev; 11217011687fSGao feng dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 255); 11221da177e4SLinus Torvalds 11233b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1124d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1125d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 11263b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 11271da177e4SLinus Torvalds 11285578689aSDaniel Lezcano fib6_force_start_gc(net); 11291da177e4SLinus Torvalds 113087a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 113187a11578SDavid S. Miller 11321da177e4SLinus Torvalds out: 113387a11578SDavid S. Miller return dst; 11341da177e4SLinus Torvalds } 11351da177e4SLinus Torvalds 11363d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 11371da177e4SLinus Torvalds { 1138e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 11393d0f24a7SStephen Hemminger int more = 0; 11401da177e4SLinus Torvalds 11413b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 11423b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 11435d0bbeebSThomas Graf 11441da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 11451da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 11461da177e4SLinus Torvalds *pprev = dst->next; 11471da177e4SLinus Torvalds dst_free(dst); 11481da177e4SLinus Torvalds } else { 11491da177e4SLinus Torvalds pprev = &dst->next; 11503d0f24a7SStephen Hemminger ++more; 11511da177e4SLinus Torvalds } 11521da177e4SLinus Torvalds } 11531da177e4SLinus Torvalds 11543b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 11555d0bbeebSThomas Graf 11563d0f24a7SStephen Hemminger return more; 11571da177e4SLinus Torvalds } 11581da177e4SLinus Torvalds 11591e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 11601e493d19SDavid S. Miller void *arg) 11611e493d19SDavid S. Miller { 11621e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 11631e493d19SDavid S. Miller 11641e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 11651e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 11661e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 11671e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 11681e493d19SDavid S. Miller if (func(rt, arg)) { 11691e493d19SDavid S. Miller *pprev = dst->next; 11701e493d19SDavid S. Miller dst_free(dst); 11711e493d19SDavid S. Miller } else { 11721e493d19SDavid S. Miller pprev = &dst->next; 11731e493d19SDavid S. Miller } 11741e493d19SDavid S. Miller } 11751e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 11761e493d19SDavid S. Miller } 11771e493d19SDavid S. Miller 1178569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 11791da177e4SLinus Torvalds { 11801da177e4SLinus Torvalds unsigned long now = jiffies; 118186393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 11827019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 11837019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 11847019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 11857019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 11867019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1187fc66f95cSEric Dumazet int entries; 11881da177e4SLinus Torvalds 1189fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 11907019b78eSDaniel Lezcano if (time_after(rt_last_gc + rt_min_interval, now) && 1191fc66f95cSEric Dumazet entries <= rt_max_size) 11921da177e4SLinus Torvalds goto out; 11931da177e4SLinus Torvalds 11946891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 11956891a346SBenjamin Thery fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net); 11966891a346SBenjamin Thery net->ipv6.ip6_rt_last_gc = now; 1197fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1198fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 11997019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 12001da177e4SLinus Torvalds out: 12017019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1202fc66f95cSEric Dumazet return entries > rt_max_size; 12031da177e4SLinus Torvalds } 12041da177e4SLinus Torvalds 12051da177e4SLinus Torvalds /* Clean host part of a prefix. Not necessary in radix tree, 12061da177e4SLinus Torvalds but results in cleaner routing tables. 12071da177e4SLinus Torvalds 12081da177e4SLinus Torvalds Remove it only when all the things will work! 12091da177e4SLinus Torvalds */ 12101da177e4SLinus Torvalds 12116b75d090SYOSHIFUJI Hideaki int ip6_dst_hoplimit(struct dst_entry *dst) 12121da177e4SLinus Torvalds { 12135170ae82SDavid S. Miller int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT); 1214a02e4b7dSDavid S. Miller if (hoplimit == 0) { 12156b75d090SYOSHIFUJI Hideaki struct net_device *dev = dst->dev; 1216c68f24ccSEric Dumazet struct inet6_dev *idev; 1217c68f24ccSEric Dumazet 1218c68f24ccSEric Dumazet rcu_read_lock(); 1219c68f24ccSEric Dumazet idev = __in6_dev_get(dev); 1220c68f24ccSEric Dumazet if (idev) 12211da177e4SLinus Torvalds hoplimit = idev->cnf.hop_limit; 1222c68f24ccSEric Dumazet else 122353b7997fSYOSHIFUJI Hideaki hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit; 1224c68f24ccSEric Dumazet rcu_read_unlock(); 12251da177e4SLinus Torvalds } 12261da177e4SLinus Torvalds return hoplimit; 12271da177e4SLinus Torvalds } 1228abbf46aeSDavid S. Miller EXPORT_SYMBOL(ip6_dst_hoplimit); 12291da177e4SLinus Torvalds 12301da177e4SLinus Torvalds /* 12311da177e4SLinus Torvalds * 12321da177e4SLinus Torvalds */ 12331da177e4SLinus Torvalds 123486872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg) 12351da177e4SLinus Torvalds { 12361da177e4SLinus Torvalds int err; 12375578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 12381da177e4SLinus Torvalds struct rt6_info *rt = NULL; 12391da177e4SLinus Torvalds struct net_device *dev = NULL; 12401da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1241c71099acSThomas Graf struct fib6_table *table; 12421da177e4SLinus Torvalds int addr_type; 12431da177e4SLinus Torvalds 124486872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 12451da177e4SLinus Torvalds return -EINVAL; 12461da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 124786872cb5SThomas Graf if (cfg->fc_src_len) 12481da177e4SLinus Torvalds return -EINVAL; 12491da177e4SLinus Torvalds #endif 125086872cb5SThomas Graf if (cfg->fc_ifindex) { 12511da177e4SLinus Torvalds err = -ENODEV; 12525578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 12531da177e4SLinus Torvalds if (!dev) 12541da177e4SLinus Torvalds goto out; 12551da177e4SLinus Torvalds idev = in6_dev_get(dev); 12561da177e4SLinus Torvalds if (!idev) 12571da177e4SLinus Torvalds goto out; 12581da177e4SLinus Torvalds } 12591da177e4SLinus Torvalds 126086872cb5SThomas Graf if (cfg->fc_metric == 0) 126186872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 12621da177e4SLinus Torvalds 1263c71099acSThomas Graf err = -ENOBUFS; 126438308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1265d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1266d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 126738308473SDavid S. Miller if (!table) { 1268d71314b4SMatti Vaittinen printk(KERN_WARNING "IPv6: NLM_F_CREATE should be specified when creating new route\n"); 1269d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1270d71314b4SMatti Vaittinen } 1271d71314b4SMatti Vaittinen } else { 1272d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1273d71314b4SMatti Vaittinen } 127438308473SDavid S. Miller 127538308473SDavid S. Miller if (!table) 1276c71099acSThomas Graf goto out; 1277c71099acSThomas Graf 1278957c665fSDavid S. Miller rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, NULL, DST_NOCOUNT); 12791da177e4SLinus Torvalds 128038308473SDavid S. Miller if (!rt) { 12811da177e4SLinus Torvalds err = -ENOMEM; 12821da177e4SLinus Torvalds goto out; 12831da177e4SLinus Torvalds } 12841da177e4SLinus Torvalds 1285d8d1f30bSChangli Gao rt->dst.obsolete = -1; 1286d1918542SDavid S. Miller rt->dst.expires = (cfg->fc_flags & RTF_EXPIRES) ? 12876f704992SYOSHIFUJI Hideaki jiffies + clock_t_to_jiffies(cfg->fc_expires) : 12886f704992SYOSHIFUJI Hideaki 0; 12891da177e4SLinus Torvalds 129086872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 129186872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 129286872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 129386872cb5SThomas Graf 129486872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 12951da177e4SLinus Torvalds 12961da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1297d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1298ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1299ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 13001da177e4SLinus Torvalds else 1301d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 13021da177e4SLinus Torvalds 1303d8d1f30bSChangli Gao rt->dst.output = ip6_output; 13041da177e4SLinus Torvalds 130586872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 130686872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 13071da177e4SLinus Torvalds if (rt->rt6i_dst.plen == 128) 130811d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 13091da177e4SLinus Torvalds 13108e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) { 13118e2ec639SYan, Zheng u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 13128e2ec639SYan, Zheng if (!metrics) { 13138e2ec639SYan, Zheng err = -ENOMEM; 13148e2ec639SYan, Zheng goto out; 13158e2ec639SYan, Zheng } 13168e2ec639SYan, Zheng dst_init_metrics(&rt->dst, metrics, 0); 13178e2ec639SYan, Zheng } 13181da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 131986872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 132086872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 13211da177e4SLinus Torvalds #endif 13221da177e4SLinus Torvalds 132386872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 13241da177e4SLinus Torvalds 13251da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 13261da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 13271da177e4SLinus Torvalds */ 132886872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 132938308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 133038308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 133138308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 13321da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 13335578689aSDaniel Lezcano if (dev != net->loopback_dev) { 13341da177e4SLinus Torvalds if (dev) { 13351da177e4SLinus Torvalds dev_put(dev); 13361da177e4SLinus Torvalds in6_dev_put(idev); 13371da177e4SLinus Torvalds } 13385578689aSDaniel Lezcano dev = net->loopback_dev; 13391da177e4SLinus Torvalds dev_hold(dev); 13401da177e4SLinus Torvalds idev = in6_dev_get(dev); 13411da177e4SLinus Torvalds if (!idev) { 13421da177e4SLinus Torvalds err = -ENODEV; 13431da177e4SLinus Torvalds goto out; 13441da177e4SLinus Torvalds } 13451da177e4SLinus Torvalds } 1346d8d1f30bSChangli Gao rt->dst.output = ip6_pkt_discard_out; 1347d8d1f30bSChangli Gao rt->dst.input = ip6_pkt_discard; 1348d8d1f30bSChangli Gao rt->dst.error = -ENETUNREACH; 13491da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 13501da177e4SLinus Torvalds goto install_route; 13511da177e4SLinus Torvalds } 13521da177e4SLinus Torvalds 135386872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1354b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 13551da177e4SLinus Torvalds int gwa_type; 13561da177e4SLinus Torvalds 135786872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 13584e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 13591da177e4SLinus Torvalds gwa_type = ipv6_addr_type(gw_addr); 13601da177e4SLinus Torvalds 13611da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 13621da177e4SLinus Torvalds struct rt6_info *grt; 13631da177e4SLinus Torvalds 13641da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 13651da177e4SLinus Torvalds addresses as nexthop address. 13661da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 13671da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 13681da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 13691da177e4SLinus Torvalds some exceptions. --ANK 13701da177e4SLinus Torvalds */ 13711da177e4SLinus Torvalds err = -EINVAL; 13721da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 13731da177e4SLinus Torvalds goto out; 13741da177e4SLinus Torvalds 13755578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 13761da177e4SLinus Torvalds 13771da177e4SLinus Torvalds err = -EHOSTUNREACH; 137838308473SDavid S. Miller if (!grt) 13791da177e4SLinus Torvalds goto out; 13801da177e4SLinus Torvalds if (dev) { 1381d1918542SDavid S. Miller if (dev != grt->dst.dev) { 1382d8d1f30bSChangli Gao dst_release(&grt->dst); 13831da177e4SLinus Torvalds goto out; 13841da177e4SLinus Torvalds } 13851da177e4SLinus Torvalds } else { 1386d1918542SDavid S. Miller dev = grt->dst.dev; 13871da177e4SLinus Torvalds idev = grt->rt6i_idev; 13881da177e4SLinus Torvalds dev_hold(dev); 13891da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 13901da177e4SLinus Torvalds } 13911da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 13921da177e4SLinus Torvalds err = 0; 1393d8d1f30bSChangli Gao dst_release(&grt->dst); 13941da177e4SLinus Torvalds 13951da177e4SLinus Torvalds if (err) 13961da177e4SLinus Torvalds goto out; 13971da177e4SLinus Torvalds } 13981da177e4SLinus Torvalds err = -EINVAL; 139938308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 14001da177e4SLinus Torvalds goto out; 14011da177e4SLinus Torvalds } 14021da177e4SLinus Torvalds 14031da177e4SLinus Torvalds err = -ENODEV; 140438308473SDavid S. Miller if (!dev) 14051da177e4SLinus Torvalds goto out; 14061da177e4SLinus Torvalds 1407c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1408c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1409c3968a85SDaniel Walter err = -EINVAL; 1410c3968a85SDaniel Walter goto out; 1411c3968a85SDaniel Walter } 14124e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1413c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1414c3968a85SDaniel Walter } else 1415c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1416c3968a85SDaniel Walter 141786872cb5SThomas Graf if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) { 14188ade06c6SDavid S. Miller err = rt6_bind_neighbour(rt, dev); 1419f83c7790SDavid S. Miller if (err) 14201da177e4SLinus Torvalds goto out; 14211da177e4SLinus Torvalds } 14221da177e4SLinus Torvalds 142386872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 14241da177e4SLinus Torvalds 14251da177e4SLinus Torvalds install_route: 142686872cb5SThomas Graf if (cfg->fc_mx) { 142786872cb5SThomas Graf struct nlattr *nla; 142886872cb5SThomas Graf int remaining; 14291da177e4SLinus Torvalds 143086872cb5SThomas Graf nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 14318f4c1f9bSThomas Graf int type = nla_type(nla); 143286872cb5SThomas Graf 143386872cb5SThomas Graf if (type) { 143486872cb5SThomas Graf if (type > RTAX_MAX) { 14351da177e4SLinus Torvalds err = -EINVAL; 14361da177e4SLinus Torvalds goto out; 14371da177e4SLinus Torvalds } 143886872cb5SThomas Graf 1439defb3519SDavid S. Miller dst_metric_set(&rt->dst, type, nla_get_u32(nla)); 14401da177e4SLinus Torvalds } 14411da177e4SLinus Torvalds } 14421da177e4SLinus Torvalds } 14431da177e4SLinus Torvalds 1444d8d1f30bSChangli Gao rt->dst.dev = dev; 14451da177e4SLinus Torvalds rt->rt6i_idev = idev; 1446c71099acSThomas Graf rt->rt6i_table = table; 144763152fc0SDaniel Lezcano 1448c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 144963152fc0SDaniel Lezcano 145086872cb5SThomas Graf return __ip6_ins_rt(rt, &cfg->fc_nlinfo); 14511da177e4SLinus Torvalds 14521da177e4SLinus Torvalds out: 14531da177e4SLinus Torvalds if (dev) 14541da177e4SLinus Torvalds dev_put(dev); 14551da177e4SLinus Torvalds if (idev) 14561da177e4SLinus Torvalds in6_dev_put(idev); 14571da177e4SLinus Torvalds if (rt) 1458d8d1f30bSChangli Gao dst_free(&rt->dst); 14591da177e4SLinus Torvalds return err; 14601da177e4SLinus Torvalds } 14611da177e4SLinus Torvalds 146286872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 14631da177e4SLinus Torvalds { 14641da177e4SLinus Torvalds int err; 1465c71099acSThomas Graf struct fib6_table *table; 1466d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 14671da177e4SLinus Torvalds 14688ed67789SDaniel Lezcano if (rt == net->ipv6.ip6_null_entry) 14696c813a72SPatrick McHardy return -ENOENT; 14706c813a72SPatrick McHardy 1471c71099acSThomas Graf table = rt->rt6i_table; 1472c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 14731da177e4SLinus Torvalds 147486872cb5SThomas Graf err = fib6_del(rt, info); 1475d8d1f30bSChangli Gao dst_release(&rt->dst); 14761da177e4SLinus Torvalds 1477c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 14781da177e4SLinus Torvalds 14791da177e4SLinus Torvalds return err; 14801da177e4SLinus Torvalds } 14811da177e4SLinus Torvalds 1482e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 1483e0a1ad73SThomas Graf { 14844d1169c1SDenis V. Lunev struct nl_info info = { 1485d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 14864d1169c1SDenis V. Lunev }; 1487528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 1488e0a1ad73SThomas Graf } 1489e0a1ad73SThomas Graf 149086872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 14911da177e4SLinus Torvalds { 1492c71099acSThomas Graf struct fib6_table *table; 14931da177e4SLinus Torvalds struct fib6_node *fn; 14941da177e4SLinus Torvalds struct rt6_info *rt; 14951da177e4SLinus Torvalds int err = -ESRCH; 14961da177e4SLinus Torvalds 14975578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 149838308473SDavid S. Miller if (!table) 1499c71099acSThomas Graf return err; 15001da177e4SLinus Torvalds 1501c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1502c71099acSThomas Graf 1503c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 150486872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 150586872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 15061da177e4SLinus Torvalds 15071da177e4SLinus Torvalds if (fn) { 1508d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 150986872cb5SThomas Graf if (cfg->fc_ifindex && 1510d1918542SDavid S. Miller (!rt->dst.dev || 1511d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 15121da177e4SLinus Torvalds continue; 151386872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 151486872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 15151da177e4SLinus Torvalds continue; 151686872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 15171da177e4SLinus Torvalds continue; 1518d8d1f30bSChangli Gao dst_hold(&rt->dst); 1519c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 15201da177e4SLinus Torvalds 152186872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 15221da177e4SLinus Torvalds } 15231da177e4SLinus Torvalds } 1524c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 15251da177e4SLinus Torvalds 15261da177e4SLinus Torvalds return err; 15271da177e4SLinus Torvalds } 15281da177e4SLinus Torvalds 15291da177e4SLinus Torvalds /* 15301da177e4SLinus Torvalds * Handle redirects 15311da177e4SLinus Torvalds */ 1532a6279458SYOSHIFUJI Hideaki struct ip6rd_flowi { 15334c9483b2SDavid S. Miller struct flowi6 fl6; 1534a6279458SYOSHIFUJI Hideaki struct in6_addr gateway; 1535a6279458SYOSHIFUJI Hideaki }; 15361da177e4SLinus Torvalds 15378ed67789SDaniel Lezcano static struct rt6_info *__ip6_route_redirect(struct net *net, 15388ed67789SDaniel Lezcano struct fib6_table *table, 15394c9483b2SDavid S. Miller struct flowi6 *fl6, 1540a6279458SYOSHIFUJI Hideaki int flags) 1541a6279458SYOSHIFUJI Hideaki { 15424c9483b2SDavid S. Miller struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 1543a6279458SYOSHIFUJI Hideaki struct rt6_info *rt; 1544a6279458SYOSHIFUJI Hideaki struct fib6_node *fn; 1545c71099acSThomas Graf 1546e843b9e1SYOSHIFUJI Hideaki /* 1547e843b9e1SYOSHIFUJI Hideaki * Get the "current" route for this destination and 1548e843b9e1SYOSHIFUJI Hideaki * check if the redirect has come from approriate router. 1549e843b9e1SYOSHIFUJI Hideaki * 1550e843b9e1SYOSHIFUJI Hideaki * RFC 2461 specifies that redirects should only be 1551e843b9e1SYOSHIFUJI Hideaki * accepted if they come from the nexthop to the target. 1552e843b9e1SYOSHIFUJI Hideaki * Due to the way the routes are chosen, this notion 1553e843b9e1SYOSHIFUJI Hideaki * is a bit fuzzy and one might need to check all possible 1554e843b9e1SYOSHIFUJI Hideaki * routes. 1555e843b9e1SYOSHIFUJI Hideaki */ 15561da177e4SLinus Torvalds 1557c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 15584c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1559e843b9e1SYOSHIFUJI Hideaki restart: 1560d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 15611da177e4SLinus Torvalds /* 15621da177e4SLinus Torvalds * Current route is on-link; redirect is always invalid. 15631da177e4SLinus Torvalds * 15641da177e4SLinus Torvalds * Seems, previous statement is not true. It could 15651da177e4SLinus Torvalds * be node, which looks for us as on-link (f.e. proxy ndisc) 15661da177e4SLinus Torvalds * But then router serving it might decide, that we should 15671da177e4SLinus Torvalds * know truth 8)8) --ANK (980726). 15681da177e4SLinus Torvalds */ 1569e843b9e1SYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 1570e843b9e1SYOSHIFUJI Hideaki continue; 15711da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_GATEWAY)) 1572e843b9e1SYOSHIFUJI Hideaki continue; 1573d1918542SDavid S. Miller if (fl6->flowi6_oif != rt->dst.dev->ifindex) 1574e843b9e1SYOSHIFUJI Hideaki continue; 1575a6279458SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) 1576e843b9e1SYOSHIFUJI Hideaki continue; 1577e843b9e1SYOSHIFUJI Hideaki break; 1578e843b9e1SYOSHIFUJI Hideaki } 1579a6279458SYOSHIFUJI Hideaki 1580cb15d9c2SYOSHIFUJI Hideaki if (!rt) 15818ed67789SDaniel Lezcano rt = net->ipv6.ip6_null_entry; 15824c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 1583cb15d9c2SYOSHIFUJI Hideaki out: 1584d8d1f30bSChangli Gao dst_hold(&rt->dst); 1585a6279458SYOSHIFUJI Hideaki 1586c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 15871da177e4SLinus Torvalds 1588a6279458SYOSHIFUJI Hideaki return rt; 1589a6279458SYOSHIFUJI Hideaki }; 1590a6279458SYOSHIFUJI Hideaki 1591b71d1d42SEric Dumazet static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest, 1592b71d1d42SEric Dumazet const struct in6_addr *src, 1593b71d1d42SEric Dumazet const struct in6_addr *gateway, 1594a6279458SYOSHIFUJI Hideaki struct net_device *dev) 1595a6279458SYOSHIFUJI Hideaki { 1596adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 1597c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 1598a6279458SYOSHIFUJI Hideaki struct ip6rd_flowi rdfl = { 15994c9483b2SDavid S. Miller .fl6 = { 16004c9483b2SDavid S. Miller .flowi6_oif = dev->ifindex, 16014c9483b2SDavid S. Miller .daddr = *dest, 16024c9483b2SDavid S. Miller .saddr = *src, 1603a6279458SYOSHIFUJI Hideaki }, 1604a6279458SYOSHIFUJI Hideaki }; 1605adaa70bbSThomas Graf 16064e3fd7a0SAlexey Dobriyan rdfl.gateway = *gateway; 160786c36ce4SBrian Haley 1608adaa70bbSThomas Graf if (rt6_need_strict(dest)) 1609adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_IFACE; 1610a6279458SYOSHIFUJI Hideaki 16114c9483b2SDavid S. Miller return (struct rt6_info *)fib6_rule_lookup(net, &rdfl.fl6, 161258f09b78SDaniel Lezcano flags, __ip6_route_redirect); 1613a6279458SYOSHIFUJI Hideaki } 1614a6279458SYOSHIFUJI Hideaki 1615b71d1d42SEric Dumazet void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, 1616b71d1d42SEric Dumazet const struct in6_addr *saddr, 1617a6279458SYOSHIFUJI Hideaki struct neighbour *neigh, u8 *lladdr, int on_link) 1618a6279458SYOSHIFUJI Hideaki { 1619a6279458SYOSHIFUJI Hideaki struct rt6_info *rt, *nrt = NULL; 1620a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 1621c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(neigh->dev); 1622a6279458SYOSHIFUJI Hideaki 1623a6279458SYOSHIFUJI Hideaki rt = ip6_route_redirect(dest, src, saddr, neigh->dev); 1624a6279458SYOSHIFUJI Hideaki 16258ed67789SDaniel Lezcano if (rt == net->ipv6.ip6_null_entry) { 16261da177e4SLinus Torvalds if (net_ratelimit()) 16271da177e4SLinus Torvalds printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop " 16281da177e4SLinus Torvalds "for redirect target\n"); 1629a6279458SYOSHIFUJI Hideaki goto out; 16301da177e4SLinus Torvalds } 16311da177e4SLinus Torvalds 16321da177e4SLinus Torvalds /* 16331da177e4SLinus Torvalds * We have finally decided to accept it. 16341da177e4SLinus Torvalds */ 16351da177e4SLinus Torvalds 16361da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 16371da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 16381da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 16391da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 16401da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 16411da177e4SLinus Torvalds ); 16421da177e4SLinus Torvalds 16431da177e4SLinus Torvalds /* 16441da177e4SLinus Torvalds * Redirect received -> path was valid. 16451da177e4SLinus Torvalds * Look, redirects are sent only in response to data packets, 16461da177e4SLinus Torvalds * so that this nexthop apparently is reachable. --ANK 16471da177e4SLinus Torvalds */ 1648d8d1f30bSChangli Gao dst_confirm(&rt->dst); 16491da177e4SLinus Torvalds 16501da177e4SLinus Torvalds /* Duplicate redirect: silently ignore. */ 165127217455SDavid Miller if (neigh == dst_get_neighbour_noref_raw(&rt->dst)) 16521da177e4SLinus Torvalds goto out; 16531da177e4SLinus Torvalds 165421efcfa0SEric Dumazet nrt = ip6_rt_copy(rt, dest); 165538308473SDavid S. Miller if (!nrt) 16561da177e4SLinus Torvalds goto out; 16571da177e4SLinus Torvalds 16581da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 16591da177e4SLinus Torvalds if (on_link) 16601da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 16611da177e4SLinus Torvalds 16624e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 166369cce1d1SDavid S. Miller dst_set_neighbour(&nrt->dst, neigh_clone(neigh)); 16641da177e4SLinus Torvalds 166540e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 16661da177e4SLinus Torvalds goto out; 16671da177e4SLinus Torvalds 1668d8d1f30bSChangli Gao netevent.old = &rt->dst; 1669d8d1f30bSChangli Gao netevent.new = &nrt->dst; 16708d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 16718d71740cSTom Tucker 16721da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 1673e0a1ad73SThomas Graf ip6_del_rt(rt); 16741da177e4SLinus Torvalds return; 16751da177e4SLinus Torvalds } 16761da177e4SLinus Torvalds 16771da177e4SLinus Torvalds out: 1678d8d1f30bSChangli Gao dst_release(&rt->dst); 16791da177e4SLinus Torvalds } 16801da177e4SLinus Torvalds 16811da177e4SLinus Torvalds /* 16821da177e4SLinus Torvalds * Handle ICMP "packet too big" messages 16831da177e4SLinus Torvalds * i.e. Path MTU discovery 16841da177e4SLinus Torvalds */ 16851da177e4SLinus Torvalds 1686b71d1d42SEric Dumazet static void rt6_do_pmtu_disc(const struct in6_addr *daddr, const struct in6_addr *saddr, 1687ae878ae2SMaciej Żenczykowski struct net *net, u32 pmtu, int ifindex) 16881da177e4SLinus Torvalds { 16891da177e4SLinus Torvalds struct rt6_info *rt, *nrt; 16901da177e4SLinus Torvalds int allfrag = 0; 1691d3052b55SAndrey Vagin again: 1692ae878ae2SMaciej Żenczykowski rt = rt6_lookup(net, daddr, saddr, ifindex, 0); 169338308473SDavid S. Miller if (!rt) 16941da177e4SLinus Torvalds return; 16951da177e4SLinus Torvalds 1696d3052b55SAndrey Vagin if (rt6_check_expired(rt)) { 1697d3052b55SAndrey Vagin ip6_del_rt(rt); 1698d3052b55SAndrey Vagin goto again; 1699d3052b55SAndrey Vagin } 1700d3052b55SAndrey Vagin 1701d8d1f30bSChangli Gao if (pmtu >= dst_mtu(&rt->dst)) 17021da177e4SLinus Torvalds goto out; 17031da177e4SLinus Torvalds 17041da177e4SLinus Torvalds if (pmtu < IPV6_MIN_MTU) { 17051da177e4SLinus Torvalds /* 17061da177e4SLinus Torvalds * According to RFC2460, PMTU is set to the IPv6 Minimum Link 17071da177e4SLinus Torvalds * MTU (1280) and a fragment header should always be included 17081da177e4SLinus Torvalds * after a node receiving Too Big message reporting PMTU is 17091da177e4SLinus Torvalds * less than the IPv6 Minimum Link MTU. 17101da177e4SLinus Torvalds */ 17111da177e4SLinus Torvalds pmtu = IPV6_MIN_MTU; 17121da177e4SLinus Torvalds allfrag = 1; 17131da177e4SLinus Torvalds } 17141da177e4SLinus Torvalds 17151da177e4SLinus Torvalds /* New mtu received -> path was valid. 17161da177e4SLinus Torvalds They are sent only in response to data packets, 17171da177e4SLinus Torvalds so that this nexthop apparently is reachable. --ANK 17181da177e4SLinus Torvalds */ 1719d8d1f30bSChangli Gao dst_confirm(&rt->dst); 17201da177e4SLinus Torvalds 17211da177e4SLinus Torvalds /* Host route. If it is static, it would be better 17221da177e4SLinus Torvalds not to override it, but add new one, so that 17231da177e4SLinus Torvalds when cache entry will expire old pmtu 17241da177e4SLinus Torvalds would return automatically. 17251da177e4SLinus Torvalds */ 17261da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 1727defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, pmtu); 1728defb3519SDavid S. Miller if (allfrag) { 1729defb3519SDavid S. Miller u32 features = dst_metric(&rt->dst, RTAX_FEATURES); 1730defb3519SDavid S. Miller features |= RTAX_FEATURE_ALLFRAG; 1731defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_FEATURES, features); 1732defb3519SDavid S. Miller } 1733d8d1f30bSChangli Gao dst_set_expires(&rt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires); 17341da177e4SLinus Torvalds rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES; 17351da177e4SLinus Torvalds goto out; 17361da177e4SLinus Torvalds } 17371da177e4SLinus Torvalds 17381da177e4SLinus Torvalds /* Network route. 17391da177e4SLinus Torvalds Two cases are possible: 17401da177e4SLinus Torvalds 1. It is connected route. Action: COW 17411da177e4SLinus Torvalds 2. It is gatewayed route or NONEXTHOP route. Action: clone it. 17421da177e4SLinus Torvalds */ 174327217455SDavid Miller if (!dst_get_neighbour_noref_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP)) 1744a1e78363SYOSHIFUJI Hideaki nrt = rt6_alloc_cow(rt, daddr, saddr); 1745d5315b50SYOSHIFUJI Hideaki else 1746d5315b50SYOSHIFUJI Hideaki nrt = rt6_alloc_clone(rt, daddr); 1747a1e78363SYOSHIFUJI Hideaki 1748d5315b50SYOSHIFUJI Hideaki if (nrt) { 1749defb3519SDavid S. Miller dst_metric_set(&nrt->dst, RTAX_MTU, pmtu); 1750defb3519SDavid S. Miller if (allfrag) { 1751defb3519SDavid S. Miller u32 features = dst_metric(&nrt->dst, RTAX_FEATURES); 1752defb3519SDavid S. Miller features |= RTAX_FEATURE_ALLFRAG; 1753defb3519SDavid S. Miller dst_metric_set(&nrt->dst, RTAX_FEATURES, features); 1754defb3519SDavid S. Miller } 1755a1e78363SYOSHIFUJI Hideaki 17561da177e4SLinus Torvalds /* According to RFC 1981, detecting PMTU increase shouldn't be 1757a1e78363SYOSHIFUJI Hideaki * happened within 5 mins, the recommended timer is 10 mins. 1758a1e78363SYOSHIFUJI Hideaki * Here this route expiration time is set to ip6_rt_mtu_expires 1759a1e78363SYOSHIFUJI Hideaki * which is 10 mins. After 10 mins the decreased pmtu is expired 1760a1e78363SYOSHIFUJI Hideaki * and detecting PMTU increase will be automatically happened. 17611da177e4SLinus Torvalds */ 1762d8d1f30bSChangli Gao dst_set_expires(&nrt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires); 17631da177e4SLinus Torvalds nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES; 1764a1e78363SYOSHIFUJI Hideaki 176540e22e8fSThomas Graf ip6_ins_rt(nrt); 17661da177e4SLinus Torvalds } 17671da177e4SLinus Torvalds out: 1768d8d1f30bSChangli Gao dst_release(&rt->dst); 17691da177e4SLinus Torvalds } 17701da177e4SLinus Torvalds 1771b71d1d42SEric Dumazet void rt6_pmtu_discovery(const struct in6_addr *daddr, const struct in6_addr *saddr, 1772ae878ae2SMaciej Żenczykowski struct net_device *dev, u32 pmtu) 1773ae878ae2SMaciej Żenczykowski { 1774ae878ae2SMaciej Żenczykowski struct net *net = dev_net(dev); 1775ae878ae2SMaciej Żenczykowski 1776ae878ae2SMaciej Żenczykowski /* 1777ae878ae2SMaciej Żenczykowski * RFC 1981 states that a node "MUST reduce the size of the packets it 1778ae878ae2SMaciej Żenczykowski * is sending along the path" that caused the Packet Too Big message. 1779ae878ae2SMaciej Żenczykowski * Since it's not possible in the general case to determine which 1780ae878ae2SMaciej Żenczykowski * interface was used to send the original packet, we update the MTU 1781ae878ae2SMaciej Żenczykowski * on the interface that will be used to send future packets. We also 1782ae878ae2SMaciej Żenczykowski * update the MTU on the interface that received the Packet Too Big in 1783ae878ae2SMaciej Żenczykowski * case the original packet was forced out that interface with 1784ae878ae2SMaciej Żenczykowski * SO_BINDTODEVICE or similar. This is the next best thing to the 1785ae878ae2SMaciej Żenczykowski * correct behaviour, which would be to update the MTU on all 1786ae878ae2SMaciej Żenczykowski * interfaces. 1787ae878ae2SMaciej Żenczykowski */ 1788ae878ae2SMaciej Żenczykowski rt6_do_pmtu_disc(daddr, saddr, net, pmtu, 0); 1789ae878ae2SMaciej Żenczykowski rt6_do_pmtu_disc(daddr, saddr, net, pmtu, dev->ifindex); 1790ae878ae2SMaciej Żenczykowski } 1791ae878ae2SMaciej Żenczykowski 17921da177e4SLinus Torvalds /* 17931da177e4SLinus Torvalds * Misc support functions 17941da177e4SLinus Torvalds */ 17951da177e4SLinus Torvalds 179621efcfa0SEric Dumazet static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort, 179721efcfa0SEric Dumazet const struct in6_addr *dest) 17981da177e4SLinus Torvalds { 1799d1918542SDavid S. Miller struct net *net = dev_net(ort->dst.dev); 18005c1e6aa3SDavid S. Miller struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, 1801957c665fSDavid S. Miller ort->dst.dev, 0); 18021da177e4SLinus Torvalds 18031da177e4SLinus Torvalds if (rt) { 1804d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 1805d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 18068e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 18071da177e4SLinus Torvalds 18084e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *dest; 18098e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 1810defb3519SDavid S. Miller dst_copy_metrics(&rt->dst, &ort->dst); 1811d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 18121da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 18131da177e4SLinus Torvalds if (rt->rt6i_idev) 18141da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 1815d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 1816d1918542SDavid S. Miller rt->dst.expires = 0; 18171da177e4SLinus Torvalds 18184e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 18191da177e4SLinus Torvalds rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES; 18201da177e4SLinus Torvalds rt->rt6i_metric = 0; 18211da177e4SLinus Torvalds 18221da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 18231da177e4SLinus Torvalds memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 18241da177e4SLinus Torvalds #endif 18250f6c6392SFlorian Westphal memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key)); 1826c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 18271da177e4SLinus Torvalds } 18281da177e4SLinus Torvalds return rt; 18291da177e4SLinus Torvalds } 18301da177e4SLinus Torvalds 183170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 1832efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 1833b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1834b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 183570ceb4f5SYOSHIFUJI Hideaki { 183670ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 183770ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 1838c71099acSThomas Graf struct fib6_table *table; 183970ceb4f5SYOSHIFUJI Hideaki 1840efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 184138308473SDavid S. Miller if (!table) 1842c71099acSThomas Graf return NULL; 1843c71099acSThomas Graf 1844c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 1845c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0); 184670ceb4f5SYOSHIFUJI Hideaki if (!fn) 184770ceb4f5SYOSHIFUJI Hideaki goto out; 184870ceb4f5SYOSHIFUJI Hideaki 1849d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1850d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 185170ceb4f5SYOSHIFUJI Hideaki continue; 185270ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 185370ceb4f5SYOSHIFUJI Hideaki continue; 185470ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 185570ceb4f5SYOSHIFUJI Hideaki continue; 1856d8d1f30bSChangli Gao dst_hold(&rt->dst); 185770ceb4f5SYOSHIFUJI Hideaki break; 185870ceb4f5SYOSHIFUJI Hideaki } 185970ceb4f5SYOSHIFUJI Hideaki out: 1860c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 186170ceb4f5SYOSHIFUJI Hideaki return rt; 186270ceb4f5SYOSHIFUJI Hideaki } 186370ceb4f5SYOSHIFUJI Hideaki 1864efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 1865b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1866b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 186770ceb4f5SYOSHIFUJI Hideaki unsigned pref) 186870ceb4f5SYOSHIFUJI Hideaki { 186986872cb5SThomas Graf struct fib6_config cfg = { 187086872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 1871238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 187286872cb5SThomas Graf .fc_ifindex = ifindex, 187386872cb5SThomas Graf .fc_dst_len = prefixlen, 187486872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 187586872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 1876efa2cea0SDaniel Lezcano .fc_nlinfo.pid = 0, 1877efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 1878efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 187986872cb5SThomas Graf }; 188070ceb4f5SYOSHIFUJI Hideaki 18814e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 18824e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 188386872cb5SThomas Graf 1884e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 1885e317da96SYOSHIFUJI Hideaki if (!prefixlen) 188686872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 188770ceb4f5SYOSHIFUJI Hideaki 188886872cb5SThomas Graf ip6_route_add(&cfg); 188970ceb4f5SYOSHIFUJI Hideaki 1890efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 189170ceb4f5SYOSHIFUJI Hideaki } 189270ceb4f5SYOSHIFUJI Hideaki #endif 189370ceb4f5SYOSHIFUJI Hideaki 1894b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 18951da177e4SLinus Torvalds { 18961da177e4SLinus Torvalds struct rt6_info *rt; 1897c71099acSThomas Graf struct fib6_table *table; 18981da177e4SLinus Torvalds 1899c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 190038308473SDavid S. Miller if (!table) 1901c71099acSThomas Graf return NULL; 19021da177e4SLinus Torvalds 1903c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 1904d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) { 1905d1918542SDavid S. Miller if (dev == rt->dst.dev && 1906045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 19071da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 19081da177e4SLinus Torvalds break; 19091da177e4SLinus Torvalds } 19101da177e4SLinus Torvalds if (rt) 1911d8d1f30bSChangli Gao dst_hold(&rt->dst); 1912c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 19131da177e4SLinus Torvalds return rt; 19141da177e4SLinus Torvalds } 19151da177e4SLinus Torvalds 1916b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 1917ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 1918ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 19191da177e4SLinus Torvalds { 192086872cb5SThomas Graf struct fib6_config cfg = { 192186872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 1922238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 192386872cb5SThomas Graf .fc_ifindex = dev->ifindex, 192486872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 192586872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 19265578689aSDaniel Lezcano .fc_nlinfo.pid = 0, 19275578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 1928c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 192986872cb5SThomas Graf }; 19301da177e4SLinus Torvalds 19314e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 19321da177e4SLinus Torvalds 193386872cb5SThomas Graf ip6_route_add(&cfg); 19341da177e4SLinus Torvalds 19351da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 19361da177e4SLinus Torvalds } 19371da177e4SLinus Torvalds 19387b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 19391da177e4SLinus Torvalds { 19401da177e4SLinus Torvalds struct rt6_info *rt; 1941c71099acSThomas Graf struct fib6_table *table; 1942c71099acSThomas Graf 1943c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 19447b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 194538308473SDavid S. Miller if (!table) 1946c71099acSThomas Graf return; 19471da177e4SLinus Torvalds 19481da177e4SLinus Torvalds restart: 1949c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1950d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 19511da177e4SLinus Torvalds if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { 1952d8d1f30bSChangli Gao dst_hold(&rt->dst); 1953c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 1954e0a1ad73SThomas Graf ip6_del_rt(rt); 19551da177e4SLinus Torvalds goto restart; 19561da177e4SLinus Torvalds } 19571da177e4SLinus Torvalds } 1958c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 19591da177e4SLinus Torvalds } 19601da177e4SLinus Torvalds 19615578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 19625578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 196386872cb5SThomas Graf struct fib6_config *cfg) 196486872cb5SThomas Graf { 196586872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 196686872cb5SThomas Graf 196786872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 196886872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 196986872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 197086872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 197186872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 197286872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 197386872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 197486872cb5SThomas Graf 19755578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 1976f1243c2dSBenjamin Thery 19774e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 19784e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 19794e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 198086872cb5SThomas Graf } 198186872cb5SThomas Graf 19825578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 19831da177e4SLinus Torvalds { 198486872cb5SThomas Graf struct fib6_config cfg; 19851da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 19861da177e4SLinus Torvalds int err; 19871da177e4SLinus Torvalds 19881da177e4SLinus Torvalds switch(cmd) { 19891da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 19901da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 19911da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 19921da177e4SLinus Torvalds return -EPERM; 19931da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 19941da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 19951da177e4SLinus Torvalds if (err) 19961da177e4SLinus Torvalds return -EFAULT; 19971da177e4SLinus Torvalds 19985578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 199986872cb5SThomas Graf 20001da177e4SLinus Torvalds rtnl_lock(); 20011da177e4SLinus Torvalds switch (cmd) { 20021da177e4SLinus Torvalds case SIOCADDRT: 200386872cb5SThomas Graf err = ip6_route_add(&cfg); 20041da177e4SLinus Torvalds break; 20051da177e4SLinus Torvalds case SIOCDELRT: 200686872cb5SThomas Graf err = ip6_route_del(&cfg); 20071da177e4SLinus Torvalds break; 20081da177e4SLinus Torvalds default: 20091da177e4SLinus Torvalds err = -EINVAL; 20101da177e4SLinus Torvalds } 20111da177e4SLinus Torvalds rtnl_unlock(); 20121da177e4SLinus Torvalds 20131da177e4SLinus Torvalds return err; 20143ff50b79SStephen Hemminger } 20151da177e4SLinus Torvalds 20161da177e4SLinus Torvalds return -EINVAL; 20171da177e4SLinus Torvalds } 20181da177e4SLinus Torvalds 20191da177e4SLinus Torvalds /* 20201da177e4SLinus Torvalds * Drop the packet on the floor 20211da177e4SLinus Torvalds */ 20221da177e4SLinus Torvalds 2023d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 20241da177e4SLinus Torvalds { 2025612f09e8SYOSHIFUJI Hideaki int type; 2026adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2027612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2028612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 20290660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 203045bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 20313bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 20323bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2033612f09e8SYOSHIFUJI Hideaki break; 2034612f09e8SYOSHIFUJI Hideaki } 2035612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2036612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 20373bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 20383bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2039612f09e8SYOSHIFUJI Hideaki break; 2040612f09e8SYOSHIFUJI Hideaki } 20413ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 20421da177e4SLinus Torvalds kfree_skb(skb); 20431da177e4SLinus Torvalds return 0; 20441da177e4SLinus Torvalds } 20451da177e4SLinus Torvalds 20469ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 20479ce8ade0SThomas Graf { 2048612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 20499ce8ade0SThomas Graf } 20509ce8ade0SThomas Graf 205120380731SArnaldo Carvalho de Melo static int ip6_pkt_discard_out(struct sk_buff *skb) 20521da177e4SLinus Torvalds { 2053adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2054612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 20551da177e4SLinus Torvalds } 20561da177e4SLinus Torvalds 20576723ab54SDavid S. Miller #ifdef CONFIG_IPV6_MULTIPLE_TABLES 20586723ab54SDavid S. Miller 20599ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 20609ce8ade0SThomas Graf { 2061612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 20629ce8ade0SThomas Graf } 20639ce8ade0SThomas Graf 20649ce8ade0SThomas Graf static int ip6_pkt_prohibit_out(struct sk_buff *skb) 20659ce8ade0SThomas Graf { 2066adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2067612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 20689ce8ade0SThomas Graf } 20699ce8ade0SThomas Graf 20706723ab54SDavid S. Miller #endif 20716723ab54SDavid S. Miller 20721da177e4SLinus Torvalds /* 20731da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 20741da177e4SLinus Torvalds */ 20751da177e4SLinus Torvalds 20761da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 20771da177e4SLinus Torvalds const struct in6_addr *addr, 20788f031519SDavid S. Miller bool anycast) 20791da177e4SLinus Torvalds { 2080c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 20815c1e6aa3SDavid S. Miller struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, 2082957c665fSDavid S. Miller net->loopback_dev, 0); 2083f83c7790SDavid S. Miller int err; 20841da177e4SLinus Torvalds 208538308473SDavid S. Miller if (!rt) { 208640385653SBen Greear if (net_ratelimit()) 208740385653SBen Greear pr_warning("IPv6: Maximum number of routes reached," 208840385653SBen Greear " consider increasing route/max_size.\n"); 20891da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 209040385653SBen Greear } 20911da177e4SLinus Torvalds 20921da177e4SLinus Torvalds in6_dev_hold(idev); 20931da177e4SLinus Torvalds 209411d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2095d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2096d8d1f30bSChangli Gao rt->dst.output = ip6_output; 20971da177e4SLinus Torvalds rt->rt6i_idev = idev; 2098d8d1f30bSChangli Gao rt->dst.obsolete = -1; 20991da177e4SLinus Torvalds 21001da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 210158c4fb86SYOSHIFUJI Hideaki if (anycast) 210258c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 210358c4fb86SYOSHIFUJI Hideaki else 21041da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 21058ade06c6SDavid S. Miller err = rt6_bind_neighbour(rt, rt->dst.dev); 2106f83c7790SDavid S. Miller if (err) { 2107d8d1f30bSChangli Gao dst_free(&rt->dst); 2108f83c7790SDavid S. Miller return ERR_PTR(err); 21091da177e4SLinus Torvalds } 21101da177e4SLinus Torvalds 21114e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 21121da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 21135578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 21141da177e4SLinus Torvalds 2115d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 21161da177e4SLinus Torvalds 21171da177e4SLinus Torvalds return rt; 21181da177e4SLinus Torvalds } 21191da177e4SLinus Torvalds 2120c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2121c3968a85SDaniel Walter struct rt6_info *rt, 2122b71d1d42SEric Dumazet const struct in6_addr *daddr, 2123c3968a85SDaniel Walter unsigned int prefs, 2124c3968a85SDaniel Walter struct in6_addr *saddr) 2125c3968a85SDaniel Walter { 2126c3968a85SDaniel Walter struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt); 2127c3968a85SDaniel Walter int err = 0; 2128c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) 21294e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2130c3968a85SDaniel Walter else 2131c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2132c3968a85SDaniel Walter daddr, prefs, saddr); 2133c3968a85SDaniel Walter return err; 2134c3968a85SDaniel Walter } 2135c3968a85SDaniel Walter 2136c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2137c3968a85SDaniel Walter struct arg_dev_net_ip { 2138c3968a85SDaniel Walter struct net_device *dev; 2139c3968a85SDaniel Walter struct net *net; 2140c3968a85SDaniel Walter struct in6_addr *addr; 2141c3968a85SDaniel Walter }; 2142c3968a85SDaniel Walter 2143c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2144c3968a85SDaniel Walter { 2145c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2146c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2147c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2148c3968a85SDaniel Walter 2149d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2150c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2151c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2152c3968a85SDaniel Walter /* remove prefsrc entry */ 2153c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2154c3968a85SDaniel Walter } 2155c3968a85SDaniel Walter return 0; 2156c3968a85SDaniel Walter } 2157c3968a85SDaniel Walter 2158c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2159c3968a85SDaniel Walter { 2160c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2161c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2162c3968a85SDaniel Walter .dev = ifp->idev->dev, 2163c3968a85SDaniel Walter .net = net, 2164c3968a85SDaniel Walter .addr = &ifp->addr, 2165c3968a85SDaniel Walter }; 2166c3968a85SDaniel Walter fib6_clean_all(net, fib6_remove_prefsrc, 0, &adni); 2167c3968a85SDaniel Walter } 2168c3968a85SDaniel Walter 21698ed67789SDaniel Lezcano struct arg_dev_net { 21708ed67789SDaniel Lezcano struct net_device *dev; 21718ed67789SDaniel Lezcano struct net *net; 21728ed67789SDaniel Lezcano }; 21738ed67789SDaniel Lezcano 21741da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 21751da177e4SLinus Torvalds { 2176bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2177bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 21788ed67789SDaniel Lezcano 2179d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2180c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 21811da177e4SLinus Torvalds return -1; 2182c159d30cSDavid S. Miller 21831da177e4SLinus Torvalds return 0; 21841da177e4SLinus Torvalds } 21851da177e4SLinus Torvalds 2186f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 21871da177e4SLinus Torvalds { 21888ed67789SDaniel Lezcano struct arg_dev_net adn = { 21898ed67789SDaniel Lezcano .dev = dev, 21908ed67789SDaniel Lezcano .net = net, 21918ed67789SDaniel Lezcano }; 21928ed67789SDaniel Lezcano 21938ed67789SDaniel Lezcano fib6_clean_all(net, fib6_ifdown, 0, &adn); 21941e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 21951da177e4SLinus Torvalds } 21961da177e4SLinus Torvalds 21971da177e4SLinus Torvalds struct rt6_mtu_change_arg 21981da177e4SLinus Torvalds { 21991da177e4SLinus Torvalds struct net_device *dev; 22001da177e4SLinus Torvalds unsigned mtu; 22011da177e4SLinus Torvalds }; 22021da177e4SLinus Torvalds 22031da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 22041da177e4SLinus Torvalds { 22051da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 22061da177e4SLinus Torvalds struct inet6_dev *idev; 22071da177e4SLinus Torvalds 22081da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 22091da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 22101da177e4SLinus Torvalds We still use this lock to block changes 22111da177e4SLinus Torvalds caused by addrconf/ndisc. 22121da177e4SLinus Torvalds */ 22131da177e4SLinus Torvalds 22141da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 221538308473SDavid S. Miller if (!idev) 22161da177e4SLinus Torvalds return 0; 22171da177e4SLinus Torvalds 22181da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 22191da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 22201da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 22211da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 22221da177e4SLinus Torvalds */ 22231da177e4SLinus Torvalds /* 22241da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 22251da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 22261da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 22271da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 22281da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 22291da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 22301da177e4SLinus Torvalds PMTU discouvery. 22311da177e4SLinus Torvalds */ 2232d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 2233d8d1f30bSChangli Gao !dst_metric_locked(&rt->dst, RTAX_MTU) && 2234d8d1f30bSChangli Gao (dst_mtu(&rt->dst) >= arg->mtu || 2235d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 2236d8d1f30bSChangli Gao dst_mtu(&rt->dst) == idev->cnf.mtu6))) { 2237defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2238566cfd8fSSimon Arlott } 22391da177e4SLinus Torvalds return 0; 22401da177e4SLinus Torvalds } 22411da177e4SLinus Torvalds 22421da177e4SLinus Torvalds void rt6_mtu_change(struct net_device *dev, unsigned mtu) 22431da177e4SLinus Torvalds { 2244c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2245c71099acSThomas Graf .dev = dev, 2246c71099acSThomas Graf .mtu = mtu, 2247c71099acSThomas Graf }; 22481da177e4SLinus Torvalds 2249c346dca1SYOSHIFUJI Hideaki fib6_clean_all(dev_net(dev), rt6_mtu_change_route, 0, &arg); 22501da177e4SLinus Torvalds } 22511da177e4SLinus Torvalds 2252ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 22535176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 225486872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2255ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 225686872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 225786872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 225886872cb5SThomas Graf }; 225986872cb5SThomas Graf 226086872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 226186872cb5SThomas Graf struct fib6_config *cfg) 22621da177e4SLinus Torvalds { 226386872cb5SThomas Graf struct rtmsg *rtm; 226486872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 226586872cb5SThomas Graf int err; 22661da177e4SLinus Torvalds 226786872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 226886872cb5SThomas Graf if (err < 0) 226986872cb5SThomas Graf goto errout; 22701da177e4SLinus Torvalds 227186872cb5SThomas Graf err = -EINVAL; 227286872cb5SThomas Graf rtm = nlmsg_data(nlh); 227386872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 227486872cb5SThomas Graf 227586872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 227686872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 227786872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 227886872cb5SThomas Graf cfg->fc_flags = RTF_UP; 227986872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 228086872cb5SThomas Graf 228186872cb5SThomas Graf if (rtm->rtm_type == RTN_UNREACHABLE) 228286872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 228386872cb5SThomas Graf 2284ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2285ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2286ab79ad14SMaciej Żenczykowski 228786872cb5SThomas Graf cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid; 228886872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 22893b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 229086872cb5SThomas Graf 229186872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 229286872cb5SThomas Graf nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16); 229386872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 22941da177e4SLinus Torvalds } 229586872cb5SThomas Graf 229686872cb5SThomas Graf if (tb[RTA_DST]) { 229786872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 229886872cb5SThomas Graf 229986872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 230086872cb5SThomas Graf goto errout; 230186872cb5SThomas Graf 230286872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 23031da177e4SLinus Torvalds } 230486872cb5SThomas Graf 230586872cb5SThomas Graf if (tb[RTA_SRC]) { 230686872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 230786872cb5SThomas Graf 230886872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 230986872cb5SThomas Graf goto errout; 231086872cb5SThomas Graf 231186872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 23121da177e4SLinus Torvalds } 231386872cb5SThomas Graf 2314c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 2315c3968a85SDaniel Walter nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16); 2316c3968a85SDaniel Walter 231786872cb5SThomas Graf if (tb[RTA_OIF]) 231886872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 231986872cb5SThomas Graf 232086872cb5SThomas Graf if (tb[RTA_PRIORITY]) 232186872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 232286872cb5SThomas Graf 232386872cb5SThomas Graf if (tb[RTA_METRICS]) { 232486872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 232586872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 23261da177e4SLinus Torvalds } 232786872cb5SThomas Graf 232886872cb5SThomas Graf if (tb[RTA_TABLE]) 232986872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 233086872cb5SThomas Graf 233186872cb5SThomas Graf err = 0; 233286872cb5SThomas Graf errout: 233386872cb5SThomas Graf return err; 23341da177e4SLinus Torvalds } 23351da177e4SLinus Torvalds 2336c127ea2cSThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 23371da177e4SLinus Torvalds { 233886872cb5SThomas Graf struct fib6_config cfg; 233986872cb5SThomas Graf int err; 23401da177e4SLinus Torvalds 234186872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 234286872cb5SThomas Graf if (err < 0) 234386872cb5SThomas Graf return err; 234486872cb5SThomas Graf 234586872cb5SThomas Graf return ip6_route_del(&cfg); 23461da177e4SLinus Torvalds } 23471da177e4SLinus Torvalds 2348c127ea2cSThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) 23491da177e4SLinus Torvalds { 235086872cb5SThomas Graf struct fib6_config cfg; 235186872cb5SThomas Graf int err; 23521da177e4SLinus Torvalds 235386872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 235486872cb5SThomas Graf if (err < 0) 235586872cb5SThomas Graf return err; 235686872cb5SThomas Graf 235786872cb5SThomas Graf return ip6_route_add(&cfg); 23581da177e4SLinus Torvalds } 23591da177e4SLinus Torvalds 2360339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void) 2361339bf98fSThomas Graf { 2362339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 2363339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 2364339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 2365339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 2366339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 2367339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 2368339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 2369339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 2370339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 23716a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 2372339bf98fSThomas Graf + nla_total_size(sizeof(struct rta_cacheinfo)); 2373339bf98fSThomas Graf } 2374339bf98fSThomas Graf 2375191cd582SBrian Haley static int rt6_fill_node(struct net *net, 2376191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 23770d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 23780d51aa80SJamal Hadi Salim int iif, int type, u32 pid, u32 seq, 23797bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 23801da177e4SLinus Torvalds { 2381346f870bSDavid S. Miller const struct inet_peer *peer; 23821da177e4SLinus Torvalds struct rtmsg *rtm; 23831da177e4SLinus Torvalds struct nlmsghdr *nlh; 2384e3703b3dSThomas Graf long expires; 23859e762a4aSPatrick McHardy u32 table; 2386f2c31e32SEric Dumazet struct neighbour *n; 2387346f870bSDavid S. Miller u32 ts, tsage; 23881da177e4SLinus Torvalds 23891da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 23901da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 23911da177e4SLinus Torvalds /* success since this is not a prefix route */ 23921da177e4SLinus Torvalds return 1; 23931da177e4SLinus Torvalds } 23941da177e4SLinus Torvalds } 23951da177e4SLinus Torvalds 23962d7202bfSThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags); 239738308473SDavid S. Miller if (!nlh) 239826932566SPatrick McHardy return -EMSGSIZE; 23992d7202bfSThomas Graf 24002d7202bfSThomas Graf rtm = nlmsg_data(nlh); 24011da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 24021da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 24031da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 24041da177e4SLinus Torvalds rtm->rtm_tos = 0; 2405c71099acSThomas Graf if (rt->rt6i_table) 24069e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 2407c71099acSThomas Graf else 24089e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 24099e762a4aSPatrick McHardy rtm->rtm_table = table; 24102d7202bfSThomas Graf NLA_PUT_U32(skb, RTA_TABLE, table); 24111da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_REJECT) 24121da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 2413ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 2414ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 2415d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 24161da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 24171da177e4SLinus Torvalds else 24181da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 24191da177e4SLinus Torvalds rtm->rtm_flags = 0; 24201da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 24211da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 24221da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 24231da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 24241da177e4SLinus Torvalds else if (rt->rt6i_flags & RTF_ADDRCONF) 24251da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_KERNEL; 24261da177e4SLinus Torvalds else if (rt->rt6i_flags & RTF_DEFAULT) 24271da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 24281da177e4SLinus Torvalds 24291da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 24301da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 24311da177e4SLinus Torvalds 24321da177e4SLinus Torvalds if (dst) { 24332d7202bfSThomas Graf NLA_PUT(skb, RTA_DST, 16, dst); 24341da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 24351da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 24362d7202bfSThomas Graf NLA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr); 24371da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 24381da177e4SLinus Torvalds if (src) { 24392d7202bfSThomas Graf NLA_PUT(skb, RTA_SRC, 16, src); 24401da177e4SLinus Torvalds rtm->rtm_src_len = 128; 24411da177e4SLinus Torvalds } else if (rtm->rtm_src_len) 24422d7202bfSThomas Graf NLA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr); 24431da177e4SLinus Torvalds #endif 24447bc570c8SYOSHIFUJI Hideaki if (iif) { 24457bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 24467bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 24478229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 24487bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 24497bc570c8SYOSHIFUJI Hideaki if (!nowait) { 24507bc570c8SYOSHIFUJI Hideaki if (err == 0) 24517bc570c8SYOSHIFUJI Hideaki return 0; 24527bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 24537bc570c8SYOSHIFUJI Hideaki } else { 24547bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 24557bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 24567bc570c8SYOSHIFUJI Hideaki } 24577bc570c8SYOSHIFUJI Hideaki } 24587bc570c8SYOSHIFUJI Hideaki } else 24597bc570c8SYOSHIFUJI Hideaki #endif 24602d7202bfSThomas Graf NLA_PUT_U32(skb, RTA_IIF, iif); 24617bc570c8SYOSHIFUJI Hideaki } else if (dst) { 24621da177e4SLinus Torvalds struct in6_addr saddr_buf; 2463c3968a85SDaniel Walter if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0) 2464c3968a85SDaniel Walter NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf); 2465c3968a85SDaniel Walter } 2466c3968a85SDaniel Walter 2467c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 2468c3968a85SDaniel Walter struct in6_addr saddr_buf; 24694e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 24702d7202bfSThomas Graf NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf); 24711da177e4SLinus Torvalds } 24722d7202bfSThomas Graf 2473defb3519SDavid S. Miller if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) 24742d7202bfSThomas Graf goto nla_put_failure; 24752d7202bfSThomas Graf 2476f2c31e32SEric Dumazet rcu_read_lock(); 247727217455SDavid Miller n = dst_get_neighbour_noref(&rt->dst); 2478f2c31e32SEric Dumazet if (n) 2479f2c31e32SEric Dumazet NLA_PUT(skb, RTA_GATEWAY, 16, &n->primary_key); 2480f2c31e32SEric Dumazet rcu_read_unlock(); 24812d7202bfSThomas Graf 2482d8d1f30bSChangli Gao if (rt->dst.dev) 2483d1918542SDavid S. Miller NLA_PUT_U32(skb, RTA_OIF, rt->dst.dev->ifindex); 24842d7202bfSThomas Graf 24852d7202bfSThomas Graf NLA_PUT_U32(skb, RTA_PRIORITY, rt->rt6i_metric); 2486e3703b3dSThomas Graf 248736e3deaeSYOSHIFUJI Hideaki if (!(rt->rt6i_flags & RTF_EXPIRES)) 248836e3deaeSYOSHIFUJI Hideaki expires = 0; 2489d1918542SDavid S. Miller else if (rt->dst.expires - jiffies < INT_MAX) 2490d1918542SDavid S. Miller expires = rt->dst.expires - jiffies; 249136e3deaeSYOSHIFUJI Hideaki else 249236e3deaeSYOSHIFUJI Hideaki expires = INT_MAX; 249369cdf8f9SYOSHIFUJI Hideaki 2494346f870bSDavid S. Miller peer = rt->rt6i_peer; 2495346f870bSDavid S. Miller ts = tsage = 0; 2496346f870bSDavid S. Miller if (peer && peer->tcp_ts_stamp) { 2497346f870bSDavid S. Miller ts = peer->tcp_ts; 2498346f870bSDavid S. Miller tsage = get_seconds() - peer->tcp_ts_stamp; 2499346f870bSDavid S. Miller } 2500346f870bSDavid S. Miller 2501346f870bSDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, ts, tsage, 2502d8d1f30bSChangli Gao expires, rt->dst.error) < 0) 2503e3703b3dSThomas Graf goto nla_put_failure; 25041da177e4SLinus Torvalds 25052d7202bfSThomas Graf return nlmsg_end(skb, nlh); 25062d7202bfSThomas Graf 25072d7202bfSThomas Graf nla_put_failure: 250826932566SPatrick McHardy nlmsg_cancel(skb, nlh); 250926932566SPatrick McHardy return -EMSGSIZE; 25101da177e4SLinus Torvalds } 25111da177e4SLinus Torvalds 25121b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 25131da177e4SLinus Torvalds { 25141da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 25151da177e4SLinus Torvalds int prefix; 25161da177e4SLinus Torvalds 25172d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 25182d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 25191da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 25201da177e4SLinus Torvalds } else 25211da177e4SLinus Torvalds prefix = 0; 25221da177e4SLinus Torvalds 2523191cd582SBrian Haley return rt6_fill_node(arg->net, 2524191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 25251da177e4SLinus Torvalds NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq, 25267bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 25271da177e4SLinus Torvalds } 25281da177e4SLinus Torvalds 2529c127ea2cSThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg) 25301da177e4SLinus Torvalds { 25313b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 2532ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 25331da177e4SLinus Torvalds struct rt6_info *rt; 2534ab364a6fSThomas Graf struct sk_buff *skb; 2535ab364a6fSThomas Graf struct rtmsg *rtm; 25364c9483b2SDavid S. Miller struct flowi6 fl6; 2537ab364a6fSThomas Graf int err, iif = 0; 2538ab364a6fSThomas Graf 2539ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 2540ab364a6fSThomas Graf if (err < 0) 2541ab364a6fSThomas Graf goto errout; 2542ab364a6fSThomas Graf 2543ab364a6fSThomas Graf err = -EINVAL; 25444c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2545ab364a6fSThomas Graf 2546ab364a6fSThomas Graf if (tb[RTA_SRC]) { 2547ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 2548ab364a6fSThomas Graf goto errout; 2549ab364a6fSThomas Graf 25504e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 2551ab364a6fSThomas Graf } 2552ab364a6fSThomas Graf 2553ab364a6fSThomas Graf if (tb[RTA_DST]) { 2554ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 2555ab364a6fSThomas Graf goto errout; 2556ab364a6fSThomas Graf 25574e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 2558ab364a6fSThomas Graf } 2559ab364a6fSThomas Graf 2560ab364a6fSThomas Graf if (tb[RTA_IIF]) 2561ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 2562ab364a6fSThomas Graf 2563ab364a6fSThomas Graf if (tb[RTA_OIF]) 25644c9483b2SDavid S. Miller fl6.flowi6_oif = nla_get_u32(tb[RTA_OIF]); 2565ab364a6fSThomas Graf 2566ab364a6fSThomas Graf if (iif) { 2567ab364a6fSThomas Graf struct net_device *dev; 25685578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 2569ab364a6fSThomas Graf if (!dev) { 2570ab364a6fSThomas Graf err = -ENODEV; 2571ab364a6fSThomas Graf goto errout; 2572ab364a6fSThomas Graf } 2573ab364a6fSThomas Graf } 25741da177e4SLinus Torvalds 25751da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 257638308473SDavid S. Miller if (!skb) { 2577ab364a6fSThomas Graf err = -ENOBUFS; 2578ab364a6fSThomas Graf goto errout; 2579ab364a6fSThomas Graf } 25801da177e4SLinus Torvalds 25811da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 25821da177e4SLinus Torvalds through good chunk of routing engine. 25831da177e4SLinus Torvalds */ 2584459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 25851da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 25861da177e4SLinus Torvalds 25874c9483b2SDavid S. Miller rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl6); 2588d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 25891da177e4SLinus Torvalds 25904c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 25911da177e4SLinus Torvalds RTM_NEWROUTE, NETLINK_CB(in_skb).pid, 25927bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 25931da177e4SLinus Torvalds if (err < 0) { 2594ab364a6fSThomas Graf kfree_skb(skb); 2595ab364a6fSThomas Graf goto errout; 25961da177e4SLinus Torvalds } 25971da177e4SLinus Torvalds 25985578689aSDaniel Lezcano err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).pid); 2599ab364a6fSThomas Graf errout: 26001da177e4SLinus Torvalds return err; 26011da177e4SLinus Torvalds } 26021da177e4SLinus Torvalds 260386872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) 26041da177e4SLinus Torvalds { 26051da177e4SLinus Torvalds struct sk_buff *skb; 26065578689aSDaniel Lezcano struct net *net = info->nl_net; 2607528c4cebSDenis V. Lunev u32 seq; 2608528c4cebSDenis V. Lunev int err; 26090d51aa80SJamal Hadi Salim 2610528c4cebSDenis V. Lunev err = -ENOBUFS; 261138308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 261286872cb5SThomas Graf 2613339bf98fSThomas Graf skb = nlmsg_new(rt6_nlmsg_size(), gfp_any()); 261438308473SDavid S. Miller if (!skb) 261521713ebcSThomas Graf goto errout; 26161da177e4SLinus Torvalds 2617191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 26187bc570c8SYOSHIFUJI Hideaki event, info->pid, seq, 0, 0, 0); 261926932566SPatrick McHardy if (err < 0) { 262026932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 262126932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 262226932566SPatrick McHardy kfree_skb(skb); 262326932566SPatrick McHardy goto errout; 262426932566SPatrick McHardy } 26251ce85fe4SPablo Neira Ayuso rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE, 26265578689aSDaniel Lezcano info->nlh, gfp_any()); 26271ce85fe4SPablo Neira Ayuso return; 262821713ebcSThomas Graf errout: 262921713ebcSThomas Graf if (err < 0) 26305578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 26311da177e4SLinus Torvalds } 26321da177e4SLinus Torvalds 26338ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 26348ed67789SDaniel Lezcano unsigned long event, void *data) 26358ed67789SDaniel Lezcano { 26368ed67789SDaniel Lezcano struct net_device *dev = (struct net_device *)data; 2637c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 26388ed67789SDaniel Lezcano 26398ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 2640d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 26418ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 26428ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 2643d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 26448ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 2645d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 26468ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 26478ed67789SDaniel Lezcano #endif 26488ed67789SDaniel Lezcano } 26498ed67789SDaniel Lezcano 26508ed67789SDaniel Lezcano return NOTIFY_OK; 26518ed67789SDaniel Lezcano } 26528ed67789SDaniel Lezcano 26531da177e4SLinus Torvalds /* 26541da177e4SLinus Torvalds * /proc 26551da177e4SLinus Torvalds */ 26561da177e4SLinus Torvalds 26571da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 26581da177e4SLinus Torvalds 26591da177e4SLinus Torvalds struct rt6_proc_arg 26601da177e4SLinus Torvalds { 26611da177e4SLinus Torvalds char *buffer; 26621da177e4SLinus Torvalds int offset; 26631da177e4SLinus Torvalds int length; 26641da177e4SLinus Torvalds int skip; 26651da177e4SLinus Torvalds int len; 26661da177e4SLinus Torvalds }; 26671da177e4SLinus Torvalds 26681da177e4SLinus Torvalds static int rt6_info_route(struct rt6_info *rt, void *p_arg) 26691da177e4SLinus Torvalds { 267033120b30SAlexey Dobriyan struct seq_file *m = p_arg; 267169cce1d1SDavid S. Miller struct neighbour *n; 26721da177e4SLinus Torvalds 26734b7a4274SHarvey Harrison seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen); 26741da177e4SLinus Torvalds 26751da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 26764b7a4274SHarvey Harrison seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen); 26771da177e4SLinus Torvalds #else 267833120b30SAlexey Dobriyan seq_puts(m, "00000000000000000000000000000000 00 "); 26791da177e4SLinus Torvalds #endif 2680f2c31e32SEric Dumazet rcu_read_lock(); 268127217455SDavid Miller n = dst_get_neighbour_noref(&rt->dst); 268269cce1d1SDavid S. Miller if (n) { 268369cce1d1SDavid S. Miller seq_printf(m, "%pi6", n->primary_key); 26841da177e4SLinus Torvalds } else { 268533120b30SAlexey Dobriyan seq_puts(m, "00000000000000000000000000000000"); 26861da177e4SLinus Torvalds } 2687f2c31e32SEric Dumazet rcu_read_unlock(); 268833120b30SAlexey Dobriyan seq_printf(m, " %08x %08x %08x %08x %8s\n", 2689d8d1f30bSChangli Gao rt->rt6i_metric, atomic_read(&rt->dst.__refcnt), 2690d8d1f30bSChangli Gao rt->dst.__use, rt->rt6i_flags, 2691d1918542SDavid S. Miller rt->dst.dev ? rt->dst.dev->name : ""); 26921da177e4SLinus Torvalds return 0; 26931da177e4SLinus Torvalds } 26941da177e4SLinus Torvalds 269533120b30SAlexey Dobriyan static int ipv6_route_show(struct seq_file *m, void *v) 26961da177e4SLinus Torvalds { 2697f3db4851SDaniel Lezcano struct net *net = (struct net *)m->private; 269832b293a5SJosh Hunt fib6_clean_all_ro(net, rt6_info_route, 0, m); 269933120b30SAlexey Dobriyan return 0; 27001da177e4SLinus Torvalds } 27011da177e4SLinus Torvalds 270233120b30SAlexey Dobriyan static int ipv6_route_open(struct inode *inode, struct file *file) 270333120b30SAlexey Dobriyan { 2704de05c557SPavel Emelyanov return single_open_net(inode, file, ipv6_route_show); 2705f3db4851SDaniel Lezcano } 2706f3db4851SDaniel Lezcano 270733120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 270833120b30SAlexey Dobriyan .owner = THIS_MODULE, 270933120b30SAlexey Dobriyan .open = ipv6_route_open, 271033120b30SAlexey Dobriyan .read = seq_read, 271133120b30SAlexey Dobriyan .llseek = seq_lseek, 2712b6fcbdb4SPavel Emelyanov .release = single_release_net, 271333120b30SAlexey Dobriyan }; 271433120b30SAlexey Dobriyan 27151da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 27161da177e4SLinus Torvalds { 271769ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 27181da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 271969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 272069ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 272169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 272269ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 272369ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 2724fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 272569ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 27261da177e4SLinus Torvalds 27271da177e4SLinus Torvalds return 0; 27281da177e4SLinus Torvalds } 27291da177e4SLinus Torvalds 27301da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 27311da177e4SLinus Torvalds { 2732de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 273369ddb805SDaniel Lezcano } 273469ddb805SDaniel Lezcano 27359a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 27361da177e4SLinus Torvalds .owner = THIS_MODULE, 27371da177e4SLinus Torvalds .open = rt6_stats_seq_open, 27381da177e4SLinus Torvalds .read = seq_read, 27391da177e4SLinus Torvalds .llseek = seq_lseek, 2740b6fcbdb4SPavel Emelyanov .release = single_release_net, 27411da177e4SLinus Torvalds }; 27421da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 27431da177e4SLinus Torvalds 27441da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 27451da177e4SLinus Torvalds 27461da177e4SLinus Torvalds static 27478d65af78SAlexey Dobriyan int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, 27481da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 27491da177e4SLinus Torvalds { 2750c486da34SLucian Adrian Grijincu struct net *net; 2751c486da34SLucian Adrian Grijincu int delay; 2752c486da34SLucian Adrian Grijincu if (!write) 2753c486da34SLucian Adrian Grijincu return -EINVAL; 2754c486da34SLucian Adrian Grijincu 2755c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 2756c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 27578d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 27585b7c931dSDaniel Lezcano fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net); 27591da177e4SLinus Torvalds return 0; 27601da177e4SLinus Torvalds } 27611da177e4SLinus Torvalds 2762760f2d01SDaniel Lezcano ctl_table ipv6_route_table_template[] = { 27631da177e4SLinus Torvalds { 27641da177e4SLinus Torvalds .procname = "flush", 27654990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 27661da177e4SLinus Torvalds .maxlen = sizeof(int), 276789c8b3a1SDave Jones .mode = 0200, 27686d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 27691da177e4SLinus Torvalds }, 27701da177e4SLinus Torvalds { 27711da177e4SLinus Torvalds .procname = "gc_thresh", 27729a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 27731da177e4SLinus Torvalds .maxlen = sizeof(int), 27741da177e4SLinus Torvalds .mode = 0644, 27756d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 27761da177e4SLinus Torvalds }, 27771da177e4SLinus Torvalds { 27781da177e4SLinus Torvalds .procname = "max_size", 27794990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 27801da177e4SLinus Torvalds .maxlen = sizeof(int), 27811da177e4SLinus Torvalds .mode = 0644, 27826d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 27831da177e4SLinus Torvalds }, 27841da177e4SLinus Torvalds { 27851da177e4SLinus Torvalds .procname = "gc_min_interval", 27864990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 27871da177e4SLinus Torvalds .maxlen = sizeof(int), 27881da177e4SLinus Torvalds .mode = 0644, 27896d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 27901da177e4SLinus Torvalds }, 27911da177e4SLinus Torvalds { 27921da177e4SLinus Torvalds .procname = "gc_timeout", 27934990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 27941da177e4SLinus Torvalds .maxlen = sizeof(int), 27951da177e4SLinus Torvalds .mode = 0644, 27966d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 27971da177e4SLinus Torvalds }, 27981da177e4SLinus Torvalds { 27991da177e4SLinus Torvalds .procname = "gc_interval", 28004990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 28011da177e4SLinus Torvalds .maxlen = sizeof(int), 28021da177e4SLinus Torvalds .mode = 0644, 28036d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28041da177e4SLinus Torvalds }, 28051da177e4SLinus Torvalds { 28061da177e4SLinus Torvalds .procname = "gc_elasticity", 28074990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 28081da177e4SLinus Torvalds .maxlen = sizeof(int), 28091da177e4SLinus Torvalds .mode = 0644, 2810f3d3f616SMin Zhang .proc_handler = proc_dointvec, 28111da177e4SLinus Torvalds }, 28121da177e4SLinus Torvalds { 28131da177e4SLinus Torvalds .procname = "mtu_expires", 28144990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 28151da177e4SLinus Torvalds .maxlen = sizeof(int), 28161da177e4SLinus Torvalds .mode = 0644, 28176d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 28181da177e4SLinus Torvalds }, 28191da177e4SLinus Torvalds { 28201da177e4SLinus Torvalds .procname = "min_adv_mss", 28214990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 28221da177e4SLinus Torvalds .maxlen = sizeof(int), 28231da177e4SLinus Torvalds .mode = 0644, 2824f3d3f616SMin Zhang .proc_handler = proc_dointvec, 28251da177e4SLinus Torvalds }, 28261da177e4SLinus Torvalds { 28271da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 28284990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 28291da177e4SLinus Torvalds .maxlen = sizeof(int), 28301da177e4SLinus Torvalds .mode = 0644, 28316d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 28321da177e4SLinus Torvalds }, 2833f8572d8fSEric W. Biederman { } 28341da177e4SLinus Torvalds }; 28351da177e4SLinus Torvalds 28362c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 2837760f2d01SDaniel Lezcano { 2838760f2d01SDaniel Lezcano struct ctl_table *table; 2839760f2d01SDaniel Lezcano 2840760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 2841760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 2842760f2d01SDaniel Lezcano GFP_KERNEL); 28435ee09105SYOSHIFUJI Hideaki 28445ee09105SYOSHIFUJI Hideaki if (table) { 28455ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 2846c486da34SLucian Adrian Grijincu table[0].extra1 = net; 284786393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 28485ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 28495ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 28505ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 28515ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 28525ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 28535ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 28545ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 28559c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 28565ee09105SYOSHIFUJI Hideaki } 28575ee09105SYOSHIFUJI Hideaki 2858760f2d01SDaniel Lezcano return table; 2859760f2d01SDaniel Lezcano } 28601da177e4SLinus Torvalds #endif 28611da177e4SLinus Torvalds 28622c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 2863cdb18761SDaniel Lezcano { 2864633d424bSPavel Emelyanov int ret = -ENOMEM; 28658ed67789SDaniel Lezcano 286686393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 286786393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 2868f2fc6a54SBenjamin Thery 2869fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 2870fc66f95cSEric Dumazet goto out_ip6_dst_ops; 2871fc66f95cSEric Dumazet 28728ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 28738ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 28748ed67789SDaniel Lezcano GFP_KERNEL); 28758ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 2876fc66f95cSEric Dumazet goto out_ip6_dst_entries; 2877d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 28788ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 2879d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 288062fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 288162fa8a84SDavid S. Miller ip6_template_metrics, true); 28828ed67789SDaniel Lezcano 28838ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 28848ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 28858ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 28868ed67789SDaniel Lezcano GFP_KERNEL); 288768fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 288868fffc67SPeter Zijlstra goto out_ip6_null_entry; 2889d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 28908ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 2891d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 289262fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 289362fa8a84SDavid S. Miller ip6_template_metrics, true); 28948ed67789SDaniel Lezcano 28958ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 28968ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 28978ed67789SDaniel Lezcano GFP_KERNEL); 289868fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 289968fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 2900d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 29018ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 2902d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 290362fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 290462fa8a84SDavid S. Miller ip6_template_metrics, true); 29058ed67789SDaniel Lezcano #endif 29068ed67789SDaniel Lezcano 2907b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 2908b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 2909b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 2910b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 2911b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 2912b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 2913b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 2914b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 2915b339a47cSPeter Zijlstra 2916cdb18761SDaniel Lezcano #ifdef CONFIG_PROC_FS 2917cdb18761SDaniel Lezcano proc_net_fops_create(net, "ipv6_route", 0, &ipv6_route_proc_fops); 2918cdb18761SDaniel Lezcano proc_net_fops_create(net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops); 2919cdb18761SDaniel Lezcano #endif 29206891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 29216891a346SBenjamin Thery 29228ed67789SDaniel Lezcano ret = 0; 29238ed67789SDaniel Lezcano out: 29248ed67789SDaniel Lezcano return ret; 2925f2fc6a54SBenjamin Thery 292668fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 292768fffc67SPeter Zijlstra out_ip6_prohibit_entry: 292868fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 292968fffc67SPeter Zijlstra out_ip6_null_entry: 293068fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 293168fffc67SPeter Zijlstra #endif 2932fc66f95cSEric Dumazet out_ip6_dst_entries: 2933fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 2934f2fc6a54SBenjamin Thery out_ip6_dst_ops: 2935f2fc6a54SBenjamin Thery goto out; 2936cdb18761SDaniel Lezcano } 2937cdb18761SDaniel Lezcano 29382c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 2939cdb18761SDaniel Lezcano { 2940cdb18761SDaniel Lezcano #ifdef CONFIG_PROC_FS 2941cdb18761SDaniel Lezcano proc_net_remove(net, "ipv6_route"); 2942cdb18761SDaniel Lezcano proc_net_remove(net, "rt6_stats"); 2943cdb18761SDaniel Lezcano #endif 29448ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 29458ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 29468ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 29478ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 29488ed67789SDaniel Lezcano #endif 294941bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 2950cdb18761SDaniel Lezcano } 2951cdb18761SDaniel Lezcano 2952cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 2953cdb18761SDaniel Lezcano .init = ip6_route_net_init, 2954cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 2955cdb18761SDaniel Lezcano }; 2956cdb18761SDaniel Lezcano 29578ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 29588ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 29598ed67789SDaniel Lezcano .priority = 0, 29608ed67789SDaniel Lezcano }; 29618ed67789SDaniel Lezcano 2962433d49c3SDaniel Lezcano int __init ip6_route_init(void) 29631da177e4SLinus Torvalds { 2964433d49c3SDaniel Lezcano int ret; 2965433d49c3SDaniel Lezcano 29669a7ec3a9SDaniel Lezcano ret = -ENOMEM; 29679a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 29689a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 29699a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 29709a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 2971c19a28e1SFernando Carrijo goto out; 297214e50e57SDavid S. Miller 2973fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 29748ed67789SDaniel Lezcano if (ret) 2975bdb3289fSDaniel Lezcano goto out_kmem_cache; 2976bdb3289fSDaniel Lezcano 2977fc66f95cSEric Dumazet ret = register_pernet_subsys(&ip6_route_net_ops); 2978fc66f95cSEric Dumazet if (ret) 2979fc66f95cSEric Dumazet goto out_dst_entries; 2980fc66f95cSEric Dumazet 29815dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 29825dc121e9SArnaud Ebalard 29838ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 29848ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 29858ed67789SDaniel Lezcano * manually for init_net */ 2986d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 29878ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 2988bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 2989d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 29908ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 2991d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 29928ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 2993bdb3289fSDaniel Lezcano #endif 2994433d49c3SDaniel Lezcano ret = fib6_init(); 2995433d49c3SDaniel Lezcano if (ret) 29968ed67789SDaniel Lezcano goto out_register_subsys; 2997433d49c3SDaniel Lezcano 2998433d49c3SDaniel Lezcano ret = xfrm6_init(); 2999433d49c3SDaniel Lezcano if (ret) 3000cdb18761SDaniel Lezcano goto out_fib6_init; 3001c35b7e72SDaniel Lezcano 3002433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3003433d49c3SDaniel Lezcano if (ret) 3004433d49c3SDaniel Lezcano goto xfrm6_init; 30057e5449c2SDaniel Lezcano 3006433d49c3SDaniel Lezcano ret = -ENOBUFS; 3007c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3008c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3009c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3010433d49c3SDaniel Lezcano goto fib6_rules_init; 3011433d49c3SDaniel Lezcano 30128ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3013cdb18761SDaniel Lezcano if (ret) 3014cdb18761SDaniel Lezcano goto fib6_rules_init; 30158ed67789SDaniel Lezcano 3016433d49c3SDaniel Lezcano out: 3017433d49c3SDaniel Lezcano return ret; 3018433d49c3SDaniel Lezcano 3019433d49c3SDaniel Lezcano fib6_rules_init: 3020433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3021433d49c3SDaniel Lezcano xfrm6_init: 3022433d49c3SDaniel Lezcano xfrm6_fini(); 3023433d49c3SDaniel Lezcano out_fib6_init: 3024433d49c3SDaniel Lezcano fib6_gc_cleanup(); 30258ed67789SDaniel Lezcano out_register_subsys: 30268ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 3027fc66f95cSEric Dumazet out_dst_entries: 3028fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3029433d49c3SDaniel Lezcano out_kmem_cache: 3030f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3031433d49c3SDaniel Lezcano goto out; 30321da177e4SLinus Torvalds } 30331da177e4SLinus Torvalds 30341da177e4SLinus Torvalds void ip6_route_cleanup(void) 30351da177e4SLinus Torvalds { 30368ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3037101367c2SThomas Graf fib6_rules_cleanup(); 30381da177e4SLinus Torvalds xfrm6_fini(); 30391da177e4SLinus Torvalds fib6_gc_cleanup(); 30408ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 304141bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3042f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 30431da177e4SLinus Torvalds } 3044