📄 route.c
字号:
/* * Linux INET6 implementation * FIB front-end. * * Authors: * Pedro Roque <roque@di.fc.ul.pt> * * $Id: route.c,v 1.35 1999/03/21 05:22:57 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/netlink.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)#endif#if RT6_DEBUG >= 1#define BUG_TRAP(x) ({ if (!(x)) { printk("Assertion (" #x ") failed at " __FILE__ "(%d):" __FUNCTION__ "\n", __LINE__); } })#else#define BUG_TRAP(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;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,};struct rt6_info ip6_null_entry = { {{NULL, ATOMIC_INIT(1), ATOMIC_INIT(1), &loopback_dev, -1, 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/* * Route lookup */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 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 */static struct rt6_info *rt6_dflt_pointer = NULL;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; RDBG(("sprt(%p): ", sprt)); if ((neigh = sprt->rt6i_nexthop)) { int m = -1; RDBG(("nxthop(%p,%d) ", neigh, neigh->nud_state)); switch (neigh->nud_state) { case NUD_REACHABLE: RDBG(("NUD_REACHABLE ")); if (sprt != rt6_dflt_pointer) { rt = sprt; RDBG(("sprt!=dflt_ptr -> %p\n", sprt)); goto out; } RDBG(("m=2, ")); m = 2; break; case NUD_DELAY: RDBG(("NUD_DELAY, m=1, ")); m = 1; break; case NUD_STALE: RDBG(("NUD_STALE, m=1, ")); m = 1; break; }; if (oif && sprt->rt6i_dev->ifindex == oif) { m += 2; } if (m >= mpri) { RDBG(("m>=mpri setmatch, ")); mpri = m; match = sprt; } } } if (match) { RDBG(("match, set rt, ")); rt = match; } else { /* * No default routers are known to be reachable. * SHOULD round robin */ RDBG(("!match, trying rt6_dflt_pointer, ")); if (rt6_dflt_pointer) { struct rt6_info *next; if ((next = rt6_dflt_pointer->u.next) && next->u.dst.error == 0) rt = next; } }out: rt6_dflt_pointer = rt; RDBG(("returning %p, dflt_ptr set\n", rt)); 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; start_bh_atomic(); fn = fib6_lookup(&ip6_routing_table, daddr, saddr); rt = rt6_device_match(fn->leaf, oif, strict); atomic_inc(&rt->u.dst.use); atomic_inc(&rt->u.dst.refcnt); end_bh_atomic(); rt->u.dst.lastuse = jiffies; if (rt->u.dst.error == 0) return rt; dst_release(&rt->u.dst); return NULL;}static int rt6_ins(struct rt6_info *rt){ int err; start_bh_atomic(); err = fib6_add(&ip6_routing_table, rt); end_bh_atomic(); return err;}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;#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; strict = ipv6_addr_type(&skb->nh.ipv6h->daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL); 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)) { rt = rt6_cow(rt, &skb->nh.ipv6h->daddr, &skb->nh.ipv6h->saddr); goto out; } dst_clone(&rt->u.dst); } else {#ifdef CONFIG_RT6_POLICY rt = rt6_flow_lookup_in(rt, skb);#else /* NEVER REACHED */#endif }out: rt->u.dst.lastuse = jiffies; atomic_inc(&rt->u.dst.refcnt); 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; strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL); start_bh_atomic(); 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)) { rt = rt6_cow(rt, fl->nl_u.ip6_u.daddr, fl->nl_u.ip6_u.saddr); goto out; } dst_clone(&rt->u.dst); } else {#ifdef CONFIG_RT6_POLICY rt = rt6_flow_lookup_out(rt, sk, fl);#else /* NEVER REACHED */#endif }out: rt->u.dst.lastuse = jiffies; atomic_inc(&rt->u.dst.refcnt); end_bh_atomic(); 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); 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; start_bh_atomic(); if ((long)(now - last_gc) < ip6_rt_gc_min_interval) 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; end_bh_atomic(); 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 device *dev){ struct inet6_dev *idev; idev = ipv6_get_idev(dev); if (idev) return idev->cnf.mtu6; else return IPV6_MIN_MTU;}static int ipv6_get_hoplimit(struct device *dev){ struct inet6_dev *idev; idev = ipv6_get_idev(dev); if (idev) return idev->cnf.hop_limit; else return ipv6_devconf.hop_limit;}/* * */int ip6_route_add(struct in6_rtmsg *rtmsg){ int err; struct rt6_info *rt; struct device *dev = NULL; int addr_type; if (rtmsg->rtmsg_dst_len > 128 || rtmsg->rtmsg_src_len > 128) return -EINVAL;#ifndef CONFIG_IPV6_SUBTREES if (rtmsg->rtmsg_src_len) return -EINVAL;#endif if (rtmsg->rtmsg_metric == 0) rtmsg->rtmsg_metric = IP6_RT_PRIO_USER; rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops); if (rt == NULL) return -ENOMEM; rt->u.dst.obsolete = -1; rt->rt6i_expires = rtmsg->rtmsg_info; addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst); if (addr_type & IPV6_ADDR_MULTICAST) rt->u.dst.input = ip6_mc_input; else rt->u.dst.input = ip6_forward; rt->u.dst.output = ip6_output; if (rtmsg->rtmsg_ifindex) { dev = dev_get_by_index(rtmsg->rtmsg_ifindex); err = -ENODEV; if (dev == NULL) goto out; } ipv6_addr_copy(&rt->rt6i_dst.addr, &rtmsg->rtmsg_dst); rt->rt6i_dst.plen = rtmsg->rtmsg_dst_len; ipv6_wash_prefix(&rt->rt6i_dst.addr, rt->rt6i_dst.plen);#ifdef CONFIG_IPV6_SUBTREES ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src); rt->rt6i_src.plen = rtmsg->rtmsg_src_len; ipv6_wash_prefix(&rt->rt6i_src.addr, rt->rt6i_src.plen);#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -