11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Linux INET6 implementation 31da177e4SLinus Torvalds * FIB front-end. 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Authors: 61da177e4SLinus Torvalds * Pedro Roque <roque@di.fc.ul.pt> 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 91da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 101da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 111da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds /* Changes: 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * YOSHIFUJI Hideaki @USAGI 171da177e4SLinus Torvalds * reworked default router selection. 181da177e4SLinus Torvalds * - respect outgoing interface 191da177e4SLinus Torvalds * - select from (probably) reachable routers (i.e. 201da177e4SLinus Torvalds * routers in REACHABLE, STALE, DELAY or PROBE states). 211da177e4SLinus Torvalds * - always select the same router if it is (probably) 221da177e4SLinus Torvalds * reachable. otherwise, round-robin the list. 23c0bece9fSYOSHIFUJI Hideaki * Ville Nuorvala 24c0bece9fSYOSHIFUJI Hideaki * Fixed routing subtrees. 251da177e4SLinus Torvalds */ 261da177e4SLinus Torvalds 27f3213831SJoe Perches #define pr_fmt(fmt) "IPv6: " fmt 28f3213831SJoe Perches 294fc268d2SRandy Dunlap #include <linux/capability.h> 301da177e4SLinus Torvalds #include <linux/errno.h> 31bc3b2d7fSPaul Gortmaker #include <linux/export.h> 321da177e4SLinus Torvalds #include <linux/types.h> 331da177e4SLinus Torvalds #include <linux/times.h> 341da177e4SLinus Torvalds #include <linux/socket.h> 351da177e4SLinus Torvalds #include <linux/sockios.h> 361da177e4SLinus Torvalds #include <linux/net.h> 371da177e4SLinus Torvalds #include <linux/route.h> 381da177e4SLinus Torvalds #include <linux/netdevice.h> 391da177e4SLinus Torvalds #include <linux/in6.h> 407bc570c8SYOSHIFUJI Hideaki #include <linux/mroute6.h> 411da177e4SLinus Torvalds #include <linux/init.h> 421da177e4SLinus Torvalds #include <linux/if_arp.h> 431da177e4SLinus Torvalds #include <linux/proc_fs.h> 441da177e4SLinus Torvalds #include <linux/seq_file.h> 455b7c931dSDaniel Lezcano #include <linux/nsproxy.h> 465a0e3ad6STejun Heo #include <linux/slab.h> 47457c4cbcSEric W. Biederman #include <net/net_namespace.h> 481da177e4SLinus Torvalds #include <net/snmp.h> 491da177e4SLinus Torvalds #include <net/ipv6.h> 501da177e4SLinus Torvalds #include <net/ip6_fib.h> 511da177e4SLinus Torvalds #include <net/ip6_route.h> 521da177e4SLinus Torvalds #include <net/ndisc.h> 531da177e4SLinus Torvalds #include <net/addrconf.h> 541da177e4SLinus Torvalds #include <net/tcp.h> 551da177e4SLinus Torvalds #include <linux/rtnetlink.h> 561da177e4SLinus Torvalds #include <net/dst.h> 571da177e4SLinus Torvalds #include <net/xfrm.h> 588d71740cSTom Tucker #include <net/netevent.h> 5921713ebcSThomas Graf #include <net/netlink.h> 6051ebd318SNicolas Dichtel #include <net/nexthop.h> 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds #include <asm/uaccess.h> 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 651da177e4SLinus Torvalds #include <linux/sysctl.h> 661da177e4SLinus Torvalds #endif 671da177e4SLinus Torvalds 68afc154e9SHannes Frederic Sowa enum rt6_nud_state { 697e980569SJiri Benc RT6_NUD_FAIL_HARD = -3, 707e980569SJiri Benc RT6_NUD_FAIL_PROBE = -2, 717e980569SJiri Benc RT6_NUD_FAIL_DO_RR = -1, 72afc154e9SHannes Frederic Sowa RT6_NUD_SUCCEED = 1 73afc154e9SHannes Frederic Sowa }; 74afc154e9SHannes Frederic Sowa 751716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 7621efcfa0SEric Dumazet const struct in6_addr *dest); 771da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); 780dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst); 79ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst); 801da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *); 811da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *); 821da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *, 831da177e4SLinus Torvalds struct net_device *dev, int how); 84569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops); 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds static int ip6_pkt_discard(struct sk_buff *skb); 871da177e4SLinus Torvalds static int ip6_pkt_discard_out(struct sk_buff *skb); 887150aedeSKamala R static int ip6_pkt_prohibit(struct sk_buff *skb); 897150aedeSKamala R static int ip6_pkt_prohibit_out(struct sk_buff *skb); 901da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb); 916700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 926700c270SDavid S. Miller struct sk_buff *skb, u32 mtu); 936700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, 946700c270SDavid S. Miller struct sk_buff *skb); 9552bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict); 961da177e4SLinus Torvalds 9770ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 98efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 99b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 100b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 10195c96174SEric Dumazet unsigned int pref); 102efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 103b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 104b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex); 10570ceb4f5SYOSHIFUJI Hideaki #endif 10670ceb4f5SYOSHIFUJI Hideaki 107e8243534Sstephen hemminger static void rt6_bind_peer(struct rt6_info *rt, int create) 108e8243534Sstephen hemminger { 109e8243534Sstephen hemminger struct inet_peer_base *base; 110e8243534Sstephen hemminger struct inet_peer *peer; 111e8243534Sstephen hemminger 112e8243534Sstephen hemminger base = inetpeer_base_ptr(rt->_rt6i_peer); 113e8243534Sstephen hemminger if (!base) 114e8243534Sstephen hemminger return; 115e8243534Sstephen hemminger 116e8243534Sstephen hemminger peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create); 117e8243534Sstephen hemminger if (peer) { 118e8243534Sstephen hemminger if (!rt6_set_peer(rt, peer)) 119e8243534Sstephen hemminger inet_putpeer(peer); 120e8243534Sstephen hemminger } 121e8243534Sstephen hemminger } 122e8243534Sstephen hemminger 123e8243534Sstephen hemminger static struct inet_peer *__rt6_get_peer(struct rt6_info *rt, int create) 124e8243534Sstephen hemminger { 125e8243534Sstephen hemminger if (rt6_has_peer(rt)) 126e8243534Sstephen hemminger return rt6_peer_ptr(rt); 127e8243534Sstephen hemminger 128e8243534Sstephen hemminger rt6_bind_peer(rt, create); 129e8243534Sstephen hemminger return (rt6_has_peer(rt) ? rt6_peer_ptr(rt) : NULL); 130e8243534Sstephen hemminger } 131e8243534Sstephen hemminger 132e8243534Sstephen hemminger static struct inet_peer *rt6_get_peer_create(struct rt6_info *rt) 133e8243534Sstephen hemminger { 134e8243534Sstephen hemminger return __rt6_get_peer(rt, 1); 135e8243534Sstephen hemminger } 136e8243534Sstephen hemminger 13706582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) 13806582540SDavid S. Miller { 13906582540SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 14006582540SDavid S. Miller struct inet_peer *peer; 14106582540SDavid S. Miller u32 *p = NULL; 14206582540SDavid S. Miller 1438e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST)) 1448e2ec639SYan, Zheng return NULL; 1458e2ec639SYan, Zheng 146fbfe95a4SDavid S. Miller peer = rt6_get_peer_create(rt); 14706582540SDavid S. Miller if (peer) { 14806582540SDavid S. Miller u32 *old_p = __DST_METRICS_PTR(old); 14906582540SDavid S. Miller unsigned long prev, new; 15006582540SDavid S. Miller 15106582540SDavid S. Miller p = peer->metrics; 15206582540SDavid S. Miller if (inet_metrics_new(peer)) 15306582540SDavid S. Miller memcpy(p, old_p, sizeof(u32) * RTAX_MAX); 15406582540SDavid S. Miller 15506582540SDavid S. Miller new = (unsigned long) p; 15606582540SDavid S. Miller prev = cmpxchg(&dst->_metrics, old, new); 15706582540SDavid S. Miller 15806582540SDavid S. Miller if (prev != old) { 15906582540SDavid S. Miller p = __DST_METRICS_PTR(prev); 16006582540SDavid S. Miller if (prev & DST_METRICS_READ_ONLY) 16106582540SDavid S. Miller p = NULL; 16206582540SDavid S. Miller } 16306582540SDavid S. Miller } 16406582540SDavid S. Miller return p; 16506582540SDavid S. Miller } 16606582540SDavid S. Miller 167f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, 168f894cbf8SDavid S. Miller struct sk_buff *skb, 169f894cbf8SDavid S. Miller const void *daddr) 17039232973SDavid S. Miller { 17139232973SDavid S. Miller struct in6_addr *p = &rt->rt6i_gateway; 17239232973SDavid S. Miller 173a7563f34SDavid S. Miller if (!ipv6_addr_any(p)) 17439232973SDavid S. Miller return (const void *) p; 175f894cbf8SDavid S. Miller else if (skb) 176f894cbf8SDavid S. Miller return &ipv6_hdr(skb)->daddr; 17739232973SDavid S. Miller return daddr; 17839232973SDavid S. Miller } 17939232973SDavid S. Miller 180f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, 181f894cbf8SDavid S. Miller struct sk_buff *skb, 182f894cbf8SDavid S. Miller const void *daddr) 183d3aaeb38SDavid S. Miller { 18439232973SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 18539232973SDavid S. Miller struct neighbour *n; 18639232973SDavid S. Miller 187f894cbf8SDavid S. Miller daddr = choose_neigh_daddr(rt, skb, daddr); 1888e022ee6SYOSHIFUJI Hideaki / 吉藤英明 n = __ipv6_neigh_lookup(dst->dev, daddr); 189f83c7790SDavid S. Miller if (n) 190f83c7790SDavid S. Miller return n; 191f83c7790SDavid S. Miller return neigh_create(&nd_tbl, daddr, dst->dev); 192f83c7790SDavid S. Miller } 193f83c7790SDavid S. Miller 1949a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 1951da177e4SLinus Torvalds .family = AF_INET6, 19609640e63SHarvey Harrison .protocol = cpu_to_be16(ETH_P_IPV6), 1971da177e4SLinus Torvalds .gc = ip6_dst_gc, 1981da177e4SLinus Torvalds .gc_thresh = 1024, 1991da177e4SLinus Torvalds .check = ip6_dst_check, 2000dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 201ebb762f2SSteffen Klassert .mtu = ip6_mtu, 20206582540SDavid S. Miller .cow_metrics = ipv6_cow_metrics, 2031da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 2041da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 2051da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 2061da177e4SLinus Torvalds .link_failure = ip6_link_failure, 2071da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 2086e157b6aSDavid S. Miller .redirect = rt6_do_redirect, 2091ac06e03SHerbert Xu .local_out = __ip6_local_out, 210d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 2111da177e4SLinus Torvalds }; 2121da177e4SLinus Torvalds 213ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 214ec831ea7SRoland Dreier { 215618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 216618f9bc7SSteffen Klassert 217618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 218ec831ea7SRoland Dreier } 219ec831ea7SRoland Dreier 2206700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, 2216700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 22214e50e57SDavid S. Miller { 22314e50e57SDavid S. Miller } 22414e50e57SDavid S. Miller 2256700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, 2266700c270SDavid S. Miller struct sk_buff *skb) 227b587ee3bSDavid S. Miller { 228b587ee3bSDavid S. Miller } 229b587ee3bSDavid S. Miller 2300972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, 2310972ddb2SHeld Bernhard unsigned long old) 2320972ddb2SHeld Bernhard { 2330972ddb2SHeld Bernhard return NULL; 2340972ddb2SHeld Bernhard } 2350972ddb2SHeld Bernhard 23614e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 23714e50e57SDavid S. Miller .family = AF_INET6, 23809640e63SHarvey Harrison .protocol = cpu_to_be16(ETH_P_IPV6), 23914e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 24014e50e57SDavid S. Miller .check = ip6_dst_check, 241ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 242214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 24314e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 244b587ee3bSDavid S. Miller .redirect = ip6_rt_blackhole_redirect, 2450972ddb2SHeld Bernhard .cow_metrics = ip6_rt_blackhole_cow_metrics, 246d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 24714e50e57SDavid S. Miller }; 24814e50e57SDavid S. Miller 24962fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 25014edd87dSLi RongQing [RTAX_HOPLIMIT - 1] = 0, 25162fa8a84SDavid S. Miller }; 25262fa8a84SDavid S. Miller 253fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = { 2541da177e4SLinus Torvalds .dst = { 2551da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 2561da177e4SLinus Torvalds .__use = 1, 2572c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 2581da177e4SLinus Torvalds .error = -ENETUNREACH, 2591da177e4SLinus Torvalds .input = ip6_pkt_discard, 2601da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 2611da177e4SLinus Torvalds }, 2621da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2634f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 2641da177e4SLinus Torvalds .rt6i_metric = ~(u32) 0, 2651da177e4SLinus Torvalds .rt6i_ref = ATOMIC_INIT(1), 2661da177e4SLinus Torvalds }; 2671da177e4SLinus Torvalds 268101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 269101367c2SThomas Graf 270fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = { 271101367c2SThomas Graf .dst = { 272101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 273101367c2SThomas Graf .__use = 1, 2742c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 275101367c2SThomas Graf .error = -EACCES, 2769ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 2779ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 278101367c2SThomas Graf }, 279101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2804f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 281101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 282101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 283101367c2SThomas Graf }; 284101367c2SThomas Graf 285fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = { 286101367c2SThomas Graf .dst = { 287101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 288101367c2SThomas Graf .__use = 1, 2892c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 290101367c2SThomas Graf .error = -EINVAL, 291352e512cSHerbert Xu .input = dst_discard, 292352e512cSHerbert Xu .output = dst_discard, 293101367c2SThomas Graf }, 294101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2954f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 296101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 297101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 298101367c2SThomas Graf }; 299101367c2SThomas Graf 300101367c2SThomas Graf #endif 301101367c2SThomas Graf 3021da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 30397bab73fSDavid S. Miller static inline struct rt6_info *ip6_dst_alloc(struct net *net, 304957c665fSDavid S. Miller struct net_device *dev, 3058b96d22dSDavid S. Miller int flags, 3068b96d22dSDavid S. Miller struct fib6_table *table) 3071da177e4SLinus Torvalds { 30897bab73fSDavid S. Miller struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 3096f3118b5SNicolas Dichtel 0, DST_OBSOLETE_FORCE_CHK, flags); 310cf911662SDavid S. Miller 31197bab73fSDavid S. Miller if (rt) { 3128104891bSSteffen Klassert struct dst_entry *dst = &rt->dst; 3138104891bSSteffen Klassert 3148104891bSSteffen Klassert memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 3158b96d22dSDavid S. Miller rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers); 316ca4c3fc2Sfan.du rt->rt6i_genid = rt_genid_ipv6(net); 31751ebd318SNicolas Dichtel INIT_LIST_HEAD(&rt->rt6i_siblings); 31897bab73fSDavid S. Miller } 319cf911662SDavid S. Miller return rt; 3201da177e4SLinus Torvalds } 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 3231da177e4SLinus Torvalds { 3241da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3251da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 326ecd98837SYOSHIFUJI Hideaki / 吉藤英明 struct dst_entry *from = dst->from; 3271da177e4SLinus Torvalds 3288e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST)) 3298e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 3308e2ec639SYan, Zheng 33138308473SDavid S. Miller if (idev) { 3321da177e4SLinus Torvalds rt->rt6i_idev = NULL; 3331da177e4SLinus Torvalds in6_dev_put(idev); 3341da177e4SLinus Torvalds } 3351716a961SGao feng 336ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst->from = NULL; 337ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst_release(from); 3381716a961SGao feng 33997bab73fSDavid S. Miller if (rt6_has_peer(rt)) { 34097bab73fSDavid S. Miller struct inet_peer *peer = rt6_peer_ptr(rt); 341b3419363SDavid S. Miller inet_putpeer(peer); 342b3419363SDavid S. Miller } 343b3419363SDavid S. Miller } 344b3419363SDavid S. Miller 3451da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 3461da177e4SLinus Torvalds int how) 3471da177e4SLinus Torvalds { 3481da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3491da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3505a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 351c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 3521da177e4SLinus Torvalds 35397cac082SDavid S. Miller if (dev != loopback_dev) { 35497cac082SDavid S. Miller if (idev && idev->dev == dev) { 3555a3e55d6SDenis V. Lunev struct inet6_dev *loopback_idev = 3565a3e55d6SDenis V. Lunev in6_dev_get(loopback_dev); 35738308473SDavid S. Miller if (loopback_idev) { 3581da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 3591da177e4SLinus Torvalds in6_dev_put(idev); 3601da177e4SLinus Torvalds } 3611da177e4SLinus Torvalds } 36297cac082SDavid S. Miller } 3631da177e4SLinus Torvalds } 3641da177e4SLinus Torvalds 365a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt) 3661da177e4SLinus Torvalds { 3671716a961SGao feng if (rt->rt6i_flags & RTF_EXPIRES) { 3681716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 369a50feda5SEric Dumazet return true; 3701716a961SGao feng } else if (rt->dst.from) { 3713fd91fb3SLi RongQing return rt6_check_expired((struct rt6_info *) rt->dst.from); 3721716a961SGao feng } 373a50feda5SEric Dumazet return false; 3741da177e4SLinus Torvalds } 3751da177e4SLinus Torvalds 376a50feda5SEric Dumazet static bool rt6_need_strict(const struct in6_addr *daddr) 377c71099acSThomas Graf { 378a02cec21SEric Dumazet return ipv6_addr_type(daddr) & 379a02cec21SEric Dumazet (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); 380c71099acSThomas Graf } 381c71099acSThomas Graf 38251ebd318SNicolas Dichtel /* Multipath route selection: 38351ebd318SNicolas Dichtel * Hash based function using packet header and flowlabel. 38451ebd318SNicolas Dichtel * Adapted from fib_info_hashfn() 38551ebd318SNicolas Dichtel */ 38651ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count, 38751ebd318SNicolas Dichtel const struct flowi6 *fl6) 38851ebd318SNicolas Dichtel { 38951ebd318SNicolas Dichtel unsigned int val = fl6->flowi6_proto; 39051ebd318SNicolas Dichtel 391c08977bbSYOSHIFUJI Hideaki / 吉藤英明 val ^= ipv6_addr_hash(&fl6->daddr); 392c08977bbSYOSHIFUJI Hideaki / 吉藤英明 val ^= ipv6_addr_hash(&fl6->saddr); 39351ebd318SNicolas Dichtel 39451ebd318SNicolas Dichtel /* Work only if this not encapsulated */ 39551ebd318SNicolas Dichtel switch (fl6->flowi6_proto) { 39651ebd318SNicolas Dichtel case IPPROTO_UDP: 39751ebd318SNicolas Dichtel case IPPROTO_TCP: 39851ebd318SNicolas Dichtel case IPPROTO_SCTP: 399b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_sport; 400b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_dport; 40151ebd318SNicolas Dichtel break; 40251ebd318SNicolas Dichtel 40351ebd318SNicolas Dichtel case IPPROTO_ICMPV6: 404b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_icmp_type; 405b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_icmp_code; 40651ebd318SNicolas Dichtel break; 40751ebd318SNicolas Dichtel } 40851ebd318SNicolas Dichtel /* RFC6438 recommands to use flowlabel */ 409b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->flowlabel; 41051ebd318SNicolas Dichtel 41151ebd318SNicolas Dichtel /* Perhaps, we need to tune, this function? */ 41251ebd318SNicolas Dichtel val = val ^ (val >> 7) ^ (val >> 12); 41351ebd318SNicolas Dichtel return val % candidate_count; 41451ebd318SNicolas Dichtel } 41551ebd318SNicolas Dichtel 41651ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match, 41752bd4c0cSNicolas Dichtel struct flowi6 *fl6, int oif, 41852bd4c0cSNicolas Dichtel int strict) 41951ebd318SNicolas Dichtel { 42051ebd318SNicolas Dichtel struct rt6_info *sibling, *next_sibling; 42151ebd318SNicolas Dichtel int route_choosen; 42251ebd318SNicolas Dichtel 42351ebd318SNicolas Dichtel route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6); 42451ebd318SNicolas Dichtel /* Don't change the route, if route_choosen == 0 42551ebd318SNicolas Dichtel * (siblings does not include ourself) 42651ebd318SNicolas Dichtel */ 42751ebd318SNicolas Dichtel if (route_choosen) 42851ebd318SNicolas Dichtel list_for_each_entry_safe(sibling, next_sibling, 42951ebd318SNicolas Dichtel &match->rt6i_siblings, rt6i_siblings) { 43051ebd318SNicolas Dichtel route_choosen--; 43151ebd318SNicolas Dichtel if (route_choosen == 0) { 43252bd4c0cSNicolas Dichtel if (rt6_score_route(sibling, oif, strict) < 0) 43352bd4c0cSNicolas Dichtel break; 43451ebd318SNicolas Dichtel match = sibling; 43551ebd318SNicolas Dichtel break; 43651ebd318SNicolas Dichtel } 43751ebd318SNicolas Dichtel } 43851ebd318SNicolas Dichtel return match; 43951ebd318SNicolas Dichtel } 44051ebd318SNicolas Dichtel 4411da177e4SLinus Torvalds /* 442c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 4431da177e4SLinus Torvalds */ 4441da177e4SLinus Torvalds 4458ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 4468ed67789SDaniel Lezcano struct rt6_info *rt, 447b71d1d42SEric Dumazet const struct in6_addr *saddr, 4481da177e4SLinus Torvalds int oif, 449d420895eSYOSHIFUJI Hideaki int flags) 4501da177e4SLinus Torvalds { 4511da177e4SLinus Torvalds struct rt6_info *local = NULL; 4521da177e4SLinus Torvalds struct rt6_info *sprt; 4531da177e4SLinus Torvalds 454dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 455dd3abc4eSYOSHIFUJI Hideaki goto out; 456dd3abc4eSYOSHIFUJI Hideaki 457d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 458d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 459dd3abc4eSYOSHIFUJI Hideaki 460dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4611da177e4SLinus Torvalds if (dev->ifindex == oif) 4621da177e4SLinus Torvalds return sprt; 4631da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 46438308473SDavid S. Miller if (!sprt->rt6i_idev || 4651da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 466d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE && oif) 4671da177e4SLinus Torvalds continue; 4681da177e4SLinus Torvalds if (local && (!oif || 4691da177e4SLinus Torvalds local->rt6i_idev->dev->ifindex == oif)) 4701da177e4SLinus Torvalds continue; 4711da177e4SLinus Torvalds } 4721da177e4SLinus Torvalds local = sprt; 4731da177e4SLinus Torvalds } 474dd3abc4eSYOSHIFUJI Hideaki } else { 475dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 476dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 477dd3abc4eSYOSHIFUJI Hideaki return sprt; 478dd3abc4eSYOSHIFUJI Hideaki } 4791da177e4SLinus Torvalds } 4801da177e4SLinus Torvalds 481dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4821da177e4SLinus Torvalds if (local) 4831da177e4SLinus Torvalds return local; 4841da177e4SLinus Torvalds 485d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 4868ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 4871da177e4SLinus Torvalds } 488dd3abc4eSYOSHIFUJI Hideaki out: 4891da177e4SLinus Torvalds return rt; 4901da177e4SLinus Torvalds } 4911da177e4SLinus Torvalds 49227097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 493c2f17e82SHannes Frederic Sowa struct __rt6_probe_work { 494c2f17e82SHannes Frederic Sowa struct work_struct work; 495c2f17e82SHannes Frederic Sowa struct in6_addr target; 496c2f17e82SHannes Frederic Sowa struct net_device *dev; 497c2f17e82SHannes Frederic Sowa }; 498c2f17e82SHannes Frederic Sowa 499c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w) 500c2f17e82SHannes Frederic Sowa { 501c2f17e82SHannes Frederic Sowa struct in6_addr mcaddr; 502c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work = 503c2f17e82SHannes Frederic Sowa container_of(w, struct __rt6_probe_work, work); 504c2f17e82SHannes Frederic Sowa 505c2f17e82SHannes Frederic Sowa addrconf_addr_solict_mult(&work->target, &mcaddr); 506c2f17e82SHannes Frederic Sowa ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL); 507c2f17e82SHannes Frederic Sowa dev_put(work->dev); 508c2f17e82SHannes Frederic Sowa kfree(w); 509c2f17e82SHannes Frederic Sowa } 510c2f17e82SHannes Frederic Sowa 51127097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 51227097255SYOSHIFUJI Hideaki { 513f2c31e32SEric Dumazet struct neighbour *neigh; 51427097255SYOSHIFUJI Hideaki /* 51527097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 51627097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 51727097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 51827097255SYOSHIFUJI Hideaki * 51927097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 52027097255SYOSHIFUJI Hideaki * to no more than one per minute. 52127097255SYOSHIFUJI Hideaki */ 5222152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!rt || !(rt->rt6i_flags & RTF_GATEWAY)) 523fdd6681dSAmerigo Wang return; 5242152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 5252152caeaSYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 5262152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 5272152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_lock(&neigh->lock); 5282152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh->nud_state & NUD_VALID) 5292152caeaSYOSHIFUJI Hideaki / 吉藤英明 goto out; 5307ff74a59SYOSHIFUJI Hideaki / 吉藤英明 } 5312152caeaSYOSHIFUJI Hideaki / 吉藤英明 5322152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!neigh || 53352e16356SYOSHIFUJI Hideaki time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { 534c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work; 53527097255SYOSHIFUJI Hideaki 536c2f17e82SHannes Frederic Sowa work = kmalloc(sizeof(*work), GFP_ATOMIC); 537c2f17e82SHannes Frederic Sowa 538c2f17e82SHannes Frederic Sowa if (neigh && work) 5397e980569SJiri Benc __neigh_set_probe_once(neigh); 5402152caeaSYOSHIFUJI Hideaki / 吉藤英明 541c2f17e82SHannes Frederic Sowa if (neigh) 542c2f17e82SHannes Frederic Sowa write_unlock(&neigh->lock); 543c2f17e82SHannes Frederic Sowa 544c2f17e82SHannes Frederic Sowa if (work) { 545c2f17e82SHannes Frederic Sowa INIT_WORK(&work->work, rt6_probe_deferred); 546c2f17e82SHannes Frederic Sowa work->target = rt->rt6i_gateway; 547c2f17e82SHannes Frederic Sowa dev_hold(rt->dst.dev); 548c2f17e82SHannes Frederic Sowa work->dev = rt->dst.dev; 549c2f17e82SHannes Frederic Sowa schedule_work(&work->work); 550c2f17e82SHannes Frederic Sowa } 551f2c31e32SEric Dumazet } else { 5522152caeaSYOSHIFUJI Hideaki / 吉藤英明 out: 5532152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_unlock(&neigh->lock); 55427097255SYOSHIFUJI Hideaki } 5552152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 556f2c31e32SEric Dumazet } 55727097255SYOSHIFUJI Hideaki #else 55827097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 55927097255SYOSHIFUJI Hideaki { 56027097255SYOSHIFUJI Hideaki } 56127097255SYOSHIFUJI Hideaki #endif 56227097255SYOSHIFUJI Hideaki 5631da177e4SLinus Torvalds /* 564554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 5651da177e4SLinus Torvalds */ 566b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 5671da177e4SLinus Torvalds { 568d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 569161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 570554cfb7eSYOSHIFUJI Hideaki return 2; 571161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 572161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 573161980f4SDavid S. Miller return 1; 574554cfb7eSYOSHIFUJI Hideaki return 0; 5751da177e4SLinus Torvalds } 5761da177e4SLinus Torvalds 577afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt) 5781da177e4SLinus Torvalds { 579f2c31e32SEric Dumazet struct neighbour *neigh; 580afc154e9SHannes Frederic Sowa enum rt6_nud_state ret = RT6_NUD_FAIL_HARD; 581f2c31e32SEric Dumazet 5824d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 5834d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 584afc154e9SHannes Frederic Sowa return RT6_NUD_SUCCEED; 585145a3621SYOSHIFUJI Hideaki / 吉藤英明 586145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 587145a3621SYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 588145a3621SYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 589145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_lock(&neigh->lock); 590554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 591afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 592398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 593a5a81f0bSPaul Marks else if (!(neigh->nud_state & NUD_FAILED)) 594afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 5957e980569SJiri Benc else 5967e980569SJiri Benc ret = RT6_NUD_FAIL_PROBE; 597398bcbebSYOSHIFUJI Hideaki #endif 598145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_unlock(&neigh->lock); 599afc154e9SHannes Frederic Sowa } else { 600afc154e9SHannes Frederic Sowa ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 6017e980569SJiri Benc RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; 602a5a81f0bSPaul Marks } 603145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 604145a3621SYOSHIFUJI Hideaki / 吉藤英明 605a5a81f0bSPaul Marks return ret; 6061da177e4SLinus Torvalds } 6071da177e4SLinus Torvalds 608554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 609554cfb7eSYOSHIFUJI Hideaki int strict) 610554cfb7eSYOSHIFUJI Hideaki { 611a5a81f0bSPaul Marks int m; 6124d0c5911SYOSHIFUJI Hideaki 6134d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 61477d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 615afc154e9SHannes Frederic Sowa return RT6_NUD_FAIL_HARD; 616ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 617ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 618ebacaaa0SYOSHIFUJI Hideaki #endif 619afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) { 620afc154e9SHannes Frederic Sowa int n = rt6_check_neigh(rt); 621afc154e9SHannes Frederic Sowa if (n < 0) 622afc154e9SHannes Frederic Sowa return n; 623afc154e9SHannes Frederic Sowa } 624554cfb7eSYOSHIFUJI Hideaki return m; 625554cfb7eSYOSHIFUJI Hideaki } 626554cfb7eSYOSHIFUJI Hideaki 627f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 628afc154e9SHannes Frederic Sowa int *mpri, struct rt6_info *match, 629afc154e9SHannes Frederic Sowa bool *do_rr) 630554cfb7eSYOSHIFUJI Hideaki { 631554cfb7eSYOSHIFUJI Hideaki int m; 632afc154e9SHannes Frederic Sowa bool match_do_rr = false; 633554cfb7eSYOSHIFUJI Hideaki 634554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 635f11e6659SDavid S. Miller goto out; 636554cfb7eSYOSHIFUJI Hideaki 637554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 6387e980569SJiri Benc if (m == RT6_NUD_FAIL_DO_RR) { 639afc154e9SHannes Frederic Sowa match_do_rr = true; 640afc154e9SHannes Frederic Sowa m = 0; /* lowest valid score */ 6417e980569SJiri Benc } else if (m == RT6_NUD_FAIL_HARD) { 642f11e6659SDavid S. Miller goto out; 6431da177e4SLinus Torvalds } 644f11e6659SDavid S. Miller 645afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) 646afc154e9SHannes Frederic Sowa rt6_probe(rt); 647afc154e9SHannes Frederic Sowa 6487e980569SJiri Benc /* note that m can be RT6_NUD_FAIL_PROBE at this point */ 649afc154e9SHannes Frederic Sowa if (m > *mpri) { 650afc154e9SHannes Frederic Sowa *do_rr = match_do_rr; 651afc154e9SHannes Frederic Sowa *mpri = m; 652afc154e9SHannes Frederic Sowa match = rt; 653afc154e9SHannes Frederic Sowa } 654f11e6659SDavid S. Miller out: 655f11e6659SDavid S. Miller return match; 6561da177e4SLinus Torvalds } 6571da177e4SLinus Torvalds 658f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 659f11e6659SDavid S. Miller struct rt6_info *rr_head, 660afc154e9SHannes Frederic Sowa u32 metric, int oif, int strict, 661afc154e9SHannes Frederic Sowa bool *do_rr) 662f11e6659SDavid S. Miller { 663f11e6659SDavid S. Miller struct rt6_info *rt, *match; 664f11e6659SDavid S. Miller int mpri = -1; 665f11e6659SDavid S. Miller 666f11e6659SDavid S. Miller match = NULL; 667f11e6659SDavid S. Miller for (rt = rr_head; rt && rt->rt6i_metric == metric; 668d8d1f30bSChangli Gao rt = rt->dst.rt6_next) 669afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 670f11e6659SDavid S. Miller for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric; 671d8d1f30bSChangli Gao rt = rt->dst.rt6_next) 672afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 673f11e6659SDavid S. Miller 674f11e6659SDavid S. Miller return match; 675f11e6659SDavid S. Miller } 676f11e6659SDavid S. Miller 677f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 678f11e6659SDavid S. Miller { 679f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 6808ed67789SDaniel Lezcano struct net *net; 681afc154e9SHannes Frederic Sowa bool do_rr = false; 682f11e6659SDavid S. Miller 683f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 684f11e6659SDavid S. Miller if (!rt0) 685f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 686f11e6659SDavid S. Miller 687afc154e9SHannes Frederic Sowa match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict, 688afc154e9SHannes Frederic Sowa &do_rr); 689f11e6659SDavid S. Miller 690afc154e9SHannes Frederic Sowa if (do_rr) { 691d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 692f11e6659SDavid S. Miller 693554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 694f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 695f11e6659SDavid S. Miller next = fn->leaf; 696f11e6659SDavid S. Miller 697f11e6659SDavid S. Miller if (next != rt0) 698f11e6659SDavid S. Miller fn->rr_ptr = next; 699554cfb7eSYOSHIFUJI Hideaki } 700554cfb7eSYOSHIFUJI Hideaki 701d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 702a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds 70570ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 70670ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 707b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 70870ceb4f5SYOSHIFUJI Hideaki { 709c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 71070ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 71170ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 71270ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 7134bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 71470ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 71570ceb4f5SYOSHIFUJI Hideaki 71670ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 71770ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 71870ceb4f5SYOSHIFUJI Hideaki } 71970ceb4f5SYOSHIFUJI Hideaki 72070ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 72170ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 72270ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 72370ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 72470ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 72570ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 72670ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 72770ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 72870ceb4f5SYOSHIFUJI Hideaki } 72970ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 73070ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 73170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 73270ceb4f5SYOSHIFUJI Hideaki } 73370ceb4f5SYOSHIFUJI Hideaki } 73470ceb4f5SYOSHIFUJI Hideaki 73570ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 73670ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 7373933fc95SJens Rosenboom return -EINVAL; 73870ceb4f5SYOSHIFUJI Hideaki 7394bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 74070ceb4f5SYOSHIFUJI Hideaki 74170ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 74270ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 74370ceb4f5SYOSHIFUJI Hideaki else { 74470ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 74570ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 74670ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 74770ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 74870ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 74970ceb4f5SYOSHIFUJI Hideaki } 75070ceb4f5SYOSHIFUJI Hideaki 751f104a567SDuan Jiong if (rinfo->prefix_len == 0) 752f104a567SDuan Jiong rt = rt6_get_dflt_router(gwaddr, dev); 753f104a567SDuan Jiong else 754f104a567SDuan Jiong rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, 755f104a567SDuan Jiong gwaddr, dev->ifindex); 75670ceb4f5SYOSHIFUJI Hideaki 75770ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 758e0a1ad73SThomas Graf ip6_del_rt(rt); 75970ceb4f5SYOSHIFUJI Hideaki rt = NULL; 76070ceb4f5SYOSHIFUJI Hideaki } 76170ceb4f5SYOSHIFUJI Hideaki 76270ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 763efa2cea0SDaniel Lezcano rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 76470ceb4f5SYOSHIFUJI Hideaki pref); 76570ceb4f5SYOSHIFUJI Hideaki else if (rt) 76670ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 76770ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 76870ceb4f5SYOSHIFUJI Hideaki 76970ceb4f5SYOSHIFUJI Hideaki if (rt) { 7701716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 7711716a961SGao feng rt6_clean_expires(rt); 7721716a961SGao feng else 7731716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 7741716a961SGao feng 77594e187c0SAmerigo Wang ip6_rt_put(rt); 77670ceb4f5SYOSHIFUJI Hideaki } 77770ceb4f5SYOSHIFUJI Hideaki return 0; 77870ceb4f5SYOSHIFUJI Hideaki } 77970ceb4f5SYOSHIFUJI Hideaki #endif 78070ceb4f5SYOSHIFUJI Hideaki 7818ed67789SDaniel Lezcano #define BACKTRACK(__net, saddr) \ 782982f56f3SYOSHIFUJI Hideaki do { \ 7838ed67789SDaniel Lezcano if (rt == __net->ipv6.ip6_null_entry) { \ 784982f56f3SYOSHIFUJI Hideaki struct fib6_node *pn; \ 785e0eda7bbSVille Nuorvala while (1) { \ 786982f56f3SYOSHIFUJI Hideaki if (fn->fn_flags & RTN_TL_ROOT) \ 787c71099acSThomas Graf goto out; \ 788982f56f3SYOSHIFUJI Hideaki pn = fn->parent; \ 789982f56f3SYOSHIFUJI Hideaki if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \ 7908bce65b9SKim Nordlund fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \ 791982f56f3SYOSHIFUJI Hideaki else \ 792982f56f3SYOSHIFUJI Hideaki fn = pn; \ 793c71099acSThomas Graf if (fn->fn_flags & RTN_RTINFO) \ 794c71099acSThomas Graf goto restart; \ 795c71099acSThomas Graf } \ 796982f56f3SYOSHIFUJI Hideaki } \ 797982f56f3SYOSHIFUJI Hideaki } while (0) 798c71099acSThomas Graf 7998ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 8008ed67789SDaniel Lezcano struct fib6_table *table, 8014c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 8021da177e4SLinus Torvalds { 8031da177e4SLinus Torvalds struct fib6_node *fn; 8041da177e4SLinus Torvalds struct rt6_info *rt; 8051da177e4SLinus Torvalds 806c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 8074c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 808c71099acSThomas Graf restart: 809c71099acSThomas Graf rt = fn->leaf; 8104c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 81151ebd318SNicolas Dichtel if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) 81252bd4c0cSNicolas Dichtel rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags); 8134c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 814c71099acSThomas Graf out: 815d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 816c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 8171da177e4SLinus Torvalds return rt; 818c71099acSThomas Graf 819c71099acSThomas Graf } 820c71099acSThomas Graf 821ea6e574eSFlorian Westphal struct dst_entry * ip6_route_lookup(struct net *net, struct flowi6 *fl6, 822ea6e574eSFlorian Westphal int flags) 823ea6e574eSFlorian Westphal { 824ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 825ea6e574eSFlorian Westphal } 826ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 827ea6e574eSFlorian Westphal 8289acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 8299acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 830c71099acSThomas Graf { 8314c9483b2SDavid S. Miller struct flowi6 fl6 = { 8324c9483b2SDavid S. Miller .flowi6_oif = oif, 8334c9483b2SDavid S. Miller .daddr = *daddr, 834c71099acSThomas Graf }; 835c71099acSThomas Graf struct dst_entry *dst; 83677d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 837c71099acSThomas Graf 838adaa70bbSThomas Graf if (saddr) { 8394c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 840adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 841adaa70bbSThomas Graf } 842adaa70bbSThomas Graf 8434c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 844c71099acSThomas Graf if (dst->error == 0) 845c71099acSThomas Graf return (struct rt6_info *) dst; 846c71099acSThomas Graf 847c71099acSThomas Graf dst_release(dst); 848c71099acSThomas Graf 8491da177e4SLinus Torvalds return NULL; 8501da177e4SLinus Torvalds } 8511da177e4SLinus Torvalds 8527159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 8537159039aSYOSHIFUJI Hideaki 854c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 8551da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 8561da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 8571da177e4SLinus Torvalds be destroyed. 8581da177e4SLinus Torvalds */ 8591da177e4SLinus Torvalds 86086872cb5SThomas Graf static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info) 8611da177e4SLinus Torvalds { 8621da177e4SLinus Torvalds int err; 863c71099acSThomas Graf struct fib6_table *table; 8641da177e4SLinus Torvalds 865c71099acSThomas Graf table = rt->rt6i_table; 866c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 86786872cb5SThomas Graf err = fib6_add(&table->tb6_root, rt, info); 868c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 8691da177e4SLinus Torvalds 8701da177e4SLinus Torvalds return err; 8711da177e4SLinus Torvalds } 8721da177e4SLinus Torvalds 87340e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 87440e22e8fSThomas Graf { 8754d1169c1SDenis V. Lunev struct nl_info info = { 876d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 8774d1169c1SDenis V. Lunev }; 878528c4cebSDenis V. Lunev return __ip6_ins_rt(rt, &info); 87940e22e8fSThomas Graf } 88040e22e8fSThomas Graf 8811716a961SGao feng static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, 88221efcfa0SEric Dumazet const struct in6_addr *daddr, 883b71d1d42SEric Dumazet const struct in6_addr *saddr) 8841da177e4SLinus Torvalds { 8851da177e4SLinus Torvalds struct rt6_info *rt; 8861da177e4SLinus Torvalds 8871da177e4SLinus Torvalds /* 8881da177e4SLinus Torvalds * Clone the route. 8891da177e4SLinus Torvalds */ 8901da177e4SLinus Torvalds 89121efcfa0SEric Dumazet rt = ip6_rt_copy(ort, daddr); 8921da177e4SLinus Torvalds 8931da177e4SLinus Torvalds if (rt) { 894bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 89521efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 89658c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 8971da177e4SLinus Torvalds 8981da177e4SLinus Torvalds rt->rt6i_flags |= RTF_CACHE; 8991da177e4SLinus Torvalds 9001da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 9011da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 9024e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 9031da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 9041da177e4SLinus Torvalds } 9051da177e4SLinus Torvalds #endif 90695a9a5baSYOSHIFUJI Hideaki } 9071da177e4SLinus Torvalds 9081da177e4SLinus Torvalds return rt; 9091da177e4SLinus Torvalds } 91095a9a5baSYOSHIFUJI Hideaki 91121efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, 91221efcfa0SEric Dumazet const struct in6_addr *daddr) 913299d9939SYOSHIFUJI Hideaki { 91421efcfa0SEric Dumazet struct rt6_info *rt = ip6_rt_copy(ort, daddr); 91521efcfa0SEric Dumazet 916887c95ccSYOSHIFUJI Hideaki / 吉藤英明 if (rt) 917299d9939SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_CACHE; 918299d9939SYOSHIFUJI Hideaki return rt; 919299d9939SYOSHIFUJI Hideaki } 920299d9939SYOSHIFUJI Hideaki 9218ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, 9224c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 9231da177e4SLinus Torvalds { 9241da177e4SLinus Torvalds struct fib6_node *fn; 925519fbd87SYOSHIFUJI Hideaki struct rt6_info *rt, *nrt; 926c71099acSThomas Graf int strict = 0; 9271da177e4SLinus Torvalds int attempts = 3; 928519fbd87SYOSHIFUJI Hideaki int err; 92953b7997fSYOSHIFUJI Hideaki int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE; 9301da177e4SLinus Torvalds 93177d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 9321da177e4SLinus Torvalds 9331da177e4SLinus Torvalds relookup: 934c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 9351da177e4SLinus Torvalds 9368238dd06SYOSHIFUJI Hideaki restart_2: 9374c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 9381da177e4SLinus Torvalds 9391da177e4SLinus Torvalds restart: 9404acad72dSPavel Emelyanov rt = rt6_select(fn, oif, strict | reachable); 94152bd4c0cSNicolas Dichtel if (rt->rt6i_nsiblings) 94252bd4c0cSNicolas Dichtel rt = rt6_multipath_select(rt, fl6, oif, strict | reachable); 9434c9483b2SDavid S. Miller BACKTRACK(net, &fl6->saddr); 9448ed67789SDaniel Lezcano if (rt == net->ipv6.ip6_null_entry || 9458238dd06SYOSHIFUJI Hideaki rt->rt6i_flags & RTF_CACHE) 9461da177e4SLinus Torvalds goto out; 9471da177e4SLinus Torvalds 948d8d1f30bSChangli Gao dst_hold(&rt->dst); 949c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 9501da177e4SLinus Torvalds 951c440f160SYOSHIFUJI Hideaki / 吉藤英明 if (!(rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY))) 9524c9483b2SDavid S. Miller nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); 9537343ff31SDavid S. Miller else if (!(rt->dst.flags & DST_HOST)) 9544c9483b2SDavid S. Miller nrt = rt6_alloc_clone(rt, &fl6->daddr); 9557343ff31SDavid S. Miller else 9567343ff31SDavid S. Miller goto out2; 9571da177e4SLinus Torvalds 95894e187c0SAmerigo Wang ip6_rt_put(rt); 9598ed67789SDaniel Lezcano rt = nrt ? : net->ipv6.ip6_null_entry; 9601da177e4SLinus Torvalds 961d8d1f30bSChangli Gao dst_hold(&rt->dst); 962e40cf353SYOSHIFUJI Hideaki if (nrt) { 96340e22e8fSThomas Graf err = ip6_ins_rt(nrt); 964e40cf353SYOSHIFUJI Hideaki if (!err) 965e40cf353SYOSHIFUJI Hideaki goto out2; 966e40cf353SYOSHIFUJI Hideaki } 967e40cf353SYOSHIFUJI Hideaki 968e40cf353SYOSHIFUJI Hideaki if (--attempts <= 0) 9691da177e4SLinus Torvalds goto out2; 9701da177e4SLinus Torvalds 971519fbd87SYOSHIFUJI Hideaki /* 972c71099acSThomas Graf * Race condition! In the gap, when table->tb6_lock was 973519fbd87SYOSHIFUJI Hideaki * released someone could insert this route. Relookup. 9741da177e4SLinus Torvalds */ 97594e187c0SAmerigo Wang ip6_rt_put(rt); 9761da177e4SLinus Torvalds goto relookup; 977e40cf353SYOSHIFUJI Hideaki 978519fbd87SYOSHIFUJI Hideaki out: 9798238dd06SYOSHIFUJI Hideaki if (reachable) { 9808238dd06SYOSHIFUJI Hideaki reachable = 0; 9818238dd06SYOSHIFUJI Hideaki goto restart_2; 9828238dd06SYOSHIFUJI Hideaki } 983d8d1f30bSChangli Gao dst_hold(&rt->dst); 984c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 9851da177e4SLinus Torvalds out2: 986d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 987d8d1f30bSChangli Gao rt->dst.__use++; 988c71099acSThomas Graf 989c71099acSThomas Graf return rt; 990c71099acSThomas Graf } 991c71099acSThomas Graf 9928ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 9934c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 9944acad72dSPavel Emelyanov { 9954c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 9964acad72dSPavel Emelyanov } 9974acad72dSPavel Emelyanov 99872331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 99972331bc0SShmulik Ladkani struct net_device *dev, 100072331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 100172331bc0SShmulik Ladkani { 100272331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 100372331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 100472331bc0SShmulik Ladkani 100572331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 100672331bc0SShmulik Ladkani } 100772331bc0SShmulik Ladkani 1008c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 1009c71099acSThomas Graf { 1010b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 1011c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 1012adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 10134c9483b2SDavid S. Miller struct flowi6 fl6 = { 10144c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 10154c9483b2SDavid S. Miller .daddr = iph->daddr, 10164c9483b2SDavid S. Miller .saddr = iph->saddr, 10176502ca52SYOSHIFUJI Hideaki / 吉藤英明 .flowlabel = ip6_flowinfo(iph), 10184c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 10194c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 1020c71099acSThomas Graf }; 1021adaa70bbSThomas Graf 102272331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 1023c71099acSThomas Graf } 1024c71099acSThomas Graf 10258ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 10264c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 1027c71099acSThomas Graf { 10284c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 1029c71099acSThomas Graf } 1030c71099acSThomas Graf 10319c7a4f9cSFlorian Westphal struct dst_entry * ip6_route_output(struct net *net, const struct sock *sk, 10324c9483b2SDavid S. Miller struct flowi6 *fl6) 1033c71099acSThomas Graf { 1034c71099acSThomas Graf int flags = 0; 1035c71099acSThomas Graf 10361fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 10374dc27d1cSDavid McCullough 10384c9483b2SDavid S. Miller if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) 103977d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 1040c71099acSThomas Graf 10414c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 1042adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 10430c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 10440c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 1045adaa70bbSThomas Graf 10464c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 10471da177e4SLinus Torvalds } 10481da177e4SLinus Torvalds 10497159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 10501da177e4SLinus Torvalds 10512774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 105214e50e57SDavid S. Miller { 10535c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 105414e50e57SDavid S. Miller struct dst_entry *new = NULL; 105514e50e57SDavid S. Miller 1056f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 105714e50e57SDavid S. Miller if (rt) { 1058d8d1f30bSChangli Gao new = &rt->dst; 105914e50e57SDavid S. Miller 10608104891bSSteffen Klassert memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); 10618104891bSSteffen Klassert rt6_init_peer(rt, net->ipv6.peers); 10628104891bSSteffen Klassert 106314e50e57SDavid S. Miller new->__use = 1; 1064352e512cSHerbert Xu new->input = dst_discard; 1065352e512cSHerbert Xu new->output = dst_discard; 106614e50e57SDavid S. Miller 106721efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 106821efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 106921efcfa0SEric Dumazet else 1070defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 107114e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 107214e50e57SDavid S. Miller if (rt->rt6i_idev) 107314e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 107414e50e57SDavid S. Miller 10754e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 10761716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 107714e50e57SDavid S. Miller rt->rt6i_metric = 0; 107814e50e57SDavid S. Miller 107914e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 108014e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 108114e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 108214e50e57SDavid S. Miller #endif 108314e50e57SDavid S. Miller 108414e50e57SDavid S. Miller dst_free(new); 108514e50e57SDavid S. Miller } 108614e50e57SDavid S. Miller 108769ead7afSDavid S. Miller dst_release(dst_orig); 108869ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 108914e50e57SDavid S. Miller } 109014e50e57SDavid S. Miller 10911da177e4SLinus Torvalds /* 10921da177e4SLinus Torvalds * Destination cache support functions 10931da177e4SLinus Torvalds */ 10941da177e4SLinus Torvalds 10951da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 10961da177e4SLinus Torvalds { 10971da177e4SLinus Torvalds struct rt6_info *rt; 10981da177e4SLinus Torvalds 10991da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 11001da177e4SLinus Torvalds 11016f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 11026f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 11036f3118b5SNicolas Dichtel * into this function always. 11046f3118b5SNicolas Dichtel */ 1105ca4c3fc2Sfan.du if (rt->rt6i_genid != rt_genid_ipv6(dev_net(rt->dst.dev))) 11066f3118b5SNicolas Dichtel return NULL; 11076f3118b5SNicolas Dichtel 1108e3bc10bdSHannes Frederic Sowa if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) 11091da177e4SLinus Torvalds return NULL; 1110e3bc10bdSHannes Frederic Sowa 1111e3bc10bdSHannes Frederic Sowa if (rt6_check_expired(rt)) 1112e3bc10bdSHannes Frederic Sowa return NULL; 1113e3bc10bdSHannes Frederic Sowa 1114e3bc10bdSHannes Frederic Sowa return dst; 11151da177e4SLinus Torvalds } 11161da177e4SLinus Torvalds 11171da177e4SLinus Torvalds static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) 11181da177e4SLinus Torvalds { 11191da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *) dst; 11201da177e4SLinus Torvalds 11211da177e4SLinus Torvalds if (rt) { 112254c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_CACHE) { 112354c1a859SYOSHIFUJI Hideaki / 吉藤英明 if (rt6_check_expired(rt)) { 1124e0a1ad73SThomas Graf ip6_del_rt(rt); 112554c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 11261da177e4SLinus Torvalds } 112754c1a859SYOSHIFUJI Hideaki / 吉藤英明 } else { 112854c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst_release(dst); 112954c1a859SYOSHIFUJI Hideaki / 吉藤英明 dst = NULL; 113054c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 113154c1a859SYOSHIFUJI Hideaki / 吉藤英明 } 113254c1a859SYOSHIFUJI Hideaki / 吉藤英明 return dst; 11331da177e4SLinus Torvalds } 11341da177e4SLinus Torvalds 11351da177e4SLinus Torvalds static void ip6_link_failure(struct sk_buff *skb) 11361da177e4SLinus Torvalds { 11371da177e4SLinus Torvalds struct rt6_info *rt; 11381da177e4SLinus Torvalds 11393ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); 11401da177e4SLinus Torvalds 1141adf30907SEric Dumazet rt = (struct rt6_info *) skb_dst(skb); 11421da177e4SLinus Torvalds if (rt) { 11431eb4f758SHannes Frederic Sowa if (rt->rt6i_flags & RTF_CACHE) { 11441eb4f758SHannes Frederic Sowa dst_hold(&rt->dst); 11451eb4f758SHannes Frederic Sowa if (ip6_del_rt(rt)) 11461eb4f758SHannes Frederic Sowa dst_free(&rt->dst); 11471eb4f758SHannes Frederic Sowa } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) { 11481da177e4SLinus Torvalds rt->rt6i_node->fn_sernum = -1; 11491da177e4SLinus Torvalds } 11501da177e4SLinus Torvalds } 11511eb4f758SHannes Frederic Sowa } 11521da177e4SLinus Torvalds 11536700c270SDavid S. Miller static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 11546700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 11551da177e4SLinus Torvalds { 11561da177e4SLinus Torvalds struct rt6_info *rt6 = (struct rt6_info*)dst; 11571da177e4SLinus Torvalds 115881aded24SDavid S. Miller dst_confirm(dst); 11591da177e4SLinus Torvalds if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) { 116081aded24SDavid S. Miller struct net *net = dev_net(dst->dev); 116181aded24SDavid S. Miller 11621da177e4SLinus Torvalds rt6->rt6i_flags |= RTF_MODIFIED; 11631da177e4SLinus Torvalds if (mtu < IPV6_MIN_MTU) { 1164defb3519SDavid S. Miller u32 features = dst_metric(dst, RTAX_FEATURES); 11651da177e4SLinus Torvalds mtu = IPV6_MIN_MTU; 1166defb3519SDavid S. Miller features |= RTAX_FEATURE_ALLFRAG; 1167defb3519SDavid S. Miller dst_metric_set(dst, RTAX_FEATURES, features); 11681da177e4SLinus Torvalds } 1169defb3519SDavid S. Miller dst_metric_set(dst, RTAX_MTU, mtu); 117081aded24SDavid S. Miller rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires); 11711da177e4SLinus Torvalds } 11721da177e4SLinus Torvalds } 11731da177e4SLinus Torvalds 117442ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 117542ae66c8SDavid S. Miller int oif, u32 mark) 117681aded24SDavid S. Miller { 117781aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 117881aded24SDavid S. Miller struct dst_entry *dst; 117981aded24SDavid S. Miller struct flowi6 fl6; 118081aded24SDavid S. Miller 118181aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 118281aded24SDavid S. Miller fl6.flowi6_oif = oif; 118381aded24SDavid S. Miller fl6.flowi6_mark = mark; 118481aded24SDavid S. Miller fl6.daddr = iph->daddr; 118581aded24SDavid S. Miller fl6.saddr = iph->saddr; 11866502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 118781aded24SDavid S. Miller 118881aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 118981aded24SDavid S. Miller if (!dst->error) 11906700c270SDavid S. Miller ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu)); 119181aded24SDavid S. Miller dst_release(dst); 119281aded24SDavid S. Miller } 119381aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 119481aded24SDavid S. Miller 119581aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 119681aded24SDavid S. Miller { 119781aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 119881aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 119981aded24SDavid S. Miller } 120081aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 120181aded24SDavid S. Miller 1202b55b76b2SDuan Jiong /* Handle redirects */ 1203b55b76b2SDuan Jiong struct ip6rd_flowi { 1204b55b76b2SDuan Jiong struct flowi6 fl6; 1205b55b76b2SDuan Jiong struct in6_addr gateway; 1206b55b76b2SDuan Jiong }; 1207b55b76b2SDuan Jiong 1208b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net, 1209b55b76b2SDuan Jiong struct fib6_table *table, 1210b55b76b2SDuan Jiong struct flowi6 *fl6, 1211b55b76b2SDuan Jiong int flags) 1212b55b76b2SDuan Jiong { 1213b55b76b2SDuan Jiong struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 1214b55b76b2SDuan Jiong struct rt6_info *rt; 1215b55b76b2SDuan Jiong struct fib6_node *fn; 1216b55b76b2SDuan Jiong 1217b55b76b2SDuan Jiong /* Get the "current" route for this destination and 1218b55b76b2SDuan Jiong * check if the redirect has come from approriate router. 1219b55b76b2SDuan Jiong * 1220b55b76b2SDuan Jiong * RFC 4861 specifies that redirects should only be 1221b55b76b2SDuan Jiong * accepted if they come from the nexthop to the target. 1222b55b76b2SDuan Jiong * Due to the way the routes are chosen, this notion 1223b55b76b2SDuan Jiong * is a bit fuzzy and one might need to check all possible 1224b55b76b2SDuan Jiong * routes. 1225b55b76b2SDuan Jiong */ 1226b55b76b2SDuan Jiong 1227b55b76b2SDuan Jiong read_lock_bh(&table->tb6_lock); 1228b55b76b2SDuan Jiong fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1229b55b76b2SDuan Jiong restart: 1230b55b76b2SDuan Jiong for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1231b55b76b2SDuan Jiong if (rt6_check_expired(rt)) 1232b55b76b2SDuan Jiong continue; 1233b55b76b2SDuan Jiong if (rt->dst.error) 1234b55b76b2SDuan Jiong break; 1235b55b76b2SDuan Jiong if (!(rt->rt6i_flags & RTF_GATEWAY)) 1236b55b76b2SDuan Jiong continue; 1237b55b76b2SDuan Jiong if (fl6->flowi6_oif != rt->dst.dev->ifindex) 1238b55b76b2SDuan Jiong continue; 1239b55b76b2SDuan Jiong if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) 1240b55b76b2SDuan Jiong continue; 1241b55b76b2SDuan Jiong break; 1242b55b76b2SDuan Jiong } 1243b55b76b2SDuan Jiong 1244b55b76b2SDuan Jiong if (!rt) 1245b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1246b55b76b2SDuan Jiong else if (rt->dst.error) { 1247b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1248b55b76b2SDuan Jiong goto out; 1249b55b76b2SDuan Jiong } 1250b55b76b2SDuan Jiong BACKTRACK(net, &fl6->saddr); 1251b55b76b2SDuan Jiong out: 1252b55b76b2SDuan Jiong dst_hold(&rt->dst); 1253b55b76b2SDuan Jiong 1254b55b76b2SDuan Jiong read_unlock_bh(&table->tb6_lock); 1255b55b76b2SDuan Jiong 1256b55b76b2SDuan Jiong return rt; 1257b55b76b2SDuan Jiong }; 1258b55b76b2SDuan Jiong 1259b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net, 1260b55b76b2SDuan Jiong const struct flowi6 *fl6, 1261b55b76b2SDuan Jiong const struct in6_addr *gateway) 1262b55b76b2SDuan Jiong { 1263b55b76b2SDuan Jiong int flags = RT6_LOOKUP_F_HAS_SADDR; 1264b55b76b2SDuan Jiong struct ip6rd_flowi rdfl; 1265b55b76b2SDuan Jiong 1266b55b76b2SDuan Jiong rdfl.fl6 = *fl6; 1267b55b76b2SDuan Jiong rdfl.gateway = *gateway; 1268b55b76b2SDuan Jiong 1269b55b76b2SDuan Jiong return fib6_rule_lookup(net, &rdfl.fl6, 1270b55b76b2SDuan Jiong flags, __ip6_route_redirect); 1271b55b76b2SDuan Jiong } 1272b55b76b2SDuan Jiong 12733a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 12743a5ad2eeSDavid S. Miller { 12753a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 12763a5ad2eeSDavid S. Miller struct dst_entry *dst; 12773a5ad2eeSDavid S. Miller struct flowi6 fl6; 12783a5ad2eeSDavid S. Miller 12793a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 12803a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 12813a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 12823a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 12833a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 12846502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 12853a5ad2eeSDavid S. Miller 1286b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); 12876700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 12883a5ad2eeSDavid S. Miller dst_release(dst); 12893a5ad2eeSDavid S. Miller } 12903a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 12913a5ad2eeSDavid S. Miller 1292c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, 1293c92a59ecSDuan Jiong u32 mark) 1294c92a59ecSDuan Jiong { 1295c92a59ecSDuan Jiong const struct ipv6hdr *iph = ipv6_hdr(skb); 1296c92a59ecSDuan Jiong const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 1297c92a59ecSDuan Jiong struct dst_entry *dst; 1298c92a59ecSDuan Jiong struct flowi6 fl6; 1299c92a59ecSDuan Jiong 1300c92a59ecSDuan Jiong memset(&fl6, 0, sizeof(fl6)); 1301c92a59ecSDuan Jiong fl6.flowi6_oif = oif; 1302c92a59ecSDuan Jiong fl6.flowi6_mark = mark; 1303c92a59ecSDuan Jiong fl6.daddr = msg->dest; 1304c92a59ecSDuan Jiong fl6.saddr = iph->daddr; 1305c92a59ecSDuan Jiong 1306b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &iph->saddr); 1307c92a59ecSDuan Jiong rt6_do_redirect(dst, NULL, skb); 1308c92a59ecSDuan Jiong dst_release(dst); 1309c92a59ecSDuan Jiong } 1310c92a59ecSDuan Jiong 13113a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 13123a5ad2eeSDavid S. Miller { 13133a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 13143a5ad2eeSDavid S. Miller } 13153a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 13163a5ad2eeSDavid S. Miller 13170dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 13181da177e4SLinus Torvalds { 13190dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 13200dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 13210dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 13220dbaee3bSDavid S. Miller 13231da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 13241da177e4SLinus Torvalds 13255578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 13265578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 13271da177e4SLinus Torvalds 13281da177e4SLinus Torvalds /* 13291da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 13301da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 13311da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 13321da177e4SLinus Torvalds * rely only on pmtu discovery" 13331da177e4SLinus Torvalds */ 13341da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 13351da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 13361da177e4SLinus Torvalds return mtu; 13371da177e4SLinus Torvalds } 13381da177e4SLinus Torvalds 1339ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1340d33e4553SDavid S. Miller { 1341d33e4553SDavid S. Miller struct inet6_dev *idev; 1342618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 1343618f9bc7SSteffen Klassert 1344618f9bc7SSteffen Klassert if (mtu) 1345618f9bc7SSteffen Klassert return mtu; 1346618f9bc7SSteffen Klassert 1347618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1348d33e4553SDavid S. Miller 1349d33e4553SDavid S. Miller rcu_read_lock(); 1350d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1351d33e4553SDavid S. Miller if (idev) 1352d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1353d33e4553SDavid S. Miller rcu_read_unlock(); 1354d33e4553SDavid S. Miller 1355d33e4553SDavid S. Miller return mtu; 1356d33e4553SDavid S. Miller } 1357d33e4553SDavid S. Miller 13583b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 13593b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 13605d0bbeebSThomas Graf 13613b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 136287a11578SDavid S. Miller struct flowi6 *fl6) 13631da177e4SLinus Torvalds { 136487a11578SDavid S. Miller struct dst_entry *dst; 13651da177e4SLinus Torvalds struct rt6_info *rt; 13661da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1367c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 13681da177e4SLinus Torvalds 136938308473SDavid S. Miller if (unlikely(!idev)) 1370122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 13711da177e4SLinus Torvalds 13728b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, dev, 0, NULL); 137338308473SDavid S. Miller if (unlikely(!rt)) { 13741da177e4SLinus Torvalds in6_dev_put(idev); 137587a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 13761da177e4SLinus Torvalds goto out; 13771da177e4SLinus Torvalds } 13781da177e4SLinus Torvalds 13798e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 13808e2ec639SYan, Zheng rt->dst.output = ip6_output; 1381d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 1382550bab42SJulian Anastasov rt->rt6i_gateway = fl6->daddr; 138387a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 13848e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 13858e2ec639SYan, Zheng rt->rt6i_idev = idev; 138614edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 13871da177e4SLinus Torvalds 13883b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1389d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1390d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 13913b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 13921da177e4SLinus Torvalds 13935578689aSDaniel Lezcano fib6_force_start_gc(net); 13941da177e4SLinus Torvalds 139587a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 139687a11578SDavid S. Miller 13971da177e4SLinus Torvalds out: 139887a11578SDavid S. Miller return dst; 13991da177e4SLinus Torvalds } 14001da177e4SLinus Torvalds 14013d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 14021da177e4SLinus Torvalds { 1403e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 14043d0f24a7SStephen Hemminger int more = 0; 14051da177e4SLinus Torvalds 14063b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 14073b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 14085d0bbeebSThomas Graf 14091da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 14101da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 14111da177e4SLinus Torvalds *pprev = dst->next; 14121da177e4SLinus Torvalds dst_free(dst); 14131da177e4SLinus Torvalds } else { 14141da177e4SLinus Torvalds pprev = &dst->next; 14153d0f24a7SStephen Hemminger ++more; 14161da177e4SLinus Torvalds } 14171da177e4SLinus Torvalds } 14181da177e4SLinus Torvalds 14193b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 14205d0bbeebSThomas Graf 14213d0f24a7SStephen Hemminger return more; 14221da177e4SLinus Torvalds } 14231da177e4SLinus Torvalds 14241e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 14251e493d19SDavid S. Miller void *arg) 14261e493d19SDavid S. Miller { 14271e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 14281e493d19SDavid S. Miller 14291e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 14301e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 14311e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 14321e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 14331e493d19SDavid S. Miller if (func(rt, arg)) { 14341e493d19SDavid S. Miller *pprev = dst->next; 14351e493d19SDavid S. Miller dst_free(dst); 14361e493d19SDavid S. Miller } else { 14371e493d19SDavid S. Miller pprev = &dst->next; 14381e493d19SDavid S. Miller } 14391e493d19SDavid S. Miller } 14401e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 14411e493d19SDavid S. Miller } 14421e493d19SDavid S. Miller 1443569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 14441da177e4SLinus Torvalds { 144586393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 14467019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 14477019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 14487019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 14497019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 14507019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1451fc66f95cSEric Dumazet int entries; 14521da177e4SLinus Torvalds 1453fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 145449a18d86SMichal Kubeček if (time_after(rt_last_gc + rt_min_interval, jiffies) && 1455fc66f95cSEric Dumazet entries <= rt_max_size) 14561da177e4SLinus Torvalds goto out; 14571da177e4SLinus Torvalds 14586891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 14592ac3ac8fSMichal Kubeček fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, entries > rt_max_size); 1460fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1461fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 14627019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 14631da177e4SLinus Torvalds out: 14647019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1465fc66f95cSEric Dumazet return entries > rt_max_size; 14661da177e4SLinus Torvalds } 14671da177e4SLinus Torvalds 14681da177e4SLinus Torvalds /* 14691da177e4SLinus Torvalds * 14701da177e4SLinus Torvalds */ 14711da177e4SLinus Torvalds 147286872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg) 14731da177e4SLinus Torvalds { 14741da177e4SLinus Torvalds int err; 14755578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 14761da177e4SLinus Torvalds struct rt6_info *rt = NULL; 14771da177e4SLinus Torvalds struct net_device *dev = NULL; 14781da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1479c71099acSThomas Graf struct fib6_table *table; 14801da177e4SLinus Torvalds int addr_type; 14811da177e4SLinus Torvalds 148286872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 14831da177e4SLinus Torvalds return -EINVAL; 14841da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 148586872cb5SThomas Graf if (cfg->fc_src_len) 14861da177e4SLinus Torvalds return -EINVAL; 14871da177e4SLinus Torvalds #endif 148886872cb5SThomas Graf if (cfg->fc_ifindex) { 14891da177e4SLinus Torvalds err = -ENODEV; 14905578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 14911da177e4SLinus Torvalds if (!dev) 14921da177e4SLinus Torvalds goto out; 14931da177e4SLinus Torvalds idev = in6_dev_get(dev); 14941da177e4SLinus Torvalds if (!idev) 14951da177e4SLinus Torvalds goto out; 14961da177e4SLinus Torvalds } 14971da177e4SLinus Torvalds 149886872cb5SThomas Graf if (cfg->fc_metric == 0) 149986872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 15001da177e4SLinus Torvalds 1501c71099acSThomas Graf err = -ENOBUFS; 150238308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1503d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1504d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 150538308473SDavid S. Miller if (!table) { 1506f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1507d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1508d71314b4SMatti Vaittinen } 1509d71314b4SMatti Vaittinen } else { 1510d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1511d71314b4SMatti Vaittinen } 151238308473SDavid S. Miller 151338308473SDavid S. Miller if (!table) 1514c71099acSThomas Graf goto out; 1515c71099acSThomas Graf 15168b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT, table); 15171da177e4SLinus Torvalds 151838308473SDavid S. Miller if (!rt) { 15191da177e4SLinus Torvalds err = -ENOMEM; 15201da177e4SLinus Torvalds goto out; 15211da177e4SLinus Torvalds } 15221da177e4SLinus Torvalds 15231716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 15241716a961SGao feng rt6_set_expires(rt, jiffies + 15251716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 15261716a961SGao feng else 15271716a961SGao feng rt6_clean_expires(rt); 15281da177e4SLinus Torvalds 152986872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 153086872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 153186872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 153286872cb5SThomas Graf 153386872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 15341da177e4SLinus Torvalds 15351da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1536d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1537ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1538ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 15391da177e4SLinus Torvalds else 1540d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 15411da177e4SLinus Torvalds 1542d8d1f30bSChangli Gao rt->dst.output = ip6_output; 15431da177e4SLinus Torvalds 154486872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 154586872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 15461da177e4SLinus Torvalds if (rt->rt6i_dst.plen == 128) 154711d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 15481da177e4SLinus Torvalds 15498e2ec639SYan, Zheng if (!(rt->dst.flags & DST_HOST) && cfg->fc_mx) { 15508e2ec639SYan, Zheng u32 *metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 15518e2ec639SYan, Zheng if (!metrics) { 15528e2ec639SYan, Zheng err = -ENOMEM; 15538e2ec639SYan, Zheng goto out; 15548e2ec639SYan, Zheng } 15558e2ec639SYan, Zheng dst_init_metrics(&rt->dst, metrics, 0); 15568e2ec639SYan, Zheng } 15571da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 155886872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 155986872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 15601da177e4SLinus Torvalds #endif 15611da177e4SLinus Torvalds 156286872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 15631da177e4SLinus Torvalds 15641da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 15651da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 15661da177e4SLinus Torvalds */ 156786872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 156838308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 156938308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 157038308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 15711da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 15725578689aSDaniel Lezcano if (dev != net->loopback_dev) { 15731da177e4SLinus Torvalds if (dev) { 15741da177e4SLinus Torvalds dev_put(dev); 15751da177e4SLinus Torvalds in6_dev_put(idev); 15761da177e4SLinus Torvalds } 15775578689aSDaniel Lezcano dev = net->loopback_dev; 15781da177e4SLinus Torvalds dev_hold(dev); 15791da177e4SLinus Torvalds idev = in6_dev_get(dev); 15801da177e4SLinus Torvalds if (!idev) { 15811da177e4SLinus Torvalds err = -ENODEV; 15821da177e4SLinus Torvalds goto out; 15831da177e4SLinus Torvalds } 15841da177e4SLinus Torvalds } 15851da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1586ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1587ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1588ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 15897150aedeSKamala R rt->dst.output = dst_discard; 15907150aedeSKamala R rt->dst.input = dst_discard; 1591ef2c7d7bSNicolas Dichtel break; 1592ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1593ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 15947150aedeSKamala R rt->dst.output = ip6_pkt_prohibit_out; 15957150aedeSKamala R rt->dst.input = ip6_pkt_prohibit; 1596ef2c7d7bSNicolas Dichtel break; 1597b4949ab2SNicolas Dichtel case RTN_THROW: 1598ef2c7d7bSNicolas Dichtel default: 15997150aedeSKamala R rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN 16007150aedeSKamala R : -ENETUNREACH; 16017150aedeSKamala R rt->dst.output = ip6_pkt_discard_out; 16027150aedeSKamala R rt->dst.input = ip6_pkt_discard; 1603ef2c7d7bSNicolas Dichtel break; 1604ef2c7d7bSNicolas Dichtel } 16051da177e4SLinus Torvalds goto install_route; 16061da177e4SLinus Torvalds } 16071da177e4SLinus Torvalds 160886872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1609b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 16101da177e4SLinus Torvalds int gwa_type; 16111da177e4SLinus Torvalds 161286872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 16134e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 16141da177e4SLinus Torvalds gwa_type = ipv6_addr_type(gw_addr); 16151da177e4SLinus Torvalds 16161da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 16171da177e4SLinus Torvalds struct rt6_info *grt; 16181da177e4SLinus Torvalds 16191da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 16201da177e4SLinus Torvalds addresses as nexthop address. 16211da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 16221da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 16231da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 16241da177e4SLinus Torvalds some exceptions. --ANK 16251da177e4SLinus Torvalds */ 16261da177e4SLinus Torvalds err = -EINVAL; 16271da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 16281da177e4SLinus Torvalds goto out; 16291da177e4SLinus Torvalds 16305578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 16311da177e4SLinus Torvalds 16321da177e4SLinus Torvalds err = -EHOSTUNREACH; 163338308473SDavid S. Miller if (!grt) 16341da177e4SLinus Torvalds goto out; 16351da177e4SLinus Torvalds if (dev) { 1636d1918542SDavid S. Miller if (dev != grt->dst.dev) { 163794e187c0SAmerigo Wang ip6_rt_put(grt); 16381da177e4SLinus Torvalds goto out; 16391da177e4SLinus Torvalds } 16401da177e4SLinus Torvalds } else { 1641d1918542SDavid S. Miller dev = grt->dst.dev; 16421da177e4SLinus Torvalds idev = grt->rt6i_idev; 16431da177e4SLinus Torvalds dev_hold(dev); 16441da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 16451da177e4SLinus Torvalds } 16461da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 16471da177e4SLinus Torvalds err = 0; 164894e187c0SAmerigo Wang ip6_rt_put(grt); 16491da177e4SLinus Torvalds 16501da177e4SLinus Torvalds if (err) 16511da177e4SLinus Torvalds goto out; 16521da177e4SLinus Torvalds } 16531da177e4SLinus Torvalds err = -EINVAL; 165438308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 16551da177e4SLinus Torvalds goto out; 16561da177e4SLinus Torvalds } 16571da177e4SLinus Torvalds 16581da177e4SLinus Torvalds err = -ENODEV; 165938308473SDavid S. Miller if (!dev) 16601da177e4SLinus Torvalds goto out; 16611da177e4SLinus Torvalds 1662c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1663c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1664c3968a85SDaniel Walter err = -EINVAL; 1665c3968a85SDaniel Walter goto out; 1666c3968a85SDaniel Walter } 16674e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1668c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1669c3968a85SDaniel Walter } else 1670c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1671c3968a85SDaniel Walter 167286872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 16731da177e4SLinus Torvalds 16741da177e4SLinus Torvalds install_route: 167586872cb5SThomas Graf if (cfg->fc_mx) { 167686872cb5SThomas Graf struct nlattr *nla; 167786872cb5SThomas Graf int remaining; 16781da177e4SLinus Torvalds 167986872cb5SThomas Graf nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 16808f4c1f9bSThomas Graf int type = nla_type(nla); 168186872cb5SThomas Graf 168286872cb5SThomas Graf if (type) { 168386872cb5SThomas Graf if (type > RTAX_MAX) { 16841da177e4SLinus Torvalds err = -EINVAL; 16851da177e4SLinus Torvalds goto out; 16861da177e4SLinus Torvalds } 168786872cb5SThomas Graf 1688defb3519SDavid S. Miller dst_metric_set(&rt->dst, type, nla_get_u32(nla)); 16891da177e4SLinus Torvalds } 16901da177e4SLinus Torvalds } 16911da177e4SLinus Torvalds } 16921da177e4SLinus Torvalds 1693d8d1f30bSChangli Gao rt->dst.dev = dev; 16941da177e4SLinus Torvalds rt->rt6i_idev = idev; 1695c71099acSThomas Graf rt->rt6i_table = table; 169663152fc0SDaniel Lezcano 1697c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 169863152fc0SDaniel Lezcano 169986872cb5SThomas Graf return __ip6_ins_rt(rt, &cfg->fc_nlinfo); 17001da177e4SLinus Torvalds 17011da177e4SLinus Torvalds out: 17021da177e4SLinus Torvalds if (dev) 17031da177e4SLinus Torvalds dev_put(dev); 17041da177e4SLinus Torvalds if (idev) 17051da177e4SLinus Torvalds in6_dev_put(idev); 17061da177e4SLinus Torvalds if (rt) 1707d8d1f30bSChangli Gao dst_free(&rt->dst); 17081da177e4SLinus Torvalds return err; 17091da177e4SLinus Torvalds } 17101da177e4SLinus Torvalds 171186872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 17121da177e4SLinus Torvalds { 17131da177e4SLinus Torvalds int err; 1714c71099acSThomas Graf struct fib6_table *table; 1715d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 17161da177e4SLinus Torvalds 17176825a26cSGao feng if (rt == net->ipv6.ip6_null_entry) { 17186825a26cSGao feng err = -ENOENT; 17196825a26cSGao feng goto out; 17206825a26cSGao feng } 17216c813a72SPatrick McHardy 1722c71099acSThomas Graf table = rt->rt6i_table; 1723c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 172486872cb5SThomas Graf err = fib6_del(rt, info); 1725c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 17261da177e4SLinus Torvalds 17276825a26cSGao feng out: 172894e187c0SAmerigo Wang ip6_rt_put(rt); 17291da177e4SLinus Torvalds return err; 17301da177e4SLinus Torvalds } 17311da177e4SLinus Torvalds 1732e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 1733e0a1ad73SThomas Graf { 17344d1169c1SDenis V. Lunev struct nl_info info = { 1735d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 17364d1169c1SDenis V. Lunev }; 1737528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 1738e0a1ad73SThomas Graf } 1739e0a1ad73SThomas Graf 174086872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 17411da177e4SLinus Torvalds { 1742c71099acSThomas Graf struct fib6_table *table; 17431da177e4SLinus Torvalds struct fib6_node *fn; 17441da177e4SLinus Torvalds struct rt6_info *rt; 17451da177e4SLinus Torvalds int err = -ESRCH; 17461da177e4SLinus Torvalds 17475578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 174838308473SDavid S. Miller if (!table) 1749c71099acSThomas Graf return err; 17501da177e4SLinus Torvalds 1751c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1752c71099acSThomas Graf 1753c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 175486872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 175586872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 17561da177e4SLinus Torvalds 17571da177e4SLinus Torvalds if (fn) { 1758d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 175986872cb5SThomas Graf if (cfg->fc_ifindex && 1760d1918542SDavid S. Miller (!rt->dst.dev || 1761d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 17621da177e4SLinus Torvalds continue; 176386872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 176486872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 17651da177e4SLinus Torvalds continue; 176686872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 17671da177e4SLinus Torvalds continue; 1768d8d1f30bSChangli Gao dst_hold(&rt->dst); 1769c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 17701da177e4SLinus Torvalds 177186872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 17721da177e4SLinus Torvalds } 17731da177e4SLinus Torvalds } 1774c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 17751da177e4SLinus Torvalds 17761da177e4SLinus Torvalds return err; 17771da177e4SLinus Torvalds } 17781da177e4SLinus Torvalds 17796700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 1780a6279458SYOSHIFUJI Hideaki { 1781e8599ff4SDavid S. Miller struct net *net = dev_net(skb->dev); 1782a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 1783e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 1784e8599ff4SDavid S. Miller struct ndisc_options ndopts; 1785e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 1786e8599ff4SDavid S. Miller struct neighbour *neigh; 178771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 struct rd_msg *msg; 17886e157b6aSDavid S. Miller int optlen, on_link; 17896e157b6aSDavid S. Miller u8 *lladdr; 1790e8599ff4SDavid S. Miller 179129a3cad5SSimon Horman optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 179271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 optlen -= sizeof(*msg); 1793e8599ff4SDavid S. Miller 1794e8599ff4SDavid S. Miller if (optlen < 0) { 17956e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 1796e8599ff4SDavid S. Miller return; 1797e8599ff4SDavid S. Miller } 1798e8599ff4SDavid S. Miller 179971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 msg = (struct rd_msg *)icmp6_hdr(skb); 1800e8599ff4SDavid S. Miller 180171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_is_multicast(&msg->dest)) { 18026e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 1803e8599ff4SDavid S. Miller return; 1804e8599ff4SDavid S. Miller } 1805e8599ff4SDavid S. Miller 18066e157b6aSDavid S. Miller on_link = 0; 180771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_equal(&msg->dest, &msg->target)) { 1808e8599ff4SDavid S. Miller on_link = 1; 180971bcdba0SYOSHIFUJI Hideaki / 吉藤英明 } else if (ipv6_addr_type(&msg->target) != 1810e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 18116e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 1812e8599ff4SDavid S. Miller return; 1813e8599ff4SDavid S. Miller } 1814e8599ff4SDavid S. Miller 1815e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 1816e8599ff4SDavid S. Miller if (!in6_dev) 1817e8599ff4SDavid S. Miller return; 1818e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 1819e8599ff4SDavid S. Miller return; 1820e8599ff4SDavid S. Miller 1821e8599ff4SDavid S. Miller /* RFC2461 8.1: 1822e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 1823e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 1824e8599ff4SDavid S. Miller */ 1825e8599ff4SDavid S. Miller 182671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) { 1827e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 1828e8599ff4SDavid S. Miller return; 1829e8599ff4SDavid S. Miller } 18306e157b6aSDavid S. Miller 18316e157b6aSDavid S. Miller lladdr = NULL; 1832e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 1833e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 1834e8599ff4SDavid S. Miller skb->dev); 1835e8599ff4SDavid S. Miller if (!lladdr) { 1836e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 1837e8599ff4SDavid S. Miller return; 1838e8599ff4SDavid S. Miller } 1839e8599ff4SDavid S. Miller } 1840e8599ff4SDavid S. Miller 18416e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 18426e157b6aSDavid S. Miller if (rt == net->ipv6.ip6_null_entry) { 18436e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 18446e157b6aSDavid S. Miller return; 18456e157b6aSDavid S. Miller } 18466e157b6aSDavid S. Miller 18476e157b6aSDavid S. Miller /* Redirect received -> path was valid. 18486e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 18496e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 18506e157b6aSDavid S. Miller */ 18516e157b6aSDavid S. Miller dst_confirm(&rt->dst); 18526e157b6aSDavid S. Miller 185371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 1854e8599ff4SDavid S. Miller if (!neigh) 1855e8599ff4SDavid S. Miller return; 1856e8599ff4SDavid S. Miller 18571da177e4SLinus Torvalds /* 18581da177e4SLinus Torvalds * We have finally decided to accept it. 18591da177e4SLinus Torvalds */ 18601da177e4SLinus Torvalds 18611da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 18621da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 18631da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 18641da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 18651da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 18661da177e4SLinus Torvalds ); 18671da177e4SLinus Torvalds 186871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 nrt = ip6_rt_copy(rt, &msg->dest); 186938308473SDavid S. Miller if (!nrt) 18701da177e4SLinus Torvalds goto out; 18711da177e4SLinus Torvalds 18721da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 18731da177e4SLinus Torvalds if (on_link) 18741da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 18751da177e4SLinus Torvalds 18764e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 18771da177e4SLinus Torvalds 187840e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 18791da177e4SLinus Torvalds goto out; 18801da177e4SLinus Torvalds 1881d8d1f30bSChangli Gao netevent.old = &rt->dst; 1882d8d1f30bSChangli Gao netevent.new = &nrt->dst; 188371bcdba0SYOSHIFUJI Hideaki / 吉藤英明 netevent.daddr = &msg->dest; 188460592833SYOSHIFUJI Hideaki / 吉藤英明 netevent.neigh = neigh; 18858d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 18868d71740cSTom Tucker 18871da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 18886e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 1889e0a1ad73SThomas Graf ip6_del_rt(rt); 18901da177e4SLinus Torvalds } 18911da177e4SLinus Torvalds 18921da177e4SLinus Torvalds out: 1893e8599ff4SDavid S. Miller neigh_release(neigh); 18946e157b6aSDavid S. Miller } 18956e157b6aSDavid S. Miller 18961da177e4SLinus Torvalds /* 18971da177e4SLinus Torvalds * Misc support functions 18981da177e4SLinus Torvalds */ 18991da177e4SLinus Torvalds 19001716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 190121efcfa0SEric Dumazet const struct in6_addr *dest) 19021da177e4SLinus Torvalds { 1903d1918542SDavid S. Miller struct net *net = dev_net(ort->dst.dev); 19048b96d22dSDavid S. Miller struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0, 19058b96d22dSDavid S. Miller ort->rt6i_table); 19061da177e4SLinus Torvalds 19071da177e4SLinus Torvalds if (rt) { 1908d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 1909d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 19108e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 19111da177e4SLinus Torvalds 19124e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *dest; 19138e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 1914defb3519SDavid S. Miller dst_copy_metrics(&rt->dst, &ort->dst); 1915d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 19161da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 19171da177e4SLinus Torvalds if (rt->rt6i_idev) 19181da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 1919d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 19201da177e4SLinus Torvalds 1921550bab42SJulian Anastasov if (ort->rt6i_flags & RTF_GATEWAY) 19224e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 1923550bab42SJulian Anastasov else 1924550bab42SJulian Anastasov rt->rt6i_gateway = *dest; 19251716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 19261716a961SGao feng if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) == 19271716a961SGao feng (RTF_DEFAULT | RTF_ADDRCONF)) 19281716a961SGao feng rt6_set_from(rt, ort); 19291da177e4SLinus Torvalds rt->rt6i_metric = 0; 19301da177e4SLinus Torvalds 19311da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 19321da177e4SLinus Torvalds memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 19331da177e4SLinus Torvalds #endif 19340f6c6392SFlorian Westphal memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key)); 1935c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 19361da177e4SLinus Torvalds } 19371da177e4SLinus Torvalds return rt; 19381da177e4SLinus Torvalds } 19391da177e4SLinus Torvalds 194070ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 1941efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 1942b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1943b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 194470ceb4f5SYOSHIFUJI Hideaki { 194570ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 194670ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 1947c71099acSThomas Graf struct fib6_table *table; 194870ceb4f5SYOSHIFUJI Hideaki 1949efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 195038308473SDavid S. Miller if (!table) 1951c71099acSThomas Graf return NULL; 1952c71099acSThomas Graf 19535744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 1954c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0); 195570ceb4f5SYOSHIFUJI Hideaki if (!fn) 195670ceb4f5SYOSHIFUJI Hideaki goto out; 195770ceb4f5SYOSHIFUJI Hideaki 1958d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1959d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 196070ceb4f5SYOSHIFUJI Hideaki continue; 196170ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 196270ceb4f5SYOSHIFUJI Hideaki continue; 196370ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 196470ceb4f5SYOSHIFUJI Hideaki continue; 1965d8d1f30bSChangli Gao dst_hold(&rt->dst); 196670ceb4f5SYOSHIFUJI Hideaki break; 196770ceb4f5SYOSHIFUJI Hideaki } 196870ceb4f5SYOSHIFUJI Hideaki out: 19695744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 197070ceb4f5SYOSHIFUJI Hideaki return rt; 197170ceb4f5SYOSHIFUJI Hideaki } 197270ceb4f5SYOSHIFUJI Hideaki 1973efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 1974b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1975b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 197695c96174SEric Dumazet unsigned int pref) 197770ceb4f5SYOSHIFUJI Hideaki { 197886872cb5SThomas Graf struct fib6_config cfg = { 197986872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 1980238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 198186872cb5SThomas Graf .fc_ifindex = ifindex, 198286872cb5SThomas Graf .fc_dst_len = prefixlen, 198386872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 198486872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 198515e47304SEric W. Biederman .fc_nlinfo.portid = 0, 1986efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 1987efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 198886872cb5SThomas Graf }; 198970ceb4f5SYOSHIFUJI Hideaki 19904e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 19914e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 199286872cb5SThomas Graf 1993e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 1994e317da96SYOSHIFUJI Hideaki if (!prefixlen) 199586872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 199670ceb4f5SYOSHIFUJI Hideaki 199786872cb5SThomas Graf ip6_route_add(&cfg); 199870ceb4f5SYOSHIFUJI Hideaki 1999efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 200070ceb4f5SYOSHIFUJI Hideaki } 200170ceb4f5SYOSHIFUJI Hideaki #endif 200270ceb4f5SYOSHIFUJI Hideaki 2003b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 20041da177e4SLinus Torvalds { 20051da177e4SLinus Torvalds struct rt6_info *rt; 2006c71099acSThomas Graf struct fib6_table *table; 20071da177e4SLinus Torvalds 2008c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 200938308473SDavid S. Miller if (!table) 2010c71099acSThomas Graf return NULL; 20111da177e4SLinus Torvalds 20125744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2013d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) { 2014d1918542SDavid S. Miller if (dev == rt->dst.dev && 2015045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 20161da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 20171da177e4SLinus Torvalds break; 20181da177e4SLinus Torvalds } 20191da177e4SLinus Torvalds if (rt) 2020d8d1f30bSChangli Gao dst_hold(&rt->dst); 20215744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 20221da177e4SLinus Torvalds return rt; 20231da177e4SLinus Torvalds } 20241da177e4SLinus Torvalds 2025b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 2026ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 2027ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 20281da177e4SLinus Torvalds { 202986872cb5SThomas Graf struct fib6_config cfg = { 203086872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 2031238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 203286872cb5SThomas Graf .fc_ifindex = dev->ifindex, 203386872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 203486872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 203515e47304SEric W. Biederman .fc_nlinfo.portid = 0, 20365578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 2037c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 203886872cb5SThomas Graf }; 20391da177e4SLinus Torvalds 20404e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 20411da177e4SLinus Torvalds 204286872cb5SThomas Graf ip6_route_add(&cfg); 20431da177e4SLinus Torvalds 20441da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 20451da177e4SLinus Torvalds } 20461da177e4SLinus Torvalds 20477b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 20481da177e4SLinus Torvalds { 20491da177e4SLinus Torvalds struct rt6_info *rt; 2050c71099acSThomas Graf struct fib6_table *table; 2051c71099acSThomas Graf 2052c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 20537b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 205438308473SDavid S. Miller if (!table) 2055c71099acSThomas Graf return; 20561da177e4SLinus Torvalds 20571da177e4SLinus Torvalds restart: 2058c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2059d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 20603e8b0ac3SLorenzo Colitti if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 20613e8b0ac3SLorenzo Colitti (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { 2062d8d1f30bSChangli Gao dst_hold(&rt->dst); 2063c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2064e0a1ad73SThomas Graf ip6_del_rt(rt); 20651da177e4SLinus Torvalds goto restart; 20661da177e4SLinus Torvalds } 20671da177e4SLinus Torvalds } 2068c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 20691da177e4SLinus Torvalds } 20701da177e4SLinus Torvalds 20715578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 20725578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 207386872cb5SThomas Graf struct fib6_config *cfg) 207486872cb5SThomas Graf { 207586872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 207686872cb5SThomas Graf 207786872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 207886872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 207986872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 208086872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 208186872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 208286872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 208386872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 208486872cb5SThomas Graf 20855578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 2086f1243c2dSBenjamin Thery 20874e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 20884e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 20894e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 209086872cb5SThomas Graf } 209186872cb5SThomas Graf 20925578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 20931da177e4SLinus Torvalds { 209486872cb5SThomas Graf struct fib6_config cfg; 20951da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 20961da177e4SLinus Torvalds int err; 20971da177e4SLinus Torvalds 20981da177e4SLinus Torvalds switch(cmd) { 20991da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 21001da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 2101af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 21021da177e4SLinus Torvalds return -EPERM; 21031da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 21041da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 21051da177e4SLinus Torvalds if (err) 21061da177e4SLinus Torvalds return -EFAULT; 21071da177e4SLinus Torvalds 21085578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 210986872cb5SThomas Graf 21101da177e4SLinus Torvalds rtnl_lock(); 21111da177e4SLinus Torvalds switch (cmd) { 21121da177e4SLinus Torvalds case SIOCADDRT: 211386872cb5SThomas Graf err = ip6_route_add(&cfg); 21141da177e4SLinus Torvalds break; 21151da177e4SLinus Torvalds case SIOCDELRT: 211686872cb5SThomas Graf err = ip6_route_del(&cfg); 21171da177e4SLinus Torvalds break; 21181da177e4SLinus Torvalds default: 21191da177e4SLinus Torvalds err = -EINVAL; 21201da177e4SLinus Torvalds } 21211da177e4SLinus Torvalds rtnl_unlock(); 21221da177e4SLinus Torvalds 21231da177e4SLinus Torvalds return err; 21243ff50b79SStephen Hemminger } 21251da177e4SLinus Torvalds 21261da177e4SLinus Torvalds return -EINVAL; 21271da177e4SLinus Torvalds } 21281da177e4SLinus Torvalds 21291da177e4SLinus Torvalds /* 21301da177e4SLinus Torvalds * Drop the packet on the floor 21311da177e4SLinus Torvalds */ 21321da177e4SLinus Torvalds 2133d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 21341da177e4SLinus Torvalds { 2135612f09e8SYOSHIFUJI Hideaki int type; 2136adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2137612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2138612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 21390660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 214045bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 21413bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 21423bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2143612f09e8SYOSHIFUJI Hideaki break; 2144612f09e8SYOSHIFUJI Hideaki } 2145612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2146612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 21473bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 21483bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2149612f09e8SYOSHIFUJI Hideaki break; 2150612f09e8SYOSHIFUJI Hideaki } 21513ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 21521da177e4SLinus Torvalds kfree_skb(skb); 21531da177e4SLinus Torvalds return 0; 21541da177e4SLinus Torvalds } 21551da177e4SLinus Torvalds 21569ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 21579ce8ade0SThomas Graf { 2158612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 21599ce8ade0SThomas Graf } 21609ce8ade0SThomas Graf 216120380731SArnaldo Carvalho de Melo static int ip6_pkt_discard_out(struct sk_buff *skb) 21621da177e4SLinus Torvalds { 2163adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2164612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 21651da177e4SLinus Torvalds } 21661da177e4SLinus Torvalds 21679ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 21689ce8ade0SThomas Graf { 2169612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 21709ce8ade0SThomas Graf } 21719ce8ade0SThomas Graf 21729ce8ade0SThomas Graf static int ip6_pkt_prohibit_out(struct sk_buff *skb) 21739ce8ade0SThomas Graf { 2174adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2175612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 21769ce8ade0SThomas Graf } 21779ce8ade0SThomas Graf 21781da177e4SLinus Torvalds /* 21791da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 21801da177e4SLinus Torvalds */ 21811da177e4SLinus Torvalds 21821da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 21831da177e4SLinus Torvalds const struct in6_addr *addr, 21848f031519SDavid S. Miller bool anycast) 21851da177e4SLinus Torvalds { 2186c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 2187a3300ef4SHannes Frederic Sowa struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 2188a3300ef4SHannes Frederic Sowa DST_NOCOUNT, NULL); 2189a3300ef4SHannes Frederic Sowa if (!rt) 21901da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 21911da177e4SLinus Torvalds 21921da177e4SLinus Torvalds in6_dev_hold(idev); 21931da177e4SLinus Torvalds 219411d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2195d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2196d8d1f30bSChangli Gao rt->dst.output = ip6_output; 21971da177e4SLinus Torvalds rt->rt6i_idev = idev; 21981da177e4SLinus Torvalds 21991da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 220058c4fb86SYOSHIFUJI Hideaki if (anycast) 220158c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 220258c4fb86SYOSHIFUJI Hideaki else 22031da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 22041da177e4SLinus Torvalds 2205550bab42SJulian Anastasov rt->rt6i_gateway = *addr; 22064e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 22071da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 22085578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 22091da177e4SLinus Torvalds 2210d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 22111da177e4SLinus Torvalds 22121da177e4SLinus Torvalds return rt; 22131da177e4SLinus Torvalds } 22141da177e4SLinus Torvalds 2215c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2216c3968a85SDaniel Walter struct rt6_info *rt, 2217b71d1d42SEric Dumazet const struct in6_addr *daddr, 2218c3968a85SDaniel Walter unsigned int prefs, 2219c3968a85SDaniel Walter struct in6_addr *saddr) 2220c3968a85SDaniel Walter { 2221c3968a85SDaniel Walter struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt); 2222c3968a85SDaniel Walter int err = 0; 2223c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) 22244e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2225c3968a85SDaniel Walter else 2226c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2227c3968a85SDaniel Walter daddr, prefs, saddr); 2228c3968a85SDaniel Walter return err; 2229c3968a85SDaniel Walter } 2230c3968a85SDaniel Walter 2231c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2232c3968a85SDaniel Walter struct arg_dev_net_ip { 2233c3968a85SDaniel Walter struct net_device *dev; 2234c3968a85SDaniel Walter struct net *net; 2235c3968a85SDaniel Walter struct in6_addr *addr; 2236c3968a85SDaniel Walter }; 2237c3968a85SDaniel Walter 2238c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2239c3968a85SDaniel Walter { 2240c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2241c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2242c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2243c3968a85SDaniel Walter 2244d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2245c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2246c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2247c3968a85SDaniel Walter /* remove prefsrc entry */ 2248c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2249c3968a85SDaniel Walter } 2250c3968a85SDaniel Walter return 0; 2251c3968a85SDaniel Walter } 2252c3968a85SDaniel Walter 2253c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2254c3968a85SDaniel Walter { 2255c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2256c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2257c3968a85SDaniel Walter .dev = ifp->idev->dev, 2258c3968a85SDaniel Walter .net = net, 2259c3968a85SDaniel Walter .addr = &ifp->addr, 2260c3968a85SDaniel Walter }; 2261*0c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 2262c3968a85SDaniel Walter } 2263c3968a85SDaniel Walter 22648ed67789SDaniel Lezcano struct arg_dev_net { 22658ed67789SDaniel Lezcano struct net_device *dev; 22668ed67789SDaniel Lezcano struct net *net; 22678ed67789SDaniel Lezcano }; 22688ed67789SDaniel Lezcano 22691da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 22701da177e4SLinus Torvalds { 2271bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2272bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 22738ed67789SDaniel Lezcano 2274d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2275c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 22761da177e4SLinus Torvalds return -1; 2277c159d30cSDavid S. Miller 22781da177e4SLinus Torvalds return 0; 22791da177e4SLinus Torvalds } 22801da177e4SLinus Torvalds 2281f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 22821da177e4SLinus Torvalds { 22838ed67789SDaniel Lezcano struct arg_dev_net adn = { 22848ed67789SDaniel Lezcano .dev = dev, 22858ed67789SDaniel Lezcano .net = net, 22868ed67789SDaniel Lezcano }; 22878ed67789SDaniel Lezcano 2288*0c3584d5SLi RongQing fib6_clean_all(net, fib6_ifdown, &adn); 22891e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 22901da177e4SLinus Torvalds } 22911da177e4SLinus Torvalds 229295c96174SEric Dumazet struct rt6_mtu_change_arg { 22931da177e4SLinus Torvalds struct net_device *dev; 229495c96174SEric Dumazet unsigned int mtu; 22951da177e4SLinus Torvalds }; 22961da177e4SLinus Torvalds 22971da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 22981da177e4SLinus Torvalds { 22991da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 23001da177e4SLinus Torvalds struct inet6_dev *idev; 23011da177e4SLinus Torvalds 23021da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 23031da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 23041da177e4SLinus Torvalds We still use this lock to block changes 23051da177e4SLinus Torvalds caused by addrconf/ndisc. 23061da177e4SLinus Torvalds */ 23071da177e4SLinus Torvalds 23081da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 230938308473SDavid S. Miller if (!idev) 23101da177e4SLinus Torvalds return 0; 23111da177e4SLinus Torvalds 23121da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 23131da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 23141da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 23151da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 23161da177e4SLinus Torvalds */ 23171da177e4SLinus Torvalds /* 23181da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 23191da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 23201da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 23211da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 23221da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 23231da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 23241da177e4SLinus Torvalds PMTU discouvery. 23251da177e4SLinus Torvalds */ 2326d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 2327d8d1f30bSChangli Gao !dst_metric_locked(&rt->dst, RTAX_MTU) && 2328d8d1f30bSChangli Gao (dst_mtu(&rt->dst) >= arg->mtu || 2329d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 2330d8d1f30bSChangli Gao dst_mtu(&rt->dst) == idev->cnf.mtu6))) { 2331defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2332566cfd8fSSimon Arlott } 23331da177e4SLinus Torvalds return 0; 23341da177e4SLinus Torvalds } 23351da177e4SLinus Torvalds 233695c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 23371da177e4SLinus Torvalds { 2338c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2339c71099acSThomas Graf .dev = dev, 2340c71099acSThomas Graf .mtu = mtu, 2341c71099acSThomas Graf }; 23421da177e4SLinus Torvalds 2343*0c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 23441da177e4SLinus Torvalds } 23451da177e4SLinus Torvalds 2346ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 23475176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 234886872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2349ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 235086872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 235186872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 235251ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 235386872cb5SThomas Graf }; 235486872cb5SThomas Graf 235586872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 235686872cb5SThomas Graf struct fib6_config *cfg) 23571da177e4SLinus Torvalds { 235886872cb5SThomas Graf struct rtmsg *rtm; 235986872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 236086872cb5SThomas Graf int err; 23611da177e4SLinus Torvalds 236286872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 236386872cb5SThomas Graf if (err < 0) 236486872cb5SThomas Graf goto errout; 23651da177e4SLinus Torvalds 236686872cb5SThomas Graf err = -EINVAL; 236786872cb5SThomas Graf rtm = nlmsg_data(nlh); 236886872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 236986872cb5SThomas Graf 237086872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 237186872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 237286872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 237386872cb5SThomas Graf cfg->fc_flags = RTF_UP; 237486872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2375ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 237686872cb5SThomas Graf 2377ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2378ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2379b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2380b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 238186872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 238286872cb5SThomas Graf 2383ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2384ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2385ab79ad14SMaciej Żenczykowski 238615e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 238786872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 23883b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 238986872cb5SThomas Graf 239086872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 239186872cb5SThomas Graf nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16); 239286872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 23931da177e4SLinus Torvalds } 239486872cb5SThomas Graf 239586872cb5SThomas Graf if (tb[RTA_DST]) { 239686872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 239786872cb5SThomas Graf 239886872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 239986872cb5SThomas Graf goto errout; 240086872cb5SThomas Graf 240186872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 24021da177e4SLinus Torvalds } 240386872cb5SThomas Graf 240486872cb5SThomas Graf if (tb[RTA_SRC]) { 240586872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 240686872cb5SThomas Graf 240786872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 240886872cb5SThomas Graf goto errout; 240986872cb5SThomas Graf 241086872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 24111da177e4SLinus Torvalds } 241286872cb5SThomas Graf 2413c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 2414c3968a85SDaniel Walter nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16); 2415c3968a85SDaniel Walter 241686872cb5SThomas Graf if (tb[RTA_OIF]) 241786872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 241886872cb5SThomas Graf 241986872cb5SThomas Graf if (tb[RTA_PRIORITY]) 242086872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 242186872cb5SThomas Graf 242286872cb5SThomas Graf if (tb[RTA_METRICS]) { 242386872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 242486872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 24251da177e4SLinus Torvalds } 242686872cb5SThomas Graf 242786872cb5SThomas Graf if (tb[RTA_TABLE]) 242886872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 242986872cb5SThomas Graf 243051ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 243151ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 243251ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 243351ebd318SNicolas Dichtel } 243451ebd318SNicolas Dichtel 243586872cb5SThomas Graf err = 0; 243686872cb5SThomas Graf errout: 243786872cb5SThomas Graf return err; 24381da177e4SLinus Torvalds } 24391da177e4SLinus Torvalds 244051ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add) 244151ebd318SNicolas Dichtel { 244251ebd318SNicolas Dichtel struct fib6_config r_cfg; 244351ebd318SNicolas Dichtel struct rtnexthop *rtnh; 244451ebd318SNicolas Dichtel int remaining; 244551ebd318SNicolas Dichtel int attrlen; 244651ebd318SNicolas Dichtel int err = 0, last_err = 0; 244751ebd318SNicolas Dichtel 244851ebd318SNicolas Dichtel beginning: 244951ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 245051ebd318SNicolas Dichtel remaining = cfg->fc_mp_len; 245151ebd318SNicolas Dichtel 245251ebd318SNicolas Dichtel /* Parse a Multipath Entry */ 245351ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 245451ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 245551ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 245651ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 245751ebd318SNicolas Dichtel 245851ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 245951ebd318SNicolas Dichtel if (attrlen > 0) { 246051ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 246151ebd318SNicolas Dichtel 246251ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 246351ebd318SNicolas Dichtel if (nla) { 246451ebd318SNicolas Dichtel nla_memcpy(&r_cfg.fc_gateway, nla, 16); 246551ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 246651ebd318SNicolas Dichtel } 246751ebd318SNicolas Dichtel } 246851ebd318SNicolas Dichtel err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg); 246951ebd318SNicolas Dichtel if (err) { 247051ebd318SNicolas Dichtel last_err = err; 247151ebd318SNicolas Dichtel /* If we are trying to remove a route, do not stop the 247251ebd318SNicolas Dichtel * loop when ip6_route_del() fails (because next hop is 247351ebd318SNicolas Dichtel * already gone), we should try to remove all next hops. 247451ebd318SNicolas Dichtel */ 247551ebd318SNicolas Dichtel if (add) { 247651ebd318SNicolas Dichtel /* If add fails, we should try to delete all 247751ebd318SNicolas Dichtel * next hops that have been already added. 247851ebd318SNicolas Dichtel */ 247951ebd318SNicolas Dichtel add = 0; 248051ebd318SNicolas Dichtel goto beginning; 248151ebd318SNicolas Dichtel } 248251ebd318SNicolas Dichtel } 24831a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 24841a72418bSNicolas Dichtel * this flag after the first nexthop (if there is a collision, 24851a72418bSNicolas Dichtel * we have already fail to add the first nexthop: 24861a72418bSNicolas Dichtel * fib6_add_rt2node() has reject it). 24871a72418bSNicolas Dichtel */ 24881a72418bSNicolas Dichtel cfg->fc_nlinfo.nlh->nlmsg_flags &= ~NLM_F_EXCL; 248951ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 249051ebd318SNicolas Dichtel } 249151ebd318SNicolas Dichtel 249251ebd318SNicolas Dichtel return last_err; 249351ebd318SNicolas Dichtel } 249451ebd318SNicolas Dichtel 2495661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh) 24961da177e4SLinus Torvalds { 249786872cb5SThomas Graf struct fib6_config cfg; 249886872cb5SThomas Graf int err; 24991da177e4SLinus Torvalds 250086872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 250186872cb5SThomas Graf if (err < 0) 250286872cb5SThomas Graf return err; 250386872cb5SThomas Graf 250451ebd318SNicolas Dichtel if (cfg.fc_mp) 250551ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 0); 250651ebd318SNicolas Dichtel else 250786872cb5SThomas Graf return ip6_route_del(&cfg); 25081da177e4SLinus Torvalds } 25091da177e4SLinus Torvalds 2510661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh) 25111da177e4SLinus Torvalds { 251286872cb5SThomas Graf struct fib6_config cfg; 251386872cb5SThomas Graf int err; 25141da177e4SLinus Torvalds 251586872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 251686872cb5SThomas Graf if (err < 0) 251786872cb5SThomas Graf return err; 251886872cb5SThomas Graf 251951ebd318SNicolas Dichtel if (cfg.fc_mp) 252051ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 1); 252151ebd318SNicolas Dichtel else 252286872cb5SThomas Graf return ip6_route_add(&cfg); 25231da177e4SLinus Torvalds } 25241da177e4SLinus Torvalds 2525339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void) 2526339bf98fSThomas Graf { 2527339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 2528339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 2529339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 2530339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 2531339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 2532339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 2533339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 2534339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 2535339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 25366a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 2537339bf98fSThomas Graf + nla_total_size(sizeof(struct rta_cacheinfo)); 2538339bf98fSThomas Graf } 2539339bf98fSThomas Graf 2540191cd582SBrian Haley static int rt6_fill_node(struct net *net, 2541191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 25420d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 254315e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 25447bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 25451da177e4SLinus Torvalds { 25461da177e4SLinus Torvalds struct rtmsg *rtm; 25471da177e4SLinus Torvalds struct nlmsghdr *nlh; 2548e3703b3dSThomas Graf long expires; 25499e762a4aSPatrick McHardy u32 table; 25501da177e4SLinus Torvalds 25511da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 25521da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 25531da177e4SLinus Torvalds /* success since this is not a prefix route */ 25541da177e4SLinus Torvalds return 1; 25551da177e4SLinus Torvalds } 25561da177e4SLinus Torvalds } 25571da177e4SLinus Torvalds 255815e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 255938308473SDavid S. Miller if (!nlh) 256026932566SPatrick McHardy return -EMSGSIZE; 25612d7202bfSThomas Graf 25622d7202bfSThomas Graf rtm = nlmsg_data(nlh); 25631da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 25641da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 25651da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 25661da177e4SLinus Torvalds rtm->rtm_tos = 0; 2567c71099acSThomas Graf if (rt->rt6i_table) 25689e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 2569c71099acSThomas Graf else 25709e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 25719e762a4aSPatrick McHardy rtm->rtm_table = table; 2572c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 2573c78679e8SDavid S. Miller goto nla_put_failure; 2574ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 2575ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 2576ef2c7d7bSNicolas Dichtel case -EINVAL: 2577ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 2578ef2c7d7bSNicolas Dichtel break; 2579ef2c7d7bSNicolas Dichtel case -EACCES: 2580ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 2581ef2c7d7bSNicolas Dichtel break; 2582b4949ab2SNicolas Dichtel case -EAGAIN: 2583b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 2584b4949ab2SNicolas Dichtel break; 2585ef2c7d7bSNicolas Dichtel default: 25861da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 2587ef2c7d7bSNicolas Dichtel break; 2588ef2c7d7bSNicolas Dichtel } 2589ef2c7d7bSNicolas Dichtel } 2590ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 2591ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 2592d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 25931da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 25941da177e4SLinus Torvalds else 25951da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 25961da177e4SLinus Torvalds rtm->rtm_flags = 0; 25971da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 25981da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 25991da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 26001da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 2601f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 2602f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 26031da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 2604f0396f60SDenis Ovsienko else 2605f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 2606f0396f60SDenis Ovsienko } 26071da177e4SLinus Torvalds 26081da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 26091da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 26101da177e4SLinus Torvalds 26111da177e4SLinus Torvalds if (dst) { 2612c78679e8SDavid S. Miller if (nla_put(skb, RTA_DST, 16, dst)) 2613c78679e8SDavid S. Miller goto nla_put_failure; 26141da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 26151da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 2616c78679e8SDavid S. Miller if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr)) 2617c78679e8SDavid S. Miller goto nla_put_failure; 26181da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 26191da177e4SLinus Torvalds if (src) { 2620c78679e8SDavid S. Miller if (nla_put(skb, RTA_SRC, 16, src)) 2621c78679e8SDavid S. Miller goto nla_put_failure; 26221da177e4SLinus Torvalds rtm->rtm_src_len = 128; 2623c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 2624c78679e8SDavid S. Miller nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr)) 2625c78679e8SDavid S. Miller goto nla_put_failure; 26261da177e4SLinus Torvalds #endif 26277bc570c8SYOSHIFUJI Hideaki if (iif) { 26287bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 26297bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 26308229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 26317bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 26327bc570c8SYOSHIFUJI Hideaki if (!nowait) { 26337bc570c8SYOSHIFUJI Hideaki if (err == 0) 26347bc570c8SYOSHIFUJI Hideaki return 0; 26357bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 26367bc570c8SYOSHIFUJI Hideaki } else { 26377bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 26387bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 26397bc570c8SYOSHIFUJI Hideaki } 26407bc570c8SYOSHIFUJI Hideaki } 26417bc570c8SYOSHIFUJI Hideaki } else 26427bc570c8SYOSHIFUJI Hideaki #endif 2643c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 2644c78679e8SDavid S. Miller goto nla_put_failure; 26457bc570c8SYOSHIFUJI Hideaki } else if (dst) { 26461da177e4SLinus Torvalds struct in6_addr saddr_buf; 2647c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 2648c78679e8SDavid S. Miller nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) 2649c78679e8SDavid S. Miller goto nla_put_failure; 2650c3968a85SDaniel Walter } 2651c3968a85SDaniel Walter 2652c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 2653c3968a85SDaniel Walter struct in6_addr saddr_buf; 26544e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 2655c78679e8SDavid S. Miller if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) 2656c78679e8SDavid S. Miller goto nla_put_failure; 26571da177e4SLinus Torvalds } 26582d7202bfSThomas Graf 2659defb3519SDavid S. Miller if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) 26602d7202bfSThomas Graf goto nla_put_failure; 26612d7202bfSThomas Graf 2662dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_GATEWAY) { 2663dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 if (nla_put(skb, RTA_GATEWAY, 16, &rt->rt6i_gateway) < 0) 266494f826b8SEric Dumazet goto nla_put_failure; 266594f826b8SEric Dumazet } 26662d7202bfSThomas Graf 2667c78679e8SDavid S. Miller if (rt->dst.dev && 2668c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 2669c78679e8SDavid S. Miller goto nla_put_failure; 2670c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 2671c78679e8SDavid S. Miller goto nla_put_failure; 26728253947eSLi Wei 26738253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 267469cdf8f9SYOSHIFUJI Hideaki 267587a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 2676e3703b3dSThomas Graf goto nla_put_failure; 26771da177e4SLinus Torvalds 26782d7202bfSThomas Graf return nlmsg_end(skb, nlh); 26792d7202bfSThomas Graf 26802d7202bfSThomas Graf nla_put_failure: 268126932566SPatrick McHardy nlmsg_cancel(skb, nlh); 268226932566SPatrick McHardy return -EMSGSIZE; 26831da177e4SLinus Torvalds } 26841da177e4SLinus Torvalds 26851b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 26861da177e4SLinus Torvalds { 26871da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 26881da177e4SLinus Torvalds int prefix; 26891da177e4SLinus Torvalds 26902d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 26912d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 26921da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 26931da177e4SLinus Torvalds } else 26941da177e4SLinus Torvalds prefix = 0; 26951da177e4SLinus Torvalds 2696191cd582SBrian Haley return rt6_fill_node(arg->net, 2697191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 269815e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 26997bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 27001da177e4SLinus Torvalds } 27011da177e4SLinus Torvalds 2702661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh) 27031da177e4SLinus Torvalds { 27043b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 2705ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 27061da177e4SLinus Torvalds struct rt6_info *rt; 2707ab364a6fSThomas Graf struct sk_buff *skb; 2708ab364a6fSThomas Graf struct rtmsg *rtm; 27094c9483b2SDavid S. Miller struct flowi6 fl6; 271072331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 2711ab364a6fSThomas Graf 2712ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 2713ab364a6fSThomas Graf if (err < 0) 2714ab364a6fSThomas Graf goto errout; 2715ab364a6fSThomas Graf 2716ab364a6fSThomas Graf err = -EINVAL; 27174c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2718ab364a6fSThomas Graf 2719ab364a6fSThomas Graf if (tb[RTA_SRC]) { 2720ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 2721ab364a6fSThomas Graf goto errout; 2722ab364a6fSThomas Graf 27234e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 2724ab364a6fSThomas Graf } 2725ab364a6fSThomas Graf 2726ab364a6fSThomas Graf if (tb[RTA_DST]) { 2727ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 2728ab364a6fSThomas Graf goto errout; 2729ab364a6fSThomas Graf 27304e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 2731ab364a6fSThomas Graf } 2732ab364a6fSThomas Graf 2733ab364a6fSThomas Graf if (tb[RTA_IIF]) 2734ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 2735ab364a6fSThomas Graf 2736ab364a6fSThomas Graf if (tb[RTA_OIF]) 273772331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 2738ab364a6fSThomas Graf 2739ab364a6fSThomas Graf if (iif) { 2740ab364a6fSThomas Graf struct net_device *dev; 274172331bc0SShmulik Ladkani int flags = 0; 274272331bc0SShmulik Ladkani 27435578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 2744ab364a6fSThomas Graf if (!dev) { 2745ab364a6fSThomas Graf err = -ENODEV; 2746ab364a6fSThomas Graf goto errout; 2747ab364a6fSThomas Graf } 274872331bc0SShmulik Ladkani 274972331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 275072331bc0SShmulik Ladkani 275172331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 275272331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 275372331bc0SShmulik Ladkani 275472331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 275572331bc0SShmulik Ladkani flags); 275672331bc0SShmulik Ladkani } else { 275772331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 275872331bc0SShmulik Ladkani 275972331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 2760ab364a6fSThomas Graf } 27611da177e4SLinus Torvalds 27621da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 276338308473SDavid S. Miller if (!skb) { 276494e187c0SAmerigo Wang ip6_rt_put(rt); 2765ab364a6fSThomas Graf err = -ENOBUFS; 2766ab364a6fSThomas Graf goto errout; 2767ab364a6fSThomas Graf } 27681da177e4SLinus Torvalds 27691da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 27701da177e4SLinus Torvalds through good chunk of routing engine. 27711da177e4SLinus Torvalds */ 2772459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 27731da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 27741da177e4SLinus Torvalds 2775d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 27761da177e4SLinus Torvalds 27774c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 277815e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 27797bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 27801da177e4SLinus Torvalds if (err < 0) { 2781ab364a6fSThomas Graf kfree_skb(skb); 2782ab364a6fSThomas Graf goto errout; 27831da177e4SLinus Torvalds } 27841da177e4SLinus Torvalds 278515e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 2786ab364a6fSThomas Graf errout: 27871da177e4SLinus Torvalds return err; 27881da177e4SLinus Torvalds } 27891da177e4SLinus Torvalds 279086872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) 27911da177e4SLinus Torvalds { 27921da177e4SLinus Torvalds struct sk_buff *skb; 27935578689aSDaniel Lezcano struct net *net = info->nl_net; 2794528c4cebSDenis V. Lunev u32 seq; 2795528c4cebSDenis V. Lunev int err; 27960d51aa80SJamal Hadi Salim 2797528c4cebSDenis V. Lunev err = -ENOBUFS; 279838308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 279986872cb5SThomas Graf 2800339bf98fSThomas Graf skb = nlmsg_new(rt6_nlmsg_size(), gfp_any()); 280138308473SDavid S. Miller if (!skb) 280221713ebcSThomas Graf goto errout; 28031da177e4SLinus Torvalds 2804191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 280515e47304SEric W. Biederman event, info->portid, seq, 0, 0, 0); 280626932566SPatrick McHardy if (err < 0) { 280726932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 280826932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 280926932566SPatrick McHardy kfree_skb(skb); 281026932566SPatrick McHardy goto errout; 281126932566SPatrick McHardy } 281215e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 28135578689aSDaniel Lezcano info->nlh, gfp_any()); 28141ce85fe4SPablo Neira Ayuso return; 281521713ebcSThomas Graf errout: 281621713ebcSThomas Graf if (err < 0) 28175578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 28181da177e4SLinus Torvalds } 28191da177e4SLinus Torvalds 28208ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 2821351638e7SJiri Pirko unsigned long event, void *ptr) 28228ed67789SDaniel Lezcano { 2823351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 2824c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 28258ed67789SDaniel Lezcano 28268ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 2827d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 28288ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 28298ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 2830d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 28318ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 2832d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 28338ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 28348ed67789SDaniel Lezcano #endif 28358ed67789SDaniel Lezcano } 28368ed67789SDaniel Lezcano 28378ed67789SDaniel Lezcano return NOTIFY_OK; 28388ed67789SDaniel Lezcano } 28398ed67789SDaniel Lezcano 28401da177e4SLinus Torvalds /* 28411da177e4SLinus Torvalds * /proc 28421da177e4SLinus Torvalds */ 28431da177e4SLinus Torvalds 28441da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 28451da177e4SLinus Torvalds 284633120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 284733120b30SAlexey Dobriyan .owner = THIS_MODULE, 284833120b30SAlexey Dobriyan .open = ipv6_route_open, 284933120b30SAlexey Dobriyan .read = seq_read, 285033120b30SAlexey Dobriyan .llseek = seq_lseek, 28518d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 285233120b30SAlexey Dobriyan }; 285333120b30SAlexey Dobriyan 28541da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 28551da177e4SLinus Torvalds { 285669ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 28571da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 285869ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 285969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 286069ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 286169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 286269ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 2863fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 286469ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 28651da177e4SLinus Torvalds 28661da177e4SLinus Torvalds return 0; 28671da177e4SLinus Torvalds } 28681da177e4SLinus Torvalds 28691da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 28701da177e4SLinus Torvalds { 2871de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 287269ddb805SDaniel Lezcano } 287369ddb805SDaniel Lezcano 28749a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 28751da177e4SLinus Torvalds .owner = THIS_MODULE, 28761da177e4SLinus Torvalds .open = rt6_stats_seq_open, 28771da177e4SLinus Torvalds .read = seq_read, 28781da177e4SLinus Torvalds .llseek = seq_lseek, 2879b6fcbdb4SPavel Emelyanov .release = single_release_net, 28801da177e4SLinus Torvalds }; 28811da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 28821da177e4SLinus Torvalds 28831da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 28841da177e4SLinus Torvalds 28851da177e4SLinus Torvalds static 2886fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 28871da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 28881da177e4SLinus Torvalds { 2889c486da34SLucian Adrian Grijincu struct net *net; 2890c486da34SLucian Adrian Grijincu int delay; 2891c486da34SLucian Adrian Grijincu if (!write) 2892c486da34SLucian Adrian Grijincu return -EINVAL; 2893c486da34SLucian Adrian Grijincu 2894c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 2895c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 28968d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 28972ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 28981da177e4SLinus Torvalds return 0; 28991da177e4SLinus Torvalds } 29001da177e4SLinus Torvalds 2901fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 29021da177e4SLinus Torvalds { 29031da177e4SLinus Torvalds .procname = "flush", 29044990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 29051da177e4SLinus Torvalds .maxlen = sizeof(int), 290689c8b3a1SDave Jones .mode = 0200, 29076d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 29081da177e4SLinus Torvalds }, 29091da177e4SLinus Torvalds { 29101da177e4SLinus Torvalds .procname = "gc_thresh", 29119a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 29121da177e4SLinus Torvalds .maxlen = sizeof(int), 29131da177e4SLinus Torvalds .mode = 0644, 29146d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 29151da177e4SLinus Torvalds }, 29161da177e4SLinus Torvalds { 29171da177e4SLinus Torvalds .procname = "max_size", 29184990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 29191da177e4SLinus Torvalds .maxlen = sizeof(int), 29201da177e4SLinus Torvalds .mode = 0644, 29216d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 29221da177e4SLinus Torvalds }, 29231da177e4SLinus Torvalds { 29241da177e4SLinus Torvalds .procname = "gc_min_interval", 29254990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 29261da177e4SLinus Torvalds .maxlen = sizeof(int), 29271da177e4SLinus Torvalds .mode = 0644, 29286d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 29291da177e4SLinus Torvalds }, 29301da177e4SLinus Torvalds { 29311da177e4SLinus Torvalds .procname = "gc_timeout", 29324990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 29331da177e4SLinus Torvalds .maxlen = sizeof(int), 29341da177e4SLinus Torvalds .mode = 0644, 29356d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 29361da177e4SLinus Torvalds }, 29371da177e4SLinus Torvalds { 29381da177e4SLinus Torvalds .procname = "gc_interval", 29394990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 29401da177e4SLinus Torvalds .maxlen = sizeof(int), 29411da177e4SLinus Torvalds .mode = 0644, 29426d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 29431da177e4SLinus Torvalds }, 29441da177e4SLinus Torvalds { 29451da177e4SLinus Torvalds .procname = "gc_elasticity", 29464990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 29471da177e4SLinus Torvalds .maxlen = sizeof(int), 29481da177e4SLinus Torvalds .mode = 0644, 2949f3d3f616SMin Zhang .proc_handler = proc_dointvec, 29501da177e4SLinus Torvalds }, 29511da177e4SLinus Torvalds { 29521da177e4SLinus Torvalds .procname = "mtu_expires", 29534990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 29541da177e4SLinus Torvalds .maxlen = sizeof(int), 29551da177e4SLinus Torvalds .mode = 0644, 29566d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 29571da177e4SLinus Torvalds }, 29581da177e4SLinus Torvalds { 29591da177e4SLinus Torvalds .procname = "min_adv_mss", 29604990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 29611da177e4SLinus Torvalds .maxlen = sizeof(int), 29621da177e4SLinus Torvalds .mode = 0644, 2963f3d3f616SMin Zhang .proc_handler = proc_dointvec, 29641da177e4SLinus Torvalds }, 29651da177e4SLinus Torvalds { 29661da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 29674990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 29681da177e4SLinus Torvalds .maxlen = sizeof(int), 29691da177e4SLinus Torvalds .mode = 0644, 29706d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 29711da177e4SLinus Torvalds }, 2972f8572d8fSEric W. Biederman { } 29731da177e4SLinus Torvalds }; 29741da177e4SLinus Torvalds 29752c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 2976760f2d01SDaniel Lezcano { 2977760f2d01SDaniel Lezcano struct ctl_table *table; 2978760f2d01SDaniel Lezcano 2979760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 2980760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 2981760f2d01SDaniel Lezcano GFP_KERNEL); 29825ee09105SYOSHIFUJI Hideaki 29835ee09105SYOSHIFUJI Hideaki if (table) { 29845ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 2985c486da34SLucian Adrian Grijincu table[0].extra1 = net; 298686393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 29875ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 29885ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 29895ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 29905ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 29915ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 29925ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 29935ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 29949c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 2995464dc801SEric W. Biederman 2996464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 2997464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 2998464dc801SEric W. Biederman table[0].procname = NULL; 29995ee09105SYOSHIFUJI Hideaki } 30005ee09105SYOSHIFUJI Hideaki 3001760f2d01SDaniel Lezcano return table; 3002760f2d01SDaniel Lezcano } 30031da177e4SLinus Torvalds #endif 30041da177e4SLinus Torvalds 30052c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 3006cdb18761SDaniel Lezcano { 3007633d424bSPavel Emelyanov int ret = -ENOMEM; 30088ed67789SDaniel Lezcano 300986393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 301086393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 3011f2fc6a54SBenjamin Thery 3012fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 3013fc66f95cSEric Dumazet goto out_ip6_dst_ops; 3014fc66f95cSEric Dumazet 30158ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 30168ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 30178ed67789SDaniel Lezcano GFP_KERNEL); 30188ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 3019fc66f95cSEric Dumazet goto out_ip6_dst_entries; 3020d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 30218ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 3022d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 302362fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 302462fa8a84SDavid S. Miller ip6_template_metrics, true); 30258ed67789SDaniel Lezcano 30268ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 30278ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 30288ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 30298ed67789SDaniel Lezcano GFP_KERNEL); 303068fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 303168fffc67SPeter Zijlstra goto out_ip6_null_entry; 3032d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 30338ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 3034d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 303562fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 303662fa8a84SDavid S. Miller ip6_template_metrics, true); 30378ed67789SDaniel Lezcano 30388ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 30398ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 30408ed67789SDaniel Lezcano GFP_KERNEL); 304168fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 304268fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 3043d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 30448ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 3045d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 304662fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 304762fa8a84SDavid S. Miller ip6_template_metrics, true); 30488ed67789SDaniel Lezcano #endif 30498ed67789SDaniel Lezcano 3050b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 3051b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 3052b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 3053b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 3054b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 3055b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 3056b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 3057b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 3058b339a47cSPeter Zijlstra 30596891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 30606891a346SBenjamin Thery 30618ed67789SDaniel Lezcano ret = 0; 30628ed67789SDaniel Lezcano out: 30638ed67789SDaniel Lezcano return ret; 3064f2fc6a54SBenjamin Thery 306568fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 306668fffc67SPeter Zijlstra out_ip6_prohibit_entry: 306768fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 306868fffc67SPeter Zijlstra out_ip6_null_entry: 306968fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 307068fffc67SPeter Zijlstra #endif 3071fc66f95cSEric Dumazet out_ip6_dst_entries: 3072fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3073f2fc6a54SBenjamin Thery out_ip6_dst_ops: 3074f2fc6a54SBenjamin Thery goto out; 3075cdb18761SDaniel Lezcano } 3076cdb18761SDaniel Lezcano 30772c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 3078cdb18761SDaniel Lezcano { 30798ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 30808ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 30818ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 30828ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 30838ed67789SDaniel Lezcano #endif 308441bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3085cdb18761SDaniel Lezcano } 3086cdb18761SDaniel Lezcano 3087d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 3088d189634eSThomas Graf { 3089d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3090d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 3091d4beaa66SGao feng proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops); 3092d189634eSThomas Graf #endif 3093d189634eSThomas Graf return 0; 3094d189634eSThomas Graf } 3095d189634eSThomas Graf 3096d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 3097d189634eSThomas Graf { 3098d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3099ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 3100ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 3101d189634eSThomas Graf #endif 3102d189634eSThomas Graf } 3103d189634eSThomas Graf 3104cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 3105cdb18761SDaniel Lezcano .init = ip6_route_net_init, 3106cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 3107cdb18761SDaniel Lezcano }; 3108cdb18761SDaniel Lezcano 3109c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 3110c3426b47SDavid S. Miller { 3111c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 3112c3426b47SDavid S. Miller 3113c3426b47SDavid S. Miller if (!bp) 3114c3426b47SDavid S. Miller return -ENOMEM; 3115c3426b47SDavid S. Miller inet_peer_base_init(bp); 3116c3426b47SDavid S. Miller net->ipv6.peers = bp; 3117c3426b47SDavid S. Miller return 0; 3118c3426b47SDavid S. Miller } 3119c3426b47SDavid S. Miller 3120c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 3121c3426b47SDavid S. Miller { 3122c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 3123c3426b47SDavid S. Miller 3124c3426b47SDavid S. Miller net->ipv6.peers = NULL; 312556a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3126c3426b47SDavid S. Miller kfree(bp); 3127c3426b47SDavid S. Miller } 3128c3426b47SDavid S. Miller 31292b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3130c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3131c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3132c3426b47SDavid S. Miller }; 3133c3426b47SDavid S. Miller 3134d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3135d189634eSThomas Graf .init = ip6_route_net_init_late, 3136d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3137d189634eSThomas Graf }; 3138d189634eSThomas Graf 31398ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 31408ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 31418ed67789SDaniel Lezcano .priority = 0, 31428ed67789SDaniel Lezcano }; 31438ed67789SDaniel Lezcano 3144433d49c3SDaniel Lezcano int __init ip6_route_init(void) 31451da177e4SLinus Torvalds { 3146433d49c3SDaniel Lezcano int ret; 3147433d49c3SDaniel Lezcano 31489a7ec3a9SDaniel Lezcano ret = -ENOMEM; 31499a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 31509a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 31519a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 31529a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3153c19a28e1SFernando Carrijo goto out; 315414e50e57SDavid S. Miller 3155fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 31568ed67789SDaniel Lezcano if (ret) 3157bdb3289fSDaniel Lezcano goto out_kmem_cache; 3158bdb3289fSDaniel Lezcano 3159c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3160c3426b47SDavid S. Miller if (ret) 3161e8803b6cSDavid S. Miller goto out_dst_entries; 31622a0c451aSThomas Graf 31637e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 31647e52b33bSDavid S. Miller if (ret) 31657e52b33bSDavid S. Miller goto out_register_inetpeer; 3166c3426b47SDavid S. Miller 31675dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 31685dc121e9SArnaud Ebalard 31698ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 31708ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 31718ed67789SDaniel Lezcano * manually for init_net */ 3172d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 31738ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3174bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3175d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 31768ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3177d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 31788ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3179bdb3289fSDaniel Lezcano #endif 3180e8803b6cSDavid S. Miller ret = fib6_init(); 3181433d49c3SDaniel Lezcano if (ret) 31828ed67789SDaniel Lezcano goto out_register_subsys; 3183433d49c3SDaniel Lezcano 3184433d49c3SDaniel Lezcano ret = xfrm6_init(); 3185433d49c3SDaniel Lezcano if (ret) 3186e8803b6cSDavid S. Miller goto out_fib6_init; 3187c35b7e72SDaniel Lezcano 3188433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3189433d49c3SDaniel Lezcano if (ret) 3190433d49c3SDaniel Lezcano goto xfrm6_init; 31917e5449c2SDaniel Lezcano 3192d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3193d189634eSThomas Graf if (ret) 3194d189634eSThomas Graf goto fib6_rules_init; 3195d189634eSThomas Graf 3196433d49c3SDaniel Lezcano ret = -ENOBUFS; 3197c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3198c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3199c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3200d189634eSThomas Graf goto out_register_late_subsys; 3201433d49c3SDaniel Lezcano 32028ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3203cdb18761SDaniel Lezcano if (ret) 3204d189634eSThomas Graf goto out_register_late_subsys; 32058ed67789SDaniel Lezcano 3206433d49c3SDaniel Lezcano out: 3207433d49c3SDaniel Lezcano return ret; 3208433d49c3SDaniel Lezcano 3209d189634eSThomas Graf out_register_late_subsys: 3210d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3211433d49c3SDaniel Lezcano fib6_rules_init: 3212433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3213433d49c3SDaniel Lezcano xfrm6_init: 3214433d49c3SDaniel Lezcano xfrm6_fini(); 32152a0c451aSThomas Graf out_fib6_init: 32162a0c451aSThomas Graf fib6_gc_cleanup(); 32178ed67789SDaniel Lezcano out_register_subsys: 32188ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 32197e52b33bSDavid S. Miller out_register_inetpeer: 32207e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3221fc66f95cSEric Dumazet out_dst_entries: 3222fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3223433d49c3SDaniel Lezcano out_kmem_cache: 3224f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3225433d49c3SDaniel Lezcano goto out; 32261da177e4SLinus Torvalds } 32271da177e4SLinus Torvalds 32281da177e4SLinus Torvalds void ip6_route_cleanup(void) 32291da177e4SLinus Torvalds { 32308ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3231d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3232101367c2SThomas Graf fib6_rules_cleanup(); 32331da177e4SLinus Torvalds xfrm6_fini(); 32341da177e4SLinus Torvalds fib6_gc_cleanup(); 3235c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 32368ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 323741bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3238f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 32391da177e4SLinus Torvalds } 3240