patch-2.1.30 linux/net/ipv6/ndisc.c
Next file: linux/net/ipv6/proc.c
Previous file: linux/net/ipv6/mcast.c
Back to the patch index
Back to the overall index
- Lines: 1995
- Date:
Thu Mar 20 18:17:15 1997
- Orig file:
v2.1.29/linux/net/ipv6/ndisc.c
- Orig date:
Mon Mar 17 14:54:36 1997
diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
@@ -6,7 +6,7 @@
* Pedro Roque <roque@di.fc.ul.pt>
* Mike Shaver <shaver@ingenia.com>
*
- * $Id: ndisc.c,v 1.28 1996/10/11 16:03:06 roque Exp $
+ * $Id: ndisc.c,v 1.13 1997/03/18 18:24:41 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -23,8 +23,15 @@
* Janos Farkas : kmalloc failure checks
*/
+/* Set to 3 to get tracing... */
#define ND_DEBUG 2
+#if ND_DEBUG >= 3
+#define NDBG(x) printk x
+#else
+#define NDBG(x)
+#endif
+
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/config.h>
@@ -37,7 +44,6 @@
#include <linux/in6.h>
#include <linux/route.h>
-#include <net/neighbour.h>
#include <linux/if_arp.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
@@ -48,7 +54,7 @@
#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
@@ -68,9 +74,7 @@
static struct neigh_table nd_tbl;
unsigned int ndisc_hash(void *primary_key);
-int ndisc_eth_resolv(unsigned char *h_dest,
- struct device *dev,
- struct sk_buff *skb);
+int ndisc_eth_resolv(unsigned char *h_dest, struct sk_buff *skb);
static struct neigh_ops nd_neigh_ops = {
ETH_P_IPV6,
@@ -86,13 +90,8 @@
* Protocol variables
*/
-int nd_max_multicast_solicit = 3;
-int nd_max_unicast_solicit = 3;
-int nd_retrans_timer = RETRANS_TIMER;
-int nd_reachable_time = RECHABLE_TIME;
-int nd_base_reachable_time = RECHABLE_TIME;
-int nd_delay_first_probe = 5 * HZ;
-int nd_gc_interval = 5 * HZ;
+unsigned long nd_reachable_time = RECHABLE_TIME;
+int nd_gc_interval = 5 * HZ;
/*
* garbage collection timeout must be greater than reachable time
@@ -106,7 +105,7 @@
static int ndisc_event_timer(struct nd_neigh *ndn);
-int ipv6_random(void)
+unsigned long ipv6_random(void)
{
nd_rand_seed=nd_rand_seed*69069L+1;
return nd_rand_seed^jiffies;
@@ -116,17 +115,15 @@
{
unsigned long val;
- val = ipv6_random() % (MAX_RANDOM_FACTOR * nd_base_reachable_time);
- if (val < (MIN_RANDOM_FACTOR * nd_base_reachable_time))
- {
- val += (MIN_RANDOM_FACTOR * nd_base_reachable_time);
- }
+ val = ipv6_random() % (MAX_RANDOM_FACTOR *
+ ipv6_config.nd_base_reachable_time);
+
+ if (val < (MIN_RANDOM_FACTOR * ipv6_config.nd_base_reachable_time))
+ val+= (MIN_RANDOM_FACTOR * ipv6_config.nd_base_reachable_time);
return val;
}
-void ndisc_verify_reachability(struct neighbour * neigh);
-
unsigned int ndisc_hash(void *primary_key)
{
struct in6_addr *addr = (struct in6_addr *) primary_key;
@@ -152,8 +149,7 @@
* periodicly compute ReachableTime from random function
*/
- if ((now - last_rand) > REACH_RANDOM_INTERVAL)
- {
+ if ((now - last_rand) > REACH_RANDOM_INTERVAL) {
last_rand = now;
nd_reachable_time = rand_reach_time();
}
@@ -161,13 +157,10 @@
neigh_table_lock(&nd_tbl);
start_bh_atomic();
- if (nd_tbl.tbl_lock == 1)
- {
+ if (nd_tbl.tbl_lock == 1) {
ntbl_walk_table(&nd_tbl, ndisc_gc_func, 0, 0, NULL);
ndisc_gc_timer.expires = now + nd_gc_interval;
- }
- else
- {
+ } else {
#if ND_DEBUG >= 2
printk(KERN_DEBUG "ndisc_gc delayed: table locked\n");
#endif
@@ -185,8 +178,7 @@
struct nd_neigh *ndn = (struct nd_neigh *) neigh;
unsigned long now = jiffies;
- if (ndn->ndn_refcnt == 0)
- {
+ if (ndn->ndn_refcnt == 0) {
switch (ndn->ndn_nud_state) {
case NUD_REACHABLE:
@@ -196,7 +188,7 @@
case NUD_FAILED:
return 1;
default:
- }
+ };
}
return 0;
}
@@ -209,9 +201,7 @@
ndn->ndn_expires = now + timer;
if (del_timer(&ndisc_timer))
- {
tval = ndisc_timer.expires;
- }
tval = min(tval, ndn->ndn_expires);
@@ -222,17 +212,15 @@
static void ndisc_del_timer(struct nd_neigh *ndn)
{
unsigned long tval = ~0UL;
-
- if (!(ndn->ndn_nud_state & NUD_IN_TIMER))
- return;
+ unsigned long neigh_val;
if (del_timer(&ndisc_timer))
- {
tval = ndisc_timer.expires;
- }
-
- if (tval == ndn->ndn_expires)
- {
+
+ neigh_val = ndn->ndn_expires;
+ ndn->ndn_expires = 0;
+
+ if (tval == neigh_val) {
int i;
tval = ~0UL;
@@ -240,27 +228,24 @@
neigh_table_lock(&nd_tbl);
/* need to search the entire neighbour cache */
- for (i=0; i < nd_tbl.tbl_size; i++)
- {
+ for (i=0; i < nd_tbl.tbl_size; i++) {
struct neighbour *neigh, *head;
head = nd_tbl.hash_buckets[i];
if ((neigh = head) == NULL)
continue;
-
- do
- {
+
+ do {
struct nd_neigh *n;
n = (struct nd_neigh *) neigh;
- if (n->ndn_nud_state & NUD_IN_TIMER)
- {
+ if ((n->ndn_nud_state & NUD_IN_TIMER) &&
+ n->ndn_expires)
tval = min(tval, n->ndn_expires);
- }
-
+
neigh = neigh->next;
-
+
} while (neigh != head);
}
neigh_table_unlock(&nd_tbl);
@@ -277,12 +262,9 @@
{
struct nd_neigh *ndn = (struct nd_neigh *) neigh;
- if (ndn->ndn_refcnt == 0)
- {
+ if (ndn->ndn_refcnt == 0) {
if (ndn->ndn_nud_state & NUD_IN_TIMER)
- {
ndisc_del_timer(ndn);
- }
return 1;
}
@@ -294,20 +276,26 @@
{
struct nd_neigh *ndn;
+ NDBG(("ndisc_new_neigh("));
+ if(dev)
+ NDBG(("%s,", dev->name));
+ else
+ NDBG(("[NULL],"));
+ NDBG(("[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]): ",
+ addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+ addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+ addr->s6_addr16[6], addr->s6_addr16[7]));
+
ndn = (struct nd_neigh *) neigh_alloc(sizeof(struct nd_neigh),
- GFP_ATOMIC);
-
- if (ndn == NULL)
- {
+ &nd_neigh_ops);
+ if (ndn == NULL) {
#if ND_DEBUG >= 2
printk(KERN_DEBUG "neigh_alloc: out of memory\n");
#endif
start_bh_atomic();
- if (nd_tbl.tbl_lock == 1)
- {
-
+ if (nd_tbl.tbl_lock == 1) {
#if ND_DEBUG >= 2
printk(KERN_DEBUG "ndisc_alloc: forcing gc\n");
#endif
@@ -329,12 +317,19 @@
ndn->ndn_dev = dev;
ndn->ndn_tstamp = jiffies;
- if (dev->type == ARPHRD_LOOPBACK || dev->type == ARPHRD_SIT)
- {
+ if ((ndn->ndn_type & IPV6_ADDR_MULTICAST)) {
+ NDBG(("MULTICAST(NCF_NOARP) "));
+ ndn->ndn_flags |= NCF_NOARP;
+ }
+
+ if (dev->type == ARPHRD_LOOPBACK || dev->type == ARPHRD_SIT) {
+ NDBG(("%s(NCF_NOARP) ",
+ (dev->type==ARPHRD_LOOPBACK) ? "LOOPBACK" : "SIT"));
ndn->ndn_flags |= NCF_NOARP;
}
neigh_insert(&nd_tbl, (struct neighbour *) ndn);
+ NDBG(("returning ndn(%p)\n", ndn));
return ndn;
}
@@ -353,10 +348,9 @@
* cached information about nexthop and addr resolution
*/
- if (dev == NULL)
- {
+ if (dev == NULL) {
#if ND_DEBUG >= 1
- printk(KERN_DEBUG "ncache_get_neigh: NULL device\n");
+ printk(KERN_DEBUG "ndisc_get_neigh: NULL device\n");
#endif
return NULL;
}
@@ -365,10 +359,7 @@
neigh = (struct nd_neigh *) neigh_lookup(&nd_tbl, (void *) addr,
sizeof(struct in6_addr), dev);
-
-
- if (neigh == NULL)
- {
+ if (neigh == NULL) {
neigh = ndisc_new_neigh(dev, addr);
if (neigh == NULL)
@@ -377,9 +368,7 @@
neigh_table_unlock(&nd_tbl);
- atomic_inc(&neigh->ndn_refcnt);
-
- return (struct neighbour *) neigh;
+ return neighbour_clone((struct neighbour *) neigh);
}
/*
@@ -388,45 +377,35 @@
* 1 - Address Resolution unfinished / packet queued
*/
-int ndisc_eth_resolv(unsigned char *h_dest, struct device *dev,
- struct sk_buff *skb)
+int ndisc_eth_resolv(unsigned char *h_dest, struct sk_buff *skb)
{
- struct nd_neigh *ndn;
+ struct nd_neigh *ndn = NULL;
- ndn = (struct nd_neigh *) skb->nexthop;
-
- if (ndn == NULL)
- {
- struct in6_addr *daddr;
- int addr_type;
-
- daddr = &skb->nh.ipv6h->daddr;
-
- addr_type = ipv6_addr_type(daddr);
-
- if (addr_type & IPV6_ADDR_MULTICAST)
- {
- ipv6_mc_map(daddr, h_dest);
- return 0;
- }
+ if (skb->dst)
+ ndn = (struct nd_neigh *) skb->dst->neighbour;
+ if (ndn == NULL) {
#if ND_DEBUG >= 2
printk(KERN_DEBUG "ndisc_eth_resolv: nexthop is NULL\n");
#endif
goto discard;
}
- if (skb->pkt_type == PACKET_NDISC)
- goto ndisc_pkt;
-
+ if ((ndn->ndn_type & IPV6_ADDR_MULTICAST)) {
+ struct in6_addr *daddr;
+
+ daddr = &skb->nh.ipv6h->daddr;
+ ipv6_mc_map(daddr, h_dest);
+ return 0;
+ }
+
switch (ndn->ndn_nud_state) {
case NUD_FAILED:
case NUD_NONE:
ndisc_event_send((struct neighbour *)ndn, skb);
case NUD_INCOMPLETE:
- if (skb_queue_len(&ndn->neigh.arp_queue) >= NDISC_QUEUE_LEN)
- {
+ if (skb_queue_len(&ndn->neigh.arp_queue) >= NDISC_QUEUE_LEN) {
struct sk_buff *buff;
buff = ndn->neigh.arp_queue.prev;
@@ -437,18 +416,15 @@
return 1;
default:
ndisc_event_send((struct neighbour *)ndn, skb);
- }
+ };
- ndisc_pkt:
-
- if ((ndn->ndn_flags & NTF_COMPLETE) == 0)
- {
+ if ((ndn->ndn_flags & NTF_COMPLETE) == 0) {
#if ND_DEBUG >=1
/* This shouldn't happen */
printk(KERN_DEBUG "ND: using incomplete entry\n");
#endif
}
- memcpy(h_dest, ndn->ndn_ha, dev->addr_len);
+ memcpy(h_dest, ndn->ndn_ha, skb->dev->addr_len);
return 0;
discard:
@@ -457,14 +433,12 @@
return 1;
}
-
/*
* Send a Neighbour Advertisement
*/
void ndisc_send_na(struct device *dev, struct nd_neigh *ndn,
- struct in6_addr *daddr,
- struct in6_addr *solicited_addr,
+ struct in6_addr *daddr, struct in6_addr *solicited_addr,
int router, int solicited, int override, int inc_opt)
{
struct sock *sk = ndisc_socket->sk;
@@ -473,38 +447,64 @@
struct sk_buff *skb;
int err;
+ NDBG(("ndisc_send_na("));
+ if(dev)
+ NDBG(("%s,", dev->name));
+ else
+ NDBG(("[NULL]"));
+ NDBG(("%p): ", ndn));
+ if(daddr)
+ NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+ daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+ daddr->s6_addr16[6], daddr->s6_addr16[7]));
+ if(solicited_addr)
+ NDBG(("solicit_addr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ solicited_addr->s6_addr16[0], solicited_addr->s6_addr16[1],
+ solicited_addr->s6_addr16[2], solicited_addr->s6_addr16[3],
+ solicited_addr->s6_addr16[4], solicited_addr->s6_addr16[5],
+ solicited_addr->s6_addr16[6], solicited_addr->s6_addr16[7]));
+ NDBG(("rtr(%d)sol(%d)ovr(%d)iopt(%d)\n", router, solicited, override, inc_opt));
+
opt_len = ((dev->addr_len + 1) >> 3) + 1;
- len = sizeof(struct icmpv6hdr) + sizeof(struct in6_addr);
+ len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+#if ND_DEBUG >=1
+ if (dev == NULL) {
+ printk(KERN_DEBUG "send_na: null device\n");
+ return;
+ }
+#endif
if (inc_opt)
- {
len += opt_len << 3;
- }
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
- if (skb == NULL)
- {
+ if (skb == NULL) {
printk(KERN_DEBUG "send_na: alloc skb failed\n");
return;
}
-
- if (ipv6_bld_hdr_2(sk, skb, dev, (struct neighbour *) ndn,
- solicited_addr, daddr, IPPROTO_ICMPV6, len) < 0)
- {
- kfree_skb(skb, FREE_WRITE);
- printk(KERN_DEBUG
- "ndisc_send_na: ipv6_build_header returned < 0\n");
- return;
+ /*
+ * build the MAC header
+ */
+
+ if (dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ dev->hard_header(skb, dev, ETH_P_IPV6, ndn->ndn_ha,
+ NULL, len);
+ skb->arp = 1;
+ }
}
- skb->pkt_type = PACKET_NDISC;
-
+ ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len);
+
msg = (struct nd_msg *) skb_put(skb, len);
- msg->icmph.type = NDISC_NEIGHBOUR_ADVERTISEMENT;
- msg->icmph.code = 0;
- msg->icmph.checksum = 0;
+ msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
+ msg->icmph.icmp6_code = 0;
+ msg->icmph.icmp6_cksum = 0;
msg->icmph.icmp6_unused = 0;
msg->icmph.icmp6_router = router;
@@ -514,47 +514,66 @@
/* Set the target address. */
ipv6_addr_copy(&msg->target, solicited_addr);
- if (inc_opt)
- {
+ if (inc_opt) {
/* Set the source link-layer address option. */
msg->opt.opt_type = ND_OPT_TARGET_LL_ADDR;
msg->opt.opt_len = opt_len;
memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
- if ((opt_len << 3) - (2 + dev->addr_len))
- {
+ if ((opt_len << 3) - (2 + dev->addr_len)) {
memset(msg->opt.link_addr + dev->addr_len, 0,
(opt_len << 3) - (2 + dev->addr_len));
}
}
/* checksum */
- msg->icmph.checksum = csum_ipv6_magic(solicited_addr, daddr, len,
- IPPROTO_ICMPV6,
- csum_partial((__u8 *) msg,
- len, 0));
+ msg->icmph.icmp6_cksum = csum_ipv6_magic(solicited_addr, daddr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) msg,
+ len, 0));
- ipv6_queue_xmit(sk, skb->dev, skb, 1);
+ dev_queue_xmit(skb);
}
void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
struct in6_addr *solicit,
struct in6_addr *daddr, struct in6_addr *saddr)
{
+ unsigned char ha[MAX_ADDR_LEN];
struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
struct nd_msg *msg;
- int len, opt_len;
+ int len, opt_len;
+ void *h_dest;
int err;
+ NDBG(("ndisc_send_ns(%s,%p): ", (dev ? dev->name : "[NULL]"), neigh));
+ if(daddr)
+ NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+ daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+ daddr->s6_addr16[6], daddr->s6_addr16[7]));
+ if(saddr)
+ NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+ saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+ saddr->s6_addr16[6], saddr->s6_addr16[7]));
+ if(solicit)
+ NDBG(("solicit[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ solicit->s6_addr16[0], solicit->s6_addr16[1],
+ solicit->s6_addr16[2], solicit->s6_addr16[3],
+ solicit->s6_addr16[4], solicit->s6_addr16[5],
+ solicit->s6_addr16[6], solicit->s6_addr16[7]));
+ NDBG(("\n"));
+
/* length of addr in 8 octet groups.*/
opt_len = ((dev->addr_len + 1) >> 3) + 1;
- len = sizeof(struct icmpv6hdr) + sizeof(struct in6_addr) +
+ len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr) +
(opt_len << 3);
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
- if (skb == NULL)
- {
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+ if (skb == NULL) {
#if ND_DEBUG >= 1
printk(KERN_DEBUG "send_ns: alloc skb failed\n");
#endif
@@ -563,41 +582,47 @@
skb->pkt_type = PACKET_NDISC;
- if (saddr == NULL)
- {
+ if (saddr == NULL) {
struct inet6_ifaddr *ifa;
/* use link local address */
ifa = ipv6_get_lladdr(dev);
if (ifa)
- {
saddr = &ifa->addr;
- }
}
- if(ipv6_addr_type(daddr) == IPV6_ADDR_MULTICAST)
- {
+ if ((ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST)) {
nd_stats.snt_probes_mcast++;
- }
- else
- {
+ ipv6_mc_map(daddr, ha);
+ h_dest = ha;
+ } else {
+ if (neigh == NULL) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "send_ns: ucast destination "
+ "with null neighbour\n");
+#endif
+ return;
+ }
+ h_dest = neigh->ha;
nd_stats.snt_probes_ucast++;
}
- if (ipv6_bld_hdr_2(sk, skb, dev, neigh, saddr, daddr, IPPROTO_ICMPV6,
- len) < 0 )
- {
- kfree_skb(skb, FREE_WRITE);
- printk(KERN_DEBUG
- "ndisc_send_ns: ipv6_build_header returned < 0\n");
- return;
+ if (dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ dev->hard_header(skb, dev, ETH_P_IPV6, h_dest, NULL,
+ len);
+ skb->arp = 1;
+ }
}
+
+ ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
msg = (struct nd_msg *)skb_put(skb, len);
- msg->icmph.type = NDISC_NEIGHBOUR_SOLICITATION;
- msg->icmph.code = 0;
- msg->icmph.checksum = 0;
+ msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
+ msg->icmph.icmp6_code = 0;
+ msg->icmph.icmp6_cksum = 0;
msg->icmph.icmp6_unused = 0;
/* Set the target address. */
@@ -609,20 +634,19 @@
memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
- if ((opt_len << 3) - (2 + dev->addr_len))
- {
+ if ((opt_len << 3) - (2 + dev->addr_len)) {
memset(msg->opt.link_addr + dev->addr_len, 0,
(opt_len << 3) - (2 + dev->addr_len));
}
/* checksum */
- msg->icmph.checksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,
- daddr, len,
- IPPROTO_ICMPV6,
- csum_partial((__u8 *) msg,
- len, 0));
+ msg->icmph.icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,
+ daddr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) msg,
+ len, 0));
/* send it! */
- ipv6_queue_xmit(sk, skb->dev, skb, 1);
+ dev_queue_xmit(skb);
}
void ndisc_send_rs(struct device *dev, struct in6_addr *saddr,
@@ -630,35 +654,52 @@
{
struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
- struct icmpv6hdr *hdr;
+ struct icmp6hdr *hdr;
__u8 * opt;
int len, opt_len;
int err;
+ NDBG(("ndisc_send_rs(%s): ", (dev ? dev->name : "[NULL]")));
+ if(daddr)
+ NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+ daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+ daddr->s6_addr16[6], daddr->s6_addr16[7]));
+ if(saddr)
+ NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+ saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+ saddr->s6_addr16[6], saddr->s6_addr16[7]));
+ NDBG(("\n"));
+
/* length of addr in 8 octet groups.*/
opt_len = ((dev->addr_len + 1) >> 3) + 1;
- len = sizeof(struct icmpv6hdr) + (opt_len << 3);
+ len = sizeof(struct icmp6hdr) + (opt_len << 3);
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
- if (skb == NULL)
- {
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+ if (skb == NULL) {
printk(KERN_DEBUG "send_ns: alloc skb failed\n");
return;
}
- if (ipv6_bld_hdr_2(sk, skb, dev, NULL, saddr, daddr, IPPROTO_ICMPV6,
- len) < 0 )
- {
- kfree_skb(skb, FREE_WRITE);
- printk(KERN_DEBUG
- "ndisc_send_ns: ipv6_build_header returned < 0\n");
- return;
- }
+ if (dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ unsigned char ha[MAX_ADDR_LEN];
+
+ ipv6_mc_map(daddr, ha);
+ dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, len);
+ skb->arp = 1;
+ }
+ }
+
+ ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
- hdr = (struct icmpv6hdr *) skb_put(skb, len);
- hdr->type = NDISC_ROUTER_SOLICITATION;
- hdr->code = 0;
- hdr->checksum = 0;
+ hdr = (struct icmp6hdr *) skb_put(skb, len);
+ hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
+ hdr->icmp6_code = 0;
+ hdr->icmp6_cksum = 0;
hdr->icmp6_unused = 0;
opt = (u8*) (hdr + 1);
@@ -669,27 +710,25 @@
memcpy(opt + 2, dev->dev_addr, dev->addr_len);
- if ((opt_len << 3) - (2 + dev->addr_len))
- {
+ if ((opt_len << 3) - (2 + dev->addr_len)) {
memset(opt + 2 + dev->addr_len, 0,
(opt_len << 3) - (2 + dev->addr_len));
}
/* checksum */
- hdr->checksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,
- IPPROTO_ICMPV6,
- csum_partial((__u8 *) hdr, len, 0));
+ hdr->icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) hdr, len, 0));
/* send it! */
- ipv6_queue_xmit(sk, skb->dev, skb, 1);
+ dev_queue_xmit(skb);
}
static int ndisc_store_hwaddr(struct nd_neigh *ndn, __u8 *opt, int opt_len,
int option)
{
- while (*opt != option && opt_len)
- {
+ while (*opt != option && opt_len) {
int len;
len = opt[1] << 3;
@@ -704,8 +743,7 @@
opt_len -= len;
}
- if (*opt == option)
- {
+ if (*opt == option) {
memcpy(ndn->neigh.ha, opt + 2, ndn->ndn_dev->addr_len);
return 0;
}
@@ -723,40 +761,32 @@
neigh_table_lock(&nd_tbl);
- for (i=0; i < nd_tbl.tbl_size; i++)
- {
+ for (i=0; i < nd_tbl.tbl_size; i++) {
struct nd_neigh *ndn, *head;
head = (struct nd_neigh *) nd_tbl.hash_buckets[i];
if ((ndn = head) == NULL)
continue;
-
- do
- {
- if (ndn->ndn_nud_state & NUD_IN_TIMER)
- {
+
+ do {
+ if (ndn->ndn_nud_state & NUD_IN_TIMER) {
unsigned long time;
time = ndn->ndn_expires - now;
if ((long) time <= 0)
- {
time = ndisc_event_timer(ndn);
- }
if (time)
- {
ntimer = min(ntimer, time);
- }
}
ndn = (struct nd_neigh *) ndn->neigh.next;
} while (ndn != head);
}
- if (ntimer != (~0UL))
- {
+ if (ntimer != (~0UL)) {
ndisc_timer.expires = now + ntimer;
add_timer(&ndisc_timer);
}
@@ -774,23 +804,20 @@
int max_probes;
if (ndn->ndn_nud_state == NUD_DELAY)
- {
ndn->ndn_nud_state = NUD_PROBE;
- }
- max_probes = (ndn->ndn_nud_state == NUD_PROBE ? nd_max_unicast_solicit:
- nd_max_multicast_solicit);
+ max_probes = (ndn->ndn_nud_state == NUD_PROBE ?
+ ipv6_config.nd_max_ucast_solicit:
+ ipv6_config.nd_max_mcast_solicit);
- if (ndn->ndn_probes == max_probes)
- {
+ if (ndn->ndn_probes == max_probes) {
struct sk_buff *skb;
ndn->ndn_nud_state = NUD_FAILED;
ndn->ndn_flags &= ~NTF_COMPLETE;
nd_stats.res_failed++;
- while((skb=skb_dequeue(&ndn->neigh.arp_queue)))
- {
+ while((skb=skb_dequeue(&ndn->neigh.arp_queue))) {
/*
* "The sender MUST return an ICMP
* destination unreachable"
@@ -808,20 +835,17 @@
dev = ndn->ndn_dev;
target = &ndn->ndn_addr;
- if (ndn->ndn_nud_state == NUD_INCOMPLETE)
- {
+ if (ndn->ndn_nud_state == NUD_INCOMPLETE) {
addrconf_addr_solict_mult(&ndn->ndn_addr, &mcaddr);
daddr = &mcaddr;
ndn = NULL;
- }
- else
- {
+ } else {
daddr = &ndn->ndn_addr;
}
ndisc_send_ns(dev, (struct neighbour *) ndn, target, daddr, NULL);
- return nd_retrans_timer;
+ return ipv6_config.nd_retrans_time;
}
void ndisc_event_send(struct neighbour *neigh, struct sk_buff *skb)
@@ -830,14 +854,15 @@
struct in6_addr daddr;
unsigned long now = jiffies;
struct in6_addr *saddr = NULL;
-
+
+ if ((ndn->ndn_flags & NCF_NOARP))
+ return;
+
switch (ndn->ndn_nud_state) {
case NUD_FAILED:
ndn->ndn_probes = 0;
case NUD_NONE:
-
- if (skb && !skb->stamp.tv_sec)
- {
+ if (skb && !skb->stamp.tv_sec) {
/*
* skb->stamp allows us to know if we are
* originating the skb or forwarding it.
@@ -850,7 +875,7 @@
addrconf_addr_solict_mult(&ndn->ndn_addr, &daddr);
ndisc_send_ns(ndn->ndn_dev, NULL, &ndn->ndn_addr, &daddr,
saddr);
- ndisc_add_timer(ndn, nd_retrans_timer);
+ ndisc_add_timer(ndn, ipv6_config.nd_retrans_time);
break;
@@ -860,7 +885,7 @@
case NUD_STALE:
ndn->ndn_nud_state = NUD_DELAY;
- ndisc_add_timer(ndn, nd_delay_first_probe);
+ ndisc_add_timer(ndn, ipv6_config.nd_delay_probe_time);
}
}
@@ -872,26 +897,21 @@
{
struct sk_buff *skb;
+ NDBG(("ndisc_event_na(%p,%p,%d,%d,%d)\n", ndn, opt, opt_len,
+ solicited, override));
+
if (ndn->ndn_nud_state == NUD_NONE)
- {
ndn->ndn_nud_state = NUD_INCOMPLETE;
- }
- if (ndn->ndn_nud_state == NUD_INCOMPLETE || override)
- {
- if (opt_len == 0)
- {
+ if (ndn->ndn_nud_state == NUD_INCOMPLETE || override) {
+ if (opt_len == 0) {
printk(KERN_DEBUG "no opt on NA\n");
- }
- else
- {
- /* record hardware address */
-
+ } else {
+ /* Record hardware address. */
ndn->ndn_flags |= NTF_COMPLETE;
if (ndisc_store_hwaddr(ndn, opt, opt_len,
- ND_OPT_TARGET_LL_ADDR))
- {
+ ND_OPT_TARGET_LL_ADDR)) {
#if ND_DEBUG >= 2
printk(KERN_DEBUG
"event_na: invalid TARGET_LL_ADDR\n");
@@ -903,42 +923,40 @@
}
}
-
- if (solicited || override || ndn->ndn_nud_state == NUD_INCOMPLETE)
- {
-
+ if (solicited || override || ndn->ndn_nud_state == NUD_INCOMPLETE) {
ndn->ndn_probes = 0;
ndn->ndn_tstamp = jiffies;
if (ndn->ndn_nud_state & NUD_IN_TIMER)
- {
ndisc_del_timer(ndn);
- }
if (solicited)
- {
ndn->ndn_nud_state = NUD_REACHABLE;
- }
else
- {
ndn->ndn_nud_state = NUD_STALE;
- }
}
while ((skb=skb_dequeue(&ndn->neigh.arp_queue)))
- {
dev_queue_xmit(skb);
- }
}
-static void ndisc_event_ns(struct in6_addr *saddr, struct sk_buff *skb)
+static struct nd_neigh * ndisc_event_ns(struct in6_addr *saddr,
+ struct sk_buff *skb)
{
struct nd_neigh *ndn;
u8 *opt;
int len;
+ NDBG(("ndisc_event_ns: "));
+ if(saddr)
+ NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+ saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+ saddr->s6_addr16[6], saddr->s6_addr16[7]));
+ NDBG(("\n"));
+
opt = skb->h.raw;
- opt += sizeof(struct icmpv6hdr) + sizeof(struct in6_addr);
+ opt += sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
len = skb->tail - opt;
@@ -949,124 +967,48 @@
skb->dev);
if (ndn == NULL)
- {
ndn = ndisc_new_neigh(skb->dev, saddr);
- }
neigh_table_unlock(&nd_tbl);
+ if (ndn == NULL)
+ return NULL;
+
switch(ndn->ndn_nud_state) {
- case NUD_REACHABLE:
- case NUD_STALE:
- case NUD_DELAY:
- if (*opt != ND_OPT_SOURCE_LL_ADDR ||
- len != ndn->ndn_dev->addr_len ||
- memcmp(ndn->neigh.ha, opt + 2, len))
- {
- break;
- }
+ case NUD_REACHABLE:
+ case NUD_STALE:
+ case NUD_DELAY:
+ if (*opt != ND_OPT_SOURCE_LL_ADDR ||
+ len != ndn->ndn_dev->addr_len ||
+ memcmp(ndn->neigh.ha, opt + 2, len))
+ break;
- if (ndn->ndn_nud_state & NUD_IN_TIMER)
- {
- ndisc_del_timer(ndn);
- }
- default:
- ndn->ndn_flags |= NTF_COMPLETE;
+ if (ndn->ndn_nud_state & NUD_IN_TIMER)
+ ndisc_del_timer(ndn);
+
+ /* FALLTHROUGH */
+ default:
+ ndn->ndn_flags |= NTF_COMPLETE;
- if (ndisc_store_hwaddr(ndn, opt, len,
- ND_OPT_SOURCE_LL_ADDR))
- {
+ if (ndisc_store_hwaddr(ndn, opt, len, ND_OPT_SOURCE_LL_ADDR)) {
#if ND_DEBUG >= 1
- printk(KERN_DEBUG
- "event_ns: invalid SOURCE_LL_ADDR\n");
+ printk(KERN_DEBUG
+ "event_ns: invalid SOURCE_LL_ADDR\n");
#endif
- ndn->ndn_flags &= ~NTF_COMPLETE;
- ndn->ndn_nud_state = NUD_NONE;
- return;
- }
-
- ndn->ndn_nud_state = NUD_STALE;
- ndn->ndn_tstamp = jiffies;
- ndn->ndn_probes = 0;
- }
-
-}
-
-static struct rt6_info *ndisc_get_dflt_router(struct device *dev,
- struct in6_addr *addr)
-{
- struct rt6_info *iter;
-
- for (iter = default_rt_list; iter; iter=iter->next)
- {
- if (dev == iter->rt_dev &&
- ipv6_addr_cmp(&iter->rt_dst, addr) == 0)
- {
- return iter;
+ ndn->ndn_flags &= ~NTF_COMPLETE;
+ ndn->ndn_nud_state = NUD_NONE;
+ return ndn;
}
- }
- return NULL;
-}
-
-static void ndisc_add_dflt_router(struct rt6_info *rt)
-{
- struct rt6_info *iter;
- rt->rt_ref++;
- rt->fib_node = &routing_table;
- rt6_stats.fib_rt_alloc++;
-
- if (default_rt_list == NULL)
- {
- default_rt_list = rt;
- return;
- }
-
- for (iter = default_rt_list; iter->next; iter=iter->next)
- ;
-
- iter->next = rt;
-}
-
-static void ndisc_del_dflt_router(struct rt6_info *rt)
-{
- struct rt6_info *iter, *back;
-
- if (rt == default_rt_list)
- {
- default_rt_list = rt->next;
- }
- else
- {
- back = NULL;
- for (iter = default_rt_list; iter; iter=iter->next)
- {
- if (iter == rt)
- {
- back->next = rt->next;
- break;
- }
- back = iter;
- }
- }
+ ndn->ndn_nud_state = NUD_STALE;
+ ndn->ndn_tstamp = jiffies;
+ ndn->ndn_probes = 0;
+ };
- rt->fib_node = NULL;
- rt_release(rt);
+ return ndn;
}
-static void ndisc_purge_dflt_routers(void)
-{
- struct rt6_info *iter, *rt;
-
- for (iter = default_rt_list; iter; )
- {
- rt = iter;
- iter=iter->next;
- rt_release(rt);
- }
- default_rt_list = NULL;
-}
static void ndisc_ll_addr_update(struct nd_neigh *ndn, u8* opt, int len,
int type)
@@ -1077,19 +1019,14 @@
case NUD_DELAY:
if (len == ndn->ndn_dev->addr_len &&
memcmp(ndn->neigh.ha, opt + 2, len) == 0)
- {
break;
- }
if (ndn->ndn_nud_state & NUD_IN_TIMER)
- {
ndisc_del_timer(ndn);
- }
default:
ndn->ndn_flags |= NTF_COMPLETE;
- if (ndisc_store_hwaddr(ndn, opt, len, type))
- {
+ if (ndisc_store_hwaddr(ndn, opt, len, type)) {
#if ND_DEBUG >=1
printk(KERN_DEBUG "NDISC: invalid LL_ADDR\n");
#endif
@@ -1101,45 +1038,7 @@
ndn->ndn_nud_state = NUD_STALE;
ndn->ndn_tstamp = jiffies;
ndn->ndn_probes = 0;
- }
-
-}
-
-struct rt6_info * dflt_rt_lookup(void)
-{
- struct rt6_info *match = NULL;
- struct rt6_info *rt;
- int score = -1;
- unsigned long now = jiffies;
-
- for (rt = default_rt_list; rt; rt=rt->next)
- {
- struct neighbour *neigh = rt->rt_nexthop;
- struct nd_neigh *ndn = (struct nd_neigh *) neigh;
-
- if (score < 0)
- {
- score = 0;
- match = rt;
- }
-
- if (ndn->ndn_nud_state == NUD_REACHABLE)
- {
- if (score < 1)
- {
- score = 1;
- match = rt;
- }
-
- if (now - ndn->ndn_tstamp < nd_reachable_time)
- {
- return rt;
- }
- }
-
- }
-
- return match;
+ };
}
static void ndisc_router_discovery(struct sk_buff *skb)
@@ -1153,10 +1052,11 @@
__u8 * opt = (__u8 *)(ra_msg + 1);
+ NDBG(("ndisc_router_discovery(%p)\n", skb));
+
optlen = (skb->tail - skb->h.raw) - sizeof(struct ra_msg);
- if (skb->nh.ipv6h->hop_limit != 255)
- {
+ if (skb->nh.ipv6h->hop_limit != 255) {
printk(KERN_INFO
"NDISC: fake router advertisment received\n");
return;
@@ -1167,14 +1067,12 @@
*/
in6_dev = ipv6_get_idev(skb->dev);
- if (in6_dev == NULL)
- {
+ if (in6_dev == NULL) {
printk(KERN_DEBUG "RA: can't find in6 device\n");
return;
}
- if (in6_dev->if_flags & IF_RS_SENT)
- {
+ if (in6_dev->if_flags & IF_RS_SENT) {
/*
* flag that an RA was received after an RS was sent
* out on this interface.
@@ -1184,94 +1082,57 @@
lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
- rt = ndisc_get_dflt_router(skb->dev, &skb->nh.ipv6h->saddr);
+ rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
- if (rt && lifetime == 0)
- {
- ndisc_del_dflt_router(rt);
+ if (rt && lifetime == 0) {
+ ip6_del_rt(rt);
rt = NULL;
}
- if (rt == NULL && lifetime)
- {
- struct in6_addr *saddr;
-
+ if (rt == NULL && lifetime) {
#if ND_DEBUG >= 2
- printk(KERN_DEBUG "ndisc_rdisc: new default router\n");
+ printk(KERN_DEBUG "ndisc_rdisc: adding default router\n");
#endif
- rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
- GFP_ATOMIC);
+ rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
- if (rt == NULL)
- {
- /* We are out-of-memory. Ignore it */
+ if (rt == NULL) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "route_add failed\n");
+#endif
return;
}
- saddr = &skb->nh.ipv6h->saddr;
- neigh_table_lock(&nd_tbl);
-
- ndn = (struct nd_neigh *) neigh_lookup(&nd_tbl, saddr,
- sizeof(struct in6_addr),
- skb->dev);
-
- if (ndn == NULL)
- {
- ndn = ndisc_new_neigh(skb->dev, saddr);
-
- if (ndn == NULL)
- {
- kfree(rt);
- neigh_table_unlock(&nd_tbl);
- return;
- }
+ ndn = (struct nd_neigh *) rt->rt6i_nexthop;
+ if (ndn == NULL) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "nd: add default router: null "
+ "neighbour\n");
+#endif
+ return;
}
-
- neigh_table_unlock(&nd_tbl);
-
- atomic_inc(&ndn->ndn_refcnt);
-
ndn->ndn_flags |= NCF_ROUTER;
-
- memset(rt, 0, sizeof(struct rt6_info));
-
- ipv6_addr_copy(&rt->rt_dst, &skb->nh.ipv6h->saddr);
- rt->rt_metric = 1;
- rt->rt_flags = RTF_GATEWAY | RTF_DYNAMIC;
- rt->rt_dev = skb->dev;
- rt->rt_nexthop = (struct neighbour *) ndn;
-
- ndisc_add_dflt_router(rt);
}
if (rt)
- {
- rt->rt_expires = jiffies + (HZ * lifetime);
- }
+ rt->rt6i_expires = jiffies + (HZ * lifetime);
if (ra_msg->icmph.icmp6_hop_limit)
- {
- ipv6_hop_limit = ra_msg->icmph.icmp6_hop_limit;
- }
+ ipv6_config.hop_limit = ra_msg->icmph.icmp6_hop_limit;
/*
* Update Reachable Time and Retrans Timer
*/
if (ra_msg->retrans_timer)
- {
- nd_retrans_timer = ntohl(ra_msg->retrans_timer);
- }
+ ipv6_config.nd_retrans_time = ntohl(ra_msg->retrans_timer);
- if (ra_msg->reachable_time)
- {
+ if (ra_msg->reachable_time) {
__u32 rtime = ntohl(ra_msg->reachable_time);
- if (rtime != nd_base_reachable_time)
- {
- nd_base_reachable_time = rtime;
- nd_gc_staletime = 3 * nd_base_reachable_time;
+ if (rtime != ipv6_config.nd_base_reachable_time) {
+ ipv6_config.nd_base_reachable_time = rtime;
+ nd_gc_staletime = 3 * rtime;
nd_reachable_time = rand_reach_time();
}
@@ -1286,22 +1147,22 @@
len = (opt[1] << 3);
- if (len == 0)
- {
+ if (len == 0) {
printk(KERN_DEBUG "RA: opt has 0 len\n");
break;
}
switch(*opt) {
case ND_OPT_SOURCE_LL_ADDR:
-
+
if (rt == NULL)
break;
- ndn = (struct nd_neigh *) rt->rt_nexthop;
+ ndn = (struct nd_neigh *) rt->rt6i_nexthop;
- ndisc_ll_addr_update(ndn, opt, len,
- ND_OPT_SOURCE_LL_ADDR);
+ if (ndn)
+ ndisc_ll_addr_update(ndn, opt, len,
+ ND_OPT_SOURCE_LL_ADDR);
break;
case ND_OPT_PREFIX_INFO:
@@ -1309,17 +1170,17 @@
break;
case ND_OPT_MTU:
-
- if (rt)
- {
+ if (rt) {
int mtu;
struct device *dev;
mtu = htonl(*(__u32 *)(opt+4));
- dev = rt->rt_nexthop->dev;
+ dev = rt->rt6i_dev;
- if (mtu < 576)
- {
+ if (dev == NULL)
+ break;
+
+ if (mtu < 576) {
printk(KERN_DEBUG "NDISC: router "
"announcement with mtu = %d\n",
mtu);
@@ -1327,13 +1188,9 @@
}
if (dev->change_mtu)
- {
dev->change_mtu(dev, mtu);
- }
else
- {
dev->mtu = mtu;
- }
}
break;
@@ -1343,32 +1200,32 @@
break;
default:
printk(KERN_DEBUG "unkown option in RA\n");
- }
+ };
optlen -= len;
opt += len;
}
-
}
void ndisc_forwarding_on(void)
{
+
/*
- * forwarding was turned on
+ * Forwarding was turned on.
*/
- ndisc_purge_dflt_routers();
+ rt6_purge_dflt_routers(0);
}
void ndisc_forwarding_off(void)
{
/*
- * forwarding was turned off
+ * Forwarding was turned off.
*/
}
static void ndisc_redirect_rcv(struct sk_buff *skb)
{
- struct icmpv6hdr *icmph;
+ struct icmp6hdr *icmph;
struct in6_addr *dest;
struct in6_addr *target; /* new first hop to destination */
struct nd_neigh *ndn;
@@ -1377,75 +1234,66 @@
int optlen;
u8 * opt;
- if (skb->nh.ipv6h->hop_limit != 255)
- {
+ NDBG(("ndisc_redirect_rcv(%p)\n", skb));
+
+ if (skb->nh.ipv6h->hop_limit != 255) {
printk(KERN_WARNING
"NDISC: fake ICMP redirect received\n");
return;
}
- if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL))
- {
+ if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
printk(KERN_WARNING
"ICMP redirect: source address is not linklocal\n");
return;
}
optlen = skb->tail - skb->h.raw;
- optlen -= sizeof(struct icmpv6hdr) + 2 * sizeof(struct in6_addr);
+ optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
- if (optlen < 0)
- {
+ if (optlen < 0) {
printk(KERN_WARNING "ICMP redirect: packet too small\n");
return;
}
- icmph = (struct icmpv6hdr *) skb->h.raw;
+ icmph = (struct icmp6hdr *) skb->h.raw;
target = (struct in6_addr *) (icmph + 1);
dest = target + 1;
- if (ipv6_addr_type(dest) & IPV6_ADDR_MULTICAST)
- {
+ if (ipv6_addr_type(dest) & IPV6_ADDR_MULTICAST) {
printk(KERN_WARNING "ICMP redirect for multicast addr\n");
return;
}
- if (ipv6_addr_cmp(dest, target) == 0)
- {
+ if (ipv6_addr_cmp(dest, target) == 0) {
on_link = 1;
- }
- else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL))
- {
+ } else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL)) {
printk(KERN_WARNING
"ICMP redirect: target address is not linklocal\n");
return;
}
/* passed validation tests */
+ rt = rt6_redirect(dest, &skb->nh.ipv6h->saddr, target, skb->dev,
+ on_link);
- rt = ipv6_rt_redirect(skb->dev, dest, target, on_link);
-
- if (rt == NULL)
- {
+ if (rt == NULL) {
printk(KERN_WARNING "ICMP redirect: no route to host\n");
return;
}
- ndn = (struct nd_neigh *) rt->rt_nexthop;
+ ndn = (struct nd_neigh *) rt->rt6i_nexthop;
opt = (u8 *) (dest + 1);
- while (optlen > 0)
- {
+ while (optlen > 0) {
int len;
len = (opt[1] << 3);
if (*opt == ND_OPT_TARGET_LL_ADDR)
- {
ndisc_ll_addr_update(ndn, opt, len,
ND_OPT_TARGET_LL_ADDR);
- }
opt += len;
optlen -= len;
@@ -1456,12 +1304,13 @@
struct in6_addr *target)
{
struct sock *sk = ndisc_socket->sk;
- int len = sizeof(struct icmpv6hdr) + 2 * sizeof(struct in6_addr);
+ int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
struct sk_buff *buff;
struct nd_neigh *ndn = (struct nd_neigh *) neigh;
struct inet6_ifaddr *ifp;
- struct icmpv6hdr *icmph;
+ struct icmp6hdr *icmph;
struct in6_addr *addrp;
+ struct device *dev;
struct rt6_info *rt;
int ta_len = 0;
u8 *opt;
@@ -1469,19 +1318,25 @@
int err;
int hlen;
- rt = fibv6_lookup(&skb->nh.ipv6h->saddr, skb->dev, 0);
-
- if (rt->rt_flags & RTF_GATEWAY)
- {
+ dev = skb->dev;
+ rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev, 0);
+
+ if (rt == NULL || rt->u.dst.error) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "ndisc_send_redirect: hostunreach\n");
+#endif
+ return;
+ }
+
+ if (rt->rt6i_flags & RTF_GATEWAY) {
#if ND_DEBUG >= 1
printk(KERN_DEBUG "ndisc_send_redirect: not a neighbour\n");
#endif
return;
}
- if (ndn->ndn_nud_state == NUD_REACHABLE)
- {
- ta_len = ((neigh->dev->addr_len + 1) >> 3) + 1;
+ if (ndn->ndn_nud_state == NUD_REACHABLE) {
+ ta_len = ((dev->addr_len + 1) >> 3) + 1;
len += (ta_len << 3);
}
@@ -1489,20 +1344,18 @@
rd_len &= ~0x7;
len += rd_len;
- ifp = ipv6_get_lladdr(skb->dev);
+ ifp = ipv6_get_lladdr(dev);
- if (ifp == NULL)
- {
+ if (ifp == NULL) {
#if ND_DEBUG >= 1
printk(KERN_DEBUG "redirect: no link_local addr for dev\n");
#endif
return;
}
- buff = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
-
- if (buff == NULL)
- {
+ buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+ if (buff == NULL) {
#if ND_DEBUG >= 2
printk(KERN_DEBUG "ndisc_send_redirect: alloc_skb failed\n");
#endif
@@ -1510,17 +1363,23 @@
}
hlen = 0;
- if (skb->dev->hard_header_len)
- {
- hlen = (skb->dev->hard_header_len + 15) & ~15;
- }
- skb_reserve(buff, hlen + sizeof(struct ipv6hdr));
+ if (dev->hard_header_len) {
+ skb_reserve(buff, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ dev->hard_header(buff, dev, ETH_P_IPV6, ndn->ndn_ha,
+ NULL, len);
+ buff->arp = 1;
+ }
+ }
- icmph = (struct icmpv6hdr *) skb_put(buff, len);
+ ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr,
+ IPPROTO_ICMPV6, len);
+
+ icmph = (struct icmp6hdr *) skb_put(buff, len);
- memset(icmph, 0, sizeof(struct icmpv6hdr));
- icmph->type = NDISC_REDIRECT;
+ memset(icmph, 0, sizeof(struct icmp6hdr));
+ icmph->icmp6_type = NDISC_REDIRECT;
/*
* copy target and destination addresses
@@ -1537,8 +1396,7 @@
* include target_address option
*/
- if (ta_len)
- {
+ if (ta_len) {
int zb;
*(opt++) = ND_OPT_TARGET_LL_ADDR;
@@ -1553,8 +1411,7 @@
*/
zb = (neigh->dev->addr_len + 2) & 0x7;
- if (zb)
- {
+ if (zb) {
int comp;
comp = 8 - zb;
@@ -1574,12 +1431,11 @@
memcpy(opt, &skb->nh.ipv6h, rd_len - 8);
- icmph->checksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
- len, IPPROTO_ICMPV6,
- csum_partial((u8 *) icmph, len, 0));
+ icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
+ len, IPPROTO_ICMPV6,
+ csum_partial((u8 *) icmph, len, 0));
- ipv6_xmit(sk, buff, &ifp->addr, &skb->nh.ipv6h->saddr, NULL,
- IPPROTO_ICMPV6);
+ dev_queue_xmit(buff);
}
/* Called by upper layers to validate neighbour cache entries. */
@@ -1587,14 +1443,15 @@
void ndisc_validate(struct neighbour *neigh)
{
struct nd_neigh *ndn = (struct nd_neigh *) neigh;
-
+
+ if (neigh == NULL)
+ return;
+
if (ndn->ndn_nud_state == NUD_INCOMPLETE)
return;
if (ndn->ndn_nud_state == NUD_DELAY)
- {
ndisc_del_timer(ndn);
- }
nd_stats.rcv_upper_conf++;
ndn->ndn_nud_state = NUD_REACHABLE;
@@ -1609,26 +1466,30 @@
struct nd_neigh *ndn;
struct inet6_ifaddr *ifp;
- switch (msg->icmph.type) {
+ NDBG(("ndisc_rcv(type=%d) ", msg->icmph.icmp6_type));
+ switch (msg->icmph.icmp6_type) {
case NDISC_NEIGHBOUR_SOLICITATION:
- if ((ifp = ipv6_chk_addr(&msg->target)))
- {
+ NDBG(("NS "));
+ if ((ifp = ipv6_chk_addr(&msg->target))) {
int addr_type;
- if (ifp->flags & DAD_INCOMPLETE)
- {
+ if (ifp->flags & DAD_INCOMPLETE) {
/*
- * DAD failed
+ * DAD failed
*/
- printk(KERN_DEBUG "duplicate address\n");
+ /* XXX Check if this came in over same interface
+ * XXX we just sent an NS from! That is valid! -DaveM
+ */
+
+ printk(KERN_DEBUG "%s: duplicate address\n",
+ ifp->idev->dev->name);
del_timer(&ifp->timer);
return 0;
}
addr_type = ipv6_addr_type(saddr);
- if (addr_type & IPV6_ADDR_UNICAST)
- {
+ if (addr_type & IPV6_ADDR_UNICAST) {
int inc;
/*
@@ -1637,25 +1498,18 @@
*/
nd_stats.rcv_probes_ucast++;
- ndisc_event_ns(saddr, skb);
-
- neigh_table_lock(&nd_tbl);
- ndn = (struct nd_neigh *)
- neigh_lookup(&nd_tbl, saddr,
- sizeof(struct in6_addr),
- dev);
+ ndn = ndisc_event_ns(saddr, skb);
- neigh_table_unlock(&nd_tbl);
+ if (ndn == NULL)
+ return 0;
inc = ipv6_addr_type(daddr);
inc &= IPV6_ADDR_MULTICAST;
ndisc_send_na(dev, ndn, saddr, &ifp->addr,
ifp->idev->router, 1, inc, inc);
- }
- else
- {
+ } else {
#if ND_DEBUG >= 1
/* FIXME */
printk(KERN_DEBUG "ns: non unicast saddr\n");
@@ -1665,38 +1519,30 @@
break;
case NDISC_NEIGHBOUR_ADVERTISEMENT:
-
+ NDBG(("NA "));
neigh_table_lock(&nd_tbl);
ndn = (struct nd_neigh *)
neigh_lookup(&nd_tbl, (void *) &msg->target,
sizeof(struct in6_addr), skb->dev);
neigh_table_unlock(&nd_tbl);
- if (ndn)
- {
- if (ndn->ndn_flags & NCF_ROUTER)
- {
- if (msg->icmph.icmp6_router == 0)
- {
+ if (ndn) {
+ if (ndn->ndn_flags & NCF_ROUTER) {
+ if (msg->icmph.icmp6_router == 0) {
/*
* 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
}
- }
- else
- {
+ } else {
if (msg->icmph.icmp6_router)
- {
ndn->ndn_flags |= NCF_ROUTER;
- }
}
ndisc_event_na(ndn, (unsigned char *) &msg->opt,
skb->tail - (u8 *)&msg->opt /*opt_len*/,
@@ -1705,24 +1551,28 @@
}
break;
- }
+ };
- if (ipv6_forwarding == 0)
- {
- switch (msg->icmph.type) {
+ if (ipv6_config.forwarding == 0) {
+ switch (msg->icmph.icmp6_type) {
case NDISC_ROUTER_ADVERTISEMENT:
- ndisc_router_discovery(skb);
+ NDBG(("RA "));
+ if (ipv6_config.accept_ra)
+ ndisc_router_discovery(skb);
break;
case NDISC_REDIRECT:
- ndisc_redirect_rcv(skb);
+ NDBG(("REDIR "));
+ if (ipv6_config.accept_redirects)
+ ndisc_redirect_rcv(skb);
break;
- }
+ };
}
return 0;
}
+#ifdef CONFIG_PROC_FS
int ndisc_get_info(char *buffer, char **start, off_t offset, int length,
int dummy)
{
@@ -1732,8 +1582,7 @@
neigh_table_lock(&nd_tbl);
- for (i = 0; i < nd_tbl.tbl_size; i++)
- {
+ for (i = 0; i < nd_tbl.tbl_size; i++) {
struct neighbour *neigh, *head;
head = nd_tbl.hash_buckets[i];
@@ -1744,36 +1593,37 @@
struct nd_neigh *ndn = (struct nd_neigh *) neigh;
int j;
- for (j=0; j<16; j++)
- {
+ for (j=0; j<16; j++) {
sprintf(buffer + len, "%02x",
ndn->ndn_addr.s6_addr[j]);
len += 2;
}
len += sprintf(buffer + len,
- " %02x %02x %08lx %08lx %04x %04lx ", i,
+ " %02x %02x %02x %02x %08lx %08lx %08lx %04x %04x %04lx %8s ", i,
+ ndn->ndn_plen,
+ ndn->ndn_type,
ndn->ndn_nud_state,
- ndn->ndn_expires - now,
+ ndn->ndn_expires ? ndn->ndn_expires - now : 0,
now - ndn->ndn_tstamp,
+ nd_reachable_time,
+ nd_gc_staletime,
ndn->ndn_refcnt,
- ndn->ndn_flags);
+ ndn->ndn_flags,
+ ndn->ndn_dev ? ndn->ndn_dev->name : "NULLDEV");
- if ((ndn->ndn_flags & NTF_COMPLETE))
- {
- for (j=0; j< neigh->dev->addr_len; j++)
- {
+ if ((ndn->ndn_flags & NTF_COMPLETE)) {
+ for (j=0; j< neigh->dev->addr_len; j++) {
sprintf(buffer + len, "%02x",
neigh->ha[j]);
len += 2;
}
- }
- else
+ } else {
len += sprintf(buffer + len, "000000000000");
+ }
len += sprintf(buffer + len, "\n");
neigh = neigh->next;
-
} while (neigh != head);
}
@@ -1795,6 +1645,7 @@
0, NULL,
&ndisc_get_info
};
+#endif /* CONFIG_PROC_FS */
void ndisc_init(struct net_proto_family *ops)
{
@@ -1845,22 +1696,13 @@
#ifdef CONFIG_PROC_FS
proc_net_register(&ndisc_proc_entry);
#endif
-#ifdef CONFIG_IPV6_MODULE
- ndisc_eth_hook = ndisc_eth_resolv;
-#endif
}
-#ifdef CONFIG_IPV6_MODULE
+#ifdef MODULE
void ndisc_cleanup(void)
{
- ndisc_eth_hook = NULL;
-
#ifdef CONFIG_PROC_FS
-#ifdef CONFIG_IPV6_MODULE
- proc_unregister(&proc_net, ndisc_proc_entry.low_ino);
-#else
- proc_net_unregister(PROC_NET_NDISC);
-#endif
+ proc_net_unregister(ndisc_proc_entry.low_ino);
#endif
del_timer(&ndisc_gc_timer);
del_timer(&ndisc_timer);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov