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); 87aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb); 887150aedeSKamala R static int ip6_pkt_prohibit(struct sk_buff *skb); 89aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, 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); 95*4b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt); 9652bd4c0cSNicolas Dichtel static int rt6_score_route(struct rt6_info *rt, int oif, int strict); 971da177e4SLinus Torvalds 9870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 99efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 100b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 101b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 10295c96174SEric Dumazet unsigned int pref); 103efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 104b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 105b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex); 10670ceb4f5SYOSHIFUJI Hideaki #endif 10770ceb4f5SYOSHIFUJI Hideaki 108e8243534Sstephen hemminger static void rt6_bind_peer(struct rt6_info *rt, int create) 109e8243534Sstephen hemminger { 110e8243534Sstephen hemminger struct inet_peer_base *base; 111e8243534Sstephen hemminger struct inet_peer *peer; 112e8243534Sstephen hemminger 113e8243534Sstephen hemminger base = inetpeer_base_ptr(rt->_rt6i_peer); 114e8243534Sstephen hemminger if (!base) 115e8243534Sstephen hemminger return; 116e8243534Sstephen hemminger 117e8243534Sstephen hemminger peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create); 118e8243534Sstephen hemminger if (peer) { 119e8243534Sstephen hemminger if (!rt6_set_peer(rt, peer)) 120e8243534Sstephen hemminger inet_putpeer(peer); 121e8243534Sstephen hemminger } 122e8243534Sstephen hemminger } 123e8243534Sstephen hemminger 124e8243534Sstephen hemminger static struct inet_peer *__rt6_get_peer(struct rt6_info *rt, int create) 125e8243534Sstephen hemminger { 126e8243534Sstephen hemminger if (rt6_has_peer(rt)) 127e8243534Sstephen hemminger return rt6_peer_ptr(rt); 128e8243534Sstephen hemminger 129e8243534Sstephen hemminger rt6_bind_peer(rt, create); 130e8243534Sstephen hemminger return (rt6_has_peer(rt) ? rt6_peer_ptr(rt) : NULL); 131e8243534Sstephen hemminger } 132e8243534Sstephen hemminger 133e8243534Sstephen hemminger static struct inet_peer *rt6_get_peer_create(struct rt6_info *rt) 134e8243534Sstephen hemminger { 135e8243534Sstephen hemminger return __rt6_get_peer(rt, 1); 136e8243534Sstephen hemminger } 137e8243534Sstephen hemminger 13806582540SDavid S. Miller static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) 13906582540SDavid S. Miller { 14006582540SDavid S. Miller struct rt6_info *rt = (struct rt6_info *)dst; 14106582540SDavid S. Miller 142*4b32b5adSMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) 143*4b32b5adSMartin KaFai Lau return NULL; 144*4b32b5adSMartin KaFai Lau else 1453b471175SMartin KaFai Lau return dst_cow_metrics_generic(dst, old); 14606582540SDavid S. Miller } 14706582540SDavid S. Miller 148f894cbf8SDavid S. Miller static inline const void *choose_neigh_daddr(struct rt6_info *rt, 149f894cbf8SDavid S. Miller struct sk_buff *skb, 150f894cbf8SDavid S. Miller const void *daddr) 15139232973SDavid S. Miller { 15239232973SDavid S. Miller struct in6_addr *p = &rt->rt6i_gateway; 15339232973SDavid S. Miller 154a7563f34SDavid S. Miller if (!ipv6_addr_any(p)) 15539232973SDavid S. Miller return (const void *) p; 156f894cbf8SDavid S. Miller else if (skb) 157f894cbf8SDavid S. Miller return &ipv6_hdr(skb)->daddr; 15839232973SDavid S. Miller return daddr; 15939232973SDavid S. Miller } 16039232973SDavid S. Miller 161f894cbf8SDavid S. Miller static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, 162f894cbf8SDavid S. Miller struct sk_buff *skb, 163f894cbf8SDavid S. Miller const void *daddr) 164d3aaeb38SDavid S. Miller { 16539232973SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 16639232973SDavid S. Miller struct neighbour *n; 16739232973SDavid S. Miller 168f894cbf8SDavid S. Miller daddr = choose_neigh_daddr(rt, skb, daddr); 1698e022ee6SYOSHIFUJI Hideaki / 吉藤英明 n = __ipv6_neigh_lookup(dst->dev, daddr); 170f83c7790SDavid S. Miller if (n) 171f83c7790SDavid S. Miller return n; 172f83c7790SDavid S. Miller return neigh_create(&nd_tbl, daddr, dst->dev); 173f83c7790SDavid S. Miller } 174f83c7790SDavid S. Miller 1759a7ec3a9SDaniel Lezcano static struct dst_ops ip6_dst_ops_template = { 1761da177e4SLinus Torvalds .family = AF_INET6, 1771da177e4SLinus Torvalds .gc = ip6_dst_gc, 1781da177e4SLinus Torvalds .gc_thresh = 1024, 1791da177e4SLinus Torvalds .check = ip6_dst_check, 1800dbaee3bSDavid S. Miller .default_advmss = ip6_default_advmss, 181ebb762f2SSteffen Klassert .mtu = ip6_mtu, 18206582540SDavid S. Miller .cow_metrics = ipv6_cow_metrics, 1831da177e4SLinus Torvalds .destroy = ip6_dst_destroy, 1841da177e4SLinus Torvalds .ifdown = ip6_dst_ifdown, 1851da177e4SLinus Torvalds .negative_advice = ip6_negative_advice, 1861da177e4SLinus Torvalds .link_failure = ip6_link_failure, 1871da177e4SLinus Torvalds .update_pmtu = ip6_rt_update_pmtu, 1886e157b6aSDavid S. Miller .redirect = rt6_do_redirect, 1891ac06e03SHerbert Xu .local_out = __ip6_local_out, 190d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 1911da177e4SLinus Torvalds }; 1921da177e4SLinus Torvalds 193ebb762f2SSteffen Klassert static unsigned int ip6_blackhole_mtu(const struct dst_entry *dst) 194ec831ea7SRoland Dreier { 195618f9bc7SSteffen Klassert unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); 196618f9bc7SSteffen Klassert 197618f9bc7SSteffen Klassert return mtu ? : dst->dev->mtu; 198ec831ea7SRoland Dreier } 199ec831ea7SRoland Dreier 2006700c270SDavid S. Miller static void ip6_rt_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk, 2016700c270SDavid S. Miller struct sk_buff *skb, u32 mtu) 20214e50e57SDavid S. Miller { 20314e50e57SDavid S. Miller } 20414e50e57SDavid S. Miller 2056700c270SDavid S. Miller static void ip6_rt_blackhole_redirect(struct dst_entry *dst, struct sock *sk, 2066700c270SDavid S. Miller struct sk_buff *skb) 207b587ee3bSDavid S. Miller { 208b587ee3bSDavid S. Miller } 209b587ee3bSDavid S. Miller 2100972ddb2SHeld Bernhard static u32 *ip6_rt_blackhole_cow_metrics(struct dst_entry *dst, 2110972ddb2SHeld Bernhard unsigned long old) 2120972ddb2SHeld Bernhard { 2130972ddb2SHeld Bernhard return NULL; 2140972ddb2SHeld Bernhard } 2150972ddb2SHeld Bernhard 21614e50e57SDavid S. Miller static struct dst_ops ip6_dst_blackhole_ops = { 21714e50e57SDavid S. Miller .family = AF_INET6, 21814e50e57SDavid S. Miller .destroy = ip6_dst_destroy, 21914e50e57SDavid S. Miller .check = ip6_dst_check, 220ebb762f2SSteffen Klassert .mtu = ip6_blackhole_mtu, 221214f45c9SEric Dumazet .default_advmss = ip6_default_advmss, 22214e50e57SDavid S. Miller .update_pmtu = ip6_rt_blackhole_update_pmtu, 223b587ee3bSDavid S. Miller .redirect = ip6_rt_blackhole_redirect, 2240972ddb2SHeld Bernhard .cow_metrics = ip6_rt_blackhole_cow_metrics, 225d3aaeb38SDavid S. Miller .neigh_lookup = ip6_neigh_lookup, 22614e50e57SDavid S. Miller }; 22714e50e57SDavid S. Miller 22862fa8a84SDavid S. Miller static const u32 ip6_template_metrics[RTAX_MAX] = { 22914edd87dSLi RongQing [RTAX_HOPLIMIT - 1] = 0, 23062fa8a84SDavid S. Miller }; 23162fa8a84SDavid S. Miller 232fb0af4c7SEric Dumazet static const struct rt6_info ip6_null_entry_template = { 2331da177e4SLinus Torvalds .dst = { 2341da177e4SLinus Torvalds .__refcnt = ATOMIC_INIT(1), 2351da177e4SLinus Torvalds .__use = 1, 2362c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 2371da177e4SLinus Torvalds .error = -ENETUNREACH, 2381da177e4SLinus Torvalds .input = ip6_pkt_discard, 2391da177e4SLinus Torvalds .output = ip6_pkt_discard_out, 2401da177e4SLinus Torvalds }, 2411da177e4SLinus Torvalds .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2424f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 2431da177e4SLinus Torvalds .rt6i_metric = ~(u32) 0, 2441da177e4SLinus Torvalds .rt6i_ref = ATOMIC_INIT(1), 2451da177e4SLinus Torvalds }; 2461da177e4SLinus Torvalds 247101367c2SThomas Graf #ifdef CONFIG_IPV6_MULTIPLE_TABLES 248101367c2SThomas Graf 249fb0af4c7SEric Dumazet static const struct rt6_info ip6_prohibit_entry_template = { 250101367c2SThomas Graf .dst = { 251101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 252101367c2SThomas Graf .__use = 1, 2532c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 254101367c2SThomas Graf .error = -EACCES, 2559ce8ade0SThomas Graf .input = ip6_pkt_prohibit, 2569ce8ade0SThomas Graf .output = ip6_pkt_prohibit_out, 257101367c2SThomas Graf }, 258101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2594f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 260101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 261101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 262101367c2SThomas Graf }; 263101367c2SThomas Graf 264fb0af4c7SEric Dumazet static const struct rt6_info ip6_blk_hole_entry_template = { 265101367c2SThomas Graf .dst = { 266101367c2SThomas Graf .__refcnt = ATOMIC_INIT(1), 267101367c2SThomas Graf .__use = 1, 2682c20cbd7SNicolas Dichtel .obsolete = DST_OBSOLETE_FORCE_CHK, 269101367c2SThomas Graf .error = -EINVAL, 270352e512cSHerbert Xu .input = dst_discard, 271aad88724SEric Dumazet .output = dst_discard_sk, 272101367c2SThomas Graf }, 273101367c2SThomas Graf .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), 2744f724279SJean-Mickael Guerin .rt6i_protocol = RTPROT_KERNEL, 275101367c2SThomas Graf .rt6i_metric = ~(u32) 0, 276101367c2SThomas Graf .rt6i_ref = ATOMIC_INIT(1), 277101367c2SThomas Graf }; 278101367c2SThomas Graf 279101367c2SThomas Graf #endif 280101367c2SThomas Graf 2811da177e4SLinus Torvalds /* allocate dst with ip6_dst_ops */ 28297bab73fSDavid S. Miller static inline struct rt6_info *ip6_dst_alloc(struct net *net, 283957c665fSDavid S. Miller struct net_device *dev, 2848b96d22dSDavid S. Miller int flags, 2858b96d22dSDavid S. Miller struct fib6_table *table) 2861da177e4SLinus Torvalds { 28797bab73fSDavid S. Miller struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 2886f3118b5SNicolas Dichtel 0, DST_OBSOLETE_FORCE_CHK, flags); 289cf911662SDavid S. Miller 29097bab73fSDavid S. Miller if (rt) { 2918104891bSSteffen Klassert struct dst_entry *dst = &rt->dst; 2928104891bSSteffen Klassert 2938104891bSSteffen Klassert memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); 2948b96d22dSDavid S. Miller rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers); 29551ebd318SNicolas Dichtel INIT_LIST_HEAD(&rt->rt6i_siblings); 29697bab73fSDavid S. Miller } 297cf911662SDavid S. Miller return rt; 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds static void ip6_dst_destroy(struct dst_entry *dst) 3011da177e4SLinus Torvalds { 3021da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3031da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 304ecd98837SYOSHIFUJI Hideaki / 吉藤英明 struct dst_entry *from = dst->from; 3051da177e4SLinus Torvalds 3068e2ec639SYan, Zheng dst_destroy_metrics_generic(dst); 3078e2ec639SYan, Zheng 30838308473SDavid S. Miller if (idev) { 3091da177e4SLinus Torvalds rt->rt6i_idev = NULL; 3101da177e4SLinus Torvalds in6_dev_put(idev); 3111da177e4SLinus Torvalds } 3121716a961SGao feng 313ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst->from = NULL; 314ecd98837SYOSHIFUJI Hideaki / 吉藤英明 dst_release(from); 315b3419363SDavid S. Miller } 316b3419363SDavid S. Miller 3171da177e4SLinus Torvalds static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, 3181da177e4SLinus Torvalds int how) 3191da177e4SLinus Torvalds { 3201da177e4SLinus Torvalds struct rt6_info *rt = (struct rt6_info *)dst; 3211da177e4SLinus Torvalds struct inet6_dev *idev = rt->rt6i_idev; 3225a3e55d6SDenis V. Lunev struct net_device *loopback_dev = 323c346dca1SYOSHIFUJI Hideaki dev_net(dev)->loopback_dev; 3241da177e4SLinus Torvalds 32597cac082SDavid S. Miller if (dev != loopback_dev) { 32697cac082SDavid S. Miller if (idev && idev->dev == dev) { 3275a3e55d6SDenis V. Lunev struct inet6_dev *loopback_idev = 3285a3e55d6SDenis V. Lunev in6_dev_get(loopback_dev); 32938308473SDavid S. Miller if (loopback_idev) { 3301da177e4SLinus Torvalds rt->rt6i_idev = loopback_idev; 3311da177e4SLinus Torvalds in6_dev_put(idev); 3321da177e4SLinus Torvalds } 3331da177e4SLinus Torvalds } 33497cac082SDavid S. Miller } 3351da177e4SLinus Torvalds } 3361da177e4SLinus Torvalds 337a50feda5SEric Dumazet static bool rt6_check_expired(const struct rt6_info *rt) 3381da177e4SLinus Torvalds { 3391716a961SGao feng if (rt->rt6i_flags & RTF_EXPIRES) { 3401716a961SGao feng if (time_after(jiffies, rt->dst.expires)) 341a50feda5SEric Dumazet return true; 3421716a961SGao feng } else if (rt->dst.from) { 3433fd91fb3SLi RongQing return rt6_check_expired((struct rt6_info *) rt->dst.from); 3441716a961SGao feng } 345a50feda5SEric Dumazet return false; 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds 34851ebd318SNicolas Dichtel /* Multipath route selection: 34951ebd318SNicolas Dichtel * Hash based function using packet header and flowlabel. 35051ebd318SNicolas Dichtel * Adapted from fib_info_hashfn() 35151ebd318SNicolas Dichtel */ 35251ebd318SNicolas Dichtel static int rt6_info_hash_nhsfn(unsigned int candidate_count, 35351ebd318SNicolas Dichtel const struct flowi6 *fl6) 35451ebd318SNicolas Dichtel { 35551ebd318SNicolas Dichtel unsigned int val = fl6->flowi6_proto; 35651ebd318SNicolas Dichtel 357c08977bbSYOSHIFUJI Hideaki / 吉藤英明 val ^= ipv6_addr_hash(&fl6->daddr); 358c08977bbSYOSHIFUJI Hideaki / 吉藤英明 val ^= ipv6_addr_hash(&fl6->saddr); 35951ebd318SNicolas Dichtel 36051ebd318SNicolas Dichtel /* Work only if this not encapsulated */ 36151ebd318SNicolas Dichtel switch (fl6->flowi6_proto) { 36251ebd318SNicolas Dichtel case IPPROTO_UDP: 36351ebd318SNicolas Dichtel case IPPROTO_TCP: 36451ebd318SNicolas Dichtel case IPPROTO_SCTP: 365b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_sport; 366b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_dport; 36751ebd318SNicolas Dichtel break; 36851ebd318SNicolas Dichtel 36951ebd318SNicolas Dichtel case IPPROTO_ICMPV6: 370b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_icmp_type; 371b3ce5ae1SNicolas Dichtel val ^= (__force u16)fl6->fl6_icmp_code; 37251ebd318SNicolas Dichtel break; 37351ebd318SNicolas Dichtel } 37451ebd318SNicolas Dichtel /* RFC6438 recommands to use flowlabel */ 375b3ce5ae1SNicolas Dichtel val ^= (__force u32)fl6->flowlabel; 37651ebd318SNicolas Dichtel 37751ebd318SNicolas Dichtel /* Perhaps, we need to tune, this function? */ 37851ebd318SNicolas Dichtel val = val ^ (val >> 7) ^ (val >> 12); 37951ebd318SNicolas Dichtel return val % candidate_count; 38051ebd318SNicolas Dichtel } 38151ebd318SNicolas Dichtel 38251ebd318SNicolas Dichtel static struct rt6_info *rt6_multipath_select(struct rt6_info *match, 38352bd4c0cSNicolas Dichtel struct flowi6 *fl6, int oif, 38452bd4c0cSNicolas Dichtel int strict) 38551ebd318SNicolas Dichtel { 38651ebd318SNicolas Dichtel struct rt6_info *sibling, *next_sibling; 38751ebd318SNicolas Dichtel int route_choosen; 38851ebd318SNicolas Dichtel 38951ebd318SNicolas Dichtel route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6); 39051ebd318SNicolas Dichtel /* Don't change the route, if route_choosen == 0 39151ebd318SNicolas Dichtel * (siblings does not include ourself) 39251ebd318SNicolas Dichtel */ 39351ebd318SNicolas Dichtel if (route_choosen) 39451ebd318SNicolas Dichtel list_for_each_entry_safe(sibling, next_sibling, 39551ebd318SNicolas Dichtel &match->rt6i_siblings, rt6i_siblings) { 39651ebd318SNicolas Dichtel route_choosen--; 39751ebd318SNicolas Dichtel if (route_choosen == 0) { 39852bd4c0cSNicolas Dichtel if (rt6_score_route(sibling, oif, strict) < 0) 39952bd4c0cSNicolas Dichtel break; 40051ebd318SNicolas Dichtel match = sibling; 40151ebd318SNicolas Dichtel break; 40251ebd318SNicolas Dichtel } 40351ebd318SNicolas Dichtel } 40451ebd318SNicolas Dichtel return match; 40551ebd318SNicolas Dichtel } 40651ebd318SNicolas Dichtel 4071da177e4SLinus Torvalds /* 408c71099acSThomas Graf * Route lookup. Any table->tb6_lock is implied. 4091da177e4SLinus Torvalds */ 4101da177e4SLinus Torvalds 4118ed67789SDaniel Lezcano static inline struct rt6_info *rt6_device_match(struct net *net, 4128ed67789SDaniel Lezcano struct rt6_info *rt, 413b71d1d42SEric Dumazet const struct in6_addr *saddr, 4141da177e4SLinus Torvalds int oif, 415d420895eSYOSHIFUJI Hideaki int flags) 4161da177e4SLinus Torvalds { 4171da177e4SLinus Torvalds struct rt6_info *local = NULL; 4181da177e4SLinus Torvalds struct rt6_info *sprt; 4191da177e4SLinus Torvalds 420dd3abc4eSYOSHIFUJI Hideaki if (!oif && ipv6_addr_any(saddr)) 421dd3abc4eSYOSHIFUJI Hideaki goto out; 422dd3abc4eSYOSHIFUJI Hideaki 423d8d1f30bSChangli Gao for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { 424d1918542SDavid S. Miller struct net_device *dev = sprt->dst.dev; 425dd3abc4eSYOSHIFUJI Hideaki 426dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4271da177e4SLinus Torvalds if (dev->ifindex == oif) 4281da177e4SLinus Torvalds return sprt; 4291da177e4SLinus Torvalds if (dev->flags & IFF_LOOPBACK) { 43038308473SDavid S. Miller if (!sprt->rt6i_idev || 4311da177e4SLinus Torvalds sprt->rt6i_idev->dev->ifindex != oif) { 432d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE && oif) 4331da177e4SLinus Torvalds continue; 4341da177e4SLinus Torvalds if (local && (!oif || 4351da177e4SLinus Torvalds local->rt6i_idev->dev->ifindex == oif)) 4361da177e4SLinus Torvalds continue; 4371da177e4SLinus Torvalds } 4381da177e4SLinus Torvalds local = sprt; 4391da177e4SLinus Torvalds } 440dd3abc4eSYOSHIFUJI Hideaki } else { 441dd3abc4eSYOSHIFUJI Hideaki if (ipv6_chk_addr(net, saddr, dev, 442dd3abc4eSYOSHIFUJI Hideaki flags & RT6_LOOKUP_F_IFACE)) 443dd3abc4eSYOSHIFUJI Hideaki return sprt; 444dd3abc4eSYOSHIFUJI Hideaki } 4451da177e4SLinus Torvalds } 4461da177e4SLinus Torvalds 447dd3abc4eSYOSHIFUJI Hideaki if (oif) { 4481da177e4SLinus Torvalds if (local) 4491da177e4SLinus Torvalds return local; 4501da177e4SLinus Torvalds 451d420895eSYOSHIFUJI Hideaki if (flags & RT6_LOOKUP_F_IFACE) 4528ed67789SDaniel Lezcano return net->ipv6.ip6_null_entry; 4531da177e4SLinus Torvalds } 454dd3abc4eSYOSHIFUJI Hideaki out: 4551da177e4SLinus Torvalds return rt; 4561da177e4SLinus Torvalds } 4571da177e4SLinus Torvalds 45827097255SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 459c2f17e82SHannes Frederic Sowa struct __rt6_probe_work { 460c2f17e82SHannes Frederic Sowa struct work_struct work; 461c2f17e82SHannes Frederic Sowa struct in6_addr target; 462c2f17e82SHannes Frederic Sowa struct net_device *dev; 463c2f17e82SHannes Frederic Sowa }; 464c2f17e82SHannes Frederic Sowa 465c2f17e82SHannes Frederic Sowa static void rt6_probe_deferred(struct work_struct *w) 466c2f17e82SHannes Frederic Sowa { 467c2f17e82SHannes Frederic Sowa struct in6_addr mcaddr; 468c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work = 469c2f17e82SHannes Frederic Sowa container_of(w, struct __rt6_probe_work, work); 470c2f17e82SHannes Frederic Sowa 471c2f17e82SHannes Frederic Sowa addrconf_addr_solict_mult(&work->target, &mcaddr); 472c2f17e82SHannes Frederic Sowa ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL); 473c2f17e82SHannes Frederic Sowa dev_put(work->dev); 474662f5533SMichael Büsch kfree(work); 475c2f17e82SHannes Frederic Sowa } 476c2f17e82SHannes Frederic Sowa 47727097255SYOSHIFUJI Hideaki static void rt6_probe(struct rt6_info *rt) 47827097255SYOSHIFUJI Hideaki { 479f2c31e32SEric Dumazet struct neighbour *neigh; 48027097255SYOSHIFUJI Hideaki /* 48127097255SYOSHIFUJI Hideaki * Okay, this does not seem to be appropriate 48227097255SYOSHIFUJI Hideaki * for now, however, we need to check if it 48327097255SYOSHIFUJI Hideaki * is really so; aka Router Reachability Probing. 48427097255SYOSHIFUJI Hideaki * 48527097255SYOSHIFUJI Hideaki * Router Reachability Probe MUST be rate-limited 48627097255SYOSHIFUJI Hideaki * to no more than one per minute. 48727097255SYOSHIFUJI Hideaki */ 4882152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!rt || !(rt->rt6i_flags & RTF_GATEWAY)) 489fdd6681dSAmerigo Wang return; 4902152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 4912152caeaSYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 4922152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 4932152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_lock(&neigh->lock); 4942152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (neigh->nud_state & NUD_VALID) 4952152caeaSYOSHIFUJI Hideaki / 吉藤英明 goto out; 4967ff74a59SYOSHIFUJI Hideaki / 吉藤英明 } 4972152caeaSYOSHIFUJI Hideaki / 吉藤英明 4982152caeaSYOSHIFUJI Hideaki / 吉藤英明 if (!neigh || 49952e16356SYOSHIFUJI Hideaki time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { 500c2f17e82SHannes Frederic Sowa struct __rt6_probe_work *work; 50127097255SYOSHIFUJI Hideaki 502c2f17e82SHannes Frederic Sowa work = kmalloc(sizeof(*work), GFP_ATOMIC); 503c2f17e82SHannes Frederic Sowa 504c2f17e82SHannes Frederic Sowa if (neigh && work) 5057e980569SJiri Benc __neigh_set_probe_once(neigh); 5062152caeaSYOSHIFUJI Hideaki / 吉藤英明 507c2f17e82SHannes Frederic Sowa if (neigh) 508c2f17e82SHannes Frederic Sowa write_unlock(&neigh->lock); 509c2f17e82SHannes Frederic Sowa 510c2f17e82SHannes Frederic Sowa if (work) { 511c2f17e82SHannes Frederic Sowa INIT_WORK(&work->work, rt6_probe_deferred); 512c2f17e82SHannes Frederic Sowa work->target = rt->rt6i_gateway; 513c2f17e82SHannes Frederic Sowa dev_hold(rt->dst.dev); 514c2f17e82SHannes Frederic Sowa work->dev = rt->dst.dev; 515c2f17e82SHannes Frederic Sowa schedule_work(&work->work); 516c2f17e82SHannes Frederic Sowa } 517f2c31e32SEric Dumazet } else { 5182152caeaSYOSHIFUJI Hideaki / 吉藤英明 out: 5192152caeaSYOSHIFUJI Hideaki / 吉藤英明 write_unlock(&neigh->lock); 52027097255SYOSHIFUJI Hideaki } 5212152caeaSYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 522f2c31e32SEric Dumazet } 52327097255SYOSHIFUJI Hideaki #else 52427097255SYOSHIFUJI Hideaki static inline void rt6_probe(struct rt6_info *rt) 52527097255SYOSHIFUJI Hideaki { 52627097255SYOSHIFUJI Hideaki } 52727097255SYOSHIFUJI Hideaki #endif 52827097255SYOSHIFUJI Hideaki 5291da177e4SLinus Torvalds /* 530554cfb7eSYOSHIFUJI Hideaki * Default Router Selection (RFC 2461 6.3.6) 5311da177e4SLinus Torvalds */ 532b6f99a21SDave Jones static inline int rt6_check_dev(struct rt6_info *rt, int oif) 5331da177e4SLinus Torvalds { 534d1918542SDavid S. Miller struct net_device *dev = rt->dst.dev; 535161980f4SDavid S. Miller if (!oif || dev->ifindex == oif) 536554cfb7eSYOSHIFUJI Hideaki return 2; 537161980f4SDavid S. Miller if ((dev->flags & IFF_LOOPBACK) && 538161980f4SDavid S. Miller rt->rt6i_idev && rt->rt6i_idev->dev->ifindex == oif) 539161980f4SDavid S. Miller return 1; 540554cfb7eSYOSHIFUJI Hideaki return 0; 5411da177e4SLinus Torvalds } 5421da177e4SLinus Torvalds 543afc154e9SHannes Frederic Sowa static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt) 5441da177e4SLinus Torvalds { 545f2c31e32SEric Dumazet struct neighbour *neigh; 546afc154e9SHannes Frederic Sowa enum rt6_nud_state ret = RT6_NUD_FAIL_HARD; 547f2c31e32SEric Dumazet 5484d0c5911SYOSHIFUJI Hideaki if (rt->rt6i_flags & RTF_NONEXTHOP || 5494d0c5911SYOSHIFUJI Hideaki !(rt->rt6i_flags & RTF_GATEWAY)) 550afc154e9SHannes Frederic Sowa return RT6_NUD_SUCCEED; 551145a3621SYOSHIFUJI Hideaki / 吉藤英明 552145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_lock_bh(); 553145a3621SYOSHIFUJI Hideaki / 吉藤英明 neigh = __ipv6_neigh_lookup_noref(rt->dst.dev, &rt->rt6i_gateway); 554145a3621SYOSHIFUJI Hideaki / 吉藤英明 if (neigh) { 555145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_lock(&neigh->lock); 556554cfb7eSYOSHIFUJI Hideaki if (neigh->nud_state & NUD_VALID) 557afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 558398bcbebSYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 559a5a81f0bSPaul Marks else if (!(neigh->nud_state & NUD_FAILED)) 560afc154e9SHannes Frederic Sowa ret = RT6_NUD_SUCCEED; 5617e980569SJiri Benc else 5627e980569SJiri Benc ret = RT6_NUD_FAIL_PROBE; 563398bcbebSYOSHIFUJI Hideaki #endif 564145a3621SYOSHIFUJI Hideaki / 吉藤英明 read_unlock(&neigh->lock); 565afc154e9SHannes Frederic Sowa } else { 566afc154e9SHannes Frederic Sowa ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ? 5677e980569SJiri Benc RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR; 568a5a81f0bSPaul Marks } 569145a3621SYOSHIFUJI Hideaki / 吉藤英明 rcu_read_unlock_bh(); 570145a3621SYOSHIFUJI Hideaki / 吉藤英明 571a5a81f0bSPaul Marks return ret; 5721da177e4SLinus Torvalds } 5731da177e4SLinus Torvalds 574554cfb7eSYOSHIFUJI Hideaki static int rt6_score_route(struct rt6_info *rt, int oif, 575554cfb7eSYOSHIFUJI Hideaki int strict) 576554cfb7eSYOSHIFUJI Hideaki { 577a5a81f0bSPaul Marks int m; 5784d0c5911SYOSHIFUJI Hideaki 5794d0c5911SYOSHIFUJI Hideaki m = rt6_check_dev(rt, oif); 58077d16f45SYOSHIFUJI Hideaki if (!m && (strict & RT6_LOOKUP_F_IFACE)) 581afc154e9SHannes Frederic Sowa return RT6_NUD_FAIL_HARD; 582ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF 583ebacaaa0SYOSHIFUJI Hideaki m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2; 584ebacaaa0SYOSHIFUJI Hideaki #endif 585afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) { 586afc154e9SHannes Frederic Sowa int n = rt6_check_neigh(rt); 587afc154e9SHannes Frederic Sowa if (n < 0) 588afc154e9SHannes Frederic Sowa return n; 589afc154e9SHannes Frederic Sowa } 590554cfb7eSYOSHIFUJI Hideaki return m; 591554cfb7eSYOSHIFUJI Hideaki } 592554cfb7eSYOSHIFUJI Hideaki 593f11e6659SDavid S. Miller static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, 594afc154e9SHannes Frederic Sowa int *mpri, struct rt6_info *match, 595afc154e9SHannes Frederic Sowa bool *do_rr) 596554cfb7eSYOSHIFUJI Hideaki { 597554cfb7eSYOSHIFUJI Hideaki int m; 598afc154e9SHannes Frederic Sowa bool match_do_rr = false; 599554cfb7eSYOSHIFUJI Hideaki 600554cfb7eSYOSHIFUJI Hideaki if (rt6_check_expired(rt)) 601f11e6659SDavid S. Miller goto out; 602554cfb7eSYOSHIFUJI Hideaki 603554cfb7eSYOSHIFUJI Hideaki m = rt6_score_route(rt, oif, strict); 6047e980569SJiri Benc if (m == RT6_NUD_FAIL_DO_RR) { 605afc154e9SHannes Frederic Sowa match_do_rr = true; 606afc154e9SHannes Frederic Sowa m = 0; /* lowest valid score */ 6077e980569SJiri Benc } else if (m == RT6_NUD_FAIL_HARD) { 608f11e6659SDavid S. Miller goto out; 6091da177e4SLinus Torvalds } 610f11e6659SDavid S. Miller 611afc154e9SHannes Frederic Sowa if (strict & RT6_LOOKUP_F_REACHABLE) 612afc154e9SHannes Frederic Sowa rt6_probe(rt); 613afc154e9SHannes Frederic Sowa 6147e980569SJiri Benc /* note that m can be RT6_NUD_FAIL_PROBE at this point */ 615afc154e9SHannes Frederic Sowa if (m > *mpri) { 616afc154e9SHannes Frederic Sowa *do_rr = match_do_rr; 617afc154e9SHannes Frederic Sowa *mpri = m; 618afc154e9SHannes Frederic Sowa match = rt; 619afc154e9SHannes Frederic Sowa } 620f11e6659SDavid S. Miller out: 621f11e6659SDavid S. Miller return match; 6221da177e4SLinus Torvalds } 6231da177e4SLinus Torvalds 624f11e6659SDavid S. Miller static struct rt6_info *find_rr_leaf(struct fib6_node *fn, 625f11e6659SDavid S. Miller struct rt6_info *rr_head, 626afc154e9SHannes Frederic Sowa u32 metric, int oif, int strict, 627afc154e9SHannes Frederic Sowa bool *do_rr) 628f11e6659SDavid S. Miller { 6299fbdcfafSSteffen Klassert struct rt6_info *rt, *match, *cont; 630f11e6659SDavid S. Miller int mpri = -1; 631f11e6659SDavid S. Miller 632f11e6659SDavid S. Miller match = NULL; 6339fbdcfafSSteffen Klassert cont = NULL; 6349fbdcfafSSteffen Klassert for (rt = rr_head; rt; rt = rt->dst.rt6_next) { 6359fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 6369fbdcfafSSteffen Klassert cont = rt; 6379fbdcfafSSteffen Klassert break; 6389fbdcfafSSteffen Klassert } 6399fbdcfafSSteffen Klassert 640afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 6419fbdcfafSSteffen Klassert } 6429fbdcfafSSteffen Klassert 6439fbdcfafSSteffen Klassert for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) { 6449fbdcfafSSteffen Klassert if (rt->rt6i_metric != metric) { 6459fbdcfafSSteffen Klassert cont = rt; 6469fbdcfafSSteffen Klassert break; 6479fbdcfafSSteffen Klassert } 6489fbdcfafSSteffen Klassert 6499fbdcfafSSteffen Klassert match = find_match(rt, oif, strict, &mpri, match, do_rr); 6509fbdcfafSSteffen Klassert } 6519fbdcfafSSteffen Klassert 6529fbdcfafSSteffen Klassert if (match || !cont) 6539fbdcfafSSteffen Klassert return match; 6549fbdcfafSSteffen Klassert 6559fbdcfafSSteffen Klassert for (rt = cont; rt; rt = rt->dst.rt6_next) 656afc154e9SHannes Frederic Sowa match = find_match(rt, oif, strict, &mpri, match, do_rr); 657f11e6659SDavid S. Miller 658f11e6659SDavid S. Miller return match; 659f11e6659SDavid S. Miller } 660f11e6659SDavid S. Miller 661f11e6659SDavid S. Miller static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) 662f11e6659SDavid S. Miller { 663f11e6659SDavid S. Miller struct rt6_info *match, *rt0; 6648ed67789SDaniel Lezcano struct net *net; 665afc154e9SHannes Frederic Sowa bool do_rr = false; 666f11e6659SDavid S. Miller 667f11e6659SDavid S. Miller rt0 = fn->rr_ptr; 668f11e6659SDavid S. Miller if (!rt0) 669f11e6659SDavid S. Miller fn->rr_ptr = rt0 = fn->leaf; 670f11e6659SDavid S. Miller 671afc154e9SHannes Frederic Sowa match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict, 672afc154e9SHannes Frederic Sowa &do_rr); 673f11e6659SDavid S. Miller 674afc154e9SHannes Frederic Sowa if (do_rr) { 675d8d1f30bSChangli Gao struct rt6_info *next = rt0->dst.rt6_next; 676f11e6659SDavid S. Miller 677554cfb7eSYOSHIFUJI Hideaki /* no entries matched; do round-robin */ 678f11e6659SDavid S. Miller if (!next || next->rt6i_metric != rt0->rt6i_metric) 679f11e6659SDavid S. Miller next = fn->leaf; 680f11e6659SDavid S. Miller 681f11e6659SDavid S. Miller if (next != rt0) 682f11e6659SDavid S. Miller fn->rr_ptr = next; 683554cfb7eSYOSHIFUJI Hideaki } 684554cfb7eSYOSHIFUJI Hideaki 685d1918542SDavid S. Miller net = dev_net(rt0->dst.dev); 686a02cec21SEric Dumazet return match ? match : net->ipv6.ip6_null_entry; 6871da177e4SLinus Torvalds } 6881da177e4SLinus Torvalds 68970ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 69070ceb4f5SYOSHIFUJI Hideaki int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, 691b71d1d42SEric Dumazet const struct in6_addr *gwaddr) 69270ceb4f5SYOSHIFUJI Hideaki { 693c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 69470ceb4f5SYOSHIFUJI Hideaki struct route_info *rinfo = (struct route_info *) opt; 69570ceb4f5SYOSHIFUJI Hideaki struct in6_addr prefix_buf, *prefix; 69670ceb4f5SYOSHIFUJI Hideaki unsigned int pref; 6974bed72e4SYOSHIFUJI Hideaki unsigned long lifetime; 69870ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt; 69970ceb4f5SYOSHIFUJI Hideaki 70070ceb4f5SYOSHIFUJI Hideaki if (len < sizeof(struct route_info)) { 70170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 70270ceb4f5SYOSHIFUJI Hideaki } 70370ceb4f5SYOSHIFUJI Hideaki 70470ceb4f5SYOSHIFUJI Hideaki /* Sanity check for prefix_len and length */ 70570ceb4f5SYOSHIFUJI Hideaki if (rinfo->length > 3) { 70670ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 70770ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 128) { 70870ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 70970ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 64) { 71070ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 2) { 71170ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 71270ceb4f5SYOSHIFUJI Hideaki } 71370ceb4f5SYOSHIFUJI Hideaki } else if (rinfo->prefix_len > 0) { 71470ceb4f5SYOSHIFUJI Hideaki if (rinfo->length < 1) { 71570ceb4f5SYOSHIFUJI Hideaki return -EINVAL; 71670ceb4f5SYOSHIFUJI Hideaki } 71770ceb4f5SYOSHIFUJI Hideaki } 71870ceb4f5SYOSHIFUJI Hideaki 71970ceb4f5SYOSHIFUJI Hideaki pref = rinfo->route_pref; 72070ceb4f5SYOSHIFUJI Hideaki if (pref == ICMPV6_ROUTER_PREF_INVALID) 7213933fc95SJens Rosenboom return -EINVAL; 72270ceb4f5SYOSHIFUJI Hideaki 7234bed72e4SYOSHIFUJI Hideaki lifetime = addrconf_timeout_fixup(ntohl(rinfo->lifetime), HZ); 72470ceb4f5SYOSHIFUJI Hideaki 72570ceb4f5SYOSHIFUJI Hideaki if (rinfo->length == 3) 72670ceb4f5SYOSHIFUJI Hideaki prefix = (struct in6_addr *)rinfo->prefix; 72770ceb4f5SYOSHIFUJI Hideaki else { 72870ceb4f5SYOSHIFUJI Hideaki /* this function is safe */ 72970ceb4f5SYOSHIFUJI Hideaki ipv6_addr_prefix(&prefix_buf, 73070ceb4f5SYOSHIFUJI Hideaki (struct in6_addr *)rinfo->prefix, 73170ceb4f5SYOSHIFUJI Hideaki rinfo->prefix_len); 73270ceb4f5SYOSHIFUJI Hideaki prefix = &prefix_buf; 73370ceb4f5SYOSHIFUJI Hideaki } 73470ceb4f5SYOSHIFUJI Hideaki 735f104a567SDuan Jiong if (rinfo->prefix_len == 0) 736f104a567SDuan Jiong rt = rt6_get_dflt_router(gwaddr, dev); 737f104a567SDuan Jiong else 738f104a567SDuan Jiong rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, 739f104a567SDuan Jiong gwaddr, dev->ifindex); 74070ceb4f5SYOSHIFUJI Hideaki 74170ceb4f5SYOSHIFUJI Hideaki if (rt && !lifetime) { 742e0a1ad73SThomas Graf ip6_del_rt(rt); 74370ceb4f5SYOSHIFUJI Hideaki rt = NULL; 74470ceb4f5SYOSHIFUJI Hideaki } 74570ceb4f5SYOSHIFUJI Hideaki 74670ceb4f5SYOSHIFUJI Hideaki if (!rt && lifetime) 747efa2cea0SDaniel Lezcano rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, 74870ceb4f5SYOSHIFUJI Hideaki pref); 74970ceb4f5SYOSHIFUJI Hideaki else if (rt) 75070ceb4f5SYOSHIFUJI Hideaki rt->rt6i_flags = RTF_ROUTEINFO | 75170ceb4f5SYOSHIFUJI Hideaki (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); 75270ceb4f5SYOSHIFUJI Hideaki 75370ceb4f5SYOSHIFUJI Hideaki if (rt) { 7541716a961SGao feng if (!addrconf_finite_timeout(lifetime)) 7551716a961SGao feng rt6_clean_expires(rt); 7561716a961SGao feng else 7571716a961SGao feng rt6_set_expires(rt, jiffies + HZ * lifetime); 7581716a961SGao feng 75994e187c0SAmerigo Wang ip6_rt_put(rt); 76070ceb4f5SYOSHIFUJI Hideaki } 76170ceb4f5SYOSHIFUJI Hideaki return 0; 76270ceb4f5SYOSHIFUJI Hideaki } 76370ceb4f5SYOSHIFUJI Hideaki #endif 76470ceb4f5SYOSHIFUJI Hideaki 765a3c00e46SMartin KaFai Lau static struct fib6_node* fib6_backtrack(struct fib6_node *fn, 766a3c00e46SMartin KaFai Lau struct in6_addr *saddr) 767a3c00e46SMartin KaFai Lau { 768a3c00e46SMartin KaFai Lau struct fib6_node *pn; 769a3c00e46SMartin KaFai Lau while (1) { 770a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_TL_ROOT) 771a3c00e46SMartin KaFai Lau return NULL; 772a3c00e46SMartin KaFai Lau pn = fn->parent; 773a3c00e46SMartin KaFai Lau if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) 774a3c00e46SMartin KaFai Lau fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); 775a3c00e46SMartin KaFai Lau else 776a3c00e46SMartin KaFai Lau fn = pn; 777a3c00e46SMartin KaFai Lau if (fn->fn_flags & RTN_RTINFO) 778a3c00e46SMartin KaFai Lau return fn; 779a3c00e46SMartin KaFai Lau } 780a3c00e46SMartin KaFai Lau } 781c71099acSThomas Graf 7828ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_lookup(struct net *net, 7838ed67789SDaniel Lezcano struct fib6_table *table, 7844c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 7851da177e4SLinus Torvalds { 7861da177e4SLinus Torvalds struct fib6_node *fn; 7871da177e4SLinus Torvalds struct rt6_info *rt; 7881da177e4SLinus Torvalds 789c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 7904c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 791c71099acSThomas Graf restart: 792c71099acSThomas Graf rt = fn->leaf; 7934c9483b2SDavid S. Miller rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); 79451ebd318SNicolas Dichtel if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) 79552bd4c0cSNicolas Dichtel rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags); 796a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 797a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 798a3c00e46SMartin KaFai Lau if (fn) 799a3c00e46SMartin KaFai Lau goto restart; 800a3c00e46SMartin KaFai Lau } 801d8d1f30bSChangli Gao dst_use(&rt->dst, jiffies); 802c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 8031da177e4SLinus Torvalds return rt; 804c71099acSThomas Graf 805c71099acSThomas Graf } 806c71099acSThomas Graf 807ea6e574eSFlorian Westphal struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, 808ea6e574eSFlorian Westphal int flags) 809ea6e574eSFlorian Westphal { 810ea6e574eSFlorian Westphal return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_lookup); 811ea6e574eSFlorian Westphal } 812ea6e574eSFlorian Westphal EXPORT_SYMBOL_GPL(ip6_route_lookup); 813ea6e574eSFlorian Westphal 8149acd9f3aSYOSHIFUJI Hideaki struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, 8159acd9f3aSYOSHIFUJI Hideaki const struct in6_addr *saddr, int oif, int strict) 816c71099acSThomas Graf { 8174c9483b2SDavid S. Miller struct flowi6 fl6 = { 8184c9483b2SDavid S. Miller .flowi6_oif = oif, 8194c9483b2SDavid S. Miller .daddr = *daddr, 820c71099acSThomas Graf }; 821c71099acSThomas Graf struct dst_entry *dst; 82277d16f45SYOSHIFUJI Hideaki int flags = strict ? RT6_LOOKUP_F_IFACE : 0; 823c71099acSThomas Graf 824adaa70bbSThomas Graf if (saddr) { 8254c9483b2SDavid S. Miller memcpy(&fl6.saddr, saddr, sizeof(*saddr)); 826adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 827adaa70bbSThomas Graf } 828adaa70bbSThomas Graf 8294c9483b2SDavid S. Miller dst = fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_lookup); 830c71099acSThomas Graf if (dst->error == 0) 831c71099acSThomas Graf return (struct rt6_info *) dst; 832c71099acSThomas Graf 833c71099acSThomas Graf dst_release(dst); 834c71099acSThomas Graf 8351da177e4SLinus Torvalds return NULL; 8361da177e4SLinus Torvalds } 8377159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(rt6_lookup); 8387159039aSYOSHIFUJI Hideaki 839c71099acSThomas Graf /* ip6_ins_rt is called with FREE table->tb6_lock. 8401da177e4SLinus Torvalds It takes new route entry, the addition fails by any reason the 8411da177e4SLinus Torvalds route is freed. In any case, if caller does not hold it, it may 8421da177e4SLinus Torvalds be destroyed. 8431da177e4SLinus Torvalds */ 8441da177e4SLinus Torvalds 845e5fd387aSMichal Kubeček static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, 846e715b6d3SFlorian Westphal struct mx6_config *mxc) 8471da177e4SLinus Torvalds { 8481da177e4SLinus Torvalds int err; 849c71099acSThomas Graf struct fib6_table *table; 8501da177e4SLinus Torvalds 851c71099acSThomas Graf table = rt->rt6i_table; 852c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 853e715b6d3SFlorian Westphal err = fib6_add(&table->tb6_root, rt, info, mxc); 854c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 8551da177e4SLinus Torvalds 8561da177e4SLinus Torvalds return err; 8571da177e4SLinus Torvalds } 8581da177e4SLinus Torvalds 85940e22e8fSThomas Graf int ip6_ins_rt(struct rt6_info *rt) 86040e22e8fSThomas Graf { 861e715b6d3SFlorian Westphal struct nl_info info = { .nl_net = dev_net(rt->dst.dev), }; 862e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 863e715b6d3SFlorian Westphal 864e715b6d3SFlorian Westphal return __ip6_ins_rt(rt, &info, &mxc); 86540e22e8fSThomas Graf } 86640e22e8fSThomas Graf 8671716a961SGao feng static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, 86821efcfa0SEric Dumazet const struct in6_addr *daddr, 869b71d1d42SEric Dumazet const struct in6_addr *saddr) 8701da177e4SLinus Torvalds { 8711da177e4SLinus Torvalds struct rt6_info *rt; 8721da177e4SLinus Torvalds 8731da177e4SLinus Torvalds /* 8741da177e4SLinus Torvalds * Clone the route. 8751da177e4SLinus Torvalds */ 8761da177e4SLinus Torvalds 87721efcfa0SEric Dumazet rt = ip6_rt_copy(ort, daddr); 8781da177e4SLinus Torvalds 8791da177e4SLinus Torvalds if (rt) { 880bb3c3686SDavid S. Miller if (ort->rt6i_dst.plen != 128 && 88121efcfa0SEric Dumazet ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) 88258c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 8831da177e4SLinus Torvalds 8841da177e4SLinus Torvalds rt->rt6i_flags |= RTF_CACHE; 8851da177e4SLinus Torvalds 8861da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 8871da177e4SLinus Torvalds if (rt->rt6i_src.plen && saddr) { 8884e3fd7a0SAlexey Dobriyan rt->rt6i_src.addr = *saddr; 8891da177e4SLinus Torvalds rt->rt6i_src.plen = 128; 8901da177e4SLinus Torvalds } 8911da177e4SLinus Torvalds #endif 89295a9a5baSYOSHIFUJI Hideaki } 8931da177e4SLinus Torvalds 8941da177e4SLinus Torvalds return rt; 8951da177e4SLinus Torvalds } 89695a9a5baSYOSHIFUJI Hideaki 89721efcfa0SEric Dumazet static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, 89821efcfa0SEric Dumazet const struct in6_addr *daddr) 899299d9939SYOSHIFUJI Hideaki { 90021efcfa0SEric Dumazet struct rt6_info *rt = ip6_rt_copy(ort, daddr); 90121efcfa0SEric Dumazet 902887c95ccSYOSHIFUJI Hideaki / 吉藤英明 if (rt) 903299d9939SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_CACHE; 904299d9939SYOSHIFUJI Hideaki return rt; 905299d9939SYOSHIFUJI Hideaki } 906299d9939SYOSHIFUJI Hideaki 9078ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, 9084c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 9091da177e4SLinus Torvalds { 910367efcb9SMartin KaFai Lau struct fib6_node *fn, *saved_fn; 911519fbd87SYOSHIFUJI Hideaki struct rt6_info *rt, *nrt; 912c71099acSThomas Graf int strict = 0; 9131da177e4SLinus Torvalds int attempts = 3; 914519fbd87SYOSHIFUJI Hideaki int err; 9151da177e4SLinus Torvalds 91677d16f45SYOSHIFUJI Hideaki strict |= flags & RT6_LOOKUP_F_IFACE; 917367efcb9SMartin KaFai Lau if (net->ipv6.devconf_all->forwarding == 0) 918367efcb9SMartin KaFai Lau strict |= RT6_LOOKUP_F_REACHABLE; 9191da177e4SLinus Torvalds 920a3c00e46SMartin KaFai Lau redo_fib6_lookup_lock: 921c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 9221da177e4SLinus Torvalds 9234c9483b2SDavid S. Miller fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 924367efcb9SMartin KaFai Lau saved_fn = fn; 9251da177e4SLinus Torvalds 926a3c00e46SMartin KaFai Lau redo_rt6_select: 927367efcb9SMartin KaFai Lau rt = rt6_select(fn, oif, strict); 92852bd4c0cSNicolas Dichtel if (rt->rt6i_nsiblings) 929367efcb9SMartin KaFai Lau rt = rt6_multipath_select(rt, fl6, oif, strict); 930a3c00e46SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 931a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 932a3c00e46SMartin KaFai Lau if (fn) 933a3c00e46SMartin KaFai Lau goto redo_rt6_select; 934367efcb9SMartin KaFai Lau else if (strict & RT6_LOOKUP_F_REACHABLE) { 935367efcb9SMartin KaFai Lau /* also consider unreachable route */ 936367efcb9SMartin KaFai Lau strict &= ~RT6_LOOKUP_F_REACHABLE; 937367efcb9SMartin KaFai Lau fn = saved_fn; 938367efcb9SMartin KaFai Lau goto redo_rt6_select; 939367efcb9SMartin KaFai Lau } else { 940367efcb9SMartin KaFai Lau dst_hold(&rt->dst); 941367efcb9SMartin KaFai Lau read_unlock_bh(&table->tb6_lock); 942367efcb9SMartin KaFai Lau goto out2; 943367efcb9SMartin KaFai Lau } 944a3c00e46SMartin KaFai Lau } 945a3c00e46SMartin KaFai Lau 946d8d1f30bSChangli Gao dst_hold(&rt->dst); 947c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 9481da177e4SLinus Torvalds 94994c77bb4SMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) 95094c77bb4SMartin KaFai Lau goto out2; 95194c77bb4SMartin KaFai Lau 952c440f160SYOSHIFUJI Hideaki / 吉藤英明 if (!(rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY))) 9534c9483b2SDavid S. Miller nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); 954653437d0SMartin KaFai Lau else if (!(rt->dst.flags & DST_HOST) || !(rt->dst.flags & RTF_LOCAL)) 9554c9483b2SDavid S. Miller nrt = rt6_alloc_clone(rt, &fl6->daddr); 9567343ff31SDavid S. Miller else 9577343ff31SDavid S. Miller goto out2; 9581da177e4SLinus Torvalds 95994e187c0SAmerigo Wang ip6_rt_put(rt); 9608ed67789SDaniel Lezcano rt = nrt ? : net->ipv6.ip6_null_entry; 9611da177e4SLinus Torvalds 962d8d1f30bSChangli Gao dst_hold(&rt->dst); 963e40cf353SYOSHIFUJI Hideaki if (nrt) { 96440e22e8fSThomas Graf err = ip6_ins_rt(nrt); 965e40cf353SYOSHIFUJI Hideaki if (!err) 966e40cf353SYOSHIFUJI Hideaki goto out2; 967e40cf353SYOSHIFUJI Hideaki } 968e40cf353SYOSHIFUJI Hideaki 969e40cf353SYOSHIFUJI Hideaki if (--attempts <= 0) 9701da177e4SLinus Torvalds goto out2; 9711da177e4SLinus Torvalds 972519fbd87SYOSHIFUJI Hideaki /* 973c71099acSThomas Graf * Race condition! In the gap, when table->tb6_lock was 974519fbd87SYOSHIFUJI Hideaki * released someone could insert this route. Relookup. 9751da177e4SLinus Torvalds */ 97694e187c0SAmerigo Wang ip6_rt_put(rt); 977a3c00e46SMartin KaFai Lau goto redo_fib6_lookup_lock; 978e40cf353SYOSHIFUJI Hideaki 9791da177e4SLinus Torvalds out2: 980*4b32b5adSMartin KaFai Lau rt6_dst_from_metrics_check(rt); 981d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 982d8d1f30bSChangli Gao rt->dst.__use++; 983c71099acSThomas Graf 984c71099acSThomas Graf return rt; 985c71099acSThomas Graf } 986c71099acSThomas Graf 9878ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table *table, 9884c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 9894acad72dSPavel Emelyanov { 9904c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); 9914acad72dSPavel Emelyanov } 9924acad72dSPavel Emelyanov 99372331bc0SShmulik Ladkani static struct dst_entry *ip6_route_input_lookup(struct net *net, 99472331bc0SShmulik Ladkani struct net_device *dev, 99572331bc0SShmulik Ladkani struct flowi6 *fl6, int flags) 99672331bc0SShmulik Ladkani { 99772331bc0SShmulik Ladkani if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) 99872331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_IFACE; 99972331bc0SShmulik Ladkani 100072331bc0SShmulik Ladkani return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); 100172331bc0SShmulik Ladkani } 100272331bc0SShmulik Ladkani 1003c71099acSThomas Graf void ip6_route_input(struct sk_buff *skb) 1004c71099acSThomas Graf { 1005b71d1d42SEric Dumazet const struct ipv6hdr *iph = ipv6_hdr(skb); 1006c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(skb->dev); 1007adaa70bbSThomas Graf int flags = RT6_LOOKUP_F_HAS_SADDR; 10084c9483b2SDavid S. Miller struct flowi6 fl6 = { 10094c9483b2SDavid S. Miller .flowi6_iif = skb->dev->ifindex, 10104c9483b2SDavid S. Miller .daddr = iph->daddr, 10114c9483b2SDavid S. Miller .saddr = iph->saddr, 10126502ca52SYOSHIFUJI Hideaki / 吉藤英明 .flowlabel = ip6_flowinfo(iph), 10134c9483b2SDavid S. Miller .flowi6_mark = skb->mark, 10144c9483b2SDavid S. Miller .flowi6_proto = iph->nexthdr, 1015c71099acSThomas Graf }; 1016adaa70bbSThomas Graf 101772331bc0SShmulik Ladkani skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); 1018c71099acSThomas Graf } 1019c71099acSThomas Graf 10208ed67789SDaniel Lezcano static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, 10214c9483b2SDavid S. Miller struct flowi6 *fl6, int flags) 1022c71099acSThomas Graf { 10234c9483b2SDavid S. Miller return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, flags); 1024c71099acSThomas Graf } 1025c71099acSThomas Graf 10269c7a4f9cSFlorian Westphal struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk, 10274c9483b2SDavid S. Miller struct flowi6 *fl6) 1028c71099acSThomas Graf { 1029c71099acSThomas Graf int flags = 0; 1030c71099acSThomas Graf 10311fb9489bSPavel Emelyanov fl6->flowi6_iif = LOOPBACK_IFINDEX; 10324dc27d1cSDavid McCullough 10334c9483b2SDavid S. Miller if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr)) 103477d16f45SYOSHIFUJI Hideaki flags |= RT6_LOOKUP_F_IFACE; 1035c71099acSThomas Graf 10364c9483b2SDavid S. Miller if (!ipv6_addr_any(&fl6->saddr)) 1037adaa70bbSThomas Graf flags |= RT6_LOOKUP_F_HAS_SADDR; 10380c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 else if (sk) 10390c9a2ac1SYOSHIFUJI Hideaki / 吉藤英明 flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); 1040adaa70bbSThomas Graf 10414c9483b2SDavid S. Miller return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_output); 10421da177e4SLinus Torvalds } 10437159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ip6_route_output); 10441da177e4SLinus Torvalds 10452774c131SDavid S. Miller struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) 104614e50e57SDavid S. Miller { 10475c1e6aa3SDavid S. Miller struct rt6_info *rt, *ort = (struct rt6_info *) dst_orig; 104814e50e57SDavid S. Miller struct dst_entry *new = NULL; 104914e50e57SDavid S. Miller 1050f5b0a874SDavid S. Miller rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, DST_OBSOLETE_NONE, 0); 105114e50e57SDavid S. Miller if (rt) { 1052d8d1f30bSChangli Gao new = &rt->dst; 105314e50e57SDavid S. Miller 10548104891bSSteffen Klassert memset(new + 1, 0, sizeof(*rt) - sizeof(*new)); 10558104891bSSteffen Klassert rt6_init_peer(rt, net->ipv6.peers); 10568104891bSSteffen Klassert 105714e50e57SDavid S. Miller new->__use = 1; 1058352e512cSHerbert Xu new->input = dst_discard; 1059aad88724SEric Dumazet new->output = dst_discard_sk; 106014e50e57SDavid S. Miller 106121efcfa0SEric Dumazet if (dst_metrics_read_only(&ort->dst)) 106221efcfa0SEric Dumazet new->_metrics = ort->dst._metrics; 106321efcfa0SEric Dumazet else 1064defb3519SDavid S. Miller dst_copy_metrics(new, &ort->dst); 106514e50e57SDavid S. Miller rt->rt6i_idev = ort->rt6i_idev; 106614e50e57SDavid S. Miller if (rt->rt6i_idev) 106714e50e57SDavid S. Miller in6_dev_hold(rt->rt6i_idev); 106814e50e57SDavid S. Miller 10694e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 10701716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 107114e50e57SDavid S. Miller rt->rt6i_metric = 0; 107214e50e57SDavid S. Miller 107314e50e57SDavid S. Miller memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 107414e50e57SDavid S. Miller #ifdef CONFIG_IPV6_SUBTREES 107514e50e57SDavid S. Miller memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 107614e50e57SDavid S. Miller #endif 107714e50e57SDavid S. Miller 107814e50e57SDavid S. Miller dst_free(new); 107914e50e57SDavid S. Miller } 108014e50e57SDavid S. Miller 108169ead7afSDavid S. Miller dst_release(dst_orig); 108269ead7afSDavid S. Miller return new ? new : ERR_PTR(-ENOMEM); 108314e50e57SDavid S. Miller } 108414e50e57SDavid S. Miller 10851da177e4SLinus Torvalds /* 10861da177e4SLinus Torvalds * Destination cache support functions 10871da177e4SLinus Torvalds */ 10881da177e4SLinus Torvalds 1089*4b32b5adSMartin KaFai Lau static void rt6_dst_from_metrics_check(struct rt6_info *rt) 1090*4b32b5adSMartin KaFai Lau { 1091*4b32b5adSMartin KaFai Lau if (rt->dst.from && 1092*4b32b5adSMartin KaFai Lau dst_metrics_ptr(&rt->dst) != dst_metrics_ptr(rt->dst.from)) 1093*4b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true); 1094*4b32b5adSMartin KaFai Lau } 1095*4b32b5adSMartin KaFai Lau 10961da177e4SLinus Torvalds static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) 10971da177e4SLinus Torvalds { 10981da177e4SLinus Torvalds struct rt6_info *rt; 10991da177e4SLinus Torvalds 11001da177e4SLinus Torvalds rt = (struct rt6_info *) dst; 11011da177e4SLinus Torvalds 11026f3118b5SNicolas Dichtel /* All IPV6 dsts are created with ->obsolete set to the value 11036f3118b5SNicolas Dichtel * DST_OBSOLETE_FORCE_CHK which forces validation calls down 11046f3118b5SNicolas Dichtel * into this function always. 11056f3118b5SNicolas Dichtel */ 1106e3bc10bdSHannes Frederic Sowa if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) 11071da177e4SLinus Torvalds return NULL; 1108e3bc10bdSHannes Frederic Sowa 1109e3bc10bdSHannes Frederic Sowa if (rt6_check_expired(rt)) 1110e3bc10bdSHannes Frederic Sowa return NULL; 1111e3bc10bdSHannes Frederic Sowa 1112*4b32b5adSMartin KaFai Lau rt6_dst_from_metrics_check(rt); 1113*4b32b5adSMartin KaFai Lau 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); 1159653437d0SMartin KaFai Lau if (mtu < dst_mtu(dst) && (rt6->rt6i_flags & RTF_CACHE)) { 116081aded24SDavid S. Miller struct net *net = dev_net(dst->dev); 116181aded24SDavid S. Miller 11621da177e4SLinus Torvalds rt6->rt6i_flags |= RTF_MODIFIED; 11639d289715SHagen Paul Pfeifer if (mtu < IPV6_MIN_MTU) 11641da177e4SLinus Torvalds mtu = IPV6_MIN_MTU; 11659d289715SHagen Paul Pfeifer 1166*4b32b5adSMartin KaFai Lau rt6->rt6i_pmtu = mtu; 116781aded24SDavid S. Miller rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires); 11681da177e4SLinus Torvalds } 11691da177e4SLinus Torvalds } 11701da177e4SLinus Torvalds 117142ae66c8SDavid S. Miller void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, 117242ae66c8SDavid S. Miller int oif, u32 mark) 117381aded24SDavid S. Miller { 117481aded24SDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 117581aded24SDavid S. Miller struct dst_entry *dst; 117681aded24SDavid S. Miller struct flowi6 fl6; 117781aded24SDavid S. Miller 117881aded24SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 117981aded24SDavid S. Miller fl6.flowi6_oif = oif; 11801b3c61dcSLorenzo Colitti fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark); 118181aded24SDavid S. Miller fl6.daddr = iph->daddr; 118281aded24SDavid S. Miller fl6.saddr = iph->saddr; 11836502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 118481aded24SDavid S. Miller 118581aded24SDavid S. Miller dst = ip6_route_output(net, NULL, &fl6); 118681aded24SDavid S. Miller if (!dst->error) 11876700c270SDavid S. Miller ip6_rt_update_pmtu(dst, NULL, skb, ntohl(mtu)); 118881aded24SDavid S. Miller dst_release(dst); 118981aded24SDavid S. Miller } 119081aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_update_pmtu); 119181aded24SDavid S. Miller 119281aded24SDavid S. Miller void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) 119381aded24SDavid S. Miller { 119481aded24SDavid S. Miller ip6_update_pmtu(skb, sock_net(sk), mtu, 119581aded24SDavid S. Miller sk->sk_bound_dev_if, sk->sk_mark); 119681aded24SDavid S. Miller } 119781aded24SDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); 119881aded24SDavid S. Miller 1199b55b76b2SDuan Jiong /* Handle redirects */ 1200b55b76b2SDuan Jiong struct ip6rd_flowi { 1201b55b76b2SDuan Jiong struct flowi6 fl6; 1202b55b76b2SDuan Jiong struct in6_addr gateway; 1203b55b76b2SDuan Jiong }; 1204b55b76b2SDuan Jiong 1205b55b76b2SDuan Jiong static struct rt6_info *__ip6_route_redirect(struct net *net, 1206b55b76b2SDuan Jiong struct fib6_table *table, 1207b55b76b2SDuan Jiong struct flowi6 *fl6, 1208b55b76b2SDuan Jiong int flags) 1209b55b76b2SDuan Jiong { 1210b55b76b2SDuan Jiong struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; 1211b55b76b2SDuan Jiong struct rt6_info *rt; 1212b55b76b2SDuan Jiong struct fib6_node *fn; 1213b55b76b2SDuan Jiong 1214b55b76b2SDuan Jiong /* Get the "current" route for this destination and 1215b55b76b2SDuan Jiong * check if the redirect has come from approriate router. 1216b55b76b2SDuan Jiong * 1217b55b76b2SDuan Jiong * RFC 4861 specifies that redirects should only be 1218b55b76b2SDuan Jiong * accepted if they come from the nexthop to the target. 1219b55b76b2SDuan Jiong * Due to the way the routes are chosen, this notion 1220b55b76b2SDuan Jiong * is a bit fuzzy and one might need to check all possible 1221b55b76b2SDuan Jiong * routes. 1222b55b76b2SDuan Jiong */ 1223b55b76b2SDuan Jiong 1224b55b76b2SDuan Jiong read_lock_bh(&table->tb6_lock); 1225b55b76b2SDuan Jiong fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); 1226b55b76b2SDuan Jiong restart: 1227b55b76b2SDuan Jiong for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 1228b55b76b2SDuan Jiong if (rt6_check_expired(rt)) 1229b55b76b2SDuan Jiong continue; 1230b55b76b2SDuan Jiong if (rt->dst.error) 1231b55b76b2SDuan Jiong break; 1232b55b76b2SDuan Jiong if (!(rt->rt6i_flags & RTF_GATEWAY)) 1233b55b76b2SDuan Jiong continue; 1234b55b76b2SDuan Jiong if (fl6->flowi6_oif != rt->dst.dev->ifindex) 1235b55b76b2SDuan Jiong continue; 1236b55b76b2SDuan Jiong if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) 1237b55b76b2SDuan Jiong continue; 1238b55b76b2SDuan Jiong break; 1239b55b76b2SDuan Jiong } 1240b55b76b2SDuan Jiong 1241b55b76b2SDuan Jiong if (!rt) 1242b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1243b55b76b2SDuan Jiong else if (rt->dst.error) { 1244b55b76b2SDuan Jiong rt = net->ipv6.ip6_null_entry; 1245b0a1ba59SMartin KaFai Lau goto out; 1246b0a1ba59SMartin KaFai Lau } 1247b0a1ba59SMartin KaFai Lau 1248b0a1ba59SMartin KaFai Lau if (rt == net->ipv6.ip6_null_entry) { 1249a3c00e46SMartin KaFai Lau fn = fib6_backtrack(fn, &fl6->saddr); 1250a3c00e46SMartin KaFai Lau if (fn) 1251a3c00e46SMartin KaFai Lau goto restart; 1252b55b76b2SDuan Jiong } 1253a3c00e46SMartin KaFai Lau 1254b0a1ba59SMartin KaFai Lau out: 1255b55b76b2SDuan Jiong dst_hold(&rt->dst); 1256b55b76b2SDuan Jiong 1257b55b76b2SDuan Jiong read_unlock_bh(&table->tb6_lock); 1258b55b76b2SDuan Jiong 1259b55b76b2SDuan Jiong return rt; 1260b55b76b2SDuan Jiong }; 1261b55b76b2SDuan Jiong 1262b55b76b2SDuan Jiong static struct dst_entry *ip6_route_redirect(struct net *net, 1263b55b76b2SDuan Jiong const struct flowi6 *fl6, 1264b55b76b2SDuan Jiong const struct in6_addr *gateway) 1265b55b76b2SDuan Jiong { 1266b55b76b2SDuan Jiong int flags = RT6_LOOKUP_F_HAS_SADDR; 1267b55b76b2SDuan Jiong struct ip6rd_flowi rdfl; 1268b55b76b2SDuan Jiong 1269b55b76b2SDuan Jiong rdfl.fl6 = *fl6; 1270b55b76b2SDuan Jiong rdfl.gateway = *gateway; 1271b55b76b2SDuan Jiong 1272b55b76b2SDuan Jiong return fib6_rule_lookup(net, &rdfl.fl6, 1273b55b76b2SDuan Jiong flags, __ip6_route_redirect); 1274b55b76b2SDuan Jiong } 1275b55b76b2SDuan Jiong 12763a5ad2eeSDavid S. Miller void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) 12773a5ad2eeSDavid S. Miller { 12783a5ad2eeSDavid S. Miller const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; 12793a5ad2eeSDavid S. Miller struct dst_entry *dst; 12803a5ad2eeSDavid S. Miller struct flowi6 fl6; 12813a5ad2eeSDavid S. Miller 12823a5ad2eeSDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 1283e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 12843a5ad2eeSDavid S. Miller fl6.flowi6_oif = oif; 12853a5ad2eeSDavid S. Miller fl6.flowi6_mark = mark; 12863a5ad2eeSDavid S. Miller fl6.daddr = iph->daddr; 12873a5ad2eeSDavid S. Miller fl6.saddr = iph->saddr; 12886502ca52SYOSHIFUJI Hideaki / 吉藤英明 fl6.flowlabel = ip6_flowinfo(iph); 12893a5ad2eeSDavid S. Miller 1290b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); 12916700c270SDavid S. Miller rt6_do_redirect(dst, NULL, skb); 12923a5ad2eeSDavid S. Miller dst_release(dst); 12933a5ad2eeSDavid S. Miller } 12943a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_redirect); 12953a5ad2eeSDavid S. Miller 1296c92a59ecSDuan Jiong void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, 1297c92a59ecSDuan Jiong u32 mark) 1298c92a59ecSDuan Jiong { 1299c92a59ecSDuan Jiong const struct ipv6hdr *iph = ipv6_hdr(skb); 1300c92a59ecSDuan Jiong const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb); 1301c92a59ecSDuan Jiong struct dst_entry *dst; 1302c92a59ecSDuan Jiong struct flowi6 fl6; 1303c92a59ecSDuan Jiong 1304c92a59ecSDuan Jiong memset(&fl6, 0, sizeof(fl6)); 1305e374c618SJulian Anastasov fl6.flowi6_iif = LOOPBACK_IFINDEX; 1306c92a59ecSDuan Jiong fl6.flowi6_oif = oif; 1307c92a59ecSDuan Jiong fl6.flowi6_mark = mark; 1308c92a59ecSDuan Jiong fl6.daddr = msg->dest; 1309c92a59ecSDuan Jiong fl6.saddr = iph->daddr; 1310c92a59ecSDuan Jiong 1311b55b76b2SDuan Jiong dst = ip6_route_redirect(net, &fl6, &iph->saddr); 1312c92a59ecSDuan Jiong rt6_do_redirect(dst, NULL, skb); 1313c92a59ecSDuan Jiong dst_release(dst); 1314c92a59ecSDuan Jiong } 1315c92a59ecSDuan Jiong 13163a5ad2eeSDavid S. Miller void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) 13173a5ad2eeSDavid S. Miller { 13183a5ad2eeSDavid S. Miller ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark); 13193a5ad2eeSDavid S. Miller } 13203a5ad2eeSDavid S. Miller EXPORT_SYMBOL_GPL(ip6_sk_redirect); 13213a5ad2eeSDavid S. Miller 13220dbaee3bSDavid S. Miller static unsigned int ip6_default_advmss(const struct dst_entry *dst) 13231da177e4SLinus Torvalds { 13240dbaee3bSDavid S. Miller struct net_device *dev = dst->dev; 13250dbaee3bSDavid S. Miller unsigned int mtu = dst_mtu(dst); 13260dbaee3bSDavid S. Miller struct net *net = dev_net(dev); 13270dbaee3bSDavid S. Miller 13281da177e4SLinus Torvalds mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); 13291da177e4SLinus Torvalds 13305578689aSDaniel Lezcano if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss) 13315578689aSDaniel Lezcano mtu = net->ipv6.sysctl.ip6_rt_min_advmss; 13321da177e4SLinus Torvalds 13331da177e4SLinus Torvalds /* 13341da177e4SLinus Torvalds * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and 13351da177e4SLinus Torvalds * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. 13361da177e4SLinus Torvalds * IPV6_MAXPLEN is also valid and means: "any MSS, 13371da177e4SLinus Torvalds * rely only on pmtu discovery" 13381da177e4SLinus Torvalds */ 13391da177e4SLinus Torvalds if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) 13401da177e4SLinus Torvalds mtu = IPV6_MAXPLEN; 13411da177e4SLinus Torvalds return mtu; 13421da177e4SLinus Torvalds } 13431da177e4SLinus Torvalds 1344ebb762f2SSteffen Klassert static unsigned int ip6_mtu(const struct dst_entry *dst) 1345d33e4553SDavid S. Miller { 1346*4b32b5adSMartin KaFai Lau const struct rt6_info *rt = (const struct rt6_info *)dst; 1347*4b32b5adSMartin KaFai Lau unsigned int mtu = rt->rt6i_pmtu; 1348d33e4553SDavid S. Miller struct inet6_dev *idev; 1349618f9bc7SSteffen Klassert 1350618f9bc7SSteffen Klassert if (mtu) 135130f78d8eSEric Dumazet goto out; 1352618f9bc7SSteffen Klassert 1353*4b32b5adSMartin KaFai Lau mtu = dst_metric_raw(dst, RTAX_MTU); 1354*4b32b5adSMartin KaFai Lau if (mtu) 1355*4b32b5adSMartin KaFai Lau goto out; 1356*4b32b5adSMartin KaFai Lau 1357618f9bc7SSteffen Klassert mtu = IPV6_MIN_MTU; 1358d33e4553SDavid S. Miller 1359d33e4553SDavid S. Miller rcu_read_lock(); 1360d33e4553SDavid S. Miller idev = __in6_dev_get(dst->dev); 1361d33e4553SDavid S. Miller if (idev) 1362d33e4553SDavid S. Miller mtu = idev->cnf.mtu6; 1363d33e4553SDavid S. Miller rcu_read_unlock(); 1364d33e4553SDavid S. Miller 136530f78d8eSEric Dumazet out: 136630f78d8eSEric Dumazet return min_t(unsigned int, mtu, IP6_MAX_MTU); 1367d33e4553SDavid S. Miller } 1368d33e4553SDavid S. Miller 13693b00944cSYOSHIFUJI Hideaki static struct dst_entry *icmp6_dst_gc_list; 13703b00944cSYOSHIFUJI Hideaki static DEFINE_SPINLOCK(icmp6_dst_lock); 13715d0bbeebSThomas Graf 13723b00944cSYOSHIFUJI Hideaki struct dst_entry *icmp6_dst_alloc(struct net_device *dev, 137387a11578SDavid S. Miller struct flowi6 *fl6) 13741da177e4SLinus Torvalds { 137587a11578SDavid S. Miller struct dst_entry *dst; 13761da177e4SLinus Torvalds struct rt6_info *rt; 13771da177e4SLinus Torvalds struct inet6_dev *idev = in6_dev_get(dev); 1378c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 13791da177e4SLinus Torvalds 138038308473SDavid S. Miller if (unlikely(!idev)) 1381122bdf67SEric Dumazet return ERR_PTR(-ENODEV); 13821da177e4SLinus Torvalds 13838b96d22dSDavid S. Miller rt = ip6_dst_alloc(net, dev, 0, NULL); 138438308473SDavid S. Miller if (unlikely(!rt)) { 13851da177e4SLinus Torvalds in6_dev_put(idev); 138687a11578SDavid S. Miller dst = ERR_PTR(-ENOMEM); 13871da177e4SLinus Torvalds goto out; 13881da177e4SLinus Torvalds } 13891da177e4SLinus Torvalds 13908e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 13918e2ec639SYan, Zheng rt->dst.output = ip6_output; 1392d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 1393550bab42SJulian Anastasov rt->rt6i_gateway = fl6->daddr; 139487a11578SDavid S. Miller rt->rt6i_dst.addr = fl6->daddr; 13958e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 13968e2ec639SYan, Zheng rt->rt6i_idev = idev; 139714edd87dSLi RongQing dst_metric_set(&rt->dst, RTAX_HOPLIMIT, 0); 13981da177e4SLinus Torvalds 13993b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 1400d8d1f30bSChangli Gao rt->dst.next = icmp6_dst_gc_list; 1401d8d1f30bSChangli Gao icmp6_dst_gc_list = &rt->dst; 14023b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 14031da177e4SLinus Torvalds 14045578689aSDaniel Lezcano fib6_force_start_gc(net); 14051da177e4SLinus Torvalds 140687a11578SDavid S. Miller dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); 140787a11578SDavid S. Miller 14081da177e4SLinus Torvalds out: 140987a11578SDavid S. Miller return dst; 14101da177e4SLinus Torvalds } 14111da177e4SLinus Torvalds 14123d0f24a7SStephen Hemminger int icmp6_dst_gc(void) 14131da177e4SLinus Torvalds { 1414e9476e95SHagen Paul Pfeifer struct dst_entry *dst, **pprev; 14153d0f24a7SStephen Hemminger int more = 0; 14161da177e4SLinus Torvalds 14173b00944cSYOSHIFUJI Hideaki spin_lock_bh(&icmp6_dst_lock); 14183b00944cSYOSHIFUJI Hideaki pprev = &icmp6_dst_gc_list; 14195d0bbeebSThomas Graf 14201da177e4SLinus Torvalds while ((dst = *pprev) != NULL) { 14211da177e4SLinus Torvalds if (!atomic_read(&dst->__refcnt)) { 14221da177e4SLinus Torvalds *pprev = dst->next; 14231da177e4SLinus Torvalds dst_free(dst); 14241da177e4SLinus Torvalds } else { 14251da177e4SLinus Torvalds pprev = &dst->next; 14263d0f24a7SStephen Hemminger ++more; 14271da177e4SLinus Torvalds } 14281da177e4SLinus Torvalds } 14291da177e4SLinus Torvalds 14303b00944cSYOSHIFUJI Hideaki spin_unlock_bh(&icmp6_dst_lock); 14315d0bbeebSThomas Graf 14323d0f24a7SStephen Hemminger return more; 14331da177e4SLinus Torvalds } 14341da177e4SLinus Torvalds 14351e493d19SDavid S. Miller static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg), 14361e493d19SDavid S. Miller void *arg) 14371e493d19SDavid S. Miller { 14381e493d19SDavid S. Miller struct dst_entry *dst, **pprev; 14391e493d19SDavid S. Miller 14401e493d19SDavid S. Miller spin_lock_bh(&icmp6_dst_lock); 14411e493d19SDavid S. Miller pprev = &icmp6_dst_gc_list; 14421e493d19SDavid S. Miller while ((dst = *pprev) != NULL) { 14431e493d19SDavid S. Miller struct rt6_info *rt = (struct rt6_info *) dst; 14441e493d19SDavid S. Miller if (func(rt, arg)) { 14451e493d19SDavid S. Miller *pprev = dst->next; 14461e493d19SDavid S. Miller dst_free(dst); 14471e493d19SDavid S. Miller } else { 14481e493d19SDavid S. Miller pprev = &dst->next; 14491e493d19SDavid S. Miller } 14501e493d19SDavid S. Miller } 14511e493d19SDavid S. Miller spin_unlock_bh(&icmp6_dst_lock); 14521e493d19SDavid S. Miller } 14531e493d19SDavid S. Miller 1454569d3645SDaniel Lezcano static int ip6_dst_gc(struct dst_ops *ops) 14551da177e4SLinus Torvalds { 145686393e52SAlexey Dobriyan struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops); 14577019b78eSDaniel Lezcano int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval; 14587019b78eSDaniel Lezcano int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size; 14597019b78eSDaniel Lezcano int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity; 14607019b78eSDaniel Lezcano int rt_gc_timeout = net->ipv6.sysctl.ip6_rt_gc_timeout; 14617019b78eSDaniel Lezcano unsigned long rt_last_gc = net->ipv6.ip6_rt_last_gc; 1462fc66f95cSEric Dumazet int entries; 14631da177e4SLinus Torvalds 1464fc66f95cSEric Dumazet entries = dst_entries_get_fast(ops); 146549a18d86SMichal Kubeček if (time_after(rt_last_gc + rt_min_interval, jiffies) && 1466fc66f95cSEric Dumazet entries <= rt_max_size) 14671da177e4SLinus Torvalds goto out; 14681da177e4SLinus Torvalds 14696891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire++; 147014956643SLi RongQing fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true); 1471fc66f95cSEric Dumazet entries = dst_entries_get_slow(ops); 1472fc66f95cSEric Dumazet if (entries < ops->gc_thresh) 14737019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1; 14741da177e4SLinus Torvalds out: 14757019b78eSDaniel Lezcano net->ipv6.ip6_rt_gc_expire -= net->ipv6.ip6_rt_gc_expire>>rt_elasticity; 1476fc66f95cSEric Dumazet return entries > rt_max_size; 14771da177e4SLinus Torvalds } 14781da177e4SLinus Torvalds 1479e715b6d3SFlorian Westphal static int ip6_convert_metrics(struct mx6_config *mxc, 1480e715b6d3SFlorian Westphal const struct fib6_config *cfg) 1481e715b6d3SFlorian Westphal { 1482e715b6d3SFlorian Westphal struct nlattr *nla; 1483e715b6d3SFlorian Westphal int remaining; 1484e715b6d3SFlorian Westphal u32 *mp; 1485e715b6d3SFlorian Westphal 148663159f29SIan Morris if (!cfg->fc_mx) 1487e715b6d3SFlorian Westphal return 0; 1488e715b6d3SFlorian Westphal 1489e715b6d3SFlorian Westphal mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); 1490e715b6d3SFlorian Westphal if (unlikely(!mp)) 1491e715b6d3SFlorian Westphal return -ENOMEM; 1492e715b6d3SFlorian Westphal 1493e715b6d3SFlorian Westphal nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { 1494e715b6d3SFlorian Westphal int type = nla_type(nla); 1495e715b6d3SFlorian Westphal 1496e715b6d3SFlorian Westphal if (type) { 1497ea697639SDaniel Borkmann u32 val; 1498ea697639SDaniel Borkmann 1499e715b6d3SFlorian Westphal if (unlikely(type > RTAX_MAX)) 1500e715b6d3SFlorian Westphal goto err; 1501ea697639SDaniel Borkmann if (type == RTAX_CC_ALGO) { 1502ea697639SDaniel Borkmann char tmp[TCP_CA_NAME_MAX]; 1503e715b6d3SFlorian Westphal 1504ea697639SDaniel Borkmann nla_strlcpy(tmp, nla, sizeof(tmp)); 1505ea697639SDaniel Borkmann val = tcp_ca_get_key_by_name(tmp); 1506ea697639SDaniel Borkmann if (val == TCP_CA_UNSPEC) 1507ea697639SDaniel Borkmann goto err; 1508ea697639SDaniel Borkmann } else { 1509ea697639SDaniel Borkmann val = nla_get_u32(nla); 1510ea697639SDaniel Borkmann } 1511ea697639SDaniel Borkmann 1512ea697639SDaniel Borkmann mp[type - 1] = val; 1513e715b6d3SFlorian Westphal __set_bit(type - 1, mxc->mx_valid); 1514e715b6d3SFlorian Westphal } 1515e715b6d3SFlorian Westphal } 1516e715b6d3SFlorian Westphal 1517e715b6d3SFlorian Westphal mxc->mx = mp; 1518e715b6d3SFlorian Westphal 1519e715b6d3SFlorian Westphal return 0; 1520e715b6d3SFlorian Westphal err: 1521e715b6d3SFlorian Westphal kfree(mp); 1522e715b6d3SFlorian Westphal return -EINVAL; 1523e715b6d3SFlorian Westphal } 15241da177e4SLinus Torvalds 152586872cb5SThomas Graf int ip6_route_add(struct fib6_config *cfg) 15261da177e4SLinus Torvalds { 15271da177e4SLinus Torvalds int err; 15285578689aSDaniel Lezcano struct net *net = cfg->fc_nlinfo.nl_net; 15291da177e4SLinus Torvalds struct rt6_info *rt = NULL; 15301da177e4SLinus Torvalds struct net_device *dev = NULL; 15311da177e4SLinus Torvalds struct inet6_dev *idev = NULL; 1532c71099acSThomas Graf struct fib6_table *table; 1533e715b6d3SFlorian Westphal struct mx6_config mxc = { .mx = NULL, }; 15341da177e4SLinus Torvalds int addr_type; 15351da177e4SLinus Torvalds 153686872cb5SThomas Graf if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) 15371da177e4SLinus Torvalds return -EINVAL; 15381da177e4SLinus Torvalds #ifndef CONFIG_IPV6_SUBTREES 153986872cb5SThomas Graf if (cfg->fc_src_len) 15401da177e4SLinus Torvalds return -EINVAL; 15411da177e4SLinus Torvalds #endif 154286872cb5SThomas Graf if (cfg->fc_ifindex) { 15431da177e4SLinus Torvalds err = -ENODEV; 15445578689aSDaniel Lezcano dev = dev_get_by_index(net, cfg->fc_ifindex); 15451da177e4SLinus Torvalds if (!dev) 15461da177e4SLinus Torvalds goto out; 15471da177e4SLinus Torvalds idev = in6_dev_get(dev); 15481da177e4SLinus Torvalds if (!idev) 15491da177e4SLinus Torvalds goto out; 15501da177e4SLinus Torvalds } 15511da177e4SLinus Torvalds 155286872cb5SThomas Graf if (cfg->fc_metric == 0) 155386872cb5SThomas Graf cfg->fc_metric = IP6_RT_PRIO_USER; 15541da177e4SLinus Torvalds 1555c71099acSThomas Graf err = -ENOBUFS; 155638308473SDavid S. Miller if (cfg->fc_nlinfo.nlh && 1557d71314b4SMatti Vaittinen !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { 1558d71314b4SMatti Vaittinen table = fib6_get_table(net, cfg->fc_table); 155938308473SDavid S. Miller if (!table) { 1560f3213831SJoe Perches pr_warn("NLM_F_CREATE should be specified when creating new route\n"); 1561d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1562d71314b4SMatti Vaittinen } 1563d71314b4SMatti Vaittinen } else { 1564d71314b4SMatti Vaittinen table = fib6_new_table(net, cfg->fc_table); 1565d71314b4SMatti Vaittinen } 156638308473SDavid S. Miller 156738308473SDavid S. Miller if (!table) 1568c71099acSThomas Graf goto out; 1569c71099acSThomas Graf 1570c88507fbSSabrina Dubroca rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table); 15711da177e4SLinus Torvalds 157238308473SDavid S. Miller if (!rt) { 15731da177e4SLinus Torvalds err = -ENOMEM; 15741da177e4SLinus Torvalds goto out; 15751da177e4SLinus Torvalds } 15761da177e4SLinus Torvalds 15771716a961SGao feng if (cfg->fc_flags & RTF_EXPIRES) 15781716a961SGao feng rt6_set_expires(rt, jiffies + 15791716a961SGao feng clock_t_to_jiffies(cfg->fc_expires)); 15801716a961SGao feng else 15811716a961SGao feng rt6_clean_expires(rt); 15821da177e4SLinus Torvalds 158386872cb5SThomas Graf if (cfg->fc_protocol == RTPROT_UNSPEC) 158486872cb5SThomas Graf cfg->fc_protocol = RTPROT_BOOT; 158586872cb5SThomas Graf rt->rt6i_protocol = cfg->fc_protocol; 158686872cb5SThomas Graf 158786872cb5SThomas Graf addr_type = ipv6_addr_type(&cfg->fc_dst); 15881da177e4SLinus Torvalds 15891da177e4SLinus Torvalds if (addr_type & IPV6_ADDR_MULTICAST) 1590d8d1f30bSChangli Gao rt->dst.input = ip6_mc_input; 1591ab79ad14SMaciej Żenczykowski else if (cfg->fc_flags & RTF_LOCAL) 1592ab79ad14SMaciej Żenczykowski rt->dst.input = ip6_input; 15931da177e4SLinus Torvalds else 1594d8d1f30bSChangli Gao rt->dst.input = ip6_forward; 15951da177e4SLinus Torvalds 1596d8d1f30bSChangli Gao rt->dst.output = ip6_output; 15971da177e4SLinus Torvalds 159886872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); 159986872cb5SThomas Graf rt->rt6i_dst.plen = cfg->fc_dst_len; 1600e5fd387aSMichal Kubeček if (rt->rt6i_dst.plen == 128) { 160111d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 1602e5fd387aSMichal Kubeček dst_metrics_set_force_overwrite(&rt->dst); 1603e5fd387aSMichal Kubeček } 16041da177e4SLinus Torvalds 16051da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 160686872cb5SThomas Graf ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); 160786872cb5SThomas Graf rt->rt6i_src.plen = cfg->fc_src_len; 16081da177e4SLinus Torvalds #endif 16091da177e4SLinus Torvalds 161086872cb5SThomas Graf rt->rt6i_metric = cfg->fc_metric; 16111da177e4SLinus Torvalds 16121da177e4SLinus Torvalds /* We cannot add true routes via loopback here, 16131da177e4SLinus Torvalds they would result in kernel looping; promote them to reject routes 16141da177e4SLinus Torvalds */ 161586872cb5SThomas Graf if ((cfg->fc_flags & RTF_REJECT) || 161638308473SDavid S. Miller (dev && (dev->flags & IFF_LOOPBACK) && 161738308473SDavid S. Miller !(addr_type & IPV6_ADDR_LOOPBACK) && 161838308473SDavid S. Miller !(cfg->fc_flags & RTF_LOCAL))) { 16191da177e4SLinus Torvalds /* hold loopback dev/idev if we haven't done so. */ 16205578689aSDaniel Lezcano if (dev != net->loopback_dev) { 16211da177e4SLinus Torvalds if (dev) { 16221da177e4SLinus Torvalds dev_put(dev); 16231da177e4SLinus Torvalds in6_dev_put(idev); 16241da177e4SLinus Torvalds } 16255578689aSDaniel Lezcano dev = net->loopback_dev; 16261da177e4SLinus Torvalds dev_hold(dev); 16271da177e4SLinus Torvalds idev = in6_dev_get(dev); 16281da177e4SLinus Torvalds if (!idev) { 16291da177e4SLinus Torvalds err = -ENODEV; 16301da177e4SLinus Torvalds goto out; 16311da177e4SLinus Torvalds } 16321da177e4SLinus Torvalds } 16331da177e4SLinus Torvalds rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; 1634ef2c7d7bSNicolas Dichtel switch (cfg->fc_type) { 1635ef2c7d7bSNicolas Dichtel case RTN_BLACKHOLE: 1636ef2c7d7bSNicolas Dichtel rt->dst.error = -EINVAL; 1637aad88724SEric Dumazet rt->dst.output = dst_discard_sk; 16387150aedeSKamala R rt->dst.input = dst_discard; 1639ef2c7d7bSNicolas Dichtel break; 1640ef2c7d7bSNicolas Dichtel case RTN_PROHIBIT: 1641ef2c7d7bSNicolas Dichtel rt->dst.error = -EACCES; 16427150aedeSKamala R rt->dst.output = ip6_pkt_prohibit_out; 16437150aedeSKamala R rt->dst.input = ip6_pkt_prohibit; 1644ef2c7d7bSNicolas Dichtel break; 1645b4949ab2SNicolas Dichtel case RTN_THROW: 1646ef2c7d7bSNicolas Dichtel default: 16477150aedeSKamala R rt->dst.error = (cfg->fc_type == RTN_THROW) ? -EAGAIN 16487150aedeSKamala R : -ENETUNREACH; 16497150aedeSKamala R rt->dst.output = ip6_pkt_discard_out; 16507150aedeSKamala R rt->dst.input = ip6_pkt_discard; 1651ef2c7d7bSNicolas Dichtel break; 1652ef2c7d7bSNicolas Dichtel } 16531da177e4SLinus Torvalds goto install_route; 16541da177e4SLinus Torvalds } 16551da177e4SLinus Torvalds 165686872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY) { 1657b71d1d42SEric Dumazet const struct in6_addr *gw_addr; 16581da177e4SLinus Torvalds int gwa_type; 16591da177e4SLinus Torvalds 166086872cb5SThomas Graf gw_addr = &cfg->fc_gateway; 16614e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = *gw_addr; 16621da177e4SLinus Torvalds gwa_type = ipv6_addr_type(gw_addr); 16631da177e4SLinus Torvalds 16641da177e4SLinus Torvalds if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { 16651da177e4SLinus Torvalds struct rt6_info *grt; 16661da177e4SLinus Torvalds 16671da177e4SLinus Torvalds /* IPv6 strictly inhibits using not link-local 16681da177e4SLinus Torvalds addresses as nexthop address. 16691da177e4SLinus Torvalds Otherwise, router will not able to send redirects. 16701da177e4SLinus Torvalds It is very good, but in some (rare!) circumstances 16711da177e4SLinus Torvalds (SIT, PtP, NBMA NOARP links) it is handy to allow 16721da177e4SLinus Torvalds some exceptions. --ANK 16731da177e4SLinus Torvalds */ 16741da177e4SLinus Torvalds err = -EINVAL; 16751da177e4SLinus Torvalds if (!(gwa_type & IPV6_ADDR_UNICAST)) 16761da177e4SLinus Torvalds goto out; 16771da177e4SLinus Torvalds 16785578689aSDaniel Lezcano grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); 16791da177e4SLinus Torvalds 16801da177e4SLinus Torvalds err = -EHOSTUNREACH; 168138308473SDavid S. Miller if (!grt) 16821da177e4SLinus Torvalds goto out; 16831da177e4SLinus Torvalds if (dev) { 1684d1918542SDavid S. Miller if (dev != grt->dst.dev) { 168594e187c0SAmerigo Wang ip6_rt_put(grt); 16861da177e4SLinus Torvalds goto out; 16871da177e4SLinus Torvalds } 16881da177e4SLinus Torvalds } else { 1689d1918542SDavid S. Miller dev = grt->dst.dev; 16901da177e4SLinus Torvalds idev = grt->rt6i_idev; 16911da177e4SLinus Torvalds dev_hold(dev); 16921da177e4SLinus Torvalds in6_dev_hold(grt->rt6i_idev); 16931da177e4SLinus Torvalds } 16941da177e4SLinus Torvalds if (!(grt->rt6i_flags & RTF_GATEWAY)) 16951da177e4SLinus Torvalds err = 0; 169694e187c0SAmerigo Wang ip6_rt_put(grt); 16971da177e4SLinus Torvalds 16981da177e4SLinus Torvalds if (err) 16991da177e4SLinus Torvalds goto out; 17001da177e4SLinus Torvalds } 17011da177e4SLinus Torvalds err = -EINVAL; 170238308473SDavid S. Miller if (!dev || (dev->flags & IFF_LOOPBACK)) 17031da177e4SLinus Torvalds goto out; 17041da177e4SLinus Torvalds } 17051da177e4SLinus Torvalds 17061da177e4SLinus Torvalds err = -ENODEV; 170738308473SDavid S. Miller if (!dev) 17081da177e4SLinus Torvalds goto out; 17091da177e4SLinus Torvalds 1710c3968a85SDaniel Walter if (!ipv6_addr_any(&cfg->fc_prefsrc)) { 1711c3968a85SDaniel Walter if (!ipv6_chk_addr(net, &cfg->fc_prefsrc, dev, 0)) { 1712c3968a85SDaniel Walter err = -EINVAL; 1713c3968a85SDaniel Walter goto out; 1714c3968a85SDaniel Walter } 17154e3fd7a0SAlexey Dobriyan rt->rt6i_prefsrc.addr = cfg->fc_prefsrc; 1716c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 128; 1717c3968a85SDaniel Walter } else 1718c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 1719c3968a85SDaniel Walter 172086872cb5SThomas Graf rt->rt6i_flags = cfg->fc_flags; 17211da177e4SLinus Torvalds 17221da177e4SLinus Torvalds install_route: 1723d8d1f30bSChangli Gao rt->dst.dev = dev; 17241da177e4SLinus Torvalds rt->rt6i_idev = idev; 1725c71099acSThomas Graf rt->rt6i_table = table; 172663152fc0SDaniel Lezcano 1727c346dca1SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = dev_net(dev); 172863152fc0SDaniel Lezcano 1729e715b6d3SFlorian Westphal err = ip6_convert_metrics(&mxc, cfg); 1730e715b6d3SFlorian Westphal if (err) 1731e715b6d3SFlorian Westphal goto out; 17321da177e4SLinus Torvalds 1733e715b6d3SFlorian Westphal err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); 1734e715b6d3SFlorian Westphal 1735e715b6d3SFlorian Westphal kfree(mxc.mx); 1736e715b6d3SFlorian Westphal return err; 17371da177e4SLinus Torvalds out: 17381da177e4SLinus Torvalds if (dev) 17391da177e4SLinus Torvalds dev_put(dev); 17401da177e4SLinus Torvalds if (idev) 17411da177e4SLinus Torvalds in6_dev_put(idev); 17421da177e4SLinus Torvalds if (rt) 1743d8d1f30bSChangli Gao dst_free(&rt->dst); 17441da177e4SLinus Torvalds return err; 17451da177e4SLinus Torvalds } 17461da177e4SLinus Torvalds 174786872cb5SThomas Graf static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) 17481da177e4SLinus Torvalds { 17491da177e4SLinus Torvalds int err; 1750c71099acSThomas Graf struct fib6_table *table; 1751d1918542SDavid S. Miller struct net *net = dev_net(rt->dst.dev); 17521da177e4SLinus Torvalds 17536825a26cSGao feng if (rt == net->ipv6.ip6_null_entry) { 17546825a26cSGao feng err = -ENOENT; 17556825a26cSGao feng goto out; 17566825a26cSGao feng } 17576c813a72SPatrick McHardy 1758c71099acSThomas Graf table = rt->rt6i_table; 1759c71099acSThomas Graf write_lock_bh(&table->tb6_lock); 176086872cb5SThomas Graf err = fib6_del(rt, info); 1761c71099acSThomas Graf write_unlock_bh(&table->tb6_lock); 17621da177e4SLinus Torvalds 17636825a26cSGao feng out: 176494e187c0SAmerigo Wang ip6_rt_put(rt); 17651da177e4SLinus Torvalds return err; 17661da177e4SLinus Torvalds } 17671da177e4SLinus Torvalds 1768e0a1ad73SThomas Graf int ip6_del_rt(struct rt6_info *rt) 1769e0a1ad73SThomas Graf { 17704d1169c1SDenis V. Lunev struct nl_info info = { 1771d1918542SDavid S. Miller .nl_net = dev_net(rt->dst.dev), 17724d1169c1SDenis V. Lunev }; 1773528c4cebSDenis V. Lunev return __ip6_del_rt(rt, &info); 1774e0a1ad73SThomas Graf } 1775e0a1ad73SThomas Graf 177686872cb5SThomas Graf static int ip6_route_del(struct fib6_config *cfg) 17771da177e4SLinus Torvalds { 1778c71099acSThomas Graf struct fib6_table *table; 17791da177e4SLinus Torvalds struct fib6_node *fn; 17801da177e4SLinus Torvalds struct rt6_info *rt; 17811da177e4SLinus Torvalds int err = -ESRCH; 17821da177e4SLinus Torvalds 17835578689aSDaniel Lezcano table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); 178438308473SDavid S. Miller if (!table) 1785c71099acSThomas Graf return err; 17861da177e4SLinus Torvalds 1787c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 1788c71099acSThomas Graf 1789c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, 179086872cb5SThomas Graf &cfg->fc_dst, cfg->fc_dst_len, 179186872cb5SThomas Graf &cfg->fc_src, cfg->fc_src_len); 17921da177e4SLinus Torvalds 17931da177e4SLinus Torvalds if (fn) { 1794d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 17951f56a01fSMartin KaFai Lau if ((rt->rt6i_flags & RTF_CACHE) && 17961f56a01fSMartin KaFai Lau !(cfg->fc_flags & RTF_CACHE)) 17971f56a01fSMartin KaFai Lau continue; 179886872cb5SThomas Graf if (cfg->fc_ifindex && 1799d1918542SDavid S. Miller (!rt->dst.dev || 1800d1918542SDavid S. Miller rt->dst.dev->ifindex != cfg->fc_ifindex)) 18011da177e4SLinus Torvalds continue; 180286872cb5SThomas Graf if (cfg->fc_flags & RTF_GATEWAY && 180386872cb5SThomas Graf !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) 18041da177e4SLinus Torvalds continue; 180586872cb5SThomas Graf if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) 18061da177e4SLinus Torvalds continue; 1807d8d1f30bSChangli Gao dst_hold(&rt->dst); 1808c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 18091da177e4SLinus Torvalds 181086872cb5SThomas Graf return __ip6_del_rt(rt, &cfg->fc_nlinfo); 18111da177e4SLinus Torvalds } 18121da177e4SLinus Torvalds } 1813c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 18141da177e4SLinus Torvalds 18151da177e4SLinus Torvalds return err; 18161da177e4SLinus Torvalds } 18171da177e4SLinus Torvalds 18186700c270SDavid S. Miller static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 1819a6279458SYOSHIFUJI Hideaki { 1820e8599ff4SDavid S. Miller struct net *net = dev_net(skb->dev); 1821a6279458SYOSHIFUJI Hideaki struct netevent_redirect netevent; 1822e8599ff4SDavid S. Miller struct rt6_info *rt, *nrt = NULL; 1823e8599ff4SDavid S. Miller struct ndisc_options ndopts; 1824e8599ff4SDavid S. Miller struct inet6_dev *in6_dev; 1825e8599ff4SDavid S. Miller struct neighbour *neigh; 182671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 struct rd_msg *msg; 18276e157b6aSDavid S. Miller int optlen, on_link; 18286e157b6aSDavid S. Miller u8 *lladdr; 1829e8599ff4SDavid S. Miller 183029a3cad5SSimon Horman optlen = skb_tail_pointer(skb) - skb_transport_header(skb); 183171bcdba0SYOSHIFUJI Hideaki / 吉藤英明 optlen -= sizeof(*msg); 1832e8599ff4SDavid S. Miller 1833e8599ff4SDavid S. Miller if (optlen < 0) { 18346e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: packet too short\n"); 1835e8599ff4SDavid S. Miller return; 1836e8599ff4SDavid S. Miller } 1837e8599ff4SDavid S. Miller 183871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 msg = (struct rd_msg *)icmp6_hdr(skb); 1839e8599ff4SDavid S. Miller 184071bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_is_multicast(&msg->dest)) { 18416e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n"); 1842e8599ff4SDavid S. Miller return; 1843e8599ff4SDavid S. Miller } 1844e8599ff4SDavid S. Miller 18456e157b6aSDavid S. Miller on_link = 0; 184671bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (ipv6_addr_equal(&msg->dest, &msg->target)) { 1847e8599ff4SDavid S. Miller on_link = 1; 184871bcdba0SYOSHIFUJI Hideaki / 吉藤英明 } else if (ipv6_addr_type(&msg->target) != 1849e8599ff4SDavid S. Miller (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) { 18506e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n"); 1851e8599ff4SDavid S. Miller return; 1852e8599ff4SDavid S. Miller } 1853e8599ff4SDavid S. Miller 1854e8599ff4SDavid S. Miller in6_dev = __in6_dev_get(skb->dev); 1855e8599ff4SDavid S. Miller if (!in6_dev) 1856e8599ff4SDavid S. Miller return; 1857e8599ff4SDavid S. Miller if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) 1858e8599ff4SDavid S. Miller return; 1859e8599ff4SDavid S. Miller 1860e8599ff4SDavid S. Miller /* RFC2461 8.1: 1861e8599ff4SDavid S. Miller * The IP source address of the Redirect MUST be the same as the current 1862e8599ff4SDavid S. Miller * first-hop router for the specified ICMP Destination Address. 1863e8599ff4SDavid S. Miller */ 1864e8599ff4SDavid S. Miller 186571bcdba0SYOSHIFUJI Hideaki / 吉藤英明 if (!ndisc_parse_options(msg->opt, optlen, &ndopts)) { 1866e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid ND options\n"); 1867e8599ff4SDavid S. Miller return; 1868e8599ff4SDavid S. Miller } 18696e157b6aSDavid S. Miller 18706e157b6aSDavid S. Miller lladdr = NULL; 1871e8599ff4SDavid S. Miller if (ndopts.nd_opts_tgt_lladdr) { 1872e8599ff4SDavid S. Miller lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, 1873e8599ff4SDavid S. Miller skb->dev); 1874e8599ff4SDavid S. Miller if (!lladdr) { 1875e8599ff4SDavid S. Miller net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n"); 1876e8599ff4SDavid S. Miller return; 1877e8599ff4SDavid S. Miller } 1878e8599ff4SDavid S. Miller } 1879e8599ff4SDavid S. Miller 18806e157b6aSDavid S. Miller rt = (struct rt6_info *) dst; 18816e157b6aSDavid S. Miller if (rt == net->ipv6.ip6_null_entry) { 18826e157b6aSDavid S. Miller net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n"); 18836e157b6aSDavid S. Miller return; 18846e157b6aSDavid S. Miller } 18856e157b6aSDavid S. Miller 18866e157b6aSDavid S. Miller /* Redirect received -> path was valid. 18876e157b6aSDavid S. Miller * Look, redirects are sent only in response to data packets, 18886e157b6aSDavid S. Miller * so that this nexthop apparently is reachable. --ANK 18896e157b6aSDavid S. Miller */ 18906e157b6aSDavid S. Miller dst_confirm(&rt->dst); 18916e157b6aSDavid S. Miller 189271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1); 1893e8599ff4SDavid S. Miller if (!neigh) 1894e8599ff4SDavid S. Miller return; 1895e8599ff4SDavid S. Miller 18961da177e4SLinus Torvalds /* 18971da177e4SLinus Torvalds * We have finally decided to accept it. 18981da177e4SLinus Torvalds */ 18991da177e4SLinus Torvalds 19001da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 19011da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE| 19021da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE| 19031da177e4SLinus Torvalds (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| 19041da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER)) 19051da177e4SLinus Torvalds ); 19061da177e4SLinus Torvalds 190771bcdba0SYOSHIFUJI Hideaki / 吉藤英明 nrt = ip6_rt_copy(rt, &msg->dest); 190838308473SDavid S. Miller if (!nrt) 19091da177e4SLinus Torvalds goto out; 19101da177e4SLinus Torvalds 19111da177e4SLinus Torvalds nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; 19121da177e4SLinus Torvalds if (on_link) 19131da177e4SLinus Torvalds nrt->rt6i_flags &= ~RTF_GATEWAY; 19141da177e4SLinus Torvalds 19154e3fd7a0SAlexey Dobriyan nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; 19161da177e4SLinus Torvalds 191740e22e8fSThomas Graf if (ip6_ins_rt(nrt)) 19181da177e4SLinus Torvalds goto out; 19191da177e4SLinus Torvalds 1920d8d1f30bSChangli Gao netevent.old = &rt->dst; 1921d8d1f30bSChangli Gao netevent.new = &nrt->dst; 192271bcdba0SYOSHIFUJI Hideaki / 吉藤英明 netevent.daddr = &msg->dest; 192360592833SYOSHIFUJI Hideaki / 吉藤英明 netevent.neigh = neigh; 19248d71740cSTom Tucker call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); 19258d71740cSTom Tucker 19261da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) { 19276e157b6aSDavid S. Miller rt = (struct rt6_info *) dst_clone(&rt->dst); 1928e0a1ad73SThomas Graf ip6_del_rt(rt); 19291da177e4SLinus Torvalds } 19301da177e4SLinus Torvalds 19311da177e4SLinus Torvalds out: 1932e8599ff4SDavid S. Miller neigh_release(neigh); 19336e157b6aSDavid S. Miller } 19346e157b6aSDavid S. Miller 19351da177e4SLinus Torvalds /* 19361da177e4SLinus Torvalds * Misc support functions 19371da177e4SLinus Torvalds */ 19381da177e4SLinus Torvalds 1939*4b32b5adSMartin KaFai Lau static void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) 1940*4b32b5adSMartin KaFai Lau { 1941*4b32b5adSMartin KaFai Lau BUG_ON(from->dst.from); 1942*4b32b5adSMartin KaFai Lau 1943*4b32b5adSMartin KaFai Lau rt->rt6i_flags &= ~RTF_EXPIRES; 1944*4b32b5adSMartin KaFai Lau dst_hold(&from->dst); 1945*4b32b5adSMartin KaFai Lau rt->dst.from = &from->dst; 1946*4b32b5adSMartin KaFai Lau dst_init_metrics(&rt->dst, dst_metrics_ptr(&from->dst), true); 1947*4b32b5adSMartin KaFai Lau } 1948*4b32b5adSMartin KaFai Lau 19491716a961SGao feng static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, 195021efcfa0SEric Dumazet const struct in6_addr *dest) 19511da177e4SLinus Torvalds { 1952d1918542SDavid S. Miller struct net *net = dev_net(ort->dst.dev); 1953*4b32b5adSMartin KaFai Lau struct rt6_info *rt; 1954*4b32b5adSMartin KaFai Lau 1955*4b32b5adSMartin KaFai Lau if (ort->rt6i_flags & RTF_CACHE) 1956*4b32b5adSMartin KaFai Lau ort = (struct rt6_info *)ort->dst.from; 1957*4b32b5adSMartin KaFai Lau 1958*4b32b5adSMartin KaFai Lau rt = ip6_dst_alloc(net, ort->dst.dev, 0, 19598b96d22dSDavid S. Miller ort->rt6i_table); 19601da177e4SLinus Torvalds 19611da177e4SLinus Torvalds if (rt) { 1962d8d1f30bSChangli Gao rt->dst.input = ort->dst.input; 1963d8d1f30bSChangli Gao rt->dst.output = ort->dst.output; 19648e2ec639SYan, Zheng rt->dst.flags |= DST_HOST; 19651da177e4SLinus Torvalds 19664e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *dest; 19678e2ec639SYan, Zheng rt->rt6i_dst.plen = 128; 1968d8d1f30bSChangli Gao rt->dst.error = ort->dst.error; 19691da177e4SLinus Torvalds rt->rt6i_idev = ort->rt6i_idev; 19701da177e4SLinus Torvalds if (rt->rt6i_idev) 19711da177e4SLinus Torvalds in6_dev_hold(rt->rt6i_idev); 1972d8d1f30bSChangli Gao rt->dst.lastuse = jiffies; 19731da177e4SLinus Torvalds 1974550bab42SJulian Anastasov if (ort->rt6i_flags & RTF_GATEWAY) 19754e3fd7a0SAlexey Dobriyan rt->rt6i_gateway = ort->rt6i_gateway; 1976550bab42SJulian Anastasov else 1977550bab42SJulian Anastasov rt->rt6i_gateway = *dest; 19781716a961SGao feng rt->rt6i_flags = ort->rt6i_flags; 19791716a961SGao feng rt6_set_from(rt, ort); 19801da177e4SLinus Torvalds rt->rt6i_metric = 0; 19811da177e4SLinus Torvalds 19821da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 19831da177e4SLinus Torvalds memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key)); 19841da177e4SLinus Torvalds #endif 19850f6c6392SFlorian Westphal memcpy(&rt->rt6i_prefsrc, &ort->rt6i_prefsrc, sizeof(struct rt6key)); 1986c71099acSThomas Graf rt->rt6i_table = ort->rt6i_table; 19871da177e4SLinus Torvalds } 19881da177e4SLinus Torvalds return rt; 19891da177e4SLinus Torvalds } 19901da177e4SLinus Torvalds 199170ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO 1992efa2cea0SDaniel Lezcano static struct rt6_info *rt6_get_route_info(struct net *net, 1993b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 1994b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex) 199570ceb4f5SYOSHIFUJI Hideaki { 199670ceb4f5SYOSHIFUJI Hideaki struct fib6_node *fn; 199770ceb4f5SYOSHIFUJI Hideaki struct rt6_info *rt = NULL; 1998c71099acSThomas Graf struct fib6_table *table; 199970ceb4f5SYOSHIFUJI Hideaki 2000efa2cea0SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_INFO); 200138308473SDavid S. Miller if (!table) 2002c71099acSThomas Graf return NULL; 2003c71099acSThomas Graf 20045744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2005c71099acSThomas Graf fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0); 200670ceb4f5SYOSHIFUJI Hideaki if (!fn) 200770ceb4f5SYOSHIFUJI Hideaki goto out; 200870ceb4f5SYOSHIFUJI Hideaki 2009d8d1f30bSChangli Gao for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { 2010d1918542SDavid S. Miller if (rt->dst.dev->ifindex != ifindex) 201170ceb4f5SYOSHIFUJI Hideaki continue; 201270ceb4f5SYOSHIFUJI Hideaki if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) 201370ceb4f5SYOSHIFUJI Hideaki continue; 201470ceb4f5SYOSHIFUJI Hideaki if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) 201570ceb4f5SYOSHIFUJI Hideaki continue; 2016d8d1f30bSChangli Gao dst_hold(&rt->dst); 201770ceb4f5SYOSHIFUJI Hideaki break; 201870ceb4f5SYOSHIFUJI Hideaki } 201970ceb4f5SYOSHIFUJI Hideaki out: 20205744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 202170ceb4f5SYOSHIFUJI Hideaki return rt; 202270ceb4f5SYOSHIFUJI Hideaki } 202370ceb4f5SYOSHIFUJI Hideaki 2024efa2cea0SDaniel Lezcano static struct rt6_info *rt6_add_route_info(struct net *net, 2025b71d1d42SEric Dumazet const struct in6_addr *prefix, int prefixlen, 2026b71d1d42SEric Dumazet const struct in6_addr *gwaddr, int ifindex, 202795c96174SEric Dumazet unsigned int pref) 202870ceb4f5SYOSHIFUJI Hideaki { 202986872cb5SThomas Graf struct fib6_config cfg = { 203086872cb5SThomas Graf .fc_table = RT6_TABLE_INFO, 2031238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 203286872cb5SThomas Graf .fc_ifindex = ifindex, 203386872cb5SThomas Graf .fc_dst_len = prefixlen, 203486872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | 203586872cb5SThomas Graf RTF_UP | RTF_PREF(pref), 203615e47304SEric W. Biederman .fc_nlinfo.portid = 0, 2037efa2cea0SDaniel Lezcano .fc_nlinfo.nlh = NULL, 2038efa2cea0SDaniel Lezcano .fc_nlinfo.nl_net = net, 203986872cb5SThomas Graf }; 204070ceb4f5SYOSHIFUJI Hideaki 20414e3fd7a0SAlexey Dobriyan cfg.fc_dst = *prefix; 20424e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 204386872cb5SThomas Graf 2044e317da96SYOSHIFUJI Hideaki /* We should treat it as a default route if prefix length is 0. */ 2045e317da96SYOSHIFUJI Hideaki if (!prefixlen) 204686872cb5SThomas Graf cfg.fc_flags |= RTF_DEFAULT; 204770ceb4f5SYOSHIFUJI Hideaki 204886872cb5SThomas Graf ip6_route_add(&cfg); 204970ceb4f5SYOSHIFUJI Hideaki 2050efa2cea0SDaniel Lezcano return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); 205170ceb4f5SYOSHIFUJI Hideaki } 205270ceb4f5SYOSHIFUJI Hideaki #endif 205370ceb4f5SYOSHIFUJI Hideaki 2054b71d1d42SEric Dumazet struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_device *dev) 20551da177e4SLinus Torvalds { 20561da177e4SLinus Torvalds struct rt6_info *rt; 2057c71099acSThomas Graf struct fib6_table *table; 20581da177e4SLinus Torvalds 2059c346dca1SYOSHIFUJI Hideaki table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); 206038308473SDavid S. Miller if (!table) 2061c71099acSThomas Graf return NULL; 20621da177e4SLinus Torvalds 20635744dd9bSLi RongQing read_lock_bh(&table->tb6_lock); 2064d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 2065d1918542SDavid S. Miller if (dev == rt->dst.dev && 2066045927ffSYOSHIFUJI Hideaki ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && 20671da177e4SLinus Torvalds ipv6_addr_equal(&rt->rt6i_gateway, addr)) 20681da177e4SLinus Torvalds break; 20691da177e4SLinus Torvalds } 20701da177e4SLinus Torvalds if (rt) 2071d8d1f30bSChangli Gao dst_hold(&rt->dst); 20725744dd9bSLi RongQing read_unlock_bh(&table->tb6_lock); 20731da177e4SLinus Torvalds return rt; 20741da177e4SLinus Torvalds } 20751da177e4SLinus Torvalds 2076b71d1d42SEric Dumazet struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, 2077ebacaaa0SYOSHIFUJI Hideaki struct net_device *dev, 2078ebacaaa0SYOSHIFUJI Hideaki unsigned int pref) 20791da177e4SLinus Torvalds { 208086872cb5SThomas Graf struct fib6_config cfg = { 208186872cb5SThomas Graf .fc_table = RT6_TABLE_DFLT, 2082238fc7eaSRami Rosen .fc_metric = IP6_RT_PRIO_USER, 208386872cb5SThomas Graf .fc_ifindex = dev->ifindex, 208486872cb5SThomas Graf .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | 208586872cb5SThomas Graf RTF_UP | RTF_EXPIRES | RTF_PREF(pref), 208615e47304SEric W. Biederman .fc_nlinfo.portid = 0, 20875578689aSDaniel Lezcano .fc_nlinfo.nlh = NULL, 2088c346dca1SYOSHIFUJI Hideaki .fc_nlinfo.nl_net = dev_net(dev), 208986872cb5SThomas Graf }; 20901da177e4SLinus Torvalds 20914e3fd7a0SAlexey Dobriyan cfg.fc_gateway = *gwaddr; 20921da177e4SLinus Torvalds 209386872cb5SThomas Graf ip6_route_add(&cfg); 20941da177e4SLinus Torvalds 20951da177e4SLinus Torvalds return rt6_get_dflt_router(gwaddr, dev); 20961da177e4SLinus Torvalds } 20971da177e4SLinus Torvalds 20987b4da532SDaniel Lezcano void rt6_purge_dflt_routers(struct net *net) 20991da177e4SLinus Torvalds { 21001da177e4SLinus Torvalds struct rt6_info *rt; 2101c71099acSThomas Graf struct fib6_table *table; 2102c71099acSThomas Graf 2103c71099acSThomas Graf /* NOTE: Keep consistent with rt6_get_dflt_router */ 21047b4da532SDaniel Lezcano table = fib6_get_table(net, RT6_TABLE_DFLT); 210538308473SDavid S. Miller if (!table) 2106c71099acSThomas Graf return; 21071da177e4SLinus Torvalds 21081da177e4SLinus Torvalds restart: 2109c71099acSThomas Graf read_lock_bh(&table->tb6_lock); 2110d8d1f30bSChangli Gao for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { 21113e8b0ac3SLorenzo Colitti if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && 21123e8b0ac3SLorenzo Colitti (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { 2113d8d1f30bSChangli Gao dst_hold(&rt->dst); 2114c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 2115e0a1ad73SThomas Graf ip6_del_rt(rt); 21161da177e4SLinus Torvalds goto restart; 21171da177e4SLinus Torvalds } 21181da177e4SLinus Torvalds } 2119c71099acSThomas Graf read_unlock_bh(&table->tb6_lock); 21201da177e4SLinus Torvalds } 21211da177e4SLinus Torvalds 21225578689aSDaniel Lezcano static void rtmsg_to_fib6_config(struct net *net, 21235578689aSDaniel Lezcano struct in6_rtmsg *rtmsg, 212486872cb5SThomas Graf struct fib6_config *cfg) 212586872cb5SThomas Graf { 212686872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 212786872cb5SThomas Graf 212886872cb5SThomas Graf cfg->fc_table = RT6_TABLE_MAIN; 212986872cb5SThomas Graf cfg->fc_ifindex = rtmsg->rtmsg_ifindex; 213086872cb5SThomas Graf cfg->fc_metric = rtmsg->rtmsg_metric; 213186872cb5SThomas Graf cfg->fc_expires = rtmsg->rtmsg_info; 213286872cb5SThomas Graf cfg->fc_dst_len = rtmsg->rtmsg_dst_len; 213386872cb5SThomas Graf cfg->fc_src_len = rtmsg->rtmsg_src_len; 213486872cb5SThomas Graf cfg->fc_flags = rtmsg->rtmsg_flags; 213586872cb5SThomas Graf 21365578689aSDaniel Lezcano cfg->fc_nlinfo.nl_net = net; 2137f1243c2dSBenjamin Thery 21384e3fd7a0SAlexey Dobriyan cfg->fc_dst = rtmsg->rtmsg_dst; 21394e3fd7a0SAlexey Dobriyan cfg->fc_src = rtmsg->rtmsg_src; 21404e3fd7a0SAlexey Dobriyan cfg->fc_gateway = rtmsg->rtmsg_gateway; 214186872cb5SThomas Graf } 214286872cb5SThomas Graf 21435578689aSDaniel Lezcano int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) 21441da177e4SLinus Torvalds { 214586872cb5SThomas Graf struct fib6_config cfg; 21461da177e4SLinus Torvalds struct in6_rtmsg rtmsg; 21471da177e4SLinus Torvalds int err; 21481da177e4SLinus Torvalds 21491da177e4SLinus Torvalds switch (cmd) { 21501da177e4SLinus Torvalds case SIOCADDRT: /* Add a route */ 21511da177e4SLinus Torvalds case SIOCDELRT: /* Delete a route */ 2152af31f412SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 21531da177e4SLinus Torvalds return -EPERM; 21541da177e4SLinus Torvalds err = copy_from_user(&rtmsg, arg, 21551da177e4SLinus Torvalds sizeof(struct in6_rtmsg)); 21561da177e4SLinus Torvalds if (err) 21571da177e4SLinus Torvalds return -EFAULT; 21581da177e4SLinus Torvalds 21595578689aSDaniel Lezcano rtmsg_to_fib6_config(net, &rtmsg, &cfg); 216086872cb5SThomas Graf 21611da177e4SLinus Torvalds rtnl_lock(); 21621da177e4SLinus Torvalds switch (cmd) { 21631da177e4SLinus Torvalds case SIOCADDRT: 216486872cb5SThomas Graf err = ip6_route_add(&cfg); 21651da177e4SLinus Torvalds break; 21661da177e4SLinus Torvalds case SIOCDELRT: 216786872cb5SThomas Graf err = ip6_route_del(&cfg); 21681da177e4SLinus Torvalds break; 21691da177e4SLinus Torvalds default: 21701da177e4SLinus Torvalds err = -EINVAL; 21711da177e4SLinus Torvalds } 21721da177e4SLinus Torvalds rtnl_unlock(); 21731da177e4SLinus Torvalds 21741da177e4SLinus Torvalds return err; 21753ff50b79SStephen Hemminger } 21761da177e4SLinus Torvalds 21771da177e4SLinus Torvalds return -EINVAL; 21781da177e4SLinus Torvalds } 21791da177e4SLinus Torvalds 21801da177e4SLinus Torvalds /* 21811da177e4SLinus Torvalds * Drop the packet on the floor 21821da177e4SLinus Torvalds */ 21831da177e4SLinus Torvalds 2184d5fdd6baSBrian Haley static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) 21851da177e4SLinus Torvalds { 2186612f09e8SYOSHIFUJI Hideaki int type; 2187adf30907SEric Dumazet struct dst_entry *dst = skb_dst(skb); 2188612f09e8SYOSHIFUJI Hideaki switch (ipstats_mib_noroutes) { 2189612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_INNOROUTES: 21900660e03fSArnaldo Carvalho de Melo type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); 219145bb0060SUlrich Weber if (type == IPV6_ADDR_ANY) { 21923bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 21933bd653c8SDenis V. Lunev IPSTATS_MIB_INADDRERRORS); 2194612f09e8SYOSHIFUJI Hideaki break; 2195612f09e8SYOSHIFUJI Hideaki } 2196612f09e8SYOSHIFUJI Hideaki /* FALLTHROUGH */ 2197612f09e8SYOSHIFUJI Hideaki case IPSTATS_MIB_OUTNOROUTES: 21983bd653c8SDenis V. Lunev IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), 21993bd653c8SDenis V. Lunev ipstats_mib_noroutes); 2200612f09e8SYOSHIFUJI Hideaki break; 2201612f09e8SYOSHIFUJI Hideaki } 22023ffe533cSAlexey Dobriyan icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); 22031da177e4SLinus Torvalds kfree_skb(skb); 22041da177e4SLinus Torvalds return 0; 22051da177e4SLinus Torvalds } 22061da177e4SLinus Torvalds 22079ce8ade0SThomas Graf static int ip6_pkt_discard(struct sk_buff *skb) 22089ce8ade0SThomas Graf { 2209612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES); 22109ce8ade0SThomas Graf } 22119ce8ade0SThomas Graf 2212aad88724SEric Dumazet static int ip6_pkt_discard_out(struct sock *sk, struct sk_buff *skb) 22131da177e4SLinus Torvalds { 2214adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2215612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES); 22161da177e4SLinus Torvalds } 22171da177e4SLinus Torvalds 22189ce8ade0SThomas Graf static int ip6_pkt_prohibit(struct sk_buff *skb) 22199ce8ade0SThomas Graf { 2220612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES); 22219ce8ade0SThomas Graf } 22229ce8ade0SThomas Graf 2223aad88724SEric Dumazet static int ip6_pkt_prohibit_out(struct sock *sk, struct sk_buff *skb) 22249ce8ade0SThomas Graf { 2225adf30907SEric Dumazet skb->dev = skb_dst(skb)->dev; 2226612f09e8SYOSHIFUJI Hideaki return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); 22279ce8ade0SThomas Graf } 22289ce8ade0SThomas Graf 22291da177e4SLinus Torvalds /* 22301da177e4SLinus Torvalds * Allocate a dst for local (unicast / anycast) address. 22311da177e4SLinus Torvalds */ 22321da177e4SLinus Torvalds 22331da177e4SLinus Torvalds struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, 22341da177e4SLinus Torvalds const struct in6_addr *addr, 22358f031519SDavid S. Miller bool anycast) 22361da177e4SLinus Torvalds { 2237c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(idev->dev); 2238a3300ef4SHannes Frederic Sowa struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 2239a3300ef4SHannes Frederic Sowa DST_NOCOUNT, NULL); 2240a3300ef4SHannes Frederic Sowa if (!rt) 22411da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 22421da177e4SLinus Torvalds 22431da177e4SLinus Torvalds in6_dev_hold(idev); 22441da177e4SLinus Torvalds 224511d53b49SDavid S. Miller rt->dst.flags |= DST_HOST; 2246d8d1f30bSChangli Gao rt->dst.input = ip6_input; 2247d8d1f30bSChangli Gao rt->dst.output = ip6_output; 22481da177e4SLinus Torvalds rt->rt6i_idev = idev; 22491da177e4SLinus Torvalds 22501da177e4SLinus Torvalds rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; 225158c4fb86SYOSHIFUJI Hideaki if (anycast) 225258c4fb86SYOSHIFUJI Hideaki rt->rt6i_flags |= RTF_ANYCAST; 225358c4fb86SYOSHIFUJI Hideaki else 22541da177e4SLinus Torvalds rt->rt6i_flags |= RTF_LOCAL; 22551da177e4SLinus Torvalds 2256550bab42SJulian Anastasov rt->rt6i_gateway = *addr; 22574e3fd7a0SAlexey Dobriyan rt->rt6i_dst.addr = *addr; 22581da177e4SLinus Torvalds rt->rt6i_dst.plen = 128; 22595578689aSDaniel Lezcano rt->rt6i_table = fib6_get_table(net, RT6_TABLE_LOCAL); 22601da177e4SLinus Torvalds 2261d8d1f30bSChangli Gao atomic_set(&rt->dst.__refcnt, 1); 22621da177e4SLinus Torvalds 22631da177e4SLinus Torvalds return rt; 22641da177e4SLinus Torvalds } 22651da177e4SLinus Torvalds 2266c3968a85SDaniel Walter int ip6_route_get_saddr(struct net *net, 2267c3968a85SDaniel Walter struct rt6_info *rt, 2268b71d1d42SEric Dumazet const struct in6_addr *daddr, 2269c3968a85SDaniel Walter unsigned int prefs, 2270c3968a85SDaniel Walter struct in6_addr *saddr) 2271c3968a85SDaniel Walter { 2272c3968a85SDaniel Walter struct inet6_dev *idev = ip6_dst_idev((struct dst_entry *)rt); 2273c3968a85SDaniel Walter int err = 0; 2274c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) 22754e3fd7a0SAlexey Dobriyan *saddr = rt->rt6i_prefsrc.addr; 2276c3968a85SDaniel Walter else 2277c3968a85SDaniel Walter err = ipv6_dev_get_saddr(net, idev ? idev->dev : NULL, 2278c3968a85SDaniel Walter daddr, prefs, saddr); 2279c3968a85SDaniel Walter return err; 2280c3968a85SDaniel Walter } 2281c3968a85SDaniel Walter 2282c3968a85SDaniel Walter /* remove deleted ip from prefsrc entries */ 2283c3968a85SDaniel Walter struct arg_dev_net_ip { 2284c3968a85SDaniel Walter struct net_device *dev; 2285c3968a85SDaniel Walter struct net *net; 2286c3968a85SDaniel Walter struct in6_addr *addr; 2287c3968a85SDaniel Walter }; 2288c3968a85SDaniel Walter 2289c3968a85SDaniel Walter static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg) 2290c3968a85SDaniel Walter { 2291c3968a85SDaniel Walter struct net_device *dev = ((struct arg_dev_net_ip *)arg)->dev; 2292c3968a85SDaniel Walter struct net *net = ((struct arg_dev_net_ip *)arg)->net; 2293c3968a85SDaniel Walter struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr; 2294c3968a85SDaniel Walter 2295d1918542SDavid S. Miller if (((void *)rt->dst.dev == dev || !dev) && 2296c3968a85SDaniel Walter rt != net->ipv6.ip6_null_entry && 2297c3968a85SDaniel Walter ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) { 2298c3968a85SDaniel Walter /* remove prefsrc entry */ 2299c3968a85SDaniel Walter rt->rt6i_prefsrc.plen = 0; 2300c3968a85SDaniel Walter } 2301c3968a85SDaniel Walter return 0; 2302c3968a85SDaniel Walter } 2303c3968a85SDaniel Walter 2304c3968a85SDaniel Walter void rt6_remove_prefsrc(struct inet6_ifaddr *ifp) 2305c3968a85SDaniel Walter { 2306c3968a85SDaniel Walter struct net *net = dev_net(ifp->idev->dev); 2307c3968a85SDaniel Walter struct arg_dev_net_ip adni = { 2308c3968a85SDaniel Walter .dev = ifp->idev->dev, 2309c3968a85SDaniel Walter .net = net, 2310c3968a85SDaniel Walter .addr = &ifp->addr, 2311c3968a85SDaniel Walter }; 23120c3584d5SLi RongQing fib6_clean_all(net, fib6_remove_prefsrc, &adni); 2313c3968a85SDaniel Walter } 2314c3968a85SDaniel Walter 2315be7a010dSDuan Jiong #define RTF_RA_ROUTER (RTF_ADDRCONF | RTF_DEFAULT | RTF_GATEWAY) 2316be7a010dSDuan Jiong #define RTF_CACHE_GATEWAY (RTF_GATEWAY | RTF_CACHE) 2317be7a010dSDuan Jiong 2318be7a010dSDuan Jiong /* Remove routers and update dst entries when gateway turn into host. */ 2319be7a010dSDuan Jiong static int fib6_clean_tohost(struct rt6_info *rt, void *arg) 2320be7a010dSDuan Jiong { 2321be7a010dSDuan Jiong struct in6_addr *gateway = (struct in6_addr *)arg; 2322be7a010dSDuan Jiong 2323be7a010dSDuan Jiong if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) || 2324be7a010dSDuan Jiong ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) && 2325be7a010dSDuan Jiong ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { 2326be7a010dSDuan Jiong return -1; 2327be7a010dSDuan Jiong } 2328be7a010dSDuan Jiong return 0; 2329be7a010dSDuan Jiong } 2330be7a010dSDuan Jiong 2331be7a010dSDuan Jiong void rt6_clean_tohost(struct net *net, struct in6_addr *gateway) 2332be7a010dSDuan Jiong { 2333be7a010dSDuan Jiong fib6_clean_all(net, fib6_clean_tohost, gateway); 2334be7a010dSDuan Jiong } 2335be7a010dSDuan Jiong 23368ed67789SDaniel Lezcano struct arg_dev_net { 23378ed67789SDaniel Lezcano struct net_device *dev; 23388ed67789SDaniel Lezcano struct net *net; 23398ed67789SDaniel Lezcano }; 23408ed67789SDaniel Lezcano 23411da177e4SLinus Torvalds static int fib6_ifdown(struct rt6_info *rt, void *arg) 23421da177e4SLinus Torvalds { 2343bc3ef660Sstephen hemminger const struct arg_dev_net *adn = arg; 2344bc3ef660Sstephen hemminger const struct net_device *dev = adn->dev; 23458ed67789SDaniel Lezcano 2346d1918542SDavid S. Miller if ((rt->dst.dev == dev || !dev) && 2347c159d30cSDavid S. Miller rt != adn->net->ipv6.ip6_null_entry) 23481da177e4SLinus Torvalds return -1; 2349c159d30cSDavid S. Miller 23501da177e4SLinus Torvalds return 0; 23511da177e4SLinus Torvalds } 23521da177e4SLinus Torvalds 2353f3db4851SDaniel Lezcano void rt6_ifdown(struct net *net, struct net_device *dev) 23541da177e4SLinus Torvalds { 23558ed67789SDaniel Lezcano struct arg_dev_net adn = { 23568ed67789SDaniel Lezcano .dev = dev, 23578ed67789SDaniel Lezcano .net = net, 23588ed67789SDaniel Lezcano }; 23598ed67789SDaniel Lezcano 23600c3584d5SLi RongQing fib6_clean_all(net, fib6_ifdown, &adn); 23611e493d19SDavid S. Miller icmp6_clean_all(fib6_ifdown, &adn); 23621da177e4SLinus Torvalds } 23631da177e4SLinus Torvalds 236495c96174SEric Dumazet struct rt6_mtu_change_arg { 23651da177e4SLinus Torvalds struct net_device *dev; 236695c96174SEric Dumazet unsigned int mtu; 23671da177e4SLinus Torvalds }; 23681da177e4SLinus Torvalds 23691da177e4SLinus Torvalds static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) 23701da177e4SLinus Torvalds { 23711da177e4SLinus Torvalds struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; 23721da177e4SLinus Torvalds struct inet6_dev *idev; 23731da177e4SLinus Torvalds 23741da177e4SLinus Torvalds /* In IPv6 pmtu discovery is not optional, 23751da177e4SLinus Torvalds so that RTAX_MTU lock cannot disable it. 23761da177e4SLinus Torvalds We still use this lock to block changes 23771da177e4SLinus Torvalds caused by addrconf/ndisc. 23781da177e4SLinus Torvalds */ 23791da177e4SLinus Torvalds 23801da177e4SLinus Torvalds idev = __in6_dev_get(arg->dev); 238138308473SDavid S. Miller if (!idev) 23821da177e4SLinus Torvalds return 0; 23831da177e4SLinus Torvalds 23841da177e4SLinus Torvalds /* For administrative MTU increase, there is no way to discover 23851da177e4SLinus Torvalds IPv6 PMTU increase, so PMTU increase should be updated here. 23861da177e4SLinus Torvalds Since RFC 1981 doesn't include administrative MTU increase 23871da177e4SLinus Torvalds update PMTU increase is a MUST. (i.e. jumbo frame) 23881da177e4SLinus Torvalds */ 23891da177e4SLinus Torvalds /* 23901da177e4SLinus Torvalds If new MTU is less than route PMTU, this new MTU will be the 23911da177e4SLinus Torvalds lowest MTU in the path, update the route PMTU to reflect PMTU 23921da177e4SLinus Torvalds decreases; if new MTU is greater than route PMTU, and the 23931da177e4SLinus Torvalds old MTU is the lowest MTU in the path, update the route PMTU 23941da177e4SLinus Torvalds to reflect the increase. In this case if the other nodes' MTU 23951da177e4SLinus Torvalds also have the lowest MTU, TOO BIG MESSAGE will be lead to 23961da177e4SLinus Torvalds PMTU discouvery. 23971da177e4SLinus Torvalds */ 2398d1918542SDavid S. Miller if (rt->dst.dev == arg->dev && 2399*4b32b5adSMartin KaFai Lau !dst_metric_locked(&rt->dst, RTAX_MTU)) { 2400*4b32b5adSMartin KaFai Lau if (rt->rt6i_flags & RTF_CACHE) { 2401*4b32b5adSMartin KaFai Lau /* For RTF_CACHE with rt6i_pmtu == 0 2402*4b32b5adSMartin KaFai Lau * (i.e. a redirected route), 2403*4b32b5adSMartin KaFai Lau * the metrics of its rt->dst.from has already 2404*4b32b5adSMartin KaFai Lau * been updated. 2405*4b32b5adSMartin KaFai Lau */ 2406*4b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu) 2407*4b32b5adSMartin KaFai Lau rt->rt6i_pmtu = arg->mtu; 2408*4b32b5adSMartin KaFai Lau } else if (dst_mtu(&rt->dst) >= arg->mtu || 2409d8d1f30bSChangli Gao (dst_mtu(&rt->dst) < arg->mtu && 2410*4b32b5adSMartin KaFai Lau dst_mtu(&rt->dst) == idev->cnf.mtu6)) { 2411defb3519SDavid S. Miller dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); 2412566cfd8fSSimon Arlott } 2413*4b32b5adSMartin KaFai Lau } 24141da177e4SLinus Torvalds return 0; 24151da177e4SLinus Torvalds } 24161da177e4SLinus Torvalds 241795c96174SEric Dumazet void rt6_mtu_change(struct net_device *dev, unsigned int mtu) 24181da177e4SLinus Torvalds { 2419c71099acSThomas Graf struct rt6_mtu_change_arg arg = { 2420c71099acSThomas Graf .dev = dev, 2421c71099acSThomas Graf .mtu = mtu, 2422c71099acSThomas Graf }; 24231da177e4SLinus Torvalds 24240c3584d5SLi RongQing fib6_clean_all(dev_net(dev), rt6_mtu_change_route, &arg); 24251da177e4SLinus Torvalds } 24261da177e4SLinus Torvalds 2427ef7c79edSPatrick McHardy static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { 24285176f91eSThomas Graf [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, 242986872cb5SThomas Graf [RTA_OIF] = { .type = NLA_U32 }, 2430ab364a6fSThomas Graf [RTA_IIF] = { .type = NLA_U32 }, 243186872cb5SThomas Graf [RTA_PRIORITY] = { .type = NLA_U32 }, 243286872cb5SThomas Graf [RTA_METRICS] = { .type = NLA_NESTED }, 243351ebd318SNicolas Dichtel [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2434c78ba6d6SLubomir Rintel [RTA_PREF] = { .type = NLA_U8 }, 243586872cb5SThomas Graf }; 243686872cb5SThomas Graf 243786872cb5SThomas Graf static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, 243886872cb5SThomas Graf struct fib6_config *cfg) 24391da177e4SLinus Torvalds { 244086872cb5SThomas Graf struct rtmsg *rtm; 244186872cb5SThomas Graf struct nlattr *tb[RTA_MAX+1]; 2442c78ba6d6SLubomir Rintel unsigned int pref; 244386872cb5SThomas Graf int err; 24441da177e4SLinus Torvalds 244586872cb5SThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 244686872cb5SThomas Graf if (err < 0) 244786872cb5SThomas Graf goto errout; 24481da177e4SLinus Torvalds 244986872cb5SThomas Graf err = -EINVAL; 245086872cb5SThomas Graf rtm = nlmsg_data(nlh); 245186872cb5SThomas Graf memset(cfg, 0, sizeof(*cfg)); 245286872cb5SThomas Graf 245386872cb5SThomas Graf cfg->fc_table = rtm->rtm_table; 245486872cb5SThomas Graf cfg->fc_dst_len = rtm->rtm_dst_len; 245586872cb5SThomas Graf cfg->fc_src_len = rtm->rtm_src_len; 245686872cb5SThomas Graf cfg->fc_flags = RTF_UP; 245786872cb5SThomas Graf cfg->fc_protocol = rtm->rtm_protocol; 2458ef2c7d7bSNicolas Dichtel cfg->fc_type = rtm->rtm_type; 245986872cb5SThomas Graf 2460ef2c7d7bSNicolas Dichtel if (rtm->rtm_type == RTN_UNREACHABLE || 2461ef2c7d7bSNicolas Dichtel rtm->rtm_type == RTN_BLACKHOLE || 2462b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_PROHIBIT || 2463b4949ab2SNicolas Dichtel rtm->rtm_type == RTN_THROW) 246486872cb5SThomas Graf cfg->fc_flags |= RTF_REJECT; 246586872cb5SThomas Graf 2466ab79ad14SMaciej Żenczykowski if (rtm->rtm_type == RTN_LOCAL) 2467ab79ad14SMaciej Żenczykowski cfg->fc_flags |= RTF_LOCAL; 2468ab79ad14SMaciej Żenczykowski 24691f56a01fSMartin KaFai Lau if (rtm->rtm_flags & RTM_F_CLONED) 24701f56a01fSMartin KaFai Lau cfg->fc_flags |= RTF_CACHE; 24711f56a01fSMartin KaFai Lau 247215e47304SEric W. Biederman cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 247386872cb5SThomas Graf cfg->fc_nlinfo.nlh = nlh; 24743b1e0a65SYOSHIFUJI Hideaki cfg->fc_nlinfo.nl_net = sock_net(skb->sk); 247586872cb5SThomas Graf 247686872cb5SThomas Graf if (tb[RTA_GATEWAY]) { 247767b61f6cSJiri Benc cfg->fc_gateway = nla_get_in6_addr(tb[RTA_GATEWAY]); 247886872cb5SThomas Graf cfg->fc_flags |= RTF_GATEWAY; 24791da177e4SLinus Torvalds } 248086872cb5SThomas Graf 248186872cb5SThomas Graf if (tb[RTA_DST]) { 248286872cb5SThomas Graf int plen = (rtm->rtm_dst_len + 7) >> 3; 248386872cb5SThomas Graf 248486872cb5SThomas Graf if (nla_len(tb[RTA_DST]) < plen) 248586872cb5SThomas Graf goto errout; 248686872cb5SThomas Graf 248786872cb5SThomas Graf nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); 24881da177e4SLinus Torvalds } 248986872cb5SThomas Graf 249086872cb5SThomas Graf if (tb[RTA_SRC]) { 249186872cb5SThomas Graf int plen = (rtm->rtm_src_len + 7) >> 3; 249286872cb5SThomas Graf 249386872cb5SThomas Graf if (nla_len(tb[RTA_SRC]) < plen) 249486872cb5SThomas Graf goto errout; 249586872cb5SThomas Graf 249686872cb5SThomas Graf nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); 24971da177e4SLinus Torvalds } 249886872cb5SThomas Graf 2499c3968a85SDaniel Walter if (tb[RTA_PREFSRC]) 250067b61f6cSJiri Benc cfg->fc_prefsrc = nla_get_in6_addr(tb[RTA_PREFSRC]); 2501c3968a85SDaniel Walter 250286872cb5SThomas Graf if (tb[RTA_OIF]) 250386872cb5SThomas Graf cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); 250486872cb5SThomas Graf 250586872cb5SThomas Graf if (tb[RTA_PRIORITY]) 250686872cb5SThomas Graf cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); 250786872cb5SThomas Graf 250886872cb5SThomas Graf if (tb[RTA_METRICS]) { 250986872cb5SThomas Graf cfg->fc_mx = nla_data(tb[RTA_METRICS]); 251086872cb5SThomas Graf cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); 25111da177e4SLinus Torvalds } 251286872cb5SThomas Graf 251386872cb5SThomas Graf if (tb[RTA_TABLE]) 251486872cb5SThomas Graf cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); 251586872cb5SThomas Graf 251651ebd318SNicolas Dichtel if (tb[RTA_MULTIPATH]) { 251751ebd318SNicolas Dichtel cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]); 251851ebd318SNicolas Dichtel cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]); 251951ebd318SNicolas Dichtel } 252051ebd318SNicolas Dichtel 2521c78ba6d6SLubomir Rintel if (tb[RTA_PREF]) { 2522c78ba6d6SLubomir Rintel pref = nla_get_u8(tb[RTA_PREF]); 2523c78ba6d6SLubomir Rintel if (pref != ICMPV6_ROUTER_PREF_LOW && 2524c78ba6d6SLubomir Rintel pref != ICMPV6_ROUTER_PREF_HIGH) 2525c78ba6d6SLubomir Rintel pref = ICMPV6_ROUTER_PREF_MEDIUM; 2526c78ba6d6SLubomir Rintel cfg->fc_flags |= RTF_PREF(pref); 2527c78ba6d6SLubomir Rintel } 2528c78ba6d6SLubomir Rintel 252986872cb5SThomas Graf err = 0; 253086872cb5SThomas Graf errout: 253186872cb5SThomas Graf return err; 25321da177e4SLinus Torvalds } 25331da177e4SLinus Torvalds 253451ebd318SNicolas Dichtel static int ip6_route_multipath(struct fib6_config *cfg, int add) 253551ebd318SNicolas Dichtel { 253651ebd318SNicolas Dichtel struct fib6_config r_cfg; 253751ebd318SNicolas Dichtel struct rtnexthop *rtnh; 253851ebd318SNicolas Dichtel int remaining; 253951ebd318SNicolas Dichtel int attrlen; 254051ebd318SNicolas Dichtel int err = 0, last_err = 0; 254151ebd318SNicolas Dichtel 254251ebd318SNicolas Dichtel beginning: 254351ebd318SNicolas Dichtel rtnh = (struct rtnexthop *)cfg->fc_mp; 254451ebd318SNicolas Dichtel remaining = cfg->fc_mp_len; 254551ebd318SNicolas Dichtel 254651ebd318SNicolas Dichtel /* Parse a Multipath Entry */ 254751ebd318SNicolas Dichtel while (rtnh_ok(rtnh, remaining)) { 254851ebd318SNicolas Dichtel memcpy(&r_cfg, cfg, sizeof(*cfg)); 254951ebd318SNicolas Dichtel if (rtnh->rtnh_ifindex) 255051ebd318SNicolas Dichtel r_cfg.fc_ifindex = rtnh->rtnh_ifindex; 255151ebd318SNicolas Dichtel 255251ebd318SNicolas Dichtel attrlen = rtnh_attrlen(rtnh); 255351ebd318SNicolas Dichtel if (attrlen > 0) { 255451ebd318SNicolas Dichtel struct nlattr *nla, *attrs = rtnh_attrs(rtnh); 255551ebd318SNicolas Dichtel 255651ebd318SNicolas Dichtel nla = nla_find(attrs, attrlen, RTA_GATEWAY); 255751ebd318SNicolas Dichtel if (nla) { 255867b61f6cSJiri Benc r_cfg.fc_gateway = nla_get_in6_addr(nla); 255951ebd318SNicolas Dichtel r_cfg.fc_flags |= RTF_GATEWAY; 256051ebd318SNicolas Dichtel } 256151ebd318SNicolas Dichtel } 256251ebd318SNicolas Dichtel err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg); 256351ebd318SNicolas Dichtel if (err) { 256451ebd318SNicolas Dichtel last_err = err; 256551ebd318SNicolas Dichtel /* If we are trying to remove a route, do not stop the 256651ebd318SNicolas Dichtel * loop when ip6_route_del() fails (because next hop is 256751ebd318SNicolas Dichtel * already gone), we should try to remove all next hops. 256851ebd318SNicolas Dichtel */ 256951ebd318SNicolas Dichtel if (add) { 257051ebd318SNicolas Dichtel /* If add fails, we should try to delete all 257151ebd318SNicolas Dichtel * next hops that have been already added. 257251ebd318SNicolas Dichtel */ 257351ebd318SNicolas Dichtel add = 0; 257451ebd318SNicolas Dichtel goto beginning; 257551ebd318SNicolas Dichtel } 257651ebd318SNicolas Dichtel } 25771a72418bSNicolas Dichtel /* Because each route is added like a single route we remove 25781a72418bSNicolas Dichtel * this flag after the first nexthop (if there is a collision, 25791a72418bSNicolas Dichtel * we have already fail to add the first nexthop: 25801a72418bSNicolas Dichtel * fib6_add_rt2node() has reject it). 25811a72418bSNicolas Dichtel */ 25821a72418bSNicolas Dichtel cfg->fc_nlinfo.nlh->nlmsg_flags &= ~NLM_F_EXCL; 258351ebd318SNicolas Dichtel rtnh = rtnh_next(rtnh, &remaining); 258451ebd318SNicolas Dichtel } 258551ebd318SNicolas Dichtel 258651ebd318SNicolas Dichtel return last_err; 258751ebd318SNicolas Dichtel } 258851ebd318SNicolas Dichtel 2589661d2967SThomas Graf static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) 25901da177e4SLinus Torvalds { 259186872cb5SThomas Graf struct fib6_config cfg; 259286872cb5SThomas Graf int err; 25931da177e4SLinus Torvalds 259486872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 259586872cb5SThomas Graf if (err < 0) 259686872cb5SThomas Graf return err; 259786872cb5SThomas Graf 259851ebd318SNicolas Dichtel if (cfg.fc_mp) 259951ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 0); 260051ebd318SNicolas Dichtel else 260186872cb5SThomas Graf return ip6_route_del(&cfg); 26021da177e4SLinus Torvalds } 26031da177e4SLinus Torvalds 2604661d2967SThomas Graf static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) 26051da177e4SLinus Torvalds { 260686872cb5SThomas Graf struct fib6_config cfg; 260786872cb5SThomas Graf int err; 26081da177e4SLinus Torvalds 260986872cb5SThomas Graf err = rtm_to_fib6_config(skb, nlh, &cfg); 261086872cb5SThomas Graf if (err < 0) 261186872cb5SThomas Graf return err; 261286872cb5SThomas Graf 261351ebd318SNicolas Dichtel if (cfg.fc_mp) 261451ebd318SNicolas Dichtel return ip6_route_multipath(&cfg, 1); 261551ebd318SNicolas Dichtel else 261686872cb5SThomas Graf return ip6_route_add(&cfg); 26171da177e4SLinus Torvalds } 26181da177e4SLinus Torvalds 2619339bf98fSThomas Graf static inline size_t rt6_nlmsg_size(void) 2620339bf98fSThomas Graf { 2621339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct rtmsg)) 2622339bf98fSThomas Graf + nla_total_size(16) /* RTA_SRC */ 2623339bf98fSThomas Graf + nla_total_size(16) /* RTA_DST */ 2624339bf98fSThomas Graf + nla_total_size(16) /* RTA_GATEWAY */ 2625339bf98fSThomas Graf + nla_total_size(16) /* RTA_PREFSRC */ 2626339bf98fSThomas Graf + nla_total_size(4) /* RTA_TABLE */ 2627339bf98fSThomas Graf + nla_total_size(4) /* RTA_IIF */ 2628339bf98fSThomas Graf + nla_total_size(4) /* RTA_OIF */ 2629339bf98fSThomas Graf + nla_total_size(4) /* RTA_PRIORITY */ 26306a2b9ce0SNoriaki TAKAMIYA + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ 2631ea697639SDaniel Borkmann + nla_total_size(sizeof(struct rta_cacheinfo)) 2632c78ba6d6SLubomir Rintel + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */ 2633c78ba6d6SLubomir Rintel + nla_total_size(1); /* RTA_PREF */ 2634339bf98fSThomas Graf } 2635339bf98fSThomas Graf 2636191cd582SBrian Haley static int rt6_fill_node(struct net *net, 2637191cd582SBrian Haley struct sk_buff *skb, struct rt6_info *rt, 26380d51aa80SJamal Hadi Salim struct in6_addr *dst, struct in6_addr *src, 263915e47304SEric W. Biederman int iif, int type, u32 portid, u32 seq, 26407bc570c8SYOSHIFUJI Hideaki int prefix, int nowait, unsigned int flags) 26411da177e4SLinus Torvalds { 2642*4b32b5adSMartin KaFai Lau u32 metrics[RTAX_MAX]; 26431da177e4SLinus Torvalds struct rtmsg *rtm; 26441da177e4SLinus Torvalds struct nlmsghdr *nlh; 2645e3703b3dSThomas Graf long expires; 26469e762a4aSPatrick McHardy u32 table; 26471da177e4SLinus Torvalds 26481da177e4SLinus Torvalds if (prefix) { /* user wants prefix routes only */ 26491da177e4SLinus Torvalds if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { 26501da177e4SLinus Torvalds /* success since this is not a prefix route */ 26511da177e4SLinus Torvalds return 1; 26521da177e4SLinus Torvalds } 26531da177e4SLinus Torvalds } 26541da177e4SLinus Torvalds 265515e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, type, sizeof(*rtm), flags); 265638308473SDavid S. Miller if (!nlh) 265726932566SPatrick McHardy return -EMSGSIZE; 26582d7202bfSThomas Graf 26592d7202bfSThomas Graf rtm = nlmsg_data(nlh); 26601da177e4SLinus Torvalds rtm->rtm_family = AF_INET6; 26611da177e4SLinus Torvalds rtm->rtm_dst_len = rt->rt6i_dst.plen; 26621da177e4SLinus Torvalds rtm->rtm_src_len = rt->rt6i_src.plen; 26631da177e4SLinus Torvalds rtm->rtm_tos = 0; 2664c71099acSThomas Graf if (rt->rt6i_table) 26659e762a4aSPatrick McHardy table = rt->rt6i_table->tb6_id; 2666c71099acSThomas Graf else 26679e762a4aSPatrick McHardy table = RT6_TABLE_UNSPEC; 26689e762a4aSPatrick McHardy rtm->rtm_table = table; 2669c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, table)) 2670c78679e8SDavid S. Miller goto nla_put_failure; 2671ef2c7d7bSNicolas Dichtel if (rt->rt6i_flags & RTF_REJECT) { 2672ef2c7d7bSNicolas Dichtel switch (rt->dst.error) { 2673ef2c7d7bSNicolas Dichtel case -EINVAL: 2674ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_BLACKHOLE; 2675ef2c7d7bSNicolas Dichtel break; 2676ef2c7d7bSNicolas Dichtel case -EACCES: 2677ef2c7d7bSNicolas Dichtel rtm->rtm_type = RTN_PROHIBIT; 2678ef2c7d7bSNicolas Dichtel break; 2679b4949ab2SNicolas Dichtel case -EAGAIN: 2680b4949ab2SNicolas Dichtel rtm->rtm_type = RTN_THROW; 2681b4949ab2SNicolas Dichtel break; 2682ef2c7d7bSNicolas Dichtel default: 26831da177e4SLinus Torvalds rtm->rtm_type = RTN_UNREACHABLE; 2684ef2c7d7bSNicolas Dichtel break; 2685ef2c7d7bSNicolas Dichtel } 2686ef2c7d7bSNicolas Dichtel } 2687ab79ad14SMaciej Żenczykowski else if (rt->rt6i_flags & RTF_LOCAL) 2688ab79ad14SMaciej Żenczykowski rtm->rtm_type = RTN_LOCAL; 2689d1918542SDavid S. Miller else if (rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK)) 26901da177e4SLinus Torvalds rtm->rtm_type = RTN_LOCAL; 26911da177e4SLinus Torvalds else 26921da177e4SLinus Torvalds rtm->rtm_type = RTN_UNICAST; 26931da177e4SLinus Torvalds rtm->rtm_flags = 0; 26941da177e4SLinus Torvalds rtm->rtm_scope = RT_SCOPE_UNIVERSE; 26951da177e4SLinus Torvalds rtm->rtm_protocol = rt->rt6i_protocol; 26961da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_DYNAMIC) 26971da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_REDIRECT; 2698f0396f60SDenis Ovsienko else if (rt->rt6i_flags & RTF_ADDRCONF) { 2699f0396f60SDenis Ovsienko if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ROUTEINFO)) 27001da177e4SLinus Torvalds rtm->rtm_protocol = RTPROT_RA; 2701f0396f60SDenis Ovsienko else 2702f0396f60SDenis Ovsienko rtm->rtm_protocol = RTPROT_KERNEL; 2703f0396f60SDenis Ovsienko } 27041da177e4SLinus Torvalds 27051da177e4SLinus Torvalds if (rt->rt6i_flags & RTF_CACHE) 27061da177e4SLinus Torvalds rtm->rtm_flags |= RTM_F_CLONED; 27071da177e4SLinus Torvalds 27081da177e4SLinus Torvalds if (dst) { 2709930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, dst)) 2710c78679e8SDavid S. Miller goto nla_put_failure; 27111da177e4SLinus Torvalds rtm->rtm_dst_len = 128; 27121da177e4SLinus Torvalds } else if (rtm->rtm_dst_len) 2713930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_DST, &rt->rt6i_dst.addr)) 2714c78679e8SDavid S. Miller goto nla_put_failure; 27151da177e4SLinus Torvalds #ifdef CONFIG_IPV6_SUBTREES 27161da177e4SLinus Torvalds if (src) { 2717930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_SRC, src)) 2718c78679e8SDavid S. Miller goto nla_put_failure; 27191da177e4SLinus Torvalds rtm->rtm_src_len = 128; 2720c78679e8SDavid S. Miller } else if (rtm->rtm_src_len && 2721930345eaSJiri Benc nla_put_in6_addr(skb, RTA_SRC, &rt->rt6i_src.addr)) 2722c78679e8SDavid S. Miller goto nla_put_failure; 27231da177e4SLinus Torvalds #endif 27247bc570c8SYOSHIFUJI Hideaki if (iif) { 27257bc570c8SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_MROUTE 27267bc570c8SYOSHIFUJI Hideaki if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { 27278229efdaSBenjamin Thery int err = ip6mr_get_route(net, skb, rtm, nowait); 27287bc570c8SYOSHIFUJI Hideaki if (err <= 0) { 27297bc570c8SYOSHIFUJI Hideaki if (!nowait) { 27307bc570c8SYOSHIFUJI Hideaki if (err == 0) 27317bc570c8SYOSHIFUJI Hideaki return 0; 27327bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 27337bc570c8SYOSHIFUJI Hideaki } else { 27347bc570c8SYOSHIFUJI Hideaki if (err == -EMSGSIZE) 27357bc570c8SYOSHIFUJI Hideaki goto nla_put_failure; 27367bc570c8SYOSHIFUJI Hideaki } 27377bc570c8SYOSHIFUJI Hideaki } 27387bc570c8SYOSHIFUJI Hideaki } else 27397bc570c8SYOSHIFUJI Hideaki #endif 2740c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_IIF, iif)) 2741c78679e8SDavid S. Miller goto nla_put_failure; 27427bc570c8SYOSHIFUJI Hideaki } else if (dst) { 27431da177e4SLinus Torvalds struct in6_addr saddr_buf; 2744c78679e8SDavid S. Miller if (ip6_route_get_saddr(net, rt, dst, 0, &saddr_buf) == 0 && 2745930345eaSJiri Benc nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 2746c78679e8SDavid S. Miller goto nla_put_failure; 2747c3968a85SDaniel Walter } 2748c3968a85SDaniel Walter 2749c3968a85SDaniel Walter if (rt->rt6i_prefsrc.plen) { 2750c3968a85SDaniel Walter struct in6_addr saddr_buf; 27514e3fd7a0SAlexey Dobriyan saddr_buf = rt->rt6i_prefsrc.addr; 2752930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf)) 2753c78679e8SDavid S. Miller goto nla_put_failure; 27541da177e4SLinus Torvalds } 27552d7202bfSThomas Graf 2756*4b32b5adSMartin KaFai Lau memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); 2757*4b32b5adSMartin KaFai Lau if (rt->rt6i_pmtu) 2758*4b32b5adSMartin KaFai Lau metrics[RTAX_MTU - 1] = rt->rt6i_pmtu; 2759*4b32b5adSMartin KaFai Lau if (rtnetlink_put_metrics(skb, metrics) < 0) 27602d7202bfSThomas Graf goto nla_put_failure; 27612d7202bfSThomas Graf 2762dd0cbf29SYOSHIFUJI Hideaki / 吉藤英明 if (rt->rt6i_flags & RTF_GATEWAY) { 2763930345eaSJiri Benc if (nla_put_in6_addr(skb, RTA_GATEWAY, &rt->rt6i_gateway) < 0) 276494f826b8SEric Dumazet goto nla_put_failure; 276594f826b8SEric Dumazet } 27662d7202bfSThomas Graf 2767c78679e8SDavid S. Miller if (rt->dst.dev && 2768c78679e8SDavid S. Miller nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 2769c78679e8SDavid S. Miller goto nla_put_failure; 2770c78679e8SDavid S. Miller if (nla_put_u32(skb, RTA_PRIORITY, rt->rt6i_metric)) 2771c78679e8SDavid S. Miller goto nla_put_failure; 27728253947eSLi Wei 27738253947eSLi Wei expires = (rt->rt6i_flags & RTF_EXPIRES) ? rt->dst.expires - jiffies : 0; 277469cdf8f9SYOSHIFUJI Hideaki 277587a50699SDavid S. Miller if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) 2776e3703b3dSThomas Graf goto nla_put_failure; 27771da177e4SLinus Torvalds 2778c78ba6d6SLubomir Rintel if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags))) 2779c78ba6d6SLubomir Rintel goto nla_put_failure; 2780c78ba6d6SLubomir Rintel 2781053c095aSJohannes Berg nlmsg_end(skb, nlh); 2782053c095aSJohannes Berg return 0; 27832d7202bfSThomas Graf 27842d7202bfSThomas Graf nla_put_failure: 278526932566SPatrick McHardy nlmsg_cancel(skb, nlh); 278626932566SPatrick McHardy return -EMSGSIZE; 27871da177e4SLinus Torvalds } 27881da177e4SLinus Torvalds 27891b43af54SPatrick McHardy int rt6_dump_route(struct rt6_info *rt, void *p_arg) 27901da177e4SLinus Torvalds { 27911da177e4SLinus Torvalds struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; 27921da177e4SLinus Torvalds int prefix; 27931da177e4SLinus Torvalds 27942d7202bfSThomas Graf if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { 27952d7202bfSThomas Graf struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); 27961da177e4SLinus Torvalds prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; 27971da177e4SLinus Torvalds } else 27981da177e4SLinus Torvalds prefix = 0; 27991da177e4SLinus Torvalds 2800191cd582SBrian Haley return rt6_fill_node(arg->net, 2801191cd582SBrian Haley arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, 280215e47304SEric W. Biederman NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq, 28037bc570c8SYOSHIFUJI Hideaki prefix, 0, NLM_F_MULTI); 28041da177e4SLinus Torvalds } 28051da177e4SLinus Torvalds 2806661d2967SThomas Graf static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) 28071da177e4SLinus Torvalds { 28083b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(in_skb->sk); 2809ab364a6fSThomas Graf struct nlattr *tb[RTA_MAX+1]; 28101da177e4SLinus Torvalds struct rt6_info *rt; 2811ab364a6fSThomas Graf struct sk_buff *skb; 2812ab364a6fSThomas Graf struct rtmsg *rtm; 28134c9483b2SDavid S. Miller struct flowi6 fl6; 281472331bc0SShmulik Ladkani int err, iif = 0, oif = 0; 2815ab364a6fSThomas Graf 2816ab364a6fSThomas Graf err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); 2817ab364a6fSThomas Graf if (err < 0) 2818ab364a6fSThomas Graf goto errout; 2819ab364a6fSThomas Graf 2820ab364a6fSThomas Graf err = -EINVAL; 28214c9483b2SDavid S. Miller memset(&fl6, 0, sizeof(fl6)); 2822ab364a6fSThomas Graf 2823ab364a6fSThomas Graf if (tb[RTA_SRC]) { 2824ab364a6fSThomas Graf if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) 2825ab364a6fSThomas Graf goto errout; 2826ab364a6fSThomas Graf 28274e3fd7a0SAlexey Dobriyan fl6.saddr = *(struct in6_addr *)nla_data(tb[RTA_SRC]); 2828ab364a6fSThomas Graf } 2829ab364a6fSThomas Graf 2830ab364a6fSThomas Graf if (tb[RTA_DST]) { 2831ab364a6fSThomas Graf if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) 2832ab364a6fSThomas Graf goto errout; 2833ab364a6fSThomas Graf 28344e3fd7a0SAlexey Dobriyan fl6.daddr = *(struct in6_addr *)nla_data(tb[RTA_DST]); 2835ab364a6fSThomas Graf } 2836ab364a6fSThomas Graf 2837ab364a6fSThomas Graf if (tb[RTA_IIF]) 2838ab364a6fSThomas Graf iif = nla_get_u32(tb[RTA_IIF]); 2839ab364a6fSThomas Graf 2840ab364a6fSThomas Graf if (tb[RTA_OIF]) 284172331bc0SShmulik Ladkani oif = nla_get_u32(tb[RTA_OIF]); 2842ab364a6fSThomas Graf 28432e47b291SLorenzo Colitti if (tb[RTA_MARK]) 28442e47b291SLorenzo Colitti fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); 28452e47b291SLorenzo Colitti 2846ab364a6fSThomas Graf if (iif) { 2847ab364a6fSThomas Graf struct net_device *dev; 284872331bc0SShmulik Ladkani int flags = 0; 284972331bc0SShmulik Ladkani 28505578689aSDaniel Lezcano dev = __dev_get_by_index(net, iif); 2851ab364a6fSThomas Graf if (!dev) { 2852ab364a6fSThomas Graf err = -ENODEV; 2853ab364a6fSThomas Graf goto errout; 2854ab364a6fSThomas Graf } 285572331bc0SShmulik Ladkani 285672331bc0SShmulik Ladkani fl6.flowi6_iif = iif; 285772331bc0SShmulik Ladkani 285872331bc0SShmulik Ladkani if (!ipv6_addr_any(&fl6.saddr)) 285972331bc0SShmulik Ladkani flags |= RT6_LOOKUP_F_HAS_SADDR; 286072331bc0SShmulik Ladkani 286172331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, 286272331bc0SShmulik Ladkani flags); 286372331bc0SShmulik Ladkani } else { 286472331bc0SShmulik Ladkani fl6.flowi6_oif = oif; 286572331bc0SShmulik Ladkani 286672331bc0SShmulik Ladkani rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); 2867ab364a6fSThomas Graf } 28681da177e4SLinus Torvalds 28691da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 287038308473SDavid S. Miller if (!skb) { 287194e187c0SAmerigo Wang ip6_rt_put(rt); 2872ab364a6fSThomas Graf err = -ENOBUFS; 2873ab364a6fSThomas Graf goto errout; 2874ab364a6fSThomas Graf } 28751da177e4SLinus Torvalds 28761da177e4SLinus Torvalds /* Reserve room for dummy headers, this skb can pass 28771da177e4SLinus Torvalds through good chunk of routing engine. 28781da177e4SLinus Torvalds */ 2879459a98edSArnaldo Carvalho de Melo skb_reset_mac_header(skb); 28801da177e4SLinus Torvalds skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); 28811da177e4SLinus Torvalds 2882d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 28831da177e4SLinus Torvalds 28844c9483b2SDavid S. Miller err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, 288515e47304SEric W. Biederman RTM_NEWROUTE, NETLINK_CB(in_skb).portid, 28867bc570c8SYOSHIFUJI Hideaki nlh->nlmsg_seq, 0, 0, 0); 28871da177e4SLinus Torvalds if (err < 0) { 2888ab364a6fSThomas Graf kfree_skb(skb); 2889ab364a6fSThomas Graf goto errout; 28901da177e4SLinus Torvalds } 28911da177e4SLinus Torvalds 289215e47304SEric W. Biederman err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 2893ab364a6fSThomas Graf errout: 28941da177e4SLinus Torvalds return err; 28951da177e4SLinus Torvalds } 28961da177e4SLinus Torvalds 289786872cb5SThomas Graf void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) 28981da177e4SLinus Torvalds { 28991da177e4SLinus Torvalds struct sk_buff *skb; 29005578689aSDaniel Lezcano struct net *net = info->nl_net; 2901528c4cebSDenis V. Lunev u32 seq; 2902528c4cebSDenis V. Lunev int err; 29030d51aa80SJamal Hadi Salim 2904528c4cebSDenis V. Lunev err = -ENOBUFS; 290538308473SDavid S. Miller seq = info->nlh ? info->nlh->nlmsg_seq : 0; 290686872cb5SThomas Graf 2907339bf98fSThomas Graf skb = nlmsg_new(rt6_nlmsg_size(), gfp_any()); 290838308473SDavid S. Miller if (!skb) 290921713ebcSThomas Graf goto errout; 29101da177e4SLinus Torvalds 2911191cd582SBrian Haley err = rt6_fill_node(net, skb, rt, NULL, NULL, 0, 291215e47304SEric W. Biederman event, info->portid, seq, 0, 0, 0); 291326932566SPatrick McHardy if (err < 0) { 291426932566SPatrick McHardy /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ 291526932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 291626932566SPatrick McHardy kfree_skb(skb); 291726932566SPatrick McHardy goto errout; 291826932566SPatrick McHardy } 291915e47304SEric W. Biederman rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE, 29205578689aSDaniel Lezcano info->nlh, gfp_any()); 29211ce85fe4SPablo Neira Ayuso return; 292221713ebcSThomas Graf errout: 292321713ebcSThomas Graf if (err < 0) 29245578689aSDaniel Lezcano rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); 29251da177e4SLinus Torvalds } 29261da177e4SLinus Torvalds 29278ed67789SDaniel Lezcano static int ip6_route_dev_notify(struct notifier_block *this, 2928351638e7SJiri Pirko unsigned long event, void *ptr) 29298ed67789SDaniel Lezcano { 2930351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 2931c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 29328ed67789SDaniel Lezcano 29338ed67789SDaniel Lezcano if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { 2934d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.dev = dev; 29358ed67789SDaniel Lezcano net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev); 29368ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 2937d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.dev = dev; 29388ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); 2939d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.dev = dev; 29408ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); 29418ed67789SDaniel Lezcano #endif 29428ed67789SDaniel Lezcano } 29438ed67789SDaniel Lezcano 29448ed67789SDaniel Lezcano return NOTIFY_OK; 29458ed67789SDaniel Lezcano } 29468ed67789SDaniel Lezcano 29471da177e4SLinus Torvalds /* 29481da177e4SLinus Torvalds * /proc 29491da177e4SLinus Torvalds */ 29501da177e4SLinus Torvalds 29511da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 29521da177e4SLinus Torvalds 295333120b30SAlexey Dobriyan static const struct file_operations ipv6_route_proc_fops = { 295433120b30SAlexey Dobriyan .owner = THIS_MODULE, 295533120b30SAlexey Dobriyan .open = ipv6_route_open, 295633120b30SAlexey Dobriyan .read = seq_read, 295733120b30SAlexey Dobriyan .llseek = seq_lseek, 29588d2ca1d7SHannes Frederic Sowa .release = seq_release_net, 295933120b30SAlexey Dobriyan }; 296033120b30SAlexey Dobriyan 29611da177e4SLinus Torvalds static int rt6_stats_seq_show(struct seq_file *seq, void *v) 29621da177e4SLinus Torvalds { 296369ddb805SDaniel Lezcano struct net *net = (struct net *)seq->private; 29641da177e4SLinus Torvalds seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", 296569ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_nodes, 296669ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_route_nodes, 296769ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_alloc, 296869ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_entries, 296969ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_rt_cache, 2970fc66f95cSEric Dumazet dst_entries_get_slow(&net->ipv6.ip6_dst_ops), 297169ddb805SDaniel Lezcano net->ipv6.rt6_stats->fib_discarded_routes); 29721da177e4SLinus Torvalds 29731da177e4SLinus Torvalds return 0; 29741da177e4SLinus Torvalds } 29751da177e4SLinus Torvalds 29761da177e4SLinus Torvalds static int rt6_stats_seq_open(struct inode *inode, struct file *file) 29771da177e4SLinus Torvalds { 2978de05c557SPavel Emelyanov return single_open_net(inode, file, rt6_stats_seq_show); 297969ddb805SDaniel Lezcano } 298069ddb805SDaniel Lezcano 29819a32144eSArjan van de Ven static const struct file_operations rt6_stats_seq_fops = { 29821da177e4SLinus Torvalds .owner = THIS_MODULE, 29831da177e4SLinus Torvalds .open = rt6_stats_seq_open, 29841da177e4SLinus Torvalds .read = seq_read, 29851da177e4SLinus Torvalds .llseek = seq_lseek, 2986b6fcbdb4SPavel Emelyanov .release = single_release_net, 29871da177e4SLinus Torvalds }; 29881da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 29891da177e4SLinus Torvalds 29901da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 29911da177e4SLinus Torvalds 29921da177e4SLinus Torvalds static 2993fe2c6338SJoe Perches int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write, 29941da177e4SLinus Torvalds void __user *buffer, size_t *lenp, loff_t *ppos) 29951da177e4SLinus Torvalds { 2996c486da34SLucian Adrian Grijincu struct net *net; 2997c486da34SLucian Adrian Grijincu int delay; 2998c486da34SLucian Adrian Grijincu if (!write) 2999c486da34SLucian Adrian Grijincu return -EINVAL; 3000c486da34SLucian Adrian Grijincu 3001c486da34SLucian Adrian Grijincu net = (struct net *)ctl->extra1; 3002c486da34SLucian Adrian Grijincu delay = net->ipv6.sysctl.flush_delay; 30038d65af78SAlexey Dobriyan proc_dointvec(ctl, write, buffer, lenp, ppos); 30042ac3ac8fSMichal Kubeček fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0); 30051da177e4SLinus Torvalds return 0; 30061da177e4SLinus Torvalds } 30071da177e4SLinus Torvalds 3008fe2c6338SJoe Perches struct ctl_table ipv6_route_table_template[] = { 30091da177e4SLinus Torvalds { 30101da177e4SLinus Torvalds .procname = "flush", 30114990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.flush_delay, 30121da177e4SLinus Torvalds .maxlen = sizeof(int), 301389c8b3a1SDave Jones .mode = 0200, 30146d9f239aSAlexey Dobriyan .proc_handler = ipv6_sysctl_rtcache_flush 30151da177e4SLinus Torvalds }, 30161da177e4SLinus Torvalds { 30171da177e4SLinus Torvalds .procname = "gc_thresh", 30189a7ec3a9SDaniel Lezcano .data = &ip6_dst_ops_template.gc_thresh, 30191da177e4SLinus Torvalds .maxlen = sizeof(int), 30201da177e4SLinus Torvalds .mode = 0644, 30216d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 30221da177e4SLinus Torvalds }, 30231da177e4SLinus Torvalds { 30241da177e4SLinus Torvalds .procname = "max_size", 30254990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_max_size, 30261da177e4SLinus Torvalds .maxlen = sizeof(int), 30271da177e4SLinus Torvalds .mode = 0644, 30286d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec, 30291da177e4SLinus Torvalds }, 30301da177e4SLinus Torvalds { 30311da177e4SLinus Torvalds .procname = "gc_min_interval", 30324990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 30331da177e4SLinus Torvalds .maxlen = sizeof(int), 30341da177e4SLinus Torvalds .mode = 0644, 30356d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 30361da177e4SLinus Torvalds }, 30371da177e4SLinus Torvalds { 30381da177e4SLinus Torvalds .procname = "gc_timeout", 30394990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout, 30401da177e4SLinus Torvalds .maxlen = sizeof(int), 30411da177e4SLinus Torvalds .mode = 0644, 30426d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 30431da177e4SLinus Torvalds }, 30441da177e4SLinus Torvalds { 30451da177e4SLinus Torvalds .procname = "gc_interval", 30464990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_interval, 30471da177e4SLinus Torvalds .maxlen = sizeof(int), 30481da177e4SLinus Torvalds .mode = 0644, 30496d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 30501da177e4SLinus Torvalds }, 30511da177e4SLinus Torvalds { 30521da177e4SLinus Torvalds .procname = "gc_elasticity", 30534990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity, 30541da177e4SLinus Torvalds .maxlen = sizeof(int), 30551da177e4SLinus Torvalds .mode = 0644, 3056f3d3f616SMin Zhang .proc_handler = proc_dointvec, 30571da177e4SLinus Torvalds }, 30581da177e4SLinus Torvalds { 30591da177e4SLinus Torvalds .procname = "mtu_expires", 30604990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires, 30611da177e4SLinus Torvalds .maxlen = sizeof(int), 30621da177e4SLinus Torvalds .mode = 0644, 30636d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 30641da177e4SLinus Torvalds }, 30651da177e4SLinus Torvalds { 30661da177e4SLinus Torvalds .procname = "min_adv_mss", 30674990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_min_advmss, 30681da177e4SLinus Torvalds .maxlen = sizeof(int), 30691da177e4SLinus Torvalds .mode = 0644, 3070f3d3f616SMin Zhang .proc_handler = proc_dointvec, 30711da177e4SLinus Torvalds }, 30721da177e4SLinus Torvalds { 30731da177e4SLinus Torvalds .procname = "gc_min_interval_ms", 30744990509fSDaniel Lezcano .data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval, 30751da177e4SLinus Torvalds .maxlen = sizeof(int), 30761da177e4SLinus Torvalds .mode = 0644, 30776d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_ms_jiffies, 30781da177e4SLinus Torvalds }, 3079f8572d8fSEric W. Biederman { } 30801da177e4SLinus Torvalds }; 30811da177e4SLinus Torvalds 30822c8c1e72SAlexey Dobriyan struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) 3083760f2d01SDaniel Lezcano { 3084760f2d01SDaniel Lezcano struct ctl_table *table; 3085760f2d01SDaniel Lezcano 3086760f2d01SDaniel Lezcano table = kmemdup(ipv6_route_table_template, 3087760f2d01SDaniel Lezcano sizeof(ipv6_route_table_template), 3088760f2d01SDaniel Lezcano GFP_KERNEL); 30895ee09105SYOSHIFUJI Hideaki 30905ee09105SYOSHIFUJI Hideaki if (table) { 30915ee09105SYOSHIFUJI Hideaki table[0].data = &net->ipv6.sysctl.flush_delay; 3092c486da34SLucian Adrian Grijincu table[0].extra1 = net; 309386393e52SAlexey Dobriyan table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; 30945ee09105SYOSHIFUJI Hideaki table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; 30955ee09105SYOSHIFUJI Hideaki table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 30965ee09105SYOSHIFUJI Hideaki table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout; 30975ee09105SYOSHIFUJI Hideaki table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval; 30985ee09105SYOSHIFUJI Hideaki table[6].data = &net->ipv6.sysctl.ip6_rt_gc_elasticity; 30995ee09105SYOSHIFUJI Hideaki table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires; 31005ee09105SYOSHIFUJI Hideaki table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss; 31019c69fabeSAlexey Dobriyan table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; 3102464dc801SEric W. Biederman 3103464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 3104464dc801SEric W. Biederman if (net->user_ns != &init_user_ns) 3105464dc801SEric W. Biederman table[0].procname = NULL; 31065ee09105SYOSHIFUJI Hideaki } 31075ee09105SYOSHIFUJI Hideaki 3108760f2d01SDaniel Lezcano return table; 3109760f2d01SDaniel Lezcano } 31101da177e4SLinus Torvalds #endif 31111da177e4SLinus Torvalds 31122c8c1e72SAlexey Dobriyan static int __net_init ip6_route_net_init(struct net *net) 3113cdb18761SDaniel Lezcano { 3114633d424bSPavel Emelyanov int ret = -ENOMEM; 31158ed67789SDaniel Lezcano 311686393e52SAlexey Dobriyan memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template, 311786393e52SAlexey Dobriyan sizeof(net->ipv6.ip6_dst_ops)); 3118f2fc6a54SBenjamin Thery 3119fc66f95cSEric Dumazet if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0) 3120fc66f95cSEric Dumazet goto out_ip6_dst_ops; 3121fc66f95cSEric Dumazet 31228ed67789SDaniel Lezcano net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template, 31238ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_null_entry), 31248ed67789SDaniel Lezcano GFP_KERNEL); 31258ed67789SDaniel Lezcano if (!net->ipv6.ip6_null_entry) 3126fc66f95cSEric Dumazet goto out_ip6_dst_entries; 3127d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.path = 31288ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_null_entry; 3129d8d1f30bSChangli Gao net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; 313062fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_null_entry->dst, 313162fa8a84SDavid S. Miller ip6_template_metrics, true); 31328ed67789SDaniel Lezcano 31338ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 31348ed67789SDaniel Lezcano net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, 31358ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_prohibit_entry), 31368ed67789SDaniel Lezcano GFP_KERNEL); 313768fffc67SPeter Zijlstra if (!net->ipv6.ip6_prohibit_entry) 313868fffc67SPeter Zijlstra goto out_ip6_null_entry; 3139d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.path = 31408ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_prohibit_entry; 3141d8d1f30bSChangli Gao net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; 314262fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, 314362fa8a84SDavid S. Miller ip6_template_metrics, true); 31448ed67789SDaniel Lezcano 31458ed67789SDaniel Lezcano net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, 31468ed67789SDaniel Lezcano sizeof(*net->ipv6.ip6_blk_hole_entry), 31478ed67789SDaniel Lezcano GFP_KERNEL); 314868fffc67SPeter Zijlstra if (!net->ipv6.ip6_blk_hole_entry) 314968fffc67SPeter Zijlstra goto out_ip6_prohibit_entry; 3150d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.path = 31518ed67789SDaniel Lezcano (struct dst_entry *)net->ipv6.ip6_blk_hole_entry; 3152d8d1f30bSChangli Gao net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; 315362fa8a84SDavid S. Miller dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, 315462fa8a84SDavid S. Miller ip6_template_metrics, true); 31558ed67789SDaniel Lezcano #endif 31568ed67789SDaniel Lezcano 3157b339a47cSPeter Zijlstra net->ipv6.sysctl.flush_delay = 0; 3158b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_max_size = 4096; 3159b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_min_interval = HZ / 2; 3160b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_timeout = 60*HZ; 3161b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_interval = 30*HZ; 3162b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_gc_elasticity = 9; 3163b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_mtu_expires = 10*60*HZ; 3164b339a47cSPeter Zijlstra net->ipv6.sysctl.ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40; 3165b339a47cSPeter Zijlstra 31666891a346SBenjamin Thery net->ipv6.ip6_rt_gc_expire = 30*HZ; 31676891a346SBenjamin Thery 31688ed67789SDaniel Lezcano ret = 0; 31698ed67789SDaniel Lezcano out: 31708ed67789SDaniel Lezcano return ret; 3171f2fc6a54SBenjamin Thery 317268fffc67SPeter Zijlstra #ifdef CONFIG_IPV6_MULTIPLE_TABLES 317368fffc67SPeter Zijlstra out_ip6_prohibit_entry: 317468fffc67SPeter Zijlstra kfree(net->ipv6.ip6_prohibit_entry); 317568fffc67SPeter Zijlstra out_ip6_null_entry: 317668fffc67SPeter Zijlstra kfree(net->ipv6.ip6_null_entry); 317768fffc67SPeter Zijlstra #endif 3178fc66f95cSEric Dumazet out_ip6_dst_entries: 3179fc66f95cSEric Dumazet dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3180f2fc6a54SBenjamin Thery out_ip6_dst_ops: 3181f2fc6a54SBenjamin Thery goto out; 3182cdb18761SDaniel Lezcano } 3183cdb18761SDaniel Lezcano 31842c8c1e72SAlexey Dobriyan static void __net_exit ip6_route_net_exit(struct net *net) 3185cdb18761SDaniel Lezcano { 31868ed67789SDaniel Lezcano kfree(net->ipv6.ip6_null_entry); 31878ed67789SDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 31888ed67789SDaniel Lezcano kfree(net->ipv6.ip6_prohibit_entry); 31898ed67789SDaniel Lezcano kfree(net->ipv6.ip6_blk_hole_entry); 31908ed67789SDaniel Lezcano #endif 319141bb78b4SXiaotian Feng dst_entries_destroy(&net->ipv6.ip6_dst_ops); 3192cdb18761SDaniel Lezcano } 3193cdb18761SDaniel Lezcano 3194d189634eSThomas Graf static int __net_init ip6_route_net_init_late(struct net *net) 3195d189634eSThomas Graf { 3196d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3197d4beaa66SGao feng proc_create("ipv6_route", 0, net->proc_net, &ipv6_route_proc_fops); 3198d4beaa66SGao feng proc_create("rt6_stats", S_IRUGO, net->proc_net, &rt6_stats_seq_fops); 3199d189634eSThomas Graf #endif 3200d189634eSThomas Graf return 0; 3201d189634eSThomas Graf } 3202d189634eSThomas Graf 3203d189634eSThomas Graf static void __net_exit ip6_route_net_exit_late(struct net *net) 3204d189634eSThomas Graf { 3205d189634eSThomas Graf #ifdef CONFIG_PROC_FS 3206ece31ffdSGao feng remove_proc_entry("ipv6_route", net->proc_net); 3207ece31ffdSGao feng remove_proc_entry("rt6_stats", net->proc_net); 3208d189634eSThomas Graf #endif 3209d189634eSThomas Graf } 3210d189634eSThomas Graf 3211cdb18761SDaniel Lezcano static struct pernet_operations ip6_route_net_ops = { 3212cdb18761SDaniel Lezcano .init = ip6_route_net_init, 3213cdb18761SDaniel Lezcano .exit = ip6_route_net_exit, 3214cdb18761SDaniel Lezcano }; 3215cdb18761SDaniel Lezcano 3216c3426b47SDavid S. Miller static int __net_init ipv6_inetpeer_init(struct net *net) 3217c3426b47SDavid S. Miller { 3218c3426b47SDavid S. Miller struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 3219c3426b47SDavid S. Miller 3220c3426b47SDavid S. Miller if (!bp) 3221c3426b47SDavid S. Miller return -ENOMEM; 3222c3426b47SDavid S. Miller inet_peer_base_init(bp); 3223c3426b47SDavid S. Miller net->ipv6.peers = bp; 3224c3426b47SDavid S. Miller return 0; 3225c3426b47SDavid S. Miller } 3226c3426b47SDavid S. Miller 3227c3426b47SDavid S. Miller static void __net_exit ipv6_inetpeer_exit(struct net *net) 3228c3426b47SDavid S. Miller { 3229c3426b47SDavid S. Miller struct inet_peer_base *bp = net->ipv6.peers; 3230c3426b47SDavid S. Miller 3231c3426b47SDavid S. Miller net->ipv6.peers = NULL; 323256a6b248SDavid S. Miller inetpeer_invalidate_tree(bp); 3233c3426b47SDavid S. Miller kfree(bp); 3234c3426b47SDavid S. Miller } 3235c3426b47SDavid S. Miller 32362b823f72SDavid S. Miller static struct pernet_operations ipv6_inetpeer_ops = { 3237c3426b47SDavid S. Miller .init = ipv6_inetpeer_init, 3238c3426b47SDavid S. Miller .exit = ipv6_inetpeer_exit, 3239c3426b47SDavid S. Miller }; 3240c3426b47SDavid S. Miller 3241d189634eSThomas Graf static struct pernet_operations ip6_route_net_late_ops = { 3242d189634eSThomas Graf .init = ip6_route_net_init_late, 3243d189634eSThomas Graf .exit = ip6_route_net_exit_late, 3244d189634eSThomas Graf }; 3245d189634eSThomas Graf 32468ed67789SDaniel Lezcano static struct notifier_block ip6_route_dev_notifier = { 32478ed67789SDaniel Lezcano .notifier_call = ip6_route_dev_notify, 32488ed67789SDaniel Lezcano .priority = 0, 32498ed67789SDaniel Lezcano }; 32508ed67789SDaniel Lezcano 3251433d49c3SDaniel Lezcano int __init ip6_route_init(void) 32521da177e4SLinus Torvalds { 3253433d49c3SDaniel Lezcano int ret; 3254433d49c3SDaniel Lezcano 32559a7ec3a9SDaniel Lezcano ret = -ENOMEM; 32569a7ec3a9SDaniel Lezcano ip6_dst_ops_template.kmem_cachep = 32579a7ec3a9SDaniel Lezcano kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, 32589a7ec3a9SDaniel Lezcano SLAB_HWCACHE_ALIGN, NULL); 32599a7ec3a9SDaniel Lezcano if (!ip6_dst_ops_template.kmem_cachep) 3260c19a28e1SFernando Carrijo goto out; 326114e50e57SDavid S. Miller 3262fc66f95cSEric Dumazet ret = dst_entries_init(&ip6_dst_blackhole_ops); 32638ed67789SDaniel Lezcano if (ret) 3264bdb3289fSDaniel Lezcano goto out_kmem_cache; 3265bdb3289fSDaniel Lezcano 3266c3426b47SDavid S. Miller ret = register_pernet_subsys(&ipv6_inetpeer_ops); 3267c3426b47SDavid S. Miller if (ret) 3268e8803b6cSDavid S. Miller goto out_dst_entries; 32692a0c451aSThomas Graf 32707e52b33bSDavid S. Miller ret = register_pernet_subsys(&ip6_route_net_ops); 32717e52b33bSDavid S. Miller if (ret) 32727e52b33bSDavid S. Miller goto out_register_inetpeer; 3273c3426b47SDavid S. Miller 32745dc121e9SArnaud Ebalard ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; 32755dc121e9SArnaud Ebalard 32768ed67789SDaniel Lezcano /* Registering of the loopback is done before this portion of code, 32778ed67789SDaniel Lezcano * the loopback reference in rt6_info will not be taken, do it 32788ed67789SDaniel Lezcano * manually for init_net */ 3279d8d1f30bSChangli Gao init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev; 32808ed67789SDaniel Lezcano init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3281bdb3289fSDaniel Lezcano #ifdef CONFIG_IPV6_MULTIPLE_TABLES 3282d8d1f30bSChangli Gao init_net.ipv6.ip6_prohibit_entry->dst.dev = init_net.loopback_dev; 32838ed67789SDaniel Lezcano init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3284d8d1f30bSChangli Gao init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; 32858ed67789SDaniel Lezcano init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); 3286bdb3289fSDaniel Lezcano #endif 3287e8803b6cSDavid S. Miller ret = fib6_init(); 3288433d49c3SDaniel Lezcano if (ret) 32898ed67789SDaniel Lezcano goto out_register_subsys; 3290433d49c3SDaniel Lezcano 3291433d49c3SDaniel Lezcano ret = xfrm6_init(); 3292433d49c3SDaniel Lezcano if (ret) 3293e8803b6cSDavid S. Miller goto out_fib6_init; 3294c35b7e72SDaniel Lezcano 3295433d49c3SDaniel Lezcano ret = fib6_rules_init(); 3296433d49c3SDaniel Lezcano if (ret) 3297433d49c3SDaniel Lezcano goto xfrm6_init; 32987e5449c2SDaniel Lezcano 3299d189634eSThomas Graf ret = register_pernet_subsys(&ip6_route_net_late_ops); 3300d189634eSThomas Graf if (ret) 3301d189634eSThomas Graf goto fib6_rules_init; 3302d189634eSThomas Graf 3303433d49c3SDaniel Lezcano ret = -ENOBUFS; 3304c7ac8679SGreg Rose if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || 3305c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || 3306c7ac8679SGreg Rose __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) 3307d189634eSThomas Graf goto out_register_late_subsys; 3308433d49c3SDaniel Lezcano 33098ed67789SDaniel Lezcano ret = register_netdevice_notifier(&ip6_route_dev_notifier); 3310cdb18761SDaniel Lezcano if (ret) 3311d189634eSThomas Graf goto out_register_late_subsys; 33128ed67789SDaniel Lezcano 3313433d49c3SDaniel Lezcano out: 3314433d49c3SDaniel Lezcano return ret; 3315433d49c3SDaniel Lezcano 3316d189634eSThomas Graf out_register_late_subsys: 3317d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3318433d49c3SDaniel Lezcano fib6_rules_init: 3319433d49c3SDaniel Lezcano fib6_rules_cleanup(); 3320433d49c3SDaniel Lezcano xfrm6_init: 3321433d49c3SDaniel Lezcano xfrm6_fini(); 33222a0c451aSThomas Graf out_fib6_init: 33232a0c451aSThomas Graf fib6_gc_cleanup(); 33248ed67789SDaniel Lezcano out_register_subsys: 33258ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 33267e52b33bSDavid S. Miller out_register_inetpeer: 33277e52b33bSDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 3328fc66f95cSEric Dumazet out_dst_entries: 3329fc66f95cSEric Dumazet dst_entries_destroy(&ip6_dst_blackhole_ops); 3330433d49c3SDaniel Lezcano out_kmem_cache: 3331f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 3332433d49c3SDaniel Lezcano goto out; 33331da177e4SLinus Torvalds } 33341da177e4SLinus Torvalds 33351da177e4SLinus Torvalds void ip6_route_cleanup(void) 33361da177e4SLinus Torvalds { 33378ed67789SDaniel Lezcano unregister_netdevice_notifier(&ip6_route_dev_notifier); 3338d189634eSThomas Graf unregister_pernet_subsys(&ip6_route_net_late_ops); 3339101367c2SThomas Graf fib6_rules_cleanup(); 33401da177e4SLinus Torvalds xfrm6_fini(); 33411da177e4SLinus Torvalds fib6_gc_cleanup(); 3342c3426b47SDavid S. Miller unregister_pernet_subsys(&ipv6_inetpeer_ops); 33438ed67789SDaniel Lezcano unregister_pernet_subsys(&ip6_route_net_ops); 334441bb78b4SXiaotian Feng dst_entries_destroy(&ip6_dst_blackhole_ops); 3345f2fc6a54SBenjamin Thery kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); 33461da177e4SLinus Torvalds } 3347