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 1516*c88507fbSSabrina Dubroca rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : 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 rt6_set_from(rt, ort); 19271da177e4SLinus Torvalds rt->rt6i_metric = 0; 19281da177e4SLinus Torvalds 19291da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 19301da177e4SLinus Torvalds memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 19311da177e4SLinus Torvalds #endif 19320f6c6392SFlorian Westphal memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key)); 1933c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 19341da177e4SLinus Torvalds } 19351da177e4SLinus Torvalds return rt; 19361da177e4SLinus Torvalds } 19371da177e4SLinus Torvalds 193870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 1939efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 1940b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1941b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 194270ceb4f5SYOSHIFUJI Hideaki { 194370ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 194470ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 1945c71099acSThomas Graf struct fib6_table *table; 194670ceb4f5SYOSHIFUJI Hideaki 1947efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 194838308473SDavid S. Miller if (!table) 1949c71099acSThomas Graf return NULL; 1950c71099acSThomas Graf 19515744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 1952c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0); 195370ceb4f5SYOSHIFUJI Hideaki if (!fn) 195470ceb4f5SYOSHIFUJI Hideaki goto out; 195570ceb4f5SYOSHIFUJI Hideaki 1956d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1957d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 195870ceb4f5SYOSHIFUJI Hideaki continue; 195970ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 196070ceb4f5SYOSHIFUJI Hideaki continue; 196170ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 196270ceb4f5SYOSHIFUJI Hideaki continue; 1963d8d1f30bSChangli Gao dst_hold(&rt->dst); 196470ceb4f5SYOSHIFUJI Hideaki break; 196570ceb4f5SYOSHIFUJI Hideaki } 196670ceb4f5SYOSHIFUJI Hideaki out: 19675744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 196870ceb4f5SYOSHIFUJI Hideaki return rt; 196970ceb4f5SYOSHIFUJI Hideaki } 197070ceb4f5SYOSHIFUJI Hideaki 1971efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 1972b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1973b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 197495c96174SEric Dumazet unsigned int pref) 197570ceb4f5SYOSHIFUJI Hideaki { 197686872cb5SThomas Graf struct fib6_config cfg = { 197786872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 1978238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 197986872cb5SThomas Graf .fc_ifindex = ifindex, 198086872cb5SThomas Graf .fc_dst_len = prefixlen, 198186872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 198286872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 198315e47304SEric W. Biederman .fc_nlinfo.portid = 0, 1984efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 1985efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 198686872cb5SThomas Graf }; 198770ceb4f5SYOSHIFUJI Hideaki 19884e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 19894e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 199086872cb5SThomas Graf 1991e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 1992e317da96SYOSHIFUJI Hideaki if (!prefixlen) 199386872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 199470ceb4f5SYOSHIFUJI Hideaki 199586872cb5SThomas Graf ip6_route_add(&cfg); 199670ceb4f5SYOSHIFUJI Hideaki 1997efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 199870ceb4f5SYOSHIFUJI Hideaki } 199970ceb4f5SYOSHIFUJI Hideaki #endif 200070ceb4f5SYOSHIFUJI Hideaki 2001b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 20021da177e4SLinus Torvalds { 20031da177e4SLinus Torvalds struct rt6_info *rt; 2004c71099acSThomas Graf struct fib6_table *table; 20051da177e4SLinus Torvalds 2006c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 200738308473SDavid S. Miller if (!table) 2008c71099acSThomas Graf return NULL; 20091da177e4SLinus Torvalds 20105744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2011d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt=rt->dst.rt6_next) { 2012d1918542SDavid S. Miller if (dev == rt->dst.dev && 2013045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 20141da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 20151da177e4SLinus Torvalds break; 20161da177e4SLinus Torvalds } 20171da177e4SLinus Torvalds if (rt) 2018d8d1f30bSChangli Gao dst_hold(&rt->dst); 20195744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 20201da177e4SLinus Torvalds return rt; 20211da177e4SLinus Torvalds } 20221da177e4SLinus Torvalds 2023b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 2024ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 2025ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 20261da177e4SLinus Torvalds { 202786872cb5SThomas Graf struct fib6_config cfg = { 202886872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 2029238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 203086872cb5SThomas Graf .fc_ifindex = dev->ifindex, 203186872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 203286872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 203315e47304SEric W. Biederman .fc_nlinfo.portid = 0, 20345578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 2035c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 203686872cb5SThomas Graf }; 20371da177e4SLinus Torvalds 20384e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 20391da177e4SLinus Torvalds 204086872cb5SThomas Graf ip6_route_add(&cfg); 20411da177e4SLinus Torvalds 20421da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 20431da177e4SLinus Torvalds } 20441da177e4SLinus Torvalds 20457b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 20461da177e4SLinus Torvalds { 20471da177e4SLinus Torvalds struct rt6_info *rt; 2048c71099acSThomas Graf struct fib6_table *table; 2049c71099acSThomas Graf 2050c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 20517b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 205238308473SDavid S. Miller if (!table) 2053c71099acSThomas Graf return; 20541da177e4SLinus Torvalds 20551da177e4SLinus Torvalds restart: 2056c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2057d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 20583e8b0ac3SLorenzo Colitti if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 20593e8b0ac3SLorenzo Colitti (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { 2060d8d1f30bSChangli Gao dst_hold(&rt->dst); 2061c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2062e0a1ad73SThomas Graf ip6_del_rt(rt); 20631da177e4SLinus Torvalds goto restart; 20641da177e4SLinus Torvalds } 20651da177e4SLinus Torvalds } 2066c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 20671da177e4SLinus Torvalds } 20681da177e4SLinus Torvalds 20695578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 20705578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 207186872cb5SThomas Graf struct fib6_config *cfg) 207286872cb5SThomas Graf { 207386872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 207486872cb5SThomas Graf 207586872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 207686872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 207786872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 207886872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 207986872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 208086872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 208186872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 208286872cb5SThomas Graf 20835578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 2084f1243c2dSBenjamin Thery 20854e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 20864e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 20874e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 208886872cb5SThomas Graf } 208986872cb5SThomas Graf 20905578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 20911da177e4SLinus Torvalds { 209286872cb5SThomas Graf struct fib6_config cfg; 20931da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 20941da177e4SLinus Torvalds int err; 20951da177e4SLinus Torvalds 20961da177e4SLinus Torvalds switch(cmd) { 20971da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 20981da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 2099af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 21001da177e4SLinus Torvalds return -EPERM; 21011da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 21021da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 21031da177e4SLinus Torvalds if (err) 21041da177e4SLinus Torvalds return -EFAULT; 21051da177e4SLinus Torvalds 21065578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 210786872cb5SThomas Graf 21081da177e4SLinus Torvalds rtnl_lock(); 21091da177e4SLinus Torvalds switch (cmd) { 21101da177e4SLinus Torvalds case SIOCADDRT: 211186872cb5SThomas Graf err = ip6_route_add(&cfg); 21121da177e4SLinus Torvalds break; 21131da177e4SLinus Torvalds case SIOCDELRT: 211486872cb5SThomas Graf err = ip6_route_del(&cfg); 21151da177e4SLinus Torvalds break; 21161da177e4SLinus Torvalds default: 21171da177e4SLinus Torvalds err = -EINVAL; 21181da177e4SLinus Torvalds } 21191da177e4SLinus Torvalds rtnl_unlock(); 21201da177e4SLinus Torvalds 21211da177e4SLinus Torvalds return err; 21223ff50b79SStephen Hemminger } 21231da177e4SLinus Torvalds 21241da177e4SLinus Torvalds return -EINVAL; 21251da177e4SLinus Torvalds } 21261da177e4SLinus Torvalds 21271da177e4SLinus Torvalds /* 21281da177e4SLinus Torvalds * Drop the packet on the floor 21291da177e4SLinus Torvalds */ 21301da177e4SLinus Torvalds 2131d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 21321da177e4SLinus Torvalds { 2133612f09e8SYOSHIFUJI Hideaki int type; 2134adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2135612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2136612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 21370660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 213845bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 21393bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 21403bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2141612f09e8SYOSHIFUJI Hideaki break; 2142612f09e8SYOSHIFUJI Hideaki } 2143612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2144612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 21453bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 21463bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2147612f09e8SYOSHIFUJI Hideaki break; 2148612f09e8SYOSHIFUJI Hideaki } 21493ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 21501da177e4SLinus Torvalds kfree_skb(skb); 21511da177e4SLinus Torvalds return 0; 21521da177e4SLinus Torvalds } 21531da177e4SLinus Torvalds 21549ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 21559ce8ade0SThomas Graf { 2156612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 21579ce8ade0SThomas Graf } 21589ce8ade0SThomas Graf 215920380731SArnaldo Carvalho de Melo static int ip6_pkt_discard_out(struct sk_buff *skb) 21601da177e4SLinus Torvalds { 2161adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2162612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 21631da177e4SLinus Torvalds } 21641da177e4SLinus Torvalds 21659ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 21669ce8ade0SThomas Graf { 2167612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 21689ce8ade0SThomas Graf } 21699ce8ade0SThomas Graf 21709ce8ade0SThomas Graf static int ip6_pkt_prohibit_out(struct sk_buff *skb) 21719ce8ade0SThomas Graf { 2172adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2173612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 21749ce8ade0SThomas Graf } 21759ce8ade0SThomas Graf 21761da177e4SLinus Torvalds /* 21771da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 21781da177e4SLinus Torvalds */ 21791da177e4SLinus Torvalds 21801da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 21811da177e4SLinus Torvalds const struct in6_addr *addr, 21828f031519SDavid S. Miller bool anycast) 21831da177e4SLinus Torvalds { 2184c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 2185a3300ef4SHannes Frederic Sowa struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 2186a3300ef4SHannes Frederic Sowa DST_NOCOUNT, NULL); 2187a3300ef4SHannes Frederic Sowa if (!rt) 21881da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 21891da177e4SLinus Torvalds 21901da177e4SLinus Torvalds in6_dev_hold(idev); 21911da177e4SLinus Torvalds 219211d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2193d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2194d8d1f30bSChangli Gao rt->dst.output = ip6_output; 21951da177e4SLinus Torvalds rt->rt6i_idev = idev; 21961da177e4SLinus Torvalds 21971da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 219858c4fb86SYOSHIFUJI Hideaki if (anycast) 219958c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 220058c4fb86SYOSHIFUJI Hideaki else 22011da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 22021da177e4SLinus Torvalds 2203550bab42SJulian Anastasov rt->rt6i_gateway = *addr; 22044e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 22051da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 22065578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 22071da177e4SLinus Torvalds 2208d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 22091da177e4SLinus Torvalds 22101da177e4SLinus Torvalds return rt; 22111da177e4SLinus Torvalds } 22121da177e4SLinus Torvalds 2213c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2214c3968a85SDaniel Walter struct rt6_info *rt, 2215b71d1d42SEric Dumazet const struct in6_addr *daddr, 2216c3968a85SDaniel Walter unsigned int prefs, 2217c3968a85SDaniel Walter struct in6_addr *saddr) 2218c3968a85SDaniel Walter { 2219c3968a85SDaniel Walter struct inet6_dev *idev = ip6_dst_idev((struct dst_entry*)rt); 2220c3968a85SDaniel Walter int err = 0; 2221c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) 22224e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2223c3968a85SDaniel Walter else 2224c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2225c3968a85SDaniel Walter daddr, prefs, saddr); 2226c3968a85SDaniel Walter return err; 2227c3968a85SDaniel Walter } 2228c3968a85SDaniel Walter 2229c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2230c3968a85SDaniel Walter struct arg_dev_net_ip { 2231c3968a85SDaniel Walter struct net_device *dev; 2232c3968a85SDaniel Walter struct net *net; 2233c3968a85SDaniel Walter struct in6_addr *addr; 2234c3968a85SDaniel Walter }; 2235c3968a85SDaniel Walter 2236c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2237c3968a85SDaniel Walter { 2238c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2239c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2240c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2241c3968a85SDaniel Walter 2242d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2243c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2244c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2245c3968a85SDaniel Walter /* remove prefsrc entry */ 2246c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2247c3968a85SDaniel Walter } 2248c3968a85SDaniel Walter return 0; 2249c3968a85SDaniel Walter } 2250c3968a85SDaniel Walter 2251c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2252c3968a85SDaniel Walter { 2253c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2254c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2255c3968a85SDaniel Walter .dev = ifp->idev->dev, 2256c3968a85SDaniel Walter .net = net, 2257c3968a85SDaniel Walter .addr = &ifp->addr, 2258c3968a85SDaniel Walter }; 22590c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 2260c3968a85SDaniel Walter } 2261c3968a85SDaniel Walter 22628ed67789SDaniel Lezcano struct arg_dev_net { 22638ed67789SDaniel Lezcano struct net_device *dev; 22648ed67789SDaniel Lezcano struct net *net; 22658ed67789SDaniel Lezcano }; 22668ed67789SDaniel Lezcano 22671da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 22681da177e4SLinus Torvalds { 2269bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2270bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 22718ed67789SDaniel Lezcano 2272d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2273c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 22741da177e4SLinus Torvalds return -1; 2275c159d30cSDavid S. Miller 22761da177e4SLinus Torvalds return 0; 22771da177e4SLinus Torvalds } 22781da177e4SLinus Torvalds 2279f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 22801da177e4SLinus Torvalds { 22818ed67789SDaniel Lezcano struct arg_dev_net adn = { 22828ed67789SDaniel Lezcano .dev = dev, 22838ed67789SDaniel Lezcano .net = net, 22848ed67789SDaniel Lezcano }; 22858ed67789SDaniel Lezcano 22860c3584d5SLi RongQing fib6_clean_all(net, fib6_ifdown, &adn); 22871e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 22881da177e4SLinus Torvalds } 22891da177e4SLinus Torvalds 229095c96174SEric Dumazet struct rt6_mtu_change_arg { 22911da177e4SLinus Torvalds struct net_device *dev; 229295c96174SEric Dumazet unsigned int mtu; 22931da177e4SLinus Torvalds }; 22941da177e4SLinus Torvalds 22951da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 22961da177e4SLinus Torvalds { 22971da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 22981da177e4SLinus Torvalds struct inet6_dev *idev; 22991da177e4SLinus Torvalds 23001da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 23011da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 23021da177e4SLinus Torvalds We still use this lock to block changes 23031da177e4SLinus Torvalds caused by addrconf/ndisc. 23041da177e4SLinus Torvalds */ 23051da177e4SLinus Torvalds 23061da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 230738308473SDavid S. Miller if (!idev) 23081da177e4SLinus Torvalds return 0; 23091da177e4SLinus Torvalds 23101da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 23111da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 23121da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 23131da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 23141da177e4SLinus Torvalds */ 23151da177e4SLinus Torvalds /* 23161da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 23171da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 23181da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 23191da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 23201da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 23211da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 23221da177e4SLinus Torvalds PMTU discouvery. 23231da177e4SLinus Torvalds */ 2324d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 2325d8d1f30bSChangli Gao !dst_metric_locked(&rt->dst, RTAX_MTU) && 2326d8d1f30bSChangli Gao (dst_mtu(&rt->dst) >= arg->mtu || 2327d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 2328d8d1f30bSChangli Gao dst_mtu(&rt->dst) == idev->cnf.mtu6))) { 2329defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2330566cfd8fSSimon Arlott } 23311da177e4SLinus Torvalds return 0; 23321da177e4SLinus Torvalds } 23331da177e4SLinus Torvalds 233495c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 23351da177e4SLinus Torvalds { 2336c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2337c71099acSThomas Graf .dev = dev, 2338c71099acSThomas Graf .mtu = mtu, 2339c71099acSThomas Graf }; 23401da177e4SLinus Torvalds 23410c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 23421da177e4SLinus Torvalds } 23431da177e4SLinus Torvalds 2344ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 23455176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 234686872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2347ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 234886872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 234986872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 235051ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 235186872cb5SThomas Graf }; 235286872cb5SThomas Graf 235386872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 235486872cb5SThomas Graf struct fib6_config *cfg) 23551da177e4SLinus Torvalds { 235686872cb5SThomas Graf struct rtmsg *rtm; 235786872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 235886872cb5SThomas Graf int err; 23591da177e4SLinus Torvalds 236086872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 236186872cb5SThomas Graf if (err < 0) 236286872cb5SThomas Graf goto errout; 23631da177e4SLinus Torvalds 236486872cb5SThomas Graf err = -EINVAL; 236586872cb5SThomas Graf rtm = nlmsg_data(nlh); 236686872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 236786872cb5SThomas Graf 236886872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 236986872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 237086872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 237186872cb5SThomas Graf cfg->fc_flags = RTF_UP; 237286872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2373ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 237486872cb5SThomas Graf 2375ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2376ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2377b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2378b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 237986872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 238086872cb5SThomas Graf 2381ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2382ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2383ab79ad14SMaciej Żenczykowski 238415e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 238586872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 23863b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 238786872cb5SThomas Graf 238886872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 238986872cb5SThomas Graf nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16); 239086872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 23911da177e4SLinus Torvalds } 239286872cb5SThomas Graf 239386872cb5SThomas Graf if (tb[RTA_DST]) { 239486872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 239586872cb5SThomas Graf 239686872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 239786872cb5SThomas Graf goto errout; 239886872cb5SThomas Graf 239986872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 24001da177e4SLinus Torvalds } 240186872cb5SThomas Graf 240286872cb5SThomas Graf if (tb[RTA_SRC]) { 240386872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 240486872cb5SThomas Graf 240586872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 240686872cb5SThomas Graf goto errout; 240786872cb5SThomas Graf 240886872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 24091da177e4SLinus Torvalds } 241086872cb5SThomas Graf 2411c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 2412c3968a85SDaniel Walter nla_memcpy(&cfg->fc_prefsrc, tb[RTA_PREFSRC], 16); 2413c3968a85SDaniel Walter 241486872cb5SThomas Graf if (tb[RTA_OIF]) 241586872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 241686872cb5SThomas Graf 241786872cb5SThomas Graf if (tb[RTA_PRIORITY]) 241886872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 241986872cb5SThomas Graf 242086872cb5SThomas Graf if (tb[RTA_METRICS]) { 242186872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 242286872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 24231da177e4SLinus Torvalds } 242486872cb5SThomas Graf 242586872cb5SThomas Graf if (tb[RTA_TABLE]) 242686872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 242786872cb5SThomas Graf 242851ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 242951ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 243051ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 243151ebd318SNicolas Dichtel } 243251ebd318SNicolas Dichtel 243386872cb5SThomas Graf err = 0; 243486872cb5SThomas Graf errout: 243586872cb5SThomas Graf return err; 24361da177e4SLinus Torvalds } 24371da177e4SLinus Torvalds 243851ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add) 243951ebd318SNicolas Dichtel { 244051ebd318SNicolas Dichtel struct fib6_config r_cfg; 244151ebd318SNicolas Dichtel struct rtnexthop *rtnh; 244251ebd318SNicolas Dichtel int remaining; 244351ebd318SNicolas Dichtel int attrlen; 244451ebd318SNicolas Dichtel int err = 0, last_err = 0; 244551ebd318SNicolas Dichtel 244651ebd318SNicolas Dichtel beginning: 244751ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 244851ebd318SNicolas Dichtel remaining = cfg->fc_mp_len; 244951ebd318SNicolas Dichtel 245051ebd318SNicolas Dichtel /* Parse a Multipath Entry */ 245151ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 245251ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 245351ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 245451ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 245551ebd318SNicolas Dichtel 245651ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 245751ebd318SNicolas Dichtel if (attrlen > 0) { 245851ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 245951ebd318SNicolas Dichtel 246051ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 246151ebd318SNicolas Dichtel if (nla) { 246251ebd318SNicolas Dichtel nla_memcpy(&r_cfg.fc_gateway, nla, 16); 246351ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 246451ebd318SNicolas Dichtel } 246551ebd318SNicolas Dichtel } 246651ebd318SNicolas Dichtel err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg); 246751ebd318SNicolas Dichtel if (err) { 246851ebd318SNicolas Dichtel last_err = err; 246951ebd318SNicolas Dichtel /* If we are trying to remove a route, do not stop the 247051ebd318SNicolas Dichtel * loop when ip6_route_del() fails (because next hop is 247151ebd318SNicolas Dichtel * already gone), we should try to remove all next hops. 247251ebd318SNicolas Dichtel */ 247351ebd318SNicolas Dichtel if (add) { 247451ebd318SNicolas Dichtel /* If add fails, we should try to delete all 247551ebd318SNicolas Dichtel * next hops that have been already added. 247651ebd318SNicolas Dichtel */ 247751ebd318SNicolas Dichtel add = 0; 247851ebd318SNicolas Dichtel goto beginning; 247951ebd318SNicolas Dichtel } 248051ebd318SNicolas Dichtel } 24811a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 24821a72418bSNicolas Dichtel * this flag after the first nexthop (if there is a collision, 24831a72418bSNicolas Dichtel * we have already fail to add the first nexthop: 24841a72418bSNicolas Dichtel * fib6_add_rt2node() has reject it). 24851a72418bSNicolas Dichtel */ 24861a72418bSNicolas Dichtel cfg->fc_nlinfo.nlh->nlmsg_flags &= ~NLM_F_EXCL; 248751ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 248851ebd318SNicolas Dichtel } 248951ebd318SNicolas Dichtel 249051ebd318SNicolas Dichtel return last_err; 249151ebd318SNicolas Dichtel } 249251ebd318SNicolas Dichtel 2493661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh) 24941da177e4SLinus Torvalds { 249586872cb5SThomas Graf struct fib6_config cfg; 249686872cb5SThomas Graf int err; 24971da177e4SLinus Torvalds 249886872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 249986872cb5SThomas Graf if (err < 0) 250086872cb5SThomas Graf return err; 250186872cb5SThomas Graf 250251ebd318SNicolas Dichtel if (cfg.fc_mp) 250351ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 0); 250451ebd318SNicolas Dichtel else 250586872cb5SThomas Graf return ip6_route_del(&cfg); 25061da177e4SLinus Torvalds } 25071da177e4SLinus Torvalds 2508661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh) 25091da177e4SLinus Torvalds { 251086872cb5SThomas Graf struct fib6_config cfg; 251186872cb5SThomas Graf int err; 25121da177e4SLinus Torvalds 251386872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 251486872cb5SThomas Graf if (err < 0) 251586872cb5SThomas Graf return err; 251686872cb5SThomas Graf 251751ebd318SNicolas Dichtel if (cfg.fc_mp) 251851ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 1); 251951ebd318SNicolas Dichtel else 252086872cb5SThomas Graf return ip6_route_add(&cfg); 25211da177e4SLinus Torvalds } 25221da177e4SLinus Torvalds 2523339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void) 2524339bf98fSThomas Graf { 2525339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 2526339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 2527339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 2528339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 2529339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 2530339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 2531339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 2532339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 2533339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 25346a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 2535339bf98fSThomas Graf + nla_total_size(sizeof(struct rta_cacheinfo)); 2536339bf98fSThomas Graf } 2537339bf98fSThomas Graf 2538191cd582SBrian Haley static int rt6_fill_node(struct net *net, 2539191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 25400d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 254115e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 25427bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 25431da177e4SLinus Torvalds { 25441da177e4SLinus Torvalds struct rtmsg *rtm; 25451da177e4SLinus Torvalds struct nlmsghdr *nlh; 2546e3703b3dSThomas Graf long expires; 25479e762a4aSPatrick McHardy u32 table; 25481da177e4SLinus Torvalds 25491da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 25501da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 25511da177e4SLinus Torvalds /* success since this is not a prefix route */ 25521da177e4SLinus Torvalds return 1; 25531da177e4SLinus Torvalds } 25541da177e4SLinus Torvalds } 25551da177e4SLinus Torvalds 255615e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 255738308473SDavid S. Miller if (!nlh) 255826932566SPatrick McHardy return -EMSGSIZE; 25592d7202bfSThomas Graf 25602d7202bfSThomas Graf rtm = nlmsg_data(nlh); 25611da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 25621da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 25631da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 25641da177e4SLinus Torvalds rtm->rtm_tos = 0; 2565c71099acSThomas Graf if (rt->rt6i_table) 25669e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 2567c71099acSThomas Graf else 25689e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 25699e762a4aSPatrick McHardy rtm->rtm_table = table; 2570c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 2571c78679e8SDavid S. Miller goto nla_put_failure; 2572ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 2573ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 2574ef2c7d7bSNicolas Dichtel case -EINVAL: 2575ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 2576ef2c7d7bSNicolas Dichtel break; 2577ef2c7d7bSNicolas Dichtel case -EACCES: 2578ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 2579ef2c7d7bSNicolas Dichtel break; 2580b4949ab2SNicolas Dichtel case -EAGAIN: 2581b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 2582b4949ab2SNicolas Dichtel break; 2583ef2c7d7bSNicolas Dichtel default: 25841da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 2585ef2c7d7bSNicolas Dichtel break; 2586ef2c7d7bSNicolas Dichtel } 2587ef2c7d7bSNicolas Dichtel } 2588ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 2589ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 2590d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 25911da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 25921da177e4SLinus Torvalds else 25931da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 25941da177e4SLinus Torvalds rtm->rtm_flags = 0; 25951da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 25961da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 25971da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 25981da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 2599f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 2600f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 26011da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 2602f0396f60SDenis Ovsienko else 2603f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 2604f0396f60SDenis Ovsienko } 26051da177e4SLinus Torvalds 26061da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 26071da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 26081da177e4SLinus Torvalds 26091da177e4SLinus Torvalds if (dst) { 2610c78679e8SDavid S. Miller if (nla_put(skb, RTA_DST, 16, dst)) 2611c78679e8SDavid S. Miller goto nla_put_failure; 26121da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 26131da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 2614c78679e8SDavid S. Miller if (nla_put(skb, RTA_DST, 16, &rt->rt6i_dst.addr)) 2615c78679e8SDavid S. Miller goto nla_put_failure; 26161da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 26171da177e4SLinus Torvalds if (src) { 2618c78679e8SDavid S. Miller if (nla_put(skb, RTA_SRC, 16, src)) 2619c78679e8SDavid S. Miller goto nla_put_failure; 26201da177e4SLinus Torvalds rtm->rtm_src_len = 128; 2621c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 2622c78679e8SDavid S. Miller nla_put(skb, RTA_SRC, 16, &rt->rt6i_src.addr)) 2623c78679e8SDavid S. Miller goto nla_put_failure; 26241da177e4SLinus Torvalds #endif 26257bc570c8SYOSHIFUJI Hideaki if (iif) { 26267bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 26277bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 26288229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 26297bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 26307bc570c8SYOSHIFUJI Hideaki if (!nowait) { 26317bc570c8SYOSHIFUJI Hideaki if (err == 0) 26327bc570c8SYOSHIFUJI Hideaki return 0; 26337bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 26347bc570c8SYOSHIFUJI Hideaki } else { 26357bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 26367bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 26377bc570c8SYOSHIFUJI Hideaki } 26387bc570c8SYOSHIFUJI Hideaki } 26397bc570c8SYOSHIFUJI Hideaki } else 26407bc570c8SYOSHIFUJI Hideaki #endif 2641c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 2642c78679e8SDavid S. Miller goto nla_put_failure; 26437bc570c8SYOSHIFUJI Hideaki } else if (dst) { 26441da177e4SLinus Torvalds struct in6_addr saddr_buf; 2645c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 2646c78679e8SDavid S. Miller nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) 2647c78679e8SDavid S. Miller goto nla_put_failure; 2648c3968a85SDaniel Walter } 2649c3968a85SDaniel Walter 2650c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 2651c3968a85SDaniel Walter struct in6_addr saddr_buf; 26524e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 2653c78679e8SDavid S. Miller if (nla_put(skb, RTA_PREFSRC, 16, &saddr_buf)) 2654c78679e8SDavid S. Miller goto nla_put_failure; 26551da177e4SLinus Torvalds } 26562d7202bfSThomas Graf 2657defb3519SDavid S. Miller if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) 26582d7202bfSThomas Graf goto nla_put_failure; 26592d7202bfSThomas Graf 2660dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_GATEWAY) { 2661dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 if (nla_put(skb, RTA_GATEWAY, 16, &rt->rt6i_gateway) < 0) 266294f826b8SEric Dumazet goto nla_put_failure; 266394f826b8SEric Dumazet } 26642d7202bfSThomas Graf 2665c78679e8SDavid S. Miller if (rt->dst.dev && 2666c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 2667c78679e8SDavid S. Miller goto nla_put_failure; 2668c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 2669c78679e8SDavid S. Miller goto nla_put_failure; 26708253947eSLi Wei 26718253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 267269cdf8f9SYOSHIFUJI Hideaki 267387a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 2674e3703b3dSThomas Graf goto nla_put_failure; 26751da177e4SLinus Torvalds 26762d7202bfSThomas Graf return nlmsg_end(skb, nlh); 26772d7202bfSThomas Graf 26782d7202bfSThomas Graf nla_put_failure: 267926932566SPatrick McHardy nlmsg_cancel(skb, nlh); 268026932566SPatrick McHardy return -EMSGSIZE; 26811da177e4SLinus Torvalds } 26821da177e4SLinus Torvalds 26831b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 26841da177e4SLinus Torvalds { 26851da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 26861da177e4SLinus Torvalds int prefix; 26871da177e4SLinus Torvalds 26882d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 26892d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 26901da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 26911da177e4SLinus Torvalds } else 26921da177e4SLinus Torvalds prefix = 0; 26931da177e4SLinus Torvalds 2694191cd582SBrian Haley return rt6_fill_node(arg->net, 2695191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 269615e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 26977bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 26981da177e4SLinus Torvalds } 26991da177e4SLinus Torvalds 2700661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh) 27011da177e4SLinus Torvalds { 27023b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 2703ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 27041da177e4SLinus Torvalds struct rt6_info *rt; 2705ab364a6fSThomas Graf struct sk_buff *skb; 2706ab364a6fSThomas Graf struct rtmsg *rtm; 27074c9483b2SDavid S. Miller struct flowi6 fl6; 270872331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 2709ab364a6fSThomas Graf 2710ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 2711ab364a6fSThomas Graf if (err < 0) 2712ab364a6fSThomas Graf goto errout; 2713ab364a6fSThomas Graf 2714ab364a6fSThomas Graf err = -EINVAL; 27154c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2716ab364a6fSThomas Graf 2717ab364a6fSThomas Graf if (tb[RTA_SRC]) { 2718ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 2719ab364a6fSThomas Graf goto errout; 2720ab364a6fSThomas Graf 27214e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 2722ab364a6fSThomas Graf } 2723ab364a6fSThomas Graf 2724ab364a6fSThomas Graf if (tb[RTA_DST]) { 2725ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 2726ab364a6fSThomas Graf goto errout; 2727ab364a6fSThomas Graf 27284e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 2729ab364a6fSThomas Graf } 2730ab364a6fSThomas Graf 2731ab364a6fSThomas Graf if (tb[RTA_IIF]) 2732ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 2733ab364a6fSThomas Graf 2734ab364a6fSThomas Graf if (tb[RTA_OIF]) 273572331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 2736ab364a6fSThomas Graf 2737ab364a6fSThomas Graf if (iif) { 2738ab364a6fSThomas Graf struct net_device *dev; 273972331bc0SShmulik Ladkani int flags = 0; 274072331bc0SShmulik Ladkani 27415578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 2742ab364a6fSThomas Graf if (!dev) { 2743ab364a6fSThomas Graf err = -ENODEV; 2744ab364a6fSThomas Graf goto errout; 2745ab364a6fSThomas Graf } 274672331bc0SShmulik Ladkani 274772331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 274872331bc0SShmulik Ladkani 274972331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 275072331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 275172331bc0SShmulik Ladkani 275272331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 275372331bc0SShmulik Ladkani flags); 275472331bc0SShmulik Ladkani } else { 275572331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 275672331bc0SShmulik Ladkani 275772331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 2758ab364a6fSThomas Graf } 27591da177e4SLinus Torvalds 27601da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 276138308473SDavid S. Miller if (!skb) { 276294e187c0SAmerigo Wang ip6_rt_put(rt); 2763ab364a6fSThomas Graf err = -ENOBUFS; 2764ab364a6fSThomas Graf goto errout; 2765ab364a6fSThomas Graf } 27661da177e4SLinus Torvalds 27671da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 27681da177e4SLinus Torvalds through good chunk of routing engine. 27691da177e4SLinus Torvalds */ 2770459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 27711da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 27721da177e4SLinus Torvalds 2773d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 27741da177e4SLinus Torvalds 27754c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 277615e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 27777bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 27781da177e4SLinus Torvalds if (err < 0) { 2779ab364a6fSThomas Graf kfree_skb(skb); 2780ab364a6fSThomas Graf goto errout; 27811da177e4SLinus Torvalds } 27821da177e4SLinus Torvalds 278315e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 2784ab364a6fSThomas Graf errout: 27851da177e4SLinus Torvalds return err; 27861da177e4SLinus Torvalds } 27871da177e4SLinus Torvalds 278886872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) 27891da177e4SLinus Torvalds { 27901da177e4SLinus Torvalds struct sk_buff *skb; 27915578689aSDaniel Lezcano struct net *net = info->nl_net; 2792528c4cebSDenis V. Lunev u32 seq; 2793528c4cebSDenis V. Lunev int err; 27940d51aa80SJamal Hadi Salim 2795528c4cebSDenis V. Lunev err = -ENOBUFS; 279638308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 279786872cb5SThomas Graf 2798339bf98fSThomas Graf skb = nlmsg_new(rt6_nlmsg_size(), gfp_any()); 279938308473SDavid S. Miller if (!skb) 280021713ebcSThomas Graf goto errout; 28011da177e4SLinus Torvalds 2802191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 280315e47304SEric W. Biederman event, info->portid, seq, 0, 0, 0); 280426932566SPatrick McHardy if (err < 0) { 280526932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 280626932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 280726932566SPatrick McHardy kfree_skb(skb); 280826932566SPatrick McHardy goto errout; 280926932566SPatrick McHardy } 281015e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 28115578689aSDaniel Lezcano info->nlh, gfp_any()); 28121ce85fe4SPablo Neira Ayuso return; 281321713ebcSThomas Graf errout: 281421713ebcSThomas Graf if (err < 0) 28155578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 28161da177e4SLinus Torvalds } 28171da177e4SLinus Torvalds 28188ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 2819351638e7SJiri Pirko unsigned long event, void *ptr) 28208ed67789SDaniel Lezcano { 2821351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 2822c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 28238ed67789SDaniel Lezcano 28248ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 2825d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 28268ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 28278ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 2828d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 28298ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 2830d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 28318ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 28328ed67789SDaniel Lezcano #endif 28338ed67789SDaniel Lezcano } 28348ed67789SDaniel Lezcano 28358ed67789SDaniel Lezcano return NOTIFY_OK; 28368ed67789SDaniel Lezcano } 28378ed67789SDaniel Lezcano 28381da177e4SLinus Torvalds /* 28391da177e4SLinus Torvalds * /proc 28401da177e4SLinus Torvalds */ 28411da177e4SLinus Torvalds 28421da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 28431da177e4SLinus Torvalds 284433120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 284533120b30SAlexey Dobriyan .owner = THIS_MODULE, 284633120b30SAlexey Dobriyan .open = ipv6_route_open, 284733120b30SAlexey Dobriyan .read = seq_read, 284833120b30SAlexey Dobriyan .llseek = seq_lseek, 28498d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 285033120b30SAlexey Dobriyan }; 285133120b30SAlexey Dobriyan 28521da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 28531da177e4SLinus Torvalds { 285469ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 28551da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 285669ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 285769ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 285869ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 285969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 286069ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 2861fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 286269ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 28631da177e4SLinus Torvalds 28641da177e4SLinus Torvalds return 0; 28651da177e4SLinus Torvalds } 28661da177e4SLinus Torvalds 28671da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 28681da177e4SLinus Torvalds { 2869de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 287069ddb805SDaniel Lezcano } 287169ddb805SDaniel Lezcano 28729a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 28731da177e4SLinus Torvalds .owner = THIS_MODULE, 28741da177e4SLinus Torvalds .open = rt6_stats_seq_open, 28751da177e4SLinus Torvalds .read = seq_read, 28761da177e4SLinus Torvalds .llseek = seq_lseek, 2877b6fcbdb4SPavel Emelyanov .release = single_release_net, 28781da177e4SLinus Torvalds }; 28791da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 28801da177e4SLinus Torvalds 28811da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 28821da177e4SLinus Torvalds 28831da177e4SLinus Torvalds static 2884fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 28851da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 28861da177e4SLinus Torvalds { 2887c486da34SLucian Adrian Grijincu struct net *net; 2888c486da34SLucian Adrian Grijincu int delay; 2889c486da34SLucian Adrian Grijincu if (!write) 2890c486da34SLucian Adrian Grijincu return -EINVAL; 2891c486da34SLucian Adrian Grijincu 2892c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 2893c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 28948d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 28952ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 28961da177e4SLinus Torvalds return 0; 28971da177e4SLinus Torvalds } 28981da177e4SLinus Torvalds 2899fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 29001da177e4SLinus Torvalds { 29011da177e4SLinus Torvalds .procname = "flush", 29024990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 29031da177e4SLinus Torvalds .maxlen = sizeof(int), 290489c8b3a1SDave Jones .mode = 0200, 29056d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 29061da177e4SLinus Torvalds }, 29071da177e4SLinus Torvalds { 29081da177e4SLinus Torvalds .procname = "gc_thresh", 29099a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 29101da177e4SLinus Torvalds .maxlen = sizeof(int), 29111da177e4SLinus Torvalds .mode = 0644, 29126d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 29131da177e4SLinus Torvalds }, 29141da177e4SLinus Torvalds { 29151da177e4SLinus Torvalds .procname = "max_size", 29164990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 29171da177e4SLinus Torvalds .maxlen = sizeof(int), 29181da177e4SLinus Torvalds .mode = 0644, 29196d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 29201da177e4SLinus Torvalds }, 29211da177e4SLinus Torvalds { 29221da177e4SLinus Torvalds .procname = "gc_min_interval", 29234990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 29241da177e4SLinus Torvalds .maxlen = sizeof(int), 29251da177e4SLinus Torvalds .mode = 0644, 29266d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 29271da177e4SLinus Torvalds }, 29281da177e4SLinus Torvalds { 29291da177e4SLinus Torvalds .procname = "gc_timeout", 29304990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 29311da177e4SLinus Torvalds .maxlen = sizeof(int), 29321da177e4SLinus Torvalds .mode = 0644, 29336d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 29341da177e4SLinus Torvalds }, 29351da177e4SLinus Torvalds { 29361da177e4SLinus Torvalds .procname = "gc_interval", 29374990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 29381da177e4SLinus Torvalds .maxlen = sizeof(int), 29391da177e4SLinus Torvalds .mode = 0644, 29406d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 29411da177e4SLinus Torvalds }, 29421da177e4SLinus Torvalds { 29431da177e4SLinus Torvalds .procname = "gc_elasticity", 29444990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 29451da177e4SLinus Torvalds .maxlen = sizeof(int), 29461da177e4SLinus Torvalds .mode = 0644, 2947f3d3f616SMin Zhang .proc_handler = proc_dointvec, 29481da177e4SLinus Torvalds }, 29491da177e4SLinus Torvalds { 29501da177e4SLinus Torvalds .procname = "mtu_expires", 29514990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 29521da177e4SLinus Torvalds .maxlen = sizeof(int), 29531da177e4SLinus Torvalds .mode = 0644, 29546d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 29551da177e4SLinus Torvalds }, 29561da177e4SLinus Torvalds { 29571da177e4SLinus Torvalds .procname = "min_adv_mss", 29584990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 29591da177e4SLinus Torvalds .maxlen = sizeof(int), 29601da177e4SLinus Torvalds .mode = 0644, 2961f3d3f616SMin Zhang .proc_handler = proc_dointvec, 29621da177e4SLinus Torvalds }, 29631da177e4SLinus Torvalds { 29641da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 29654990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 29661da177e4SLinus Torvalds .maxlen = sizeof(int), 29671da177e4SLinus Torvalds .mode = 0644, 29686d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 29691da177e4SLinus Torvalds }, 2970f8572d8fSEric W. Biederman { } 29711da177e4SLinus Torvalds }; 29721da177e4SLinus Torvalds 29732c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 2974760f2d01SDaniel Lezcano { 2975760f2d01SDaniel Lezcano struct ctl_table *table; 2976760f2d01SDaniel Lezcano 2977760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 2978760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 2979760f2d01SDaniel Lezcano GFP_KERNEL); 29805ee09105SYOSHIFUJI Hideaki 29815ee09105SYOSHIFUJI Hideaki if (table) { 29825ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 2983c486da34SLucian Adrian Grijincu table[0].extra1 = net; 298486393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 29855ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 29865ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 29875ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 29885ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 29895ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 29905ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 29915ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 29929c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 2993464dc801SEric W. Biederman 2994464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 2995464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 2996464dc801SEric W. Biederman table[0].procname = NULL; 29975ee09105SYOSHIFUJI Hideaki } 29985ee09105SYOSHIFUJI Hideaki 2999760f2d01SDaniel Lezcano return table; 3000760f2d01SDaniel Lezcano } 30011da177e4SLinus Torvalds #endif 30021da177e4SLinus Torvalds 30032c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 3004cdb18761SDaniel Lezcano { 3005633d424bSPavel Emelyanov int ret = -ENOMEM; 30068ed67789SDaniel Lezcano 300786393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 300886393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 3009f2fc6a54SBenjamin Thery 3010fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 3011fc66f95cSEric Dumazet goto out_ip6_dst_ops; 3012fc66f95cSEric Dumazet 30138ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 30148ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 30158ed67789SDaniel Lezcano GFP_KERNEL); 30168ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 3017fc66f95cSEric Dumazet goto out_ip6_dst_entries; 3018d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 30198ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 3020d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 302162fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 302262fa8a84SDavid S. Miller ip6_template_metrics, true); 30238ed67789SDaniel Lezcano 30248ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 30258ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 30268ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 30278ed67789SDaniel Lezcano GFP_KERNEL); 302868fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 302968fffc67SPeter Zijlstra goto out_ip6_null_entry; 3030d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 30318ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 3032d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 303362fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 303462fa8a84SDavid S. Miller ip6_template_metrics, true); 30358ed67789SDaniel Lezcano 30368ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 30378ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 30388ed67789SDaniel Lezcano GFP_KERNEL); 303968fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 304068fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 3041d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 30428ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 3043d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 304462fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 304562fa8a84SDavid S. Miller ip6_template_metrics, true); 30468ed67789SDaniel Lezcano #endif 30478ed67789SDaniel Lezcano 3048b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 3049b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 3050b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 3051b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 3052b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 3053b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 3054b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 3055b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 3056b339a47cSPeter Zijlstra 30576891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 30586891a346SBenjamin Thery 30598ed67789SDaniel Lezcano ret = 0; 30608ed67789SDaniel Lezcano out: 30618ed67789SDaniel Lezcano return ret; 3062f2fc6a54SBenjamin Thery 306368fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 306468fffc67SPeter Zijlstra out_ip6_prohibit_entry: 306568fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 306668fffc67SPeter Zijlstra out_ip6_null_entry: 306768fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 306868fffc67SPeter Zijlstra #endif 3069fc66f95cSEric Dumazet out_ip6_dst_entries: 3070fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3071f2fc6a54SBenjamin Thery out_ip6_dst_ops: 3072f2fc6a54SBenjamin Thery goto out; 3073cdb18761SDaniel Lezcano } 3074cdb18761SDaniel Lezcano 30752c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 3076cdb18761SDaniel Lezcano { 30778ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 30788ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 30798ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 30808ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 30818ed67789SDaniel Lezcano #endif 308241bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3083cdb18761SDaniel Lezcano } 3084cdb18761SDaniel Lezcano 3085d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 3086d189634eSThomas Graf { 3087d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3088d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 3089d4beaa66SGao feng proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops); 3090d189634eSThomas Graf #endif 3091d189634eSThomas Graf return 0; 3092d189634eSThomas Graf } 3093d189634eSThomas Graf 3094d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 3095d189634eSThomas Graf { 3096d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3097ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 3098ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 3099d189634eSThomas Graf #endif 3100d189634eSThomas Graf } 3101d189634eSThomas Graf 3102cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 3103cdb18761SDaniel Lezcano .init = ip6_route_net_init, 3104cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 3105cdb18761SDaniel Lezcano }; 3106cdb18761SDaniel Lezcano 3107c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 3108c3426b47SDavid S. Miller { 3109c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 3110c3426b47SDavid S. Miller 3111c3426b47SDavid S. Miller if (!bp) 3112c3426b47SDavid S. Miller return -ENOMEM; 3113c3426b47SDavid S. Miller inet_peer_base_init(bp); 3114c3426b47SDavid S. Miller net->ipv6.peers = bp; 3115c3426b47SDavid S. Miller return 0; 3116c3426b47SDavid S. Miller } 3117c3426b47SDavid S. Miller 3118c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 3119c3426b47SDavid S. Miller { 3120c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 3121c3426b47SDavid S. Miller 3122c3426b47SDavid S. Miller net->ipv6.peers = NULL; 312356a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3124c3426b47SDavid S. Miller kfree(bp); 3125c3426b47SDavid S. Miller } 3126c3426b47SDavid S. Miller 31272b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3128c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3129c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3130c3426b47SDavid S. Miller }; 3131c3426b47SDavid S. Miller 3132d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3133d189634eSThomas Graf .init = ip6_route_net_init_late, 3134d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3135d189634eSThomas Graf }; 3136d189634eSThomas Graf 31378ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 31388ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 31398ed67789SDaniel Lezcano .priority = 0, 31408ed67789SDaniel Lezcano }; 31418ed67789SDaniel Lezcano 3142433d49c3SDaniel Lezcano int __init ip6_route_init(void) 31431da177e4SLinus Torvalds { 3144433d49c3SDaniel Lezcano int ret; 3145433d49c3SDaniel Lezcano 31469a7ec3a9SDaniel Lezcano ret = -ENOMEM; 31479a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 31489a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 31499a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 31509a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3151c19a28e1SFernando Carrijo goto out; 315214e50e57SDavid S. Miller 3153fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 31548ed67789SDaniel Lezcano if (ret) 3155bdb3289fSDaniel Lezcano goto out_kmem_cache; 3156bdb3289fSDaniel Lezcano 3157c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3158c3426b47SDavid S. Miller if (ret) 3159e8803b6cSDavid S. Miller goto out_dst_entries; 31602a0c451aSThomas Graf 31617e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 31627e52b33bSDavid S. Miller if (ret) 31637e52b33bSDavid S. Miller goto out_register_inetpeer; 3164c3426b47SDavid S. Miller 31655dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 31665dc121e9SArnaud Ebalard 31678ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 31688ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 31698ed67789SDaniel Lezcano * manually for init_net */ 3170d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 31718ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3172bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3173d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 31748ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3175d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 31768ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3177bdb3289fSDaniel Lezcano #endif 3178e8803b6cSDavid S. Miller ret = fib6_init(); 3179433d49c3SDaniel Lezcano if (ret) 31808ed67789SDaniel Lezcano goto out_register_subsys; 3181433d49c3SDaniel Lezcano 3182433d49c3SDaniel Lezcano ret = xfrm6_init(); 3183433d49c3SDaniel Lezcano if (ret) 3184e8803b6cSDavid S. Miller goto out_fib6_init; 3185c35b7e72SDaniel Lezcano 3186433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3187433d49c3SDaniel Lezcano if (ret) 3188433d49c3SDaniel Lezcano goto xfrm6_init; 31897e5449c2SDaniel Lezcano 3190d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3191d189634eSThomas Graf if (ret) 3192d189634eSThomas Graf goto fib6_rules_init; 3193d189634eSThomas Graf 3194433d49c3SDaniel Lezcano ret = -ENOBUFS; 3195c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3196c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3197c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3198d189634eSThomas Graf goto out_register_late_subsys; 3199433d49c3SDaniel Lezcano 32008ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3201cdb18761SDaniel Lezcano if (ret) 3202d189634eSThomas Graf goto out_register_late_subsys; 32038ed67789SDaniel Lezcano 3204433d49c3SDaniel Lezcano out: 3205433d49c3SDaniel Lezcano return ret; 3206433d49c3SDaniel Lezcano 3207d189634eSThomas Graf out_register_late_subsys: 3208d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3209433d49c3SDaniel Lezcano fib6_rules_init: 3210433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3211433d49c3SDaniel Lezcano xfrm6_init: 3212433d49c3SDaniel Lezcano xfrm6_fini(); 32132a0c451aSThomas Graf out_fib6_init: 32142a0c451aSThomas Graf fib6_gc_cleanup(); 32158ed67789SDaniel Lezcano out_register_subsys: 32168ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 32177e52b33bSDavid S. Miller out_register_inetpeer: 32187e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3219fc66f95cSEric Dumazet out_dst_entries: 3220fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3221433d49c3SDaniel Lezcano out_kmem_cache: 3222f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3223433d49c3SDaniel Lezcano goto out; 32241da177e4SLinus Torvalds } 32251da177e4SLinus Torvalds 32261da177e4SLinus Torvalds void ip6_route_cleanup(void) 32271da177e4SLinus Torvalds { 32288ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3229d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3230101367c2SThomas Graf fib6_rules_cleanup(); 32311da177e4SLinus Torvalds xfrm6_fini(); 32321da177e4SLinus Torvalds fib6_gc_cleanup(); 3233c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 32348ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 323541bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3236f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 32371da177e4SLinus Torvalds } 3238