-/* linux/net/inet/arp.c
+/* linux/net/ipv4/arp.c
*
* Version: $Id: arp.c,v 1.99 2001/08/30 22:55:42 davem Exp $
*
* arp_xmit so intermediate drivers like
* bonding can change the skb before
* sending (e.g. insert 8021q tag).
+ * Harald Welte : convert to make use of jenkins hash
*/
#include <linux/module.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/sched.h>
-#include <linux/config.h>
+#include <linux/capability.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/mm.h>
#include <linux/inet.h>
+#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/fddidevice.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/net.h>
+#include <linux/rcupdate.h>
+#include <linux/jhash.h>
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif
.gc_thresh3 = 1024,
};
-int arp_mc_map(u32 addr, u8 *haddr, struct net_device *dev, int dir)
+int arp_mc_map(__be32 addr, u8 *haddr, struct net_device *dev, int dir)
{
switch (dev->type) {
case ARPHRD_ETHER:
case ARPHRD_IEEE802_TR:
ip_tr_mc_map(addr, haddr);
return 0;
+ case ARPHRD_INFINIBAND:
+ ip_ib_mc_map(addr, haddr);
+ return 0;
default:
if (dir) {
memcpy(haddr, dev->broadcast, dev->addr_len);
static u32 arp_hash(const void *pkey, const struct net_device *dev)
{
- u32 hash_val;
-
- hash_val = *(u32*)pkey;
- hash_val ^= (hash_val>>16);
- hash_val ^= hash_val>>8;
- hash_val ^= hash_val>>3;
- hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK;
-
- return hash_val;
+ return jhash_2words(*(u32 *)pkey, dev->ifindex, arp_tbl.hash_rnd);
}
static int arp_constructor(struct neighbour *neigh)
{
- u32 addr = *(u32*)neigh->primary_key;
+ __be32 addr = *(__be32*)neigh->primary_key;
struct net_device *dev = neigh->dev;
- struct in_device *in_dev = in_dev_get(dev);
-
- if (in_dev == NULL)
- return -EINVAL;
+ struct in_device *in_dev;
+ struct neigh_parms *parms;
neigh->type = inet_addr_type(addr);
- if (in_dev->arp_parms)
- neigh->parms = in_dev->arp_parms;
- in_dev_put(in_dev);
+ rcu_read_lock();
+ in_dev = __in_dev_get_rcu(dev);
+ if (in_dev == NULL) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+
+ parms = in_dev->arp_parms;
+ __neigh_parms_put(neigh->parms);
+ neigh->parms = neigh_parms_clone(parms);
+ rcu_read_unlock();
if (dev->hard_header == NULL) {
neigh->nud_state = NUD_NOARP;
static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
{
- u32 saddr = 0;
+ __be32 saddr = 0;
u8 *dst_ha = NULL;
struct net_device *dev = neigh->dev;
- u32 target = *(u32*)neigh->primary_key;
+ __be32 target = *(__be32*)neigh->primary_key;
int probes = atomic_read(&neigh->probes);
struct in_device *in_dev = in_dev_get(dev);
}
static int arp_ignore(struct in_device *in_dev, struct net_device *dev,
- u32 sip, u32 tip)
+ __be32 sip, __be32 tip)
{
int scope;
return !inet_confirm_addr(dev, sip, tip, scope);
}
-static int arp_filter(__u32 sip, __u32 tip, struct net_device *dev)
+static int arp_filter(__be32 sip, __be32 tip, struct net_device *dev)
{
struct flowi fl = { .nl_u = { .ip4_u = { .daddr = sip,
.saddr = tip } } };
if (ip_route_output_key(&rt, &fl) < 0)
return 1;
if (rt->u.dst.dev != dev) {
- NET_INC_STATS_BH(ArpFilter);
+ NET_INC_STATS_BH(LINUX_MIB_ARPFILTER);
flag = 1;
}
ip_rt_put(rt);
* is allowed to use this function, it is scheduled to be removed. --ANK
*/
-static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, struct net_device * dev)
+static int arp_set_predefined(int addr_hint, unsigned char * haddr, __be32 paddr, struct net_device * dev)
{
switch (addr_hint) {
case RTN_LOCAL:
int arp_find(unsigned char *haddr, struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
- u32 paddr;
+ __be32 paddr;
struct neighbour *n;
if (!skb->dst) {
if (dev == NULL)
return -EINVAL;
if (n == NULL) {
- u32 nexthop = ((struct rtable*)dst)->rt_gateway;
+ __be32 nexthop = ((struct rtable*)dst)->rt_gateway;
if (dev->flags&(IFF_LOOPBACK|IFF_POINTOPOINT))
nexthop = 0;
n = __neigh_lookup_errno(
* Create an arp packet. If (dest_hw == NULL), we create a broadcast
* message.
*/
-struct sk_buff *arp_create(int type, int ptype, u32 dest_ip,
- struct net_device *dev, u32 src_ip,
+struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip,
+ struct net_device *dev, __be32 src_ip,
unsigned char *dest_hw, unsigned char *src_hw,
unsigned char *target_hw)
{
/*
* Create and send an arp packet.
*/
-void arp_send(int type, int ptype, u32 dest_ip,
- struct net_device *dev, u32 src_ip,
+void arp_send(int type, int ptype, __be32 dest_ip,
+ struct net_device *dev, __be32 src_ip,
unsigned char *dest_hw, unsigned char *src_hw,
unsigned char *target_hw)
{
arp_xmit(skb);
}
-static void parp_redo(struct sk_buff *skb)
-{
- arp_rcv(skb, skb->dev, NULL);
-}
-
/*
* Process an arp request.
*/
-int arp_process(struct sk_buff *skb)
+static int arp_process(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct in_device *in_dev = in_dev_get(dev);
unsigned char *arp_ptr;
struct rtable *rt;
unsigned char *sha, *tha;
- u32 sip, tip;
+ __be32 sip, tip;
u16 dev_type = dev->type;
int addr_type;
struct neighbour *n;
if (n)
neigh_release(n);
- if (skb->stamp.tv_sec == LOCALLY_ENQUEUED ||
+ if (NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED ||
skb->pkt_type == PACKET_HOST ||
in_dev->arp_parms->proxy_delay == 0) {
arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
n = __neigh_lookup(&arp_tbl, &sip, dev, 0);
-#ifdef CONFIG_IP_ACCEPT_UNSOLICITED_ARP
- /* Unsolicited ARP is not accepted by default.
- It is possible, that this option should be enabled for some
- devices (strip is candidate)
- */
- if (n == NULL &&
- arp->ar_op == htons(ARPOP_REPLY) &&
- inet_addr_type(sip) == RTN_UNICAST)
- n = __neigh_lookup(&arp_tbl, &sip, dev, -1);
-#endif
+ if (ipv4_devconf.arp_accept) {
+ /* Unsolicited ARP is not accepted by default.
+ It is possible, that this option should be enabled for some
+ devices (strip is candidate)
+ */
+ if (n == NULL &&
+ arp->ar_op == htons(ARPOP_REPLY) &&
+ inet_addr_type(sip) == RTN_UNICAST)
+ n = __neigh_lookup(&arp_tbl, &sip, dev, -1);
+ }
if (n) {
int state = NUD_REACHABLE;
if (arp->ar_op != htons(ARPOP_REPLY) ||
skb->pkt_type != PACKET_HOST)
state = NUD_STALE;
- neigh_update(n, sha, state, override, 1);
+ neigh_update(n, sha, state, override ? NEIGH_UPDATE_F_OVERRIDE : 0);
neigh_release(n);
}
return 0;
}
+static void parp_redo(struct sk_buff *skb)
+{
+ arp_process(skb);
+}
+
/*
* Receive an arp request from the device layer.
*/
-int arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
+static int arp_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
{
struct arphdr *arp;
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
goto out_of_mem;
+ memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
+
return NF_HOOK(NF_ARP, NF_ARP_IN, skb, dev, NULL, arp_process);
freeskb:
* Set (create) an ARP cache entry.
*/
-int arp_req_set(struct arpreq *r, struct net_device * dev)
+static int arp_req_set(struct arpreq *r, struct net_device * dev)
{
- u32 ip = ((struct sockaddr_in *) &r->arp_pa)->sin_addr.s_addr;
+ __be32 ip = ((struct sockaddr_in *) &r->arp_pa)->sin_addr.s_addr;
struct neighbour *neigh;
int err;
if (r->arp_flags&ATF_PUBL) {
- u32 mask = ((struct sockaddr_in *) &r->arp_netmask)->sin_addr.s_addr;
- if (mask && mask != 0xFFFFFFFF)
+ __be32 mask = ((struct sockaddr_in *) &r->arp_netmask)->sin_addr.s_addr;
+ if (mask && mask != htonl(0xFFFFFFFF))
return -EINVAL;
if (!dev && (r->arp_flags & ATF_COM)) {
dev = dev_getbyhwaddr(r->arp_ha.sa_family, r->arp_ha.sa_data);
ipv4_devconf.proxy_arp = 1;
return 0;
}
- if (__in_dev_get(dev)) {
- __in_dev_get(dev)->cnf.proxy_arp = 1;
+ if (__in_dev_get_rtnl(dev)) {
+ __in_dev_get_rtnl(dev)->cnf.proxy_arp = 1;
return 0;
}
return -ENXIO;
if (!dev)
return -EINVAL;
}
- if (r->arp_ha.sa_family != dev->type)
- return -EINVAL;
+ switch (dev->type) {
+#ifdef CONFIG_FDDI
+ case ARPHRD_FDDI:
+ /*
+ * According to RFC 1390, FDDI devices should accept ARP
+ * hardware types of 1 (Ethernet). However, to be more
+ * robust, we'll accept hardware types of either 1 (Ethernet)
+ * or 6 (IEEE 802.2).
+ */
+ if (r->arp_ha.sa_family != ARPHRD_FDDI &&
+ r->arp_ha.sa_family != ARPHRD_ETHER &&
+ r->arp_ha.sa_family != ARPHRD_IEEE802)
+ return -EINVAL;
+ break;
+#endif
+ default:
+ if (r->arp_ha.sa_family != dev->type)
+ return -EINVAL;
+ break;
+ }
neigh = __neigh_lookup_errno(&arp_tbl, &ip, dev);
err = PTR_ERR(neigh);
if (r->arp_flags & ATF_PERM)
state = NUD_PERMANENT;
err = neigh_update(neigh, (r->arp_flags&ATF_COM) ?
- r->arp_ha.sa_data : NULL, state, 1, 0);
+ r->arp_ha.sa_data : NULL, state,
+ NEIGH_UPDATE_F_OVERRIDE|
+ NEIGH_UPDATE_F_ADMIN);
neigh_release(neigh);
}
return err;
static int arp_req_get(struct arpreq *r, struct net_device *dev)
{
- u32 ip = ((struct sockaddr_in *) &r->arp_pa)->sin_addr.s_addr;
+ __be32 ip = ((struct sockaddr_in *) &r->arp_pa)->sin_addr.s_addr;
struct neighbour *neigh;
int err = -ENXIO;
return err;
}
-int arp_req_delete(struct arpreq *r, struct net_device * dev)
+static int arp_req_delete(struct arpreq *r, struct net_device * dev)
{
int err;
- u32 ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
+ __be32 ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
struct neighbour *neigh;
if (r->arp_flags & ATF_PUBL) {
- u32 mask =
+ __be32 mask =
((struct sockaddr_in *)&r->arp_netmask)->sin_addr.s_addr;
- if (mask == 0xFFFFFFFF)
+ if (mask == htonl(0xFFFFFFFF))
return pneigh_delete(&arp_tbl, &ip, dev);
if (mask == 0) {
if (dev == NULL) {
ipv4_devconf.proxy_arp = 0;
return 0;
}
- if (__in_dev_get(dev)) {
- __in_dev_get(dev)->cnf.proxy_arp = 0;
+ if (__in_dev_get_rtnl(dev)) {
+ __in_dev_get_rtnl(dev)->cnf.proxy_arp = 0;
return 0;
}
return -ENXIO;
neigh = neigh_lookup(&arp_tbl, &ip, dev);
if (neigh) {
if (neigh->nud_state&~NUD_NOARP)
- err = neigh_update(neigh, NULL, NUD_FAILED, 1, 0);
+ err = neigh_update(neigh, NULL, NUD_FAILED,
+ NEIGH_UPDATE_F_OVERRIDE|
+ NEIGH_UPDATE_F_ADMIN);
neigh_release(neigh);
}
return err;
* Handle an ARP layer I/O control request.
*/
-int arp_ioctl(unsigned int cmd, void *arg)
+int arp_ioctl(unsigned int cmd, void __user *arg)
{
int err;
struct arpreq r;
return NOTIFY_DONE;
}
-struct notifier_block arp_netdev_notifier = {
+static struct notifier_block arp_netdev_notifier = {
.notifier_call = arp_netdev_event,
};
arp_proc_init();
#ifdef CONFIG_SYSCTL
neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4,
- NET_IPV4_NEIGH, "ipv4", NULL);
+ NET_IPV4_NEIGH, "ipv4", NULL, NULL);
#endif
register_netdevice_notifier(&arp_netdev_notifier);
}
}
#endif /* CONFIG_AX25 */
-struct arp_iter_state {
- int is_pneigh, bucket;
-};
-
-static struct neighbour *neigh_get_first(struct seq_file *seq)
-{
- struct arp_iter_state* state = seq->private;
- struct neighbour *n = NULL;
-
- state->is_pneigh = 0;
-
- for (state->bucket = 0;
- state->bucket <= NEIGH_HASHMASK;
- ++state->bucket) {
- n = arp_tbl.hash_buckets[state->bucket];
- while (n && !(n->nud_state & ~NUD_NOARP))
- n = n->next;
- if (n)
- break;
- }
-
- return n;
-}
-
-static struct neighbour *neigh_get_next(struct seq_file *seq,
- struct neighbour *n)
-{
- struct arp_iter_state* state = seq->private;
-
- do {
- n = n->next;
- /* Don't confuse "arp -a" w/ magic entries */
-try_again:
- ;
- } while (n && !(n->nud_state & ~NUD_NOARP));
-
- if (n)
- goto out;
- if (++state->bucket > NEIGH_HASHMASK)
- goto out;
- n = arp_tbl.hash_buckets[state->bucket];
- goto try_again;
-out:
- return n;
-}
-
-static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos)
-{
- struct neighbour *n = neigh_get_first(seq);
-
- if (n)
- while (*pos && (n = neigh_get_next(seq, n)))
- --*pos;
- return *pos ? NULL : n;
-}
-
-static struct pneigh_entry *pneigh_get_first(struct seq_file *seq)
-{
- struct arp_iter_state* state = seq->private;
- struct pneigh_entry *pn;
-
- state->is_pneigh = 1;
-
- for (state->bucket = 0;
- state->bucket <= PNEIGH_HASHMASK;
- ++state->bucket) {
- pn = arp_tbl.phash_buckets[state->bucket];
- if (pn)
- break;
- }
- return pn;
-}
-
-static struct pneigh_entry *pneigh_get_next(struct seq_file *seq,
- struct pneigh_entry *pn)
-{
- struct arp_iter_state* state = seq->private;
-
- pn = pn->next;
- while (!pn) {
- if (++state->bucket > PNEIGH_HASHMASK)
- break;
- pn = arp_tbl.phash_buckets[state->bucket];
- }
- return pn;
-}
-
-static struct pneigh_entry *pneigh_get_idx(struct seq_file *seq, loff_t pos)
-{
- struct pneigh_entry *pn = pneigh_get_first(seq);
-
- if (pn)
- while (pos && (pn = pneigh_get_next(seq, pn)))
- --pos;
- return pos ? NULL : pn;
-}
-
-static void *arp_get_idx(struct seq_file *seq, loff_t pos)
-{
- void *rc;
-
- read_lock_bh(&arp_tbl.lock);
- rc = neigh_get_idx(seq, &pos);
-
- if (!rc) {
- read_unlock_bh(&arp_tbl.lock);
- rc = pneigh_get_idx(seq, pos);
- }
- return rc;
-}
-
-static void *arp_seq_start(struct seq_file *seq, loff_t *pos)
-{
- struct arp_iter_state* state = seq->private;
-
- state->is_pneigh = 0;
- state->bucket = 0;
- return *pos ? arp_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
-}
-
-static void *arp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- void *rc;
- struct arp_iter_state* state;
-
- if (v == SEQ_START_TOKEN) {
- rc = arp_get_idx(seq, 0);
- goto out;
- }
-
- state = seq->private;
- if (!state->is_pneigh) {
- rc = neigh_get_next(seq, v);
- if (rc)
- goto out;
- read_unlock_bh(&arp_tbl.lock);
- rc = pneigh_get_first(seq);
- } else
- rc = pneigh_get_next(seq, v);
-out:
- ++*pos;
- return rc;
-}
-
-static void arp_seq_stop(struct seq_file *seq, void *v)
-{
- struct arp_iter_state* state = seq->private;
-
- if (!state->is_pneigh && v != SEQ_START_TOKEN)
- read_unlock_bh(&arp_tbl.lock);
-}
-
#define HBUFFERLEN 30
-static __inline__ void arp_format_neigh_entry(struct seq_file *seq,
- struct neighbour *n)
+static void arp_format_neigh_entry(struct seq_file *seq,
+ struct neighbour *n)
{
char hbuffer[HBUFFERLEN];
const char hexbuf[] = "0123456789ABCDEF";
read_unlock(&n->lock);
}
-static __inline__ void arp_format_pneigh_entry(struct seq_file *seq,
- struct pneigh_entry *n)
+static void arp_format_pneigh_entry(struct seq_file *seq,
+ struct pneigh_entry *n)
{
struct net_device *dev = n->dev;
int hatype = dev ? dev->type : 0;
static int arp_seq_show(struct seq_file *seq, void *v)
{
- if (v == SEQ_START_TOKEN)
+ if (v == SEQ_START_TOKEN) {
seq_puts(seq, "IP address HW type Flags "
"HW address Mask Device\n");
- else {
- struct arp_iter_state* state = seq->private;
+ } else {
+ struct neigh_seq_state *state = seq->private;
- if (state->is_pneigh)
+ if (state->flags & NEIGH_SEQ_IS_PNEIGH)
arp_format_pneigh_entry(seq, v);
else
arp_format_neigh_entry(seq, v);
return 0;
}
+static void *arp_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ /* Don't want to confuse "arp -a" w/ magic entries,
+ * so we tell the generic iterator to skip NUD_NOARP.
+ */
+ return neigh_seq_start(seq, pos, &arp_tbl, NEIGH_SEQ_SKIP_NOARP);
+}
+
/* ------------------------------------------------------------------------ */
static struct seq_operations arp_seq_ops = {
.start = arp_seq_start,
- .next = arp_seq_next,
- .stop = arp_seq_stop,
+ .next = neigh_seq_next,
+ .stop = neigh_seq_stop,
.show = arp_seq_show,
};
{
struct seq_file *seq;
int rc = -ENOMEM;
- struct arp_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
+ struct neigh_seq_state *s = kzalloc(sizeof(*s), GFP_KERNEL);
if (!s)
goto out;
EXPORT_SYMBOL(arp_broken_ops);
EXPORT_SYMBOL(arp_find);
-EXPORT_SYMBOL(arp_rcv);
EXPORT_SYMBOL(arp_create);
EXPORT_SYMBOL(arp_xmit);
EXPORT_SYMBOL(arp_send);