* FIB front-end.
*
* Authors:
- * Pedro Roque <roque@di.fc.ul.pt>
+ * Pedro Roque <roque@di.fc.ul.pt>
*
* $Id: route.c,v 1.56 2001/10/31 21:55:55 davem Exp $
*
if (idev != NULL) {
rt->rt6i_idev = NULL;
in6_dev_put(idev);
- }
+ }
}
static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
struct rt6_info *sprt;
if (oif) {
- for (sprt = rt; sprt; sprt = sprt->u.next) {
+ for (sprt = rt; sprt; sprt = sprt->u.dst.rt6_next) {
struct net_device *dev = sprt->rt6i_dev;
if (dev->ifindex == oif)
return sprt;
sprt->rt6i_idev->dev->ifindex != oif) {
if (strict && oif)
continue;
- if (local && (!oif ||
+ if (local && (!oif ||
local->rt6i_idev->dev->ifindex == oif))
continue;
}
/*
* Default Router Selection (RFC 2461 6.3.6)
*/
-static int inline rt6_check_dev(struct rt6_info *rt, int oif)
+static inline int rt6_check_dev(struct rt6_info *rt, int oif)
{
struct net_device *dev = rt->rt6i_dev;
if (!oif || dev->ifindex == oif)
return 0;
}
-static int inline rt6_check_neigh(struct rt6_info *rt)
+static inline int rt6_check_neigh(struct rt6_info *rt)
{
struct neighbour *neigh = rt->rt6i_nexthop;
int m = 0;
int strict)
{
int m, n;
-
+
m = rt6_check_dev(rt, oif);
if (!m && (strict & RT6_LOOKUP_F_IFACE))
return -1;
return m;
}
-static struct rt6_info *rt6_select(struct rt6_info **head, int oif,
- int strict)
+static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
+ int *mpri, struct rt6_info *match)
+{
+ int m;
+
+ if (rt6_check_expired(rt))
+ goto out;
+
+ m = rt6_score_route(rt, oif, strict);
+ if (m < 0)
+ goto out;
+
+ if (m > *mpri) {
+ if (strict & RT6_LOOKUP_F_REACHABLE)
+ rt6_probe(match);
+ *mpri = m;
+ match = rt;
+ } else if (strict & RT6_LOOKUP_F_REACHABLE) {
+ rt6_probe(rt);
+ }
+
+out:
+ return match;
+}
+
+static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
+ struct rt6_info *rr_head,
+ u32 metric, int oif, int strict)
{
- struct rt6_info *match = NULL, *last = NULL;
- struct rt6_info *rt, *rt0 = *head;
- u32 metric;
+ struct rt6_info *rt, *match;
int mpri = -1;
- RT6_TRACE("%s(head=%p(*head=%p), oif=%d)\n",
- __FUNCTION__, head, head ? *head : NULL, oif);
+ match = NULL;
+ for (rt = rr_head; rt && rt->rt6i_metric == metric;
+ rt = rt->u.dst.rt6_next)
+ match = find_match(rt, oif, strict, &mpri, match);
+ for (rt = fn->leaf; rt && rt != rr_head && rt->rt6i_metric == metric;
+ rt = rt->u.dst.rt6_next)
+ match = find_match(rt, oif, strict, &mpri, match);
- for (rt = rt0, metric = rt0->rt6i_metric;
- rt && rt->rt6i_metric == metric && (!last || rt != rt0);
- rt = rt->u.next) {
- int m;
+ return match;
+}
- if (rt6_check_expired(rt))
- continue;
+static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
+{
+ struct rt6_info *match, *rt0;
- last = rt;
+ RT6_TRACE("%s(fn->leaf=%p, oif=%d)\n",
+ __FUNCTION__, fn->leaf, oif);
- m = rt6_score_route(rt, oif, strict);
- if (m < 0)
- continue;
+ rt0 = fn->rr_ptr;
+ if (!rt0)
+ fn->rr_ptr = rt0 = fn->leaf;
- if (m > mpri) {
- if (strict & RT6_LOOKUP_F_REACHABLE)
- rt6_probe(match);
- match = rt;
- mpri = m;
- } else if (strict & RT6_LOOKUP_F_REACHABLE) {
- rt6_probe(rt);
- }
- }
+ match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict);
if (!match &&
- (strict & RT6_LOOKUP_F_REACHABLE) &&
- last && last != rt0) {
+ (strict & RT6_LOOKUP_F_REACHABLE)) {
+ struct rt6_info *next = rt0->u.dst.rt6_next;
+
/* no entries matched; do round-robin */
- static DEFINE_SPINLOCK(lock);
- spin_lock(&lock);
- *head = rt0->u.next;
- rt0->u.next = last->u.next;
- last->u.next = rt0;
- spin_unlock(&lock);
+ if (!next || next->rt6i_metric != rt0->rt6i_metric)
+ next = fn->leaf;
+
+ if (next != rt0)
+ fn->rr_ptr = next;
}
- RT6_TRACE("%s() => %p, score=%d\n",
- __FUNCTION__, match, mpri);
+ RT6_TRACE("%s() => %p\n",
+ __FUNCTION__, match);
return (match ? match : &ip6_null_entry);
}
fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
restart:
- rt = rt6_select(&fn->leaf, fl->iif, strict | reachable);
+ rt = rt6_select(fn, fl->iif, strict | reachable);
BACKTRACK(&fl->fl6_src);
if (rt == &ip6_null_entry ||
rt->rt6i_flags & RTF_CACHE)
.flowlabel = (* (__be32 *) iph)&IPV6_FLOWINFO_MASK,
},
},
- .mark = skb->mark,
+ .mark = skb->mark,
.proto = iph->nexthdr,
};
fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
restart:
- rt = rt6_select(&fn->leaf, fl->oif, strict | reachable);
+ rt = rt6_select(fn, fl->oif, strict | reachable);
BACKTRACK(&fl->fl6_src);
if (rt == &ip6_null_entry ||
rt->rt6i_flags & RTF_CACHE)
mtu = ip6_rt_min_advmss;
/*
- * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
- * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
- * IPV6_MAXPLEN is also valid and means: "any MSS,
+ * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
+ * corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
+ * IPV6_MAXPLEN is also valid and means: "any MSS,
* rely only on pmtu discovery"
*/
if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr))
static struct dst_entry *ndisc_dst_gc_list;
static DEFINE_SPINLOCK(ndisc_lock);
-struct dst_entry *ndisc_dst_alloc(struct net_device *dev,
+struct dst_entry *ndisc_dst_alloc(struct net_device *dev,
struct neighbour *neigh,
struct in6_addr *addr,
int (*output)(struct sk_buff *))
rt->u.dst.output = output;
#if 0 /* there's no chance to use these for ndisc */
- rt->u.dst.flags = ipv6_addr_type(addr) & IPV6_ADDR_UNICAST
- ? DST_HOST
+ rt->u.dst.flags = ipv6_addr_type(addr) & IPV6_ADDR_UNICAST
+ ? DST_HOST
: 0;
ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
rt->rt6i_dst.plen = 128;
int freed;
next = NULL;
- freed = 0;
+ freed = 0;
spin_lock_bh(&ndisc_lock);
pprev = &ndisc_dst_gc_list;
fn = fib6_locate(&table->tb6_root,
&cfg->fc_dst, cfg->fc_dst_len,
&cfg->fc_src, cfg->fc_src_len);
-
+
if (fn) {
- for (rt = fn->leaf; rt; rt = rt->u.next) {
+ for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) {
if (cfg->fc_ifindex &&
(rt->rt6i_dev == NULL ||
rt->rt6i_dev->ifindex != cfg->fc_ifindex))
read_lock_bh(&table->tb6_lock);
fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);
restart:
- for (rt = fn->leaf; rt; rt = rt->u.next) {
+ for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) {
/*
* Current route is on-link; redirect is always invalid.
*
* We have finally decided to accept it.
*/
- neigh_update(neigh, lladdr, NUD_STALE,
+ neigh_update(neigh, lladdr, NUD_STALE,
NEIGH_UPDATE_F_WEAK_OVERRIDE|
NEIGH_UPDATE_F_OVERRIDE|
(on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
}
out:
- dst_release(&rt->u.dst);
+ dst_release(&rt->u.dst);
return;
}
if (pmtu < IPV6_MIN_MTU) {
/*
- * According to RFC2460, PMTU is set to the IPv6 Minimum Link
+ * According to RFC2460, PMTU is set to the IPv6 Minimum Link
* MTU (1280) and a fragment header should always be included
* after a node receiving Too Big message reporting PMTU is
* less than the IPv6 Minimum Link MTU.
if (!fn)
goto out;
- for (rt = fn->leaf; rt; rt = rt->u.next) {
+ for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) {
if (rt->rt6i_dev->ifindex != ifindex)
continue;
if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY))
#endif
struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev)
-{
+{
struct rt6_info *rt;
struct fib6_table *table;
return NULL;
write_lock_bh(&table->tb6_lock);
- for (rt = table->tb6_root.leaf; rt; rt=rt->u.next) {
+ for (rt = table->tb6_root.leaf; rt; rt=rt->u.dst.rt6_next) {
if (dev == rt->rt6i_dev &&
((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) &&
ipv6_addr_equal(&rt->rt6i_gateway, addr))
restart:
read_lock_bh(&table->tb6_lock);
- for (rt = table->tb6_root.leaf; rt; rt = rt->u.next) {
+ for (rt = table->tb6_root.leaf; rt; rt = rt->u.dst.rt6_next) {
if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
dst_hold(&rt->u.dst);
read_unlock_bh(&table->tb6_lock);
* Drop the packet on the floor
*/
-static inline int ip6_pkt_drop(struct sk_buff *skb, int code)
+static inline int ip6_pkt_drop(struct sk_buff *skb, int code,
+ int ipstats_mib_noroutes)
{
- int type = ipv6_addr_type(&skb->nh.ipv6h->daddr);
- if (type == IPV6_ADDR_ANY || type == IPV6_ADDR_RESERVED)
- IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INADDRERRORS);
-
- IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_OUTNOROUTES);
+ int type;
+ switch (ipstats_mib_noroutes) {
+ case IPSTATS_MIB_INNOROUTES:
+ type = ipv6_addr_type(&skb->nh.ipv6h->daddr);
+ if (type == IPV6_ADDR_ANY || type == IPV6_ADDR_RESERVED) {
+ IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INADDRERRORS);
+ break;
+ }
+ /* FALLTHROUGH */
+ case IPSTATS_MIB_OUTNOROUTES:
+ IP6_INC_STATS(ip6_dst_idev(skb->dst), ipstats_mib_noroutes);
+ break;
+ }
icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0, skb->dev);
kfree_skb(skb);
return 0;
static int ip6_pkt_discard(struct sk_buff *skb)
{
- return ip6_pkt_drop(skb, ICMPV6_NOROUTE);
+ return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);
}
static int ip6_pkt_discard_out(struct sk_buff *skb)
{
skb->dev = skb->dst->dev;
- return ip6_pkt_discard(skb);
+ return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);
}
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
static int ip6_pkt_prohibit(struct sk_buff *skb)
{
- return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED);
+ return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);
}
static int ip6_pkt_prohibit_out(struct sk_buff *skb)
{
skb->dev = skb->dst->dev;
- return ip6_pkt_prohibit(skb);
+ return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
}
static int ip6_pkt_blk_hole(struct sk_buff *skb)
*/
if (rt->rt6i_dev == arg->dev &&
!dst_metric_locked(&rt->u.dst, RTAX_MTU) &&
- (dst_mtu(&rt->u.dst) > arg->mtu ||
- (dst_mtu(&rt->u.dst) < arg->mtu &&
+ (dst_mtu(&rt->u.dst) > arg->mtu ||
+ (dst_mtu(&rt->u.dst) < arg->mtu &&
dst_mtu(&rt->u.dst) == idev->cnf.mtu6)))
rt->u.dst.metrics[RTAX_MTU-1] = arg->mtu;
rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(arg->mtu);
nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags);
if (nlh == NULL)
- return -ENOBUFS;
+ return -EMSGSIZE;
rtm = nlmsg_data(nlh);
rtm->rtm_family = AF_INET6;
if (dst) {
NLA_PUT(skb, RTA_DST, 16, dst);
- rtm->rtm_dst_len = 128;
+ rtm->rtm_dst_len = 128;
} else if (rtm->rtm_dst_len)
NLA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
#ifdef CONFIG_IPV6_SUBTREES
if (src) {
NLA_PUT(skb, RTA_SRC, 16, src);
- rtm->rtm_src_len = 128;
+ rtm->rtm_src_len = 128;
} else if (rtm->rtm_src_len)
NLA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
#endif
return nlmsg_end(skb, nlh);
nla_put_failure:
- return nlmsg_cancel(skb, nlh);
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
}
int rt6_dump_route(struct rt6_info *rt, void *p_arg)
goto errout;
err = rt6_fill_node(skb, rt, NULL, NULL, 0, event, pid, seq, 0, 0);
- /* failure implies BUG in rt6_nlmsg_size() */
- BUG_ON(err < 0);
-
+ if (err < 0) {
+ /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(skb);
+ goto errout;
+ }
err = rtnl_notify(skb, pid, RTNLGRP_IPV6_ROUTE, nlh, gfp_any());
errout:
if (err < 0)
arg->len += sprintf(arg->buffer + arg->len,
" %08x %08x %08x %08x %8s\n",
rt->rt6i_metric, atomic_read(&rt->u.dst.__refcnt),
- rt->u.dst.__use, rt->rt6i_flags,
+ rt->u.dst.__use, rt->rt6i_flags,
rt->rt6i_dev ? rt->rt6i_dev->name : "");
return 0;
}
return single_open(file, rt6_stats_seq_show, NULL);
}
-static struct file_operations rt6_stats_seq_fops = {
+static const struct file_operations rt6_stats_seq_fops = {
.owner = THIS_MODULE,
.open = rt6_stats_seq_open,
.read = seq_read,
}
ctl_table ipv6_route_table[] = {
- {
- .ctl_name = NET_IPV6_ROUTE_FLUSH,
+ {
+ .ctl_name = NET_IPV6_ROUTE_FLUSH,
.procname = "flush",
- .data = &flush_delay,
+ .data = &flush_delay,
.maxlen = sizeof(int),
.mode = 0200,
- .proc_handler = &ipv6_sysctl_rtcache_flush
+ .proc_handler = &ipv6_sysctl_rtcache_flush
},
{
.ctl_name = NET_IPV6_ROUTE_GC_THRESH,
.procname = "gc_thresh",
- .data = &ip6_dst_ops.gc_thresh,
+ .data = &ip6_dst_ops.gc_thresh,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = &proc_dointvec,
},
{
.ctl_name = NET_IPV6_ROUTE_MAX_SIZE,
.procname = "max_size",
- .data = &ip6_rt_max_size,
+ .data = &ip6_rt_max_size,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = &proc_dointvec,
},
{
.ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL,
.procname = "gc_min_interval",
- .data = &ip6_rt_gc_min_interval,
+ .data = &ip6_rt_gc_min_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_ROUTE_GC_TIMEOUT,
.procname = "gc_timeout",
- .data = &ip6_rt_gc_timeout,
+ .data = &ip6_rt_gc_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_ROUTE_GC_INTERVAL,
.procname = "gc_interval",
- .data = &ip6_rt_gc_interval,
+ .data = &ip6_rt_gc_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_ROUTE_GC_ELASTICITY,
.procname = "gc_elasticity",
- .data = &ip6_rt_gc_elasticity,
+ .data = &ip6_rt_gc_elasticity,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_ROUTE_MTU_EXPIRES,
.procname = "mtu_expires",
- .data = &ip6_rt_mtu_expires,
+ .data = &ip6_rt_mtu_expires,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_ROUTE_MIN_ADVMSS,
.procname = "min_adv_mss",
- .data = &ip6_rt_min_advmss,
+ .data = &ip6_rt_min_advmss,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL_MS,
.procname = "gc_min_interval_ms",
- .data = &ip6_rt_gc_min_interval,
+ .data = &ip6_rt_gc_min_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_ms_jiffies,
+ .proc_handler = &proc_dointvec_ms_jiffies,
.strategy = &sysctl_ms_jiffies,
},
{ .ctl_name = 0 }