📄 route.c
字号:
/* * Linux INET6 implementation * FIB front-end. * * Authors: * Pedro Roque <roque@di.fc.ul.pt> * * $Id: route.c,v 1.56 2001/10/31 21:55:55 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#include <linux/config.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/net.h>#include <linux/route.h>#include <linux/netdevice.h>#include <linux/in6.h>#include <linux/init.h>#include <linux/netlink.h>#include <linux/if_arp.h>#ifdef CONFIG_PROC_FS#include <linux/proc_fs.h>#endif#include <net/snmp.h>#include <net/ipv6.h>#include <net/ip6_fib.h>#include <net/ip6_route.h>#include <net/ndisc.h>#include <net/addrconf.h>#include <net/tcp.h>#include <linux/rtnetlink.h>#include <asm/uaccess.h>#ifdef CONFIG_SYSCTL#include <linux/sysctl.h>#endif#undef CONFIG_RT6_POLICY/* Set to 3 to get tracing. */#define RT6_DEBUG 2#if RT6_DEBUG >= 3#define RDBG(x) printk x#define RT6_TRACE(x...) printk(KERN_DEBUG x)#else#define RDBG(x)#define RT6_TRACE(x...) do { ; } while (0)#endifint ip6_rt_max_size = 4096;int ip6_rt_gc_min_interval = 5*HZ;int ip6_rt_gc_timeout = 60*HZ;int ip6_rt_gc_interval = 30*HZ;int ip6_rt_gc_elasticity = 9;int ip6_rt_mtu_expires = 10*60*HZ;int ip6_rt_min_advmss = IPV6_MIN_MTU - 20 - 40;static struct rt6_info * ip6_rt_copy(struct rt6_info *ort);static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);static struct dst_entry *ip6_dst_reroute(struct dst_entry *dst, struct sk_buff *skb);static struct dst_entry *ip6_negative_advice(struct dst_entry *);static int ip6_dst_gc(void);static int ip6_pkt_discard(struct sk_buff *skb);static void ip6_link_failure(struct sk_buff *skb);struct dst_ops ip6_dst_ops = { AF_INET6, __constant_htons(ETH_P_IPV6), 1024, ip6_dst_gc, ip6_dst_check, ip6_dst_reroute, NULL, ip6_negative_advice, ip6_link_failure, sizeof(struct rt6_info),};struct rt6_info ip6_null_entry = { {{NULL, ATOMIC_INIT(1), 1, &loopback_dev, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL, ip6_pkt_discard, ip6_pkt_discard,#ifdef CONFIG_NET_CLS_ROUTE 0,#endif &ip6_dst_ops}}, NULL, {{{0}}}, RTF_REJECT|RTF_NONEXTHOP, ~0U, 255, ATOMIC_INIT(1), {NULL}, {{{{0}}}, 0}, {{{{0}}}, 0}};struct fib6_node ip6_routing_table = { NULL, NULL, NULL, NULL, &ip6_null_entry, 0, RTN_ROOT|RTN_TL_ROOT|RTN_RTINFO, 0};#ifdef CONFIG_RT6_POLICYint ip6_rt_policy = 0;struct pol_chain *rt6_pol_list = NULL;static int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb);static int rt6_flow_match_out(struct rt6_info *rt, struct sock *sk);static struct rt6_info *rt6_flow_lookup(struct rt6_info *rt, struct in6_addr *daddr, struct in6_addr *saddr, struct fl_acc_args *args);#else#define ip6_rt_policy (0)#endif/* Protects all the ip6 fib */rwlock_t rt6_lock = RW_LOCK_UNLOCKED;/* * Route lookup. Any rt6_lock is implied. */static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt, int oif, int strict){ struct rt6_info *local = NULL; struct rt6_info *sprt; if (oif) { for (sprt = rt; sprt; sprt = sprt->u.next) { struct net_device *dev = sprt->rt6i_dev; if (dev->ifindex == oif) return sprt; if (dev->flags&IFF_LOOPBACK) local = sprt; } if (local) return local; if (strict) return &ip6_null_entry; } return rt;}/* * pointer to the last default router chosen. BH is disabled locally. */static struct rt6_info *rt6_dflt_pointer = NULL;static spinlock_t rt6_dflt_lock = SPIN_LOCK_UNLOCKED;static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, int oif){ struct rt6_info *match = NULL; struct rt6_info *sprt; int mpri = 0; for (sprt = rt; sprt; sprt = sprt->u.next) { struct neighbour *neigh; if ((neigh = sprt->rt6i_nexthop) != NULL) { int m = -1; switch (neigh->nud_state) { case NUD_REACHABLE: if (sprt != rt6_dflt_pointer) { rt = sprt; goto out; } m = 2; break; case NUD_DELAY: m = 1; break; case NUD_STALE: m = 1; break; }; if (oif && sprt->rt6i_dev->ifindex == oif) { m += 2; } if (m >= mpri) { mpri = m; match = sprt; } } } if (match) { rt = match; } else { /* * No default routers are known to be reachable. * SHOULD round robin */ spin_lock(&rt6_dflt_lock); if (rt6_dflt_pointer) { struct rt6_info *next; if ((next = rt6_dflt_pointer->u.next) != NULL && next->u.dst.obsolete <= 0 && next->u.dst.error == 0) rt = next; } spin_unlock(&rt6_dflt_lock); }out: spin_lock(&rt6_dflt_lock); rt6_dflt_pointer = rt; spin_unlock(&rt6_dflt_lock); return rt;}struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr, int oif, int strict){ struct fib6_node *fn; struct rt6_info *rt; read_lock_bh(&rt6_lock); fn = fib6_lookup(&ip6_routing_table, daddr, saddr); rt = rt6_device_match(fn->leaf, oif, strict); dst_hold(&rt->u.dst); rt->u.dst.__use++; read_unlock_bh(&rt6_lock); rt->u.dst.lastuse = jiffies; if (rt->u.dst.error == 0) return rt; dst_release(&rt->u.dst); return NULL;}/* rt6_ins is called with FREE rt6_lock. It takes new route entry, the addition fails by any reason the route is freed. In any case, if caller does not hold it, it may be destroyed. */static int rt6_ins(struct rt6_info *rt){ int err; write_lock_bh(&rt6_lock); err = fib6_add(&ip6_routing_table, rt); write_unlock_bh(&rt6_lock); return err;}/* No rt6_lock! If COW faild, the function returns dead route entry with dst->error set to errno value. */static struct rt6_info *rt6_cow(struct rt6_info *ort, struct in6_addr *daddr, struct in6_addr *saddr){ int err; struct rt6_info *rt; /* * Clone the route. */ rt = ip6_rt_copy(ort); if (rt) { ipv6_addr_copy(&rt->rt6i_dst.addr, daddr); if (!(rt->rt6i_flags&RTF_GATEWAY)) ipv6_addr_copy(&rt->rt6i_gateway, daddr); rt->rt6i_dst.plen = 128; rt->rt6i_flags |= RTF_CACHE; rt->u.dst.flags |= DST_HOST;#ifdef CONFIG_IPV6_SUBTREES if (rt->rt6i_src.plen && saddr) { ipv6_addr_copy(&rt->rt6i_src.addr, saddr); rt->rt6i_src.plen = 128; }#endif rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); dst_clone(&rt->u.dst); err = rt6_ins(rt); if (err == 0) return rt; rt->u.dst.error = err; return rt; } dst_clone(&ip6_null_entry.u.dst); return &ip6_null_entry;}#ifdef CONFIG_RT6_POLICYstatic __inline__ struct rt6_info *rt6_flow_lookup_in(struct rt6_info *rt, struct sk_buff *skb){ struct in6_addr *daddr, *saddr; struct fl_acc_args arg; arg.type = FL_ARG_FORWARD; arg.fl_u.skb = skb; saddr = &skb->nh.ipv6h->saddr; daddr = &skb->nh.ipv6h->daddr; return rt6_flow_lookup(rt, daddr, saddr, &arg);}static __inline__ struct rt6_info *rt6_flow_lookup_out(struct rt6_info *rt, struct sock *sk, struct flowi *fl){ struct fl_acc_args arg; arg.type = FL_ARG_ORIGIN; arg.fl_u.fl_o.sk = sk; arg.fl_u.fl_o.flow = fl; return rt6_flow_lookup(rt, fl->nl_u.ip6_u.daddr, fl->nl_u.ip6_u.saddr, &arg);}#endif#define BACKTRACK() \if (rt == &ip6_null_entry && strict) { \ while ((fn = fn->parent) != NULL) { \ if (fn->fn_flags & RTN_ROOT) { \ dst_clone(&rt->u.dst); \ goto out; \ } \ if (fn->fn_flags & RTN_RTINFO) \ goto restart; \ } \}void ip6_route_input(struct sk_buff *skb){ struct fib6_node *fn; struct rt6_info *rt; int strict; int attempts = 3; strict = ipv6_addr_type(&skb->nh.ipv6h->daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL);relookup: read_lock_bh(&rt6_lock); fn = fib6_lookup(&ip6_routing_table, &skb->nh.ipv6h->daddr, &skb->nh.ipv6h->saddr);restart: rt = fn->leaf; if ((rt->rt6i_flags & RTF_CACHE)) { if (ip6_rt_policy == 0) { rt = rt6_device_match(rt, skb->dev->ifindex, strict); BACKTRACK(); dst_clone(&rt->u.dst); goto out; }#ifdef CONFIG_RT6_POLICY if ((rt->rt6i_flags & RTF_FLOW)) { struct rt6_info *sprt; for (sprt = rt; sprt; sprt = sprt->u.next) { if (rt6_flow_match_in(sprt, skb)) { rt = sprt; dst_clone(&rt->u.dst); goto out; } } }#endif } rt = rt6_device_match(rt, skb->dev->ifindex, 0); BACKTRACK(); if (ip6_rt_policy == 0) { if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) { read_unlock_bh(&rt6_lock); rt = rt6_cow(rt, &skb->nh.ipv6h->daddr, &skb->nh.ipv6h->saddr); if (rt->u.dst.error != -EEXIST || --attempts <= 0) goto out2; /* Race condition! In the gap, when rt6_lock was released someone could insert this route. Relookup. */ goto relookup; } dst_clone(&rt->u.dst); } else {#ifdef CONFIG_RT6_POLICY rt = rt6_flow_lookup_in(rt, skb);#else /* NEVER REACHED */#endif }out: read_unlock_bh(&rt6_lock);out2: rt->u.dst.lastuse = jiffies; rt->u.dst.__use++; skb->dst = (struct dst_entry *) rt;}struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl){ struct fib6_node *fn; struct rt6_info *rt; int strict; int attempts = 3; strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL);relookup: read_lock_bh(&rt6_lock); fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr, fl->nl_u.ip6_u.saddr);restart: rt = fn->leaf; if ((rt->rt6i_flags & RTF_CACHE)) { if (ip6_rt_policy == 0) { rt = rt6_device_match(rt, fl->oif, strict); BACKTRACK(); dst_clone(&rt->u.dst); goto out; }#ifdef CONFIG_RT6_POLICY if ((rt->rt6i_flags & RTF_FLOW)) { struct rt6_info *sprt; for (sprt = rt; sprt; sprt = sprt->u.next) { if (rt6_flow_match_out(sprt, sk)) { rt = sprt; dst_clone(&rt->u.dst); goto out; } } }#endif } if (rt->rt6i_flags & RTF_DEFAULT) { if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF) rt = rt6_best_dflt(rt, fl->oif); } else { rt = rt6_device_match(rt, fl->oif, strict); BACKTRACK(); } if (ip6_rt_policy == 0) { if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) { read_unlock_bh(&rt6_lock); rt = rt6_cow(rt, fl->nl_u.ip6_u.daddr, fl->nl_u.ip6_u.saddr); if (rt->u.dst.error != -EEXIST || --attempts <= 0) goto out2; /* Race condition! In the gap, when rt6_lock was released someone could insert this route. Relookup. */ goto relookup; } dst_clone(&rt->u.dst); } else {#ifdef CONFIG_RT6_POLICY rt = rt6_flow_lookup_out(rt, sk, fl);#else /* NEVER REACHED */#endif }out: read_unlock_bh(&rt6_lock);out2: rt->u.dst.lastuse = jiffies; rt->u.dst.__use++; return &rt->u.dst;}/* * Destination cache support functions */static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie){ struct rt6_info *rt; rt = (struct rt6_info *) dst; if (rt && rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) return dst; dst_release(dst); return NULL;}static struct dst_entry *ip6_dst_reroute(struct dst_entry *dst, struct sk_buff *skb){ /* * FIXME */ RDBG(("ip6_dst_reroute(%p,%p)[%p] (AIEEE)\n", dst, skb, __builtin_return_address(0))); return NULL;}static struct dst_entry *ip6_negative_advice(struct dst_entry *dst){ struct rt6_info *rt = (struct rt6_info *) dst; if (rt) { if (rt->rt6i_flags & RTF_CACHE) ip6_del_rt(rt); else dst_release(dst); } return NULL;}static void ip6_link_failure(struct sk_buff *skb){ struct rt6_info *rt; icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev); rt = (struct rt6_info *) skb->dst; if (rt) { if (rt->rt6i_flags&RTF_CACHE) { dst_set_expires(&rt->u.dst, 0); rt->rt6i_flags |= RTF_EXPIRES; } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) rt->rt6i_node->fn_sernum = -1; }}static int ip6_dst_gc(){ static unsigned expire = 30*HZ; static unsigned long last_gc; unsigned long now = jiffies; if ((long)(now - last_gc) < ip6_rt_gc_min_interval && atomic_read(&ip6_dst_ops.entries) <= ip6_rt_max_size) goto out; expire++; fib6_run_gc(expire); last_gc = now; if (atomic_read(&ip6_dst_ops.entries) < ip6_dst_ops.gc_thresh) expire = ip6_rt_gc_timeout>>1;out: expire -= expire>>ip6_rt_gc_elasticity; return (atomic_read(&ip6_dst_ops.entries) > ip6_rt_max_size);}/* Clean host part of a prefix. Not necessary in radix tree, but results in cleaner routing tables. Remove it only when all the things will work! */static void ipv6_wash_prefix(struct in6_addr *pfx, int plen){ int b = plen&0x7; int o = (plen + 7)>>3; if (o < 16) memset(pfx->s6_addr + o, 0, 16 - o); if (b != 0) pfx->s6_addr[plen>>3] &= (0xFF<<(8-b));}static int ipv6_get_mtu(struct net_device *dev){ int mtu = IPV6_MIN_MTU; struct inet6_dev *idev; idev = in6_dev_get(dev); if (idev) { mtu = idev->cnf.mtu6; in6_dev_put(idev); } return mtu;}static int ipv6_get_hoplimit(struct net_device *dev){ int hoplimit = ipv6_devconf.hop_limit; struct inet6_dev *idev; idev = in6_dev_get(dev); if (idev) { hoplimit = idev->cnf.hop_limit; in6_dev_put(idev); } return hoplimit;}/* * */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -