#include <linux/config.h>
#include <linux/net.h>
+#include <linux/module.h>
#include <linux/socket.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/netfilter_decnet.h>
#include <linux/spinlock.h>
#include <linux/seq_file.h>
+#include <linux/rcupdate.h>
+#include <linux/jhash.h>
#include <asm/atomic.h>
#include <net/neighbour.h>
#include <net/dst.h>
struct neigh_table dn_neigh_table = {
.family = PF_DECnet,
.entry_size = sizeof(struct dn_neigh),
- .key_len = sizeof(dn_address),
+ .key_len = sizeof(__le16),
.hash = dn_neigh_hash,
.constructor = dn_neigh_construct,
.id = "dn_neigh_cache",
.parms ={
.tbl = &dn_neigh_table,
- .entries = 0,
.base_reachable_time = 30 * HZ,
.retrans_time = 1 * HZ,
.gc_staletime = 60 * HZ,
static u32 dn_neigh_hash(const void *pkey, const struct net_device *dev)
{
- u32 hash_val;
-
- hash_val = *(dn_address *)pkey;
- hash_val ^= (hash_val >> 10);
- hash_val ^= (hash_val >> 3);
-
- return hash_val & NEIGH_HASHMASK;
+ return jhash_2words(*(__u16 *)pkey, 0, dn_neigh_table.hash_rnd);
}
static int dn_neigh_construct(struct neighbour *neigh)
{
struct net_device *dev = neigh->dev;
struct dn_neigh *dn = (struct dn_neigh *)neigh;
- struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
+ struct dn_dev *dn_db;
+ struct neigh_parms *parms;
- if (dn_db == NULL)
+ rcu_read_lock();
+ dn_db = rcu_dereference(dev->dn_ptr);
+ if (dn_db == NULL) {
+ rcu_read_unlock();
return -EINVAL;
+ }
- if (dn_db->neigh_parms)
- neigh->parms = dn_db->neigh_parms;
+ parms = dn_db->neigh_parms;
+ if (!parms) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+
+ __neigh_parms_put(neigh->parms);
+ neigh->parms = neigh_parms_clone(parms);
if (dn_db->use_long)
neigh->ops = &dn_long_ops;
else
neigh->ops = &dn_short_ops;
+ rcu_read_unlock();
if (dn->flags & DN_NDFLAG_P3)
neigh->ops = &dn_phase3_ops;
data = skb_push(skb, sizeof(struct dn_long_packet) + 3);
lp = (struct dn_long_packet *)(data+3);
- *((unsigned short *)data) = dn_htons(skb->len - 2);
+ *((__le16 *)data) = dn_htons(skb->len - 2);
*(data + 2) = 1 | DN_RT_F_PF; /* Padding */
lp->msgflg = DN_RT_PKT_LONG|(cb->rt_flags&(DN_RT_F_IE|DN_RT_F_RQR|DN_RT_F_RTS));
lp->d_area = lp->d_subarea = 0;
- dn_dn2eth(lp->d_id, dn_ntohs(cb->dst));
+ dn_dn2eth(lp->d_id, cb->dst);
lp->s_area = lp->s_subarea = 0;
- dn_dn2eth(lp->s_id, dn_ntohs(cb->src));
+ dn_dn2eth(lp->s_id, cb->src);
lp->nl2 = 0;
lp->visit_ct = cb->hops & 0x3f;
lp->s_class = 0;
}
data = skb_push(skb, sizeof(struct dn_short_packet) + 2);
- *((unsigned short *)data) = dn_htons(skb->len - 2);
+ *((__le16 *)data) = dn_htons(skb->len - 2);
sp = (struct dn_short_packet *)(data+2);
sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS));
}
data = skb_push(skb, sizeof(struct dn_short_packet) + 2);
- *((unsigned short *)data) = dn_htons(skb->len - 2);
+ *((__le16 *)data) = dn_htons(skb->len - 2);
sp = (struct dn_short_packet *)(data + 2);
sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS));
* basically does a neigh_lookup(), but without comparing the device
* field. This is required for the On-Ethernet cache
*/
-struct neighbour *dn_neigh_lookup(struct neigh_table *tbl, const void *ptr)
-{
- struct neighbour *neigh;
- u32 hash_val;
-
- hash_val = tbl->hash(ptr, NULL);
-
- read_lock_bh(&tbl->lock);
- for(neigh = tbl->hash_buckets[hash_val]; neigh != NULL; neigh = neigh->next) {
- if (memcmp(neigh->primary_key, ptr, tbl->key_len) == 0) {
- atomic_inc(&neigh->refcnt);
- read_unlock_bh(&tbl->lock);
- return neigh;
- }
- }
- read_unlock_bh(&tbl->lock);
-
- return NULL;
-}
-
-
-/*
- * Any traffic on a pointopoint link causes the timer to be reset
- * for the entry in the neighbour table.
- */
-void dn_neigh_pointopoint_notify(struct sk_buff *skb)
-{
- return;
-}
/*
* Pointopoint link receives a hello message
struct neighbour *neigh;
struct dn_neigh *dn;
struct dn_dev *dn_db;
- dn_address src;
+ __le16 src;
- src = dn_htons(dn_eth2dn(msg->id));
+ src = dn_eth2dn(msg->id);
neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1);
neigh->updated = jiffies;
if (neigh->dev->type == ARPHRD_ETHER)
- memcpy(neigh->ha, &skb->mac.ethernet->h_source, ETH_ALEN);
+ memcpy(neigh->ha, ð_hdr(skb)->h_source, ETH_ALEN);
dn->blksize = dn_ntohs(msg->blksize);
dn->priority = msg->priority;
}
}
- if (!dn_db->router) {
- dn_db->router = neigh_clone(neigh);
- } else {
- if (msg->priority > ((struct dn_neigh *)dn_db->router)->priority)
- neigh_release(xchg(&dn_db->router, neigh_clone(neigh)));
+ /* Only use routers in our area */
+ if ((dn_ntohs(src)>>10) == (dn_ntohs((decnet_address))>>10)) {
+ if (!dn_db->router) {
+ dn_db->router = neigh_clone(neigh);
+ } else {
+ if (msg->priority > ((struct dn_neigh *)dn_db->router)->priority)
+ neigh_release(xchg(&dn_db->router, neigh_clone(neigh)));
+ }
}
write_unlock(&neigh->lock);
neigh_release(neigh);
struct endnode_hello_message *msg = (struct endnode_hello_message *)skb->data;
struct neighbour *neigh;
struct dn_neigh *dn;
- dn_address src;
+ __le16 src;
- src = dn_htons(dn_eth2dn(msg->id));
+ src = dn_eth2dn(msg->id);
neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1);
neigh->updated = jiffies;
if (neigh->dev->type == ARPHRD_ETHER)
- memcpy(neigh->ha, &skb->mac.ethernet->h_source, ETH_ALEN);
+ memcpy(neigh->ha, ð_hdr(skb)->h_source, ETH_ALEN);
dn->flags &= ~(DN_NDFLAG_R1 | DN_NDFLAG_R2);
dn->blksize = dn_ntohs(msg->blksize);
dn->priority = 0;
return (*min < priority) ? (min - 6) : NULL;
}
-int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n)
-{
- int t = 0;
- int i;
- struct neighbour *neigh;
- struct dn_neigh *dn;
- struct neigh_table *tbl = &dn_neigh_table;
- unsigned char *rs = ptr;
- struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
-
- read_lock_bh(&tbl->lock);
-
- for(i = 0; i < NEIGH_HASHMASK; i++) {
- for(neigh = tbl->hash_buckets[i]; neigh != NULL; neigh = neigh->next) {
- if (neigh->dev != dev)
- continue;
- dn = (struct dn_neigh *)neigh;
- if (!(dn->flags & (DN_NDFLAG_R1|DN_NDFLAG_R2)))
- continue;
- if (dn_db->parms.forwarding == 1 && (dn->flags & DN_NDFLAG_R2))
- continue;
- if (t == n)
- rs = dn_find_slot(ptr, n, dn->priority);
- else
- t++;
- if (rs == NULL)
- continue;
- dn_dn2eth(rs, dn->addr);
- rs += 6;
- *rs = neigh->nud_state & NUD_CONNECTED ? 0x80 : 0x0;
- *rs |= dn->priority;
- rs++;
- }
- }
-
- read_unlock_bh(&tbl->lock);
-
- return t;
-}
-
-
-#ifdef CONFIG_PROC_FS
-
-struct dn_neigh_iter_state {
- int bucket;
+struct elist_cb_state {
+ struct net_device *dev;
+ unsigned char *ptr;
+ unsigned char *rs;
+ int t, n;
};
-static struct neighbour *neigh_get_first(struct seq_file *seq)
+static void neigh_elist_cb(struct neighbour *neigh, void *_info)
{
- struct dn_neigh_iter_state *state = seq->private;
- struct neighbour *n = NULL;
-
- for(state->bucket = 0;
- state->bucket <= NEIGH_HASHMASK;
- ++state->bucket) {
- n = dn_neigh_table.hash_buckets[state->bucket];
- if (n)
- break;
- }
+ struct elist_cb_state *s = _info;
+ struct dn_neigh *dn;
- return n;
-}
+ if (neigh->dev != s->dev)
+ return;
-static struct neighbour *neigh_get_next(struct seq_file *seq,
- struct neighbour *n)
-{
- struct dn_neigh_iter_state *state = seq->private;
+ dn = (struct dn_neigh *) neigh;
+ if (!(dn->flags & (DN_NDFLAG_R1|DN_NDFLAG_R2)))
+ return;
- n = n->next;
-try_again:
- if (n)
- goto out;
- if (++state->bucket > NEIGH_HASHMASK)
- goto out;
- n = dn_neigh_table.hash_buckets[state->bucket];
- goto try_again;
-out:
- return n;
+ if (s->t == s->n)
+ s->rs = dn_find_slot(s->ptr, s->n, dn->priority);
+ else
+ s->t++;
+ if (s->rs == NULL)
+ return;
+
+ dn_dn2eth(s->rs, dn->addr);
+ s->rs += 6;
+ *(s->rs) = neigh->nud_state & NUD_CONNECTED ? 0x80 : 0x0;
+ *(s->rs) |= dn->priority;
+ s->rs++;
}
-static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos)
+int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n)
{
- struct neighbour *n = neigh_get_first(seq);
+ struct elist_cb_state state;
- if (n)
- while(*pos && (n = neigh_get_next(seq, n)))
- --*pos;
- return *pos ? NULL : n;
-}
+ state.dev = dev;
+ state.t = 0;
+ state.n = n;
+ state.ptr = ptr;
+ state.rs = ptr;
-static void *dn_neigh_get_idx(struct seq_file *seq, loff_t pos)
-{
- void *rc;
- read_lock_bh(&dn_neigh_table.lock);
- rc = neigh_get_idx(seq, &pos);
- if (!rc) {
- read_unlock_bh(&dn_neigh_table.lock);
- }
- return rc;
-}
+ neigh_for_each(&dn_neigh_table, neigh_elist_cb, &state);
-static void *dn_neigh_seq_start(struct seq_file *seq, loff_t *pos)
-{
- return *pos ? dn_neigh_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
+ return state.t;
}
-static void *dn_neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- void *rc;
-
- if (v == SEQ_START_TOKEN) {
- rc = dn_neigh_get_idx(seq, 0);
- goto out;
- }
-
- rc = neigh_get_next(seq, v);
- if (rc)
- goto out;
- read_unlock_bh(&dn_neigh_table.lock);
-out:
- ++*pos;
- return rc;
-}
-
-static void dn_neigh_seq_stop(struct seq_file *seq, void *v)
-{
- if (v && v != SEQ_START_TOKEN)
- read_unlock_bh(&dn_neigh_table.lock);
-}
+#ifdef CONFIG_PROC_FS
static inline void dn_neigh_format_entry(struct seq_file *seq,
struct neighbour *n)
{
- struct dn_neigh *dn = (struct dn_neigh *)n;
+ struct dn_neigh *dn = (struct dn_neigh *) n;
char buf[DN_ASCBUF_LEN];
read_lock(&n->lock);
return 0;
}
+static void *dn_neigh_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ return neigh_seq_start(seq, pos, &dn_neigh_table,
+ NEIGH_SEQ_NEIGH_ONLY);
+}
+
static struct seq_operations dn_neigh_seq_ops = {
.start = dn_neigh_seq_start,
- .next = dn_neigh_seq_next,
- .stop = dn_neigh_seq_stop,
+ .next = neigh_seq_next,
+ .stop = neigh_seq_stop,
.show = dn_neigh_seq_show,
};
{
struct seq_file *seq;
int rc = -ENOMEM;
- struct dn_neigh_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ struct neigh_seq_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s)
goto out;
+ memset(s, 0, sizeof(*s));
rc = seq_open(file, &dn_neigh_seq_ops);
if (rc)
goto out_kfree;