patch-2.1.79 linux/net/ipv6/route.c
Next file: linux/net/ipv6/sit.c
Previous file: linux/net/ipv6/reassembly.c
Back to the patch index
Back to the overall index
- Lines: 956
- Date:
Mon Jan 12 15:28:28 1998
- Orig file:
v2.1.78/linux/net/ipv6/route.c
- Orig date:
Mon Dec 1 12:04:17 1997
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/route.c linux/net/ipv6/route.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: route.c,v 1.18 1997/10/17 00:15:05 freitag Exp $
+ * $Id: route.c,v 1.19 1997/12/13 21:53:16 kuznet Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -37,6 +37,7 @@
#include <net/ndisc.h>
#include <net/addrconf.h>
#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
#include <asm/uaccess.h>
@@ -51,24 +52,29 @@
#define RDBG(x)
#endif
+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_pkt_discard(struct sk_buff *skb);
struct dst_ops ip6_dst_ops = {
AF_INET6,
+ __constant_htons(ETH_P_IPV6),
ip6_dst_check,
ip6_dst_reroute,
- NULL
+ NULL,
+ ip6_negative_advice
};
struct rt6_info ip6_null_entry = {
{{NULL, ATOMIC_INIT(0), ATOMIC_INIT(0), NULL,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL,
+ -1, 0, 0, 0, 0, 0, 0, 0,
+ -ENETUNREACH, NULL, NULL,
ip6_pkt_discard, ip6_pkt_discard, &ip6_dst_ops}},
- NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0UL,
+ NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0U,
0, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128}
};
@@ -220,14 +226,14 @@
RDBG(("rt6_best_dflt(%p,%p): ", rt, dev));
for (sprt = rt; sprt; sprt = sprt->u.next) {
- struct nd_neigh *ndn;
+ struct neighbour *neigh;
RDBG(("sprt(%p): ", sprt));
- if ((ndn = (struct nd_neigh *) sprt->rt6i_nexthop)) {
+ if ((neigh = sprt->rt6i_nexthop)) {
int m = -1;
- RDBG(("nxthop(%p,%d) ", ndn, ndn->ndn_nud_state));
- switch (ndn->ndn_nud_state) {
+ RDBG(("nxthop(%p,%d) ", neigh, neigh->nud_state));
+ switch (neigh->nud_state) {
case NUD_REACHABLE:
RDBG(("NUD_REACHABLE "));
if (sprt != rt6_dflt_pointer) {
@@ -304,14 +310,16 @@
return rt;
}
-static struct rt6_info *rt6_cow(struct rt6_info *rt, struct in6_addr *daddr,
+static struct rt6_info *rt6_cow(struct rt6_info *ort, struct in6_addr *daddr,
struct in6_addr *saddr)
{
+ struct rt6_info *rt;
+
/*
* Clone the route.
*/
- rt = ip6_rt_copy(rt);
+ rt = ip6_rt_copy(ort);
if (rt) {
ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
@@ -432,7 +440,7 @@
RDBG(("ip6_route_output(%p,%p) from(%p)", sk, fl,
__builtin_return_address(0)));
- strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & IPV6_ADDR_MULTICAST;
+ strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL);
rt6_lock();
#if RT6_DEBUG >= 3
@@ -461,12 +469,28 @@
RDBG(("-->(%p[%s])) ", fn, fn == &ip6_routing_table ? "ROOT" : "!ROOT"));
+restart:
rt = fn->leaf;
if ((rt->rt6i_flags & RTF_CACHE)) {
RDBG(("RTF_CACHE "));
if (ip6_rt_policy == 0) {
rt = rt6_device_match(rt, fl->dev, strict);
+
+ /* BUGGGG! It is capital bug, that was hidden
+ by not-cloning multicast routes. However,
+ the same problem was with link-local addresses.
+ Fix is the following if-statement,
+ but it will not properly handle Pedro's subtrees --ANK
+ */
+ if (rt == &ip6_null_entry && strict) {
+ while ((fn = fn->parent) != NULL) {
+ if (fn->fn_flags & RTN_ROOT)
+ goto out;
+ if (fn->fn_flags & RTN_RTINFO)
+ goto restart;
+ }
+ }
RDBG(("devmatch(%p) ", rt));
goto out;
}
@@ -517,7 +541,7 @@
}
-void rt6_ins(struct rt6_info *rt)
+static void rt6_ins(struct rt6_info *rt)
{
start_bh_atomic();
if (atomic_read(&rt6_tbl_lock) == 1)
@@ -529,29 +553,33 @@
/*
* Destination cache support functions
+ *
+ * BUGGG! This function is absolutely wrong.
+ * First of all it is never called. (look at include/net/dst.h)
+ * Second, even when it is called rt->rt6i_node == NULL
+ * ** partially fixed: now dst->obsolete = -1 for IPv6 not cache routes.
+ * Third, even we fixed previous bugs,
+ * it will not work because sernum is incorrectly checked/updated and
+ * it does not handle change of the parent of cloned route.
+ * Purging stray clones is not easy task, it would require
+ * massive remake of ip6_fib.c. Alas...
+ * --ANK
*/
-struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
+static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
{
struct rt6_info *rt;
- RDBG(("ip6dstchk(%p,%08x)[%p]\n", dst, cookie,
- __builtin_return_address(0)));
-
rt = (struct rt6_info *) dst;
- if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
- if (rt->rt6i_nexthop)
- ndisc_event_send(rt->rt6i_nexthop, NULL);
-
+ if (rt && rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
return dst;
- }
dst_release(dst);
return NULL;
}
-struct dst_entry *ip6_dst_reroute(struct dst_entry *dst, struct sk_buff *skb)
+static struct dst_entry *ip6_dst_reroute(struct dst_entry *dst, struct sk_buff *skb)
{
/*
* FIXME
@@ -561,6 +589,13 @@
return NULL;
}
+static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
+{
+ dst_release(dst);
+ return NULL;
+}
+
+
/* Clean host part of a prefix. Not necessary in radix tree,
but results in cleaner routing tables.
@@ -592,6 +627,8 @@
*err = -EINVAL;
return NULL;
}
+ if (rtmsg->rtmsg_metric == 0)
+ rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
*err = 0;
@@ -603,6 +640,9 @@
goto out;
}
+ 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) {
@@ -613,7 +653,7 @@
rt->u.dst.input = ip6_forward;
}
- rt->u.dst.output = dev_queue_xmit;
+ rt->u.dst.output = ip6_output;
if (rtmsg->rtmsg_ifindex) {
dev = dev_get_by_index(rtmsg->rtmsg_ifindex);
@@ -665,9 +705,16 @@
*err = -EINVAL;
goto out;
}
+ }
- rt->rt6i_nexthop = ndisc_get_neigh(dev, gw_addr);
+ if (dev == NULL) {
+ RDBG(("!dev, "));
+ *err = -ENODEV;
+ goto out;
+ }
+ if (rtmsg->rtmsg_flags & (RTF_GATEWAY|RTF_NONEXTHOP)) {
+ rt->rt6i_nexthop = ndisc_get_neigh(dev, &rt->rt6i_gateway);
if (rt->rt6i_nexthop == NULL) {
RDBG(("!nxthop, "));
*err = -ENOMEM;
@@ -676,12 +723,6 @@
RDBG(("nxthop, "));
}
- if (dev == NULL) {
- RDBG(("!dev, "));
- *err = -ENODEV;
- goto out;
- }
-
rt->rt6i_metric = rtmsg->rtmsg_metric;
rt->rt6i_dev = dev;
@@ -694,6 +735,29 @@
rt6_ins(rt);
rt6_unlock();
+ /* BUGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG!
+
+ If rt6_ins will fail (and it occurs regularly f.e. if route
+ already existed), the route will be freed -> Finita.
+ Crash. No recovery. NO FIX. Unfortunately, it is not the only
+ place will it is fatal. It is sad, I believed this
+ code is a bit more accurate :-(
+
+ Really, the problem can be solved in two ways:
+
+ * As I did in old 2.0 IPv4: to increase use count and force
+ user to destroy stray route. It requires some care,
+ well, much more care.
+ * Second and the best: to get rid of this damn backlogging
+ system. I wonder why Pedro so liked it. It was the most
+ unhappy day when I invented it (well, by a strange reason
+ I believed that it is very clever :-)),
+ and when I managed to clean IPv4 of this crap,
+ it was really great win.
+ BTW I forgot how 2.0 route/arp works :-) :-)
+ --ANK
+ */
+
out:
if (*err) {
RDBG(("dfree(%p) ", rt));
@@ -701,7 +765,17 @@
rt = NULL;
}
RDBG(("ret(%p)\n", rt));
+#if 0
return rt;
+#else
+ /* BUGGG! For now always return NULL. (see above)
+
+ Really, it was used only in two places, and one of them
+ (rt6_add_dflt_router) is repaired, ip6_fw is not essential
+ at all. --ANK
+ */
+ return NULL;
+#endif
}
int ip6_del_rt(struct rt6_info *rt)
@@ -710,6 +784,12 @@
start_bh_atomic();
+ /* I'd add here couple of cli()
+ cli(); cli(); cli();
+
+ Now it is really LOCKED. :-) :-) --ANK
+ */
+
rt6_dflt_pointer = NULL;
if (atomic_read(&rt6_tbl_lock) == 1)
@@ -737,15 +817,34 @@
/*
* Find route
*/
- rt=rt6_lookup(&rtmsg->rtmsg_dst, &rtmsg->rtmsg_src, dev, dev ? RTF_LINKRT : 0);
+ rt=rt6_lookup(&rtmsg->rtmsg_dst, &rtmsg->rtmsg_src, dev, RTF_LINKRT);
/*
* Blow it away
+ *
+ * BUGGGG It will not help with Pedro's subtrees.
+ * We urgently need fib6_locate_node function, and
+ * it is not the only place where rt6_lookup is used
+ * for wrong purpose.
+ * --ANK
*/
- if(rt && rt->rt6i_dst.plen == rtmsg->rtmsg_dst_len &&
- rt->rt6i_src.plen == rtmsg->rtmsg_src_len) {
- ip6_del_rt(rt);
- return 0;
+restart:
+ if (rt && rt->rt6i_src.plen == rtmsg->rtmsg_src_len) {
+ if (rt->rt6i_dst.plen > rtmsg->rtmsg_dst_len) {
+ struct fib6_node *fn = rt->rt6i_node;
+ while ((fn = fn->parent) != NULL) {
+ if (fn->fn_flags & RTN_ROOT)
+ break;
+ if (fn->fn_flags & RTN_RTINFO) {
+ rt = rt6_device_match(rt, dev, RTF_LINKRT);
+ goto restart;
+ }
+ }
+ }
+ if (rt->rt6i_dst.plen == rtmsg->rtmsg_dst_len) {
+ ip6_del_rt(rt);
+ return 0;
+ }
}
return -ESRCH;
@@ -773,7 +872,7 @@
rt6_bh_mask = 0;
}
-#ifdef CONFIG_NETLINK
+#ifdef CONFIG_IPV6_NETLINK
/*
* NETLINK interface
* routing socket moral equivalent
@@ -785,6 +884,7 @@
struct in6_rtmsg *rtmsg;
int err;
+ rtnl_lock();
while (skb->len) {
if (skb->len < sizeof(struct in6_rtmsg)) {
count = -EINVAL;
@@ -809,10 +909,10 @@
}
out:
+ rtnl_unlock();
kfree_skb(skb, FREE_READ);
return count;
}
-#endif /* CONFIG_NETLINK */
static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg)
{
@@ -825,9 +925,7 @@
memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg,
sizeof(struct in6_rtmsg));
-#ifdef CONFIG_NETLINK
if (netlink_post(NETLINK_ROUTE6, skb))
-#endif
kfree_skb(skb, FREE_WRITE);
}
@@ -867,11 +965,10 @@
msg->rtmsg_flags = flags;
-#ifdef CONFIG_NETLINK
if (netlink_post(NETLINK_ROUTE6, skb))
-#endif
kfree_skb(skb, FREE_WRITE);
}
+#endif /* CONFIG_IPV6_NETLINK */
/*
* Handle redirects
@@ -888,6 +985,12 @@
if (rt == NULL || rt->u.dst.error)
return NULL;
+ /* Redirect received -> path was valid.
+ Look, redirects are sent only in response to data packets,
+ so that this nexthop apparently is reachable. --ANK
+ */
+ dst_confirm(&rt->u.dst);
+
/* Duplicate redirect: silently ignore. */
if (ipv6_addr_cmp(target, &rt->rt6i_gateway) == 0)
return NULL;
@@ -931,21 +1034,32 @@
* We have finally decided to accept it.
*/
if (rt->rt6i_dst.plen == 128) {
+ /* BUGGGG! Very bad bug. Fast path code does not protect
+ * itself of changing nexthop on the fly, it was supposed
+ * that crucial parameters (dev, nexthop, hh) ARE VOLATILE.
+ * --ANK
+ * Not fixed!! I plugged it to avoid random crashes
+ * (they are very unlikely, but I do not want to shrug
+ * every time when redirect arrives)
+ * but the plug must be removed. --ANK
+ */
+
+#if 0
/*
* Already a host route.
*
*/
if (rt->rt6i_nexthop)
neigh_release(rt->rt6i_nexthop);
- /*
- * purge hh_cache
- */
rt->rt6i_flags |= RTF_MODIFIED | RTF_CACHE;
if (on_link)
rt->rt6i_flags &= ~RTF_GATEWAY;
ipv6_addr_copy(&rt->rt6i_gateway, target);
rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, target);
return rt;
+#else
+ return NULL;
+#endif
}
nrt = ip6_rt_copy(rt);
@@ -965,6 +1079,7 @@
rt6_ins(nrt);
rt6_unlock();
+ /* BUGGGGGGG! nrt can point to nowhere. */
return nrt;
}
@@ -975,7 +1090,7 @@
void rt6_pmtu_discovery(struct in6_addr *addr, struct device *dev, int pmtu)
{
- struct rt6_info *rt;
+ struct rt6_info *rt, *nrt;
if (pmtu < 576 || pmtu > 65536) {
#if RT6_DEBUG >= 1
@@ -994,13 +1109,21 @@
return;
}
+ if (pmtu >= rt->u.dst.pmtu)
+ return;
+
+ /* New mtu received -> path was valid.
+ They are sent only in response to data packets,
+ so that this nexthop apparently is reachable. --ANK
+ */
+ dst_confirm(&rt->u.dst);
+
/* It is wrong, but I plugged the hole here.
On-link routes are cloned differently,
look at rt6_redirect --ANK
*/
- if (!(rt->rt6i_flags&RTF_GATEWAY)) {
+ if (!(rt->rt6i_flags&RTF_GATEWAY))
return;
- }
if (rt->rt6i_dst.plen == 128) {
/*
@@ -1012,11 +1135,18 @@
return;
}
- rt = ip6_rt_copy(rt);
- ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
- rt->rt6i_dst.plen = 128;
+ nrt = ip6_rt_copy(rt);
+ ipv6_addr_copy(&nrt->rt6i_dst.addr, addr);
+ nrt->rt6i_dst.plen = 128;
+
+ nrt->rt6i_flags |= (RTF_DYNAMIC | RTF_CACHE);
- rt->rt6i_flags |= (RTF_DYNAMIC | RTF_CACHE);
+ /* It was missing. :-) :-)
+ I wonder, kernel was deemed to crash after pkt_too_big
+ and nobody noticed it. Hey, guys, do someone really
+ use it? --ANK
+ */
+ nrt->rt6i_nexthop = neigh_clone(rt->rt6i_nexthop);
rt6_lock();
rt6_ins(rt);
@@ -1027,7 +1157,7 @@
* Misc support functions
*/
-struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
+static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
{
struct rt6_info *rt;
@@ -1076,7 +1206,7 @@
for (rt = fn->leaf; rt; rt=rt->u.next) {
if (dev == rt->rt6i_dev &&
- ipv6_addr_cmp(&rt->rt6i_dst.addr, addr) == 0)
+ ipv6_addr_cmp(&rt->rt6i_gateway, addr) == 0)
break;
}
@@ -1117,6 +1247,10 @@
rt = ip6_route_add(&rtmsg, &err);
+ /* BUGGGGGGGGGGGGGGGGGGGG!
+ rt can be not NULL, but point to heavens.
+ */
+
if (err) {
printk(KERN_DEBUG "rt6_add_dflt: ip6_route_add error %d\n",
err);
@@ -1172,6 +1306,7 @@
if (err)
return -EFAULT;
+ rtnl_lock();
switch (cmd) {
case SIOCADDRT:
ip6_route_add(&rtmsg, &err);
@@ -1182,9 +1317,12 @@
default:
err = -EINVAL;
};
+ rtnl_unlock();
+#ifdef CONFIG_IPV6_NETLINK
if (err == 0)
rt6_sndrtmsg(&rtmsg);
+#endif
return err;
};
@@ -1229,15 +1367,19 @@
if (rt == NULL)
return -ENOMEM;
- memset(rt, 0, sizeof(struct rt6_info));
-
rt->u.dst.input = ip6_input;
- rt->u.dst.output = dev_queue_xmit;
+ rt->u.dst.output = ip6_output;
rt->rt6i_dev = dev_get("lo");
rt->u.dst.pmtu = rt->rt6i_dev->mtu;
+ rt->u.dst.obsolete = -1;
rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
-
+ rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+ if (rt->rt6i_nexthop == NULL) {
+ dst_free((struct dst_entry *) rt);
+ return -ENOMEM;
+ }
+
ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
rt->rt6i_dst.plen = 128;
@@ -1248,6 +1390,21 @@
return 0;
}
+/* Delete address. Warning: you should check that this address
+ disappeared before calling this function.
+ */
+
+int ip6_rt_addr_del(struct in6_addr *addr, struct device *dev)
+{
+ struct rt6_info *rt;
+
+ rt = rt6_lookup(addr, NULL, dev_get("lo"), RTF_LINKRT);
+ if (rt && rt->rt6i_dst.plen == 128)
+ return ip6_del_rt(rt);
+
+ return 0;
+}
+
#ifdef CONFIG_RT6_POLICY
static int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb)
@@ -1355,18 +1512,260 @@
goto error;
nrt->rt6i_flags |= RTF_CACHE;
+ /* BUGGGG! nrt can point to nowhere! */
rt6_ins(nrt);
return nrt;
}
#endif
+/*
+ * Nope, I am not idiot. I see that it is the ugliest of ugly routines.
+ * Anyone is advertised to write better one. --ANK
+ */
+
+struct rt6_ifdown_arg {
+ struct device *dev;
+ struct rt6_info *rt;
+};
+
+
+static void rt6_ifdown_node(struct fib6_node *fn, void *p_arg)
+{
+ struct rt6_info *rt;
+ struct rt6_ifdown_arg *arg = (struct rt6_ifdown_arg *) p_arg;
+
+ if (arg->rt != NULL)
+ return;
+
+ for (rt = fn->leaf; rt; rt = rt->u.next) {
+ if (rt->rt6i_dev == arg->dev || arg->dev == NULL) {
+ arg->rt = rt;
+ return;
+ }
+ }
+}
+
+void rt6_ifdown(struct device *dev)
+{
+ int count = 0;
+ struct rt6_ifdown_arg arg;
+ struct rt6_info *rt;
+
+ do {
+ arg.dev = dev;
+ arg.rt = NULL;
+ fib6_walk_tree(&ip6_routing_table, rt6_ifdown_node, &arg,
+ RT6_FILTER_RTNODES);
+ if (arg.rt != NULL)
+ ip6_del_rt(arg.rt);
+ count++;
+ } while (arg.rt != NULL);
+
+ /* And default routes ... */
+
+ for (rt = ip6_routing_table.leaf; rt; ) {
+ if (rt != &ip6_null_entry && (rt->rt6i_dev == dev || dev == NULL)) {
+ struct rt6_info *deleting = rt;
+ rt = rt->u.next;
+ ip6_del_rt(deleting);
+ continue;
+ }
+ rt = rt->u.next;
+ }
+}
+
+#ifdef CONFIG_RTNETLINK
+
+static void inet6_rtm_to_rtmsg(struct rtmsg *r, struct kern_rta *rta,
+ struct in6_rtmsg *rtmsg)
+{
+ memset(rtmsg, 0, sizeof(*rtmsg));
+ rtmsg->rtmsg_dst_len = r->rtm_dst_len;
+ rtmsg->rtmsg_src_len = r->rtm_src_len;
+ rtmsg->rtmsg_flags = RTF_UP;
+ rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
+ if (rta->rta_gw) {
+ memcpy(&rtmsg->rtmsg_gateway, rta->rta_gw, 16);
+ rtmsg->rtmsg_flags |= RTF_GATEWAY;
+ }
+ if (rta->rta_dst)
+ memcpy(&rtmsg->rtmsg_dst, rta->rta_dst, 16);
+ if (rta->rta_src)
+ memcpy(&rtmsg->rtmsg_src, rta->rta_src, 16);
+ if (rta->rta_oif)
+ memcpy(&rtmsg->rtmsg_ifindex, rta->rta_oif, 4);
+ if (rta->rta_priority)
+ memcpy(&rtmsg->rtmsg_metric, rta->rta_priority, 4);
+}
+
+int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+{
+ struct kern_rta *rta = arg;
+ struct rtmsg *r = NLMSG_DATA(nlh);
+ struct in6_rtmsg rtmsg;
+
+ inet6_rtm_to_rtmsg(r, rta, &rtmsg);
+ return ip6_route_del(&rtmsg);
+}
+
+int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+{
+ struct kern_rta *rta = arg;
+ struct rtmsg *r = NLMSG_DATA(nlh);
+ struct in6_rtmsg rtmsg;
+ int err = 0;
+
+ inet6_rtm_to_rtmsg(r, rta, &rtmsg);
+ ip6_route_add(&rtmsg, &err);
+ return err;
+}
+
+
+struct rt6_rtnl_dump_arg
+{
+ struct sk_buff *skb;
+ struct netlink_callback *cb;
+ int skip;
+ int count;
+ int stop;
+};
+
+static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
+ int type, pid_t pid, u32 seq)
+{
+ struct rtmsg *rtm;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+ unsigned char *o;
+ struct rta_cacheinfo ci;
+
+ nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*rtm));
+ rtm = NLMSG_DATA(nlh);
+ rtm->rtm_family = AF_INET6;
+ rtm->rtm_dst_len = rt->rt6i_dst.plen;
+ rtm->rtm_src_len = rt->rt6i_src.plen;
+ rtm->rtm_tos = 0;
+ rtm->rtm_table = RT_TABLE_MAIN;
+ rtm->rtm_type = RTN_UNICAST;
+ rtm->rtm_flags = 0;
+ rtm->rtm_scope = RT_SCOPE_UNIVERSE;
+ rtm->rtm_nhs = 0;
+ rtm->rtm_protocol = RTPROT_BOOT;
+ if (rt->rt6i_flags&RTF_DYNAMIC)
+ rtm->rtm_protocol = RTPROT_REDIRECT;
+ else if (rt->rt6i_flags&(RTF_ADDRCONF|RTF_ALLONLINK))
+ rtm->rtm_protocol = RTPROT_KERNEL;
+ else if (rt->rt6i_flags&RTF_DEFAULT)
+ rtm->rtm_protocol = RTPROT_RA;
+
+ if (rt->rt6i_flags&RTF_CACHE)
+ rtm->rtm_flags |= RTM_F_CLONED;
+
+ o = skb->tail;
+ if (rtm->rtm_dst_len)
+ RTA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
+ if (rtm->rtm_src_len)
+ RTA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
+ if (rt->u.dst.pmtu)
+ RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
+ if (rt->u.dst.window)
+ RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &rt->u.dst.window);
+ if (rt->u.dst.rtt)
+ RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+ if (rt->u.dst.neighbour)
+ RTA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key);
+ if (rt->u.dst.dev)
+ RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->rt6i_dev->ifindex);
+ RTA_PUT(skb, RTA_PRIORITY, 4, &rt->rt6i_metric);
+ ci.rta_lastuse = jiffies - rt->u.dst.lastuse;
+ if (rt->rt6i_expires)
+ ci.rta_expires = rt->rt6i_expires - jiffies;
+ else
+ ci.rta_expires = 0;
+ ci.rta_used = 0;
+ ci.rta_clntref = atomic_read(&rt->u.dst.use);
+ ci.rta_error = rt->u.dst.error;
+ RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
+ rtm->rtm_optlen = skb->tail - o;
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_put(skb, b - skb->tail);
+ return -1;
+}
+
+static void rt6_dump_node(struct fib6_node *fn, void *p_arg)
+{
+ struct rt6_info *rt;
+ struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
+
+ if (arg->stop)
+ return;
+
+ for (rt = fn->leaf; rt; rt = rt->u.next) {
+ if (arg->count < arg->skip) {
+ arg->count++;
+ continue;
+ }
+ if (rt6_fill_node(arg->skb, rt, RTM_NEWROUTE,
+ NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq) <= 0) {
+ arg->stop = 1;
+ break;
+ }
+ arg->count++;
+ }
+}
+
+
+int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct rt6_rtnl_dump_arg arg;
+
+ arg.skb = skb;
+ arg.cb = cb;
+ arg.skip = cb->args[0];
+ arg.count = 0;
+ arg.stop = 0;
+ start_bh_atomic();
+ fib6_walk_tree(&ip6_routing_table, rt6_dump_node, &arg, RT6_FILTER_RTNODES);
+ if (arg.stop == 0)
+ rt6_dump_node(&ip6_routing_table, &arg);
+ end_bh_atomic();
+ cb->args[0] = arg.count;
+ return skb->len;
+}
+
+void inet6_rt_notify(int event, struct rt6_info *rt)
+{
+ struct sk_buff *skb;
+ int size = NLMSG_SPACE(sizeof(struct rtmsg)+256);
+
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb) {
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, ENOBUFS);
+ return;
+ }
+ if (rt6_fill_node(skb, rt, event, 0, 0) < 0) {
+ kfree_skb(skb, 0);
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, EINVAL);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_IPV6_ROUTE;
+ netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_ROUTE, GFP_ATOMIC);
+}
+
+#endif
+
/*
* /proc
*/
#ifdef CONFIG_PROC_FS
+
#define RT6_INFO_LEN (32 + 4 + 32 + 4 + 32 + 40 + 5 + 1)
struct rt6_proc_arg {
@@ -1411,11 +1810,8 @@
if (rt->rt6i_nexthop) {
for (i=0; i<16; i++) {
- struct nd_neigh *ndn;
-
- ndn = (struct nd_neigh *) rt->rt6i_nexthop;
sprintf(arg->buffer + arg->len, "%02x",
- ndn->ndn_addr.s6_addr[i]);
+ rt->rt6i_nexthop->primary_key[i]);
arg->len += 2;
}
} else {
@@ -1424,7 +1820,7 @@
arg->len += 32;
}
arg->len += sprintf(arg->buffer + arg->len,
- " %08lx %08x %08x %08lx %8s\n",
+ " %08x %08x %08x %08x %8s\n",
rt->rt6i_metric, atomic_read(&rt->rt6i_use),
atomic_read(&rt->rt6i_ref), rt->rt6i_flags,
rt->rt6i_dev ? rt->rt6i_dev->name : "");
@@ -1528,6 +1924,7 @@
return arg.len;
}
+
extern struct rt6_statistics rt6_stats;
static int rt6_proc_stats(char *buffer, char **start, off_t offset, int length,
@@ -1558,28 +1955,28 @@
0, &proc_net_inode_operations,
rt6_proc_info
};
-static struct proc_dir_entry proc_rt6_stats = {
- PROC_NET_RT6_STATS, 9, "rt6_stats",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- rt6_proc_stats
-};
static struct proc_dir_entry proc_rt6_tree = {
PROC_NET_RT6_TREE, 7, "ip6_fib",
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_net_inode_operations,
rt6_proc_tree
};
+static struct proc_dir_entry proc_rt6_stats = {
+ PROC_NET_RT6_STATS, 9, "rt6_stats",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rt6_proc_stats
+};
#endif /* CONFIG_PROC_FS */
__initfunc(void ip6_route_init(void))
{
#ifdef CONFIG_PROC_FS
proc_net_register(&proc_rt6_info);
- proc_net_register(&proc_rt6_stats);
proc_net_register(&proc_rt6_tree);
+ proc_net_register(&proc_rt6_stats);
#endif
-#ifdef CONFIG_NETLINK
+#ifdef CONFIG_IPV6_NETLINK
netlink_attach(NETLINK_ROUTE6, rt6_msgrcv);
#endif
}
@@ -1592,11 +1989,9 @@
proc_net_unregister(PROC_NET_RT6_TREE);
proc_net_unregister(PROC_NET_RT6_STATS);
#endif
-#ifdef CONFIG_NETLINK
+#ifdef CONFIG_IPV6_NETLINK
netlink_detach(NETLINK_ROUTE6);
#endif
-#if 0
- fib6_flush();
-#endif
+ rt6_ifdown(NULL);
}
#endif /* MODULE */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov