summaryrefslogtreecommitdiffstats
path: root/net/ipv6/ndisc.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ndisc.c')
-rw-r--r--net/ipv6/ndisc.c104
1 files changed, 58 insertions, 46 deletions
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 26e42a1ed..b6c855a59 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -68,8 +68,7 @@
#include <net/ndisc.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
-
-
+#include <net/icmp.h>
#include <net/checksum.h>
#include <linux/proc_fs.h>
@@ -350,6 +349,9 @@ void ndisc_send_na(struct device *dev, struct neighbour *neigh,
len, 0));
dev_queue_xmit(skb);
+
+ icmpv6_statistics.Icmp6OutNeighborAdvertisements++;
+ icmpv6_statistics.Icmp6OutMsgs++;
}
void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
@@ -410,6 +412,9 @@ void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
len, 0));
/* send it! */
dev_queue_xmit(skb);
+
+ icmpv6_statistics.Icmp6OutNeighborSolicits++;
+ icmpv6_statistics.Icmp6OutMsgs++;
}
void ndisc_send_rs(struct device *dev, struct in6_addr *saddr,
@@ -458,6 +463,9 @@ void ndisc_send_rs(struct device *dev, struct in6_addr *saddr,
/* send it! */
dev_queue_xmit(skb);
+
+ icmpv6_statistics.Icmp6OutRouterSolicits++;
+ icmpv6_statistics.Icmp6OutMsgs++;
}
@@ -575,6 +583,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
if (rt && lifetime == 0) {
ip6_del_rt(rt);
+ dst_release(&rt->u.dst);
rt = NULL;
}
@@ -582,11 +591,6 @@ static void ndisc_router_discovery(struct sk_buff *skb)
ND_PRINTK2("ndisc_rdisc: adding default router\n");
rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
-
-#if 1
- /* BUGGGGG! Previous routine can return invalid pointer. */
- rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
-#endif
if (rt == NULL) {
ND_PRINTK1("route_add failed\n");
return;
@@ -595,6 +599,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
neigh = rt->rt6i_nexthop;
if (neigh == NULL) {
ND_PRINTK1("nd: add default router: null neighbour\n");
+ dst_release(&rt->u.dst);
return;
}
neigh->flags |= NTF_ROUTER;
@@ -658,7 +663,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
mtu = htonl(*(__u32 *)(opt+4));
- if (mtu < 576 || mtu > skb->dev->mtu) {
+ if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
ND_PRINTK0("NDISC: router "
"announcement with mtu = %d\n",
mtu);
@@ -671,10 +676,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
if (rt)
rt->u.dst.pmtu = mtu;
- /* BUGGG... Scan routing tables and
- adjust mtu on routes going
- via this device
- */
+ rt6_mtu_change(skb->dev, mtu);
}
}
break;
@@ -689,6 +691,8 @@ static void ndisc_router_discovery(struct sk_buff *skb)
optlen -= len;
opt += len;
}
+ if (rt)
+ dst_release(&rt->u.dst);
}
static void ndisc_redirect_rcv(struct sk_buff *skb)
@@ -698,7 +702,6 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
struct in6_addr *dest;
struct in6_addr *target; /* new first hop to destination */
struct neighbour *neigh;
- struct rt6_info *rt;
int on_link = 0;
int optlen;
@@ -740,20 +743,21 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
if (!in6_dev || in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
return;
- /* passed validation tests
+ /* passed validation tests */
- NOTE We should not install redirect if sender did not supply
- ll address on link, which requires it. It would break, if
- we have non-transitive address resolution protocol.
- Fix it later. --ANK
+ /*
+ We install redirect only if nexthop state is valid.
*/
- rt = rt6_redirect(dest, &skb->nh.ipv6h->saddr, target, skb->dev, on_link);
-
- if (rt == NULL)
- return;
- neigh = rt->rt6i_nexthop;
- ndisc_update(neigh, (u8*)(dest + 1), optlen, ND_OPT_TARGET_LL_ADDR);
+ neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
+ if (neigh) {
+ ndisc_update(neigh, (u8*)(dest + 1), optlen, ND_OPT_TARGET_LL_ADDR);
+ if (neigh->nud_state&NUD_VALID)
+ rt6_redirect(dest, &skb->nh.ipv6h->saddr, neigh, on_link);
+ else
+ __neigh_event_send(neigh, NULL);
+ neigh_release(neigh);
+ }
}
void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
@@ -773,17 +777,21 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
int hlen;
dev = skb->dev;
- rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev->ifindex, 0);
+ rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev->ifindex, 1);
- if (rt == NULL || rt->u.dst.error) {
- ND_PRINTK1("ndisc_send_redirect: hostunreach\n");
+ if (rt == NULL)
return;
- }
if (rt->rt6i_flags & RTF_GATEWAY) {
ND_PRINTK1("ndisc_send_redirect: not a neighbour\n");
+ dst_release(&rt->u.dst);
return;
}
+ if (!xrlim_allow(&rt->u.dst, 1*HZ)) {
+ dst_release(&rt->u.dst);
+ return;
+ }
+ dst_release(&rt->u.dst);
if (dev->addr_len) {
if (neigh->nud_state&NUD_VALID) {
@@ -797,7 +805,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
}
}
- rd_len = min(536 - len, ntohs(skb->nh.ipv6h->payload_len) + 8);
+ rd_len = min(IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, ntohs(skb->nh.ipv6h->payload_len) + 8);
rd_len &= ~0x7;
len += rd_len;
@@ -814,14 +822,14 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
ND_PRINTK1("ndisc_send_redirect: alloc_skb failed\n");
return;
}
-
+
hlen = 0;
if (ndisc_build_ll_hdr(buff, dev, &skb->nh.ipv6h->saddr, NULL, len) == 0) {
kfree_skb(buff);
return;
}
-
+
ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr,
IPPROTO_ICMPV6, len);
@@ -838,9 +846,9 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
ipv6_addr_copy(addrp, target);
addrp++;
ipv6_addr_copy(addrp, &skb->nh.ipv6h->daddr);
-
+
opt = (u8*) (addrp + 1);
-
+
/*
* include target_address option
*/
@@ -858,12 +866,15 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
opt += 6;
memcpy(opt, &skb->nh.ipv6h, rd_len - 8);
-
+
icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
len, IPPROTO_ICMPV6,
csum_partial((u8 *) icmph, len, 0));
dev_queue_xmit(buff);
+
+ icmpv6_statistics.Icmp6OutRedirects++;
+ icmpv6_statistics.Icmp6OutMsgs++;
}
static __inline__ struct neighbour *
@@ -894,15 +905,15 @@ static __inline__ int ndisc_recv_na(struct neighbour *neigh, struct sk_buff *skb
static void pndisc_redo(struct sk_buff *skb)
{
- ndisc_rcv(skb, skb->dev, &skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
- NULL, skb->len);
+ ndisc_rcv(skb, skb->len);
kfree_skb(skb);
}
-int ndisc_rcv(struct sk_buff *skb, struct device *dev,
- struct in6_addr *saddr, struct in6_addr *daddr,
- struct ipv6_options *opt, unsigned short len)
+int ndisc_rcv(struct sk_buff *skb, unsigned long len)
{
+ struct device *dev = skb->dev;
+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
struct neighbour *neigh;
struct inet6_ifaddr *ifp;
@@ -977,7 +988,7 @@ int ndisc_rcv(struct sk_buff *skb, struct device *dev,
if (neigh) {
ndisc_send_na(dev, neigh, saddr, &msg->target,
- 1, 0, inc, inc);
+ 0, 0, inc, inc);
neigh_release(neigh);
}
} else {
@@ -1023,13 +1034,14 @@ int ndisc_rcv(struct sk_buff *skb, struct device *dev,
/*
* Change: router to host
*/
-#if 0
struct rt6_info *rt;
- rt = ndisc_get_dflt_router(skb->dev,
- saddr);
- if (rt)
- ndisc_del_dflt_router(rt);
-#endif
+ rt = rt6_get_dflt_router(saddr, skb->dev);
+ if (rt) {
+ /* It is safe only because
+ we aer in BH */
+ dst_release(&rt->u.dst);
+ ip6_del_rt(rt);
+ }
}
} else {
if (msg->icmph.icmp6_router)