Skip to content
Snippets Groups Projects
arp.c 34.6 KiB
Newer Older
/* linux/net/ipv4/arp.c
Linus Torvalds's avatar
Linus Torvalds committed
 *
 * Copyright (C) 1994 by Florian  La Roche
 *
 * This module implements the Address Resolution Protocol ARP (RFC 826),
 * which is used to convert IP addresses (or in the future maybe other
 * high-level addresses) into a low-level hardware address (like an Ethernet
 * address).
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 * Fixes:
 *		Alan Cox	:	Removed the Ethernet assumptions in
Linus Torvalds's avatar
Linus Torvalds committed
 *					Florian's code
 *		Alan Cox	:	Fixed some small errors in the ARP
Linus Torvalds's avatar
Linus Torvalds committed
 *					logic
 *		Alan Cox	:	Allow >4K in /proc
 *		Alan Cox	:	Make ARP add its own protocol entry
 *		Ross Martin     :       Rewrote arp_rcv() and arp_get_info()
 *		Stephen Henson	:	Add AX25 support to arp_get_info()
 *		Alan Cox	:	Drop data when a device is downed.
 *		Alan Cox	:	Use init_timer().
 *		Alan Cox	:	Double lock fixes.
 *		Martin Seine	:	Move the arphdr structure
 *					to if_arp.h for compatibility.
 *					with BSD based programs.
 *		Andrew Tridgell :       Added ARP netmask code and
 *					re-arranged proxy handling.
 *		Alan Cox	:	Changed to use notifiers.
 *		Niibe Yutaka	:	Reply for this device or proxies only.
 *		Alan Cox	:	Don't proxy across hardware types!
 *		Jonathan Naylor :	Added support for NET/ROM.
 *		Mike Shaver     :       RFC1122 checks.
 *		Jonathan Naylor :	Only lookup the hardware address for
 *					the correct hardware type.
 *		Germano Caronni	:	Assorted subtle races.
 *		Craig Schlenter :	Don't modify permanent entry
Linus Torvalds's avatar
Linus Torvalds committed
 *					during arp_rcv.
 *		Russ Nelson	:	Tidied up a few bits.
 *		Alexey Kuznetsov:	Major changes to caching and behaviour,
 *					eg intelligent arp probing and
Linus Torvalds's avatar
Linus Torvalds committed
 *					generation
 *					of host down events.
 *		Alan Cox	:	Missing unlock in device events.
 *		Eckes		:	ARP ioctl control errors.
 *		Alexey Kuznetsov:	Arp free fix.
 *		Manuel Rodriguez:	Gratuitous ARP.
 *              Jonathan Layes  :       Added arpd support through kerneld
Linus Torvalds's avatar
Linus Torvalds committed
 *                                      message queue (960314)
 *		Mike Shaver	:	/proc/sys/net/ipv4/arp_* support
 *		Mike McLagan    :	Routing by source
 *		Stuart Cheshire	:	Metricom and grat arp fixes
 *					*** FOR 2.1 clean this up ***
 *		Lawrence V. Stefani: (08/12/96) Added FDDI support.
Changli Gao's avatar
Changli Gao committed
 *		Alan Cox	:	Took the AP1000 nasty FDDI hack and
Linus Torvalds's avatar
Linus Torvalds committed
 *					folded into the mainstream FDDI code.
 *					Ack spit, Linus how did you allow that
 *					one in...
 *		Jes Sorensen	:	Make FDDI work again in 2.1.x and
 *					clean up the APFDDI & gen. FDDI bits.
 *		Alexey Kuznetsov:	new arp state machine;
 *					now it is in net/core/neighbour.c.
 *		Krzysztof Halasa:	Added Frame Relay ARP support.
 *		Arnaldo C. Melo :	convert /proc/net/arp to seq_file
 *		Shmulik Hen:		Split arp_send to arp_create and
 *					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
 *		Jesper D. Brouer:       Proxy ARP PVLAN RFC 3069 support.
Linus Torvalds's avatar
Linus Torvalds committed
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/capability.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/mm.h>
#include <linux/inet.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/fddidevice.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/net.h>
#include <linux/rcupdate.h>
Linus Torvalds's avatar
Linus Torvalds committed
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif

#include <net/net_namespace.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <net/ip.h>
#include <net/icmp.h>
#include <net/route.h>
#include <net/protocol.h>
#include <net/tcp.h>
#include <net/sock.h>
#include <net/arp.h>
#include <net/ax25.h>
#include <net/netrom.h>

Changli Gao's avatar
Changli Gao committed
#include <linux/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed

#include <linux/netfilter_arp.h>

/*
 *	Interface to generic neighbour cache.
 */
static u32 arp_hash(const void *pkey, const struct net_device *dev, __u32 *hash_rnd);
Linus Torvalds's avatar
Linus Torvalds committed
static int arp_constructor(struct neighbour *neigh);
static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb);
static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb);
static void parp_redo(struct sk_buff *skb);

static const struct neigh_ops arp_generic_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
	.family =		AF_INET,
	.solicit =		arp_solicit,
	.error_report =		arp_error_report,
	.output =		neigh_resolve_output,
	.connected_output =	neigh_connected_output,
};

static const struct neigh_ops arp_hh_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
	.family =		AF_INET,
	.solicit =		arp_solicit,
	.error_report =		arp_error_report,
	.output =		neigh_resolve_output,
	.connected_output =	neigh_resolve_output,
};

static const struct neigh_ops arp_direct_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
	.family =		AF_INET,
	.output =		neigh_direct_output,
	.connected_output =	neigh_direct_output,
Linus Torvalds's avatar
Linus Torvalds committed
};

static const struct neigh_ops arp_broken_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
	.family =		AF_INET,
	.solicit =		arp_solicit,
	.error_report =		arp_error_report,
	.output =		neigh_compat_output,
	.connected_output =	neigh_compat_output,
};

struct neigh_table arp_tbl = {
Changli Gao's avatar
Changli Gao committed
	.family		= AF_INET,
	.key_len	= 4,
	.hash		= arp_hash,
	.constructor	= arp_constructor,
	.proxy_redo	= parp_redo,
	.id		= "arp_cache",
	.parms		= {
		.tbl			= &arp_tbl,
		.base_reachable_time	= 30 * HZ,
		.retrans_time		= 1 * HZ,
		.gc_staletime		= 60 * HZ,
		.reachable_time		= 30 * HZ,
		.delay_probe_time	= 5 * HZ,
		.queue_len_bytes	= 64*1024,
Changli Gao's avatar
Changli Gao committed
		.ucast_probes		= 3,
		.mcast_probes		= 3,
		.anycast_delay		= 1 * HZ,
		.proxy_delay		= (8 * HZ) / 10,
		.proxy_qlen		= 64,
		.locktime		= 1 * HZ,
Linus Torvalds's avatar
Linus Torvalds committed
	},
Changli Gao's avatar
Changli Gao committed
	.gc_interval	= 30 * HZ,
	.gc_thresh1	= 128,
	.gc_thresh2	= 512,
	.gc_thresh3	= 1024,
Linus Torvalds's avatar
Linus Torvalds committed
};
EXPORT_SYMBOL(arp_tbl);
Linus Torvalds's avatar
Linus Torvalds committed

int arp_mc_map(__be32 addr, u8 *haddr, struct net_device *dev, int dir)
Linus Torvalds's avatar
Linus Torvalds committed
{
	switch (dev->type) {
	case ARPHRD_ETHER:
	case ARPHRD_FDDI:
	case ARPHRD_IEEE802:
		ip_eth_mc_map(addr, haddr);
Linus Torvalds's avatar
Linus Torvalds committed
	case ARPHRD_INFINIBAND:
		ip_ib_mc_map(addr, dev->broadcast, haddr);
Linus Torvalds's avatar
Linus Torvalds committed
		return 0;
	case ARPHRD_IPGRE:
		ip_ipgre_mc_map(addr, dev->broadcast, haddr);
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
	default:
		if (dir) {
			memcpy(haddr, dev->broadcast, dev->addr_len);
			return 0;
		}
	}
	return -EINVAL;
}


static u32 arp_hash(const void *pkey,
		    const struct net_device *dev,
		    __u32 *hash_rnd)
Linus Torvalds's avatar
Linus Torvalds committed
{
	return arp_hashfn(*(u32 *)pkey, dev, *hash_rnd);
Linus Torvalds's avatar
Linus Torvalds committed
}

static int arp_constructor(struct neighbour *neigh)
{
Changli Gao's avatar
Changli Gao committed
	__be32 addr = *(__be32 *)neigh->primary_key;
Linus Torvalds's avatar
Linus Torvalds committed
	struct net_device *dev = neigh->dev;
	struct in_device *in_dev;
	struct neigh_parms *parms;

	rcu_read_lock();
	in_dev = __in_dev_get_rcu(dev);
Linus Torvalds's avatar
Linus Torvalds committed
	if (in_dev == NULL) {
		rcu_read_unlock();
		return -EINVAL;
	}

	neigh->type = inet_addr_type(dev_net(dev), addr);
Linus Torvalds's avatar
Linus Torvalds committed
	parms = in_dev->arp_parms;
	__neigh_parms_put(neigh->parms);
	neigh->parms = neigh_parms_clone(parms);
	rcu_read_unlock();

Linus Torvalds's avatar
Linus Torvalds committed
		neigh->nud_state = NUD_NOARP;
		neigh->ops = &arp_direct_ops;
		neigh->output = neigh_direct_output;
Linus Torvalds's avatar
Linus Torvalds committed
	} else {
		/* Good devices (checked by reading texts, but only Ethernet is
		   tested)

		   ARPHRD_ETHER: (ethernet, apfddi)
		   ARPHRD_FDDI: (fddi)
		   ARPHRD_IEEE802: (tr)
		   ARPHRD_METRICOM: (strip)
		   ARPHRD_ARCNET:
		   etc. etc. etc.

		   ARPHRD_IPDDP will also work, if author repairs it.
		   I did not it, because this driver does not work even
		   in old paradigm.
		 */

#if 1
		/* So... these "amateur" devices are hopeless.
		   The only thing, that I can say now:
		   It is very sad that we need to keep ugly obsolete
		   code to make them happy.

		   They should be moved to more reasonable state, now
		   they use rebuild_header INSTEAD OF hard_start_xmit!!!
		   Besides that, they are sort of out of date
		   (a lot of redundant clones/copies, useless in 2.1),
		   I wonder why people believe that they work.
		 */
		switch (dev->type) {
		default:
			break;
		case ARPHRD_ROSE:
#if IS_ENABLED(CONFIG_AX25)
Linus Torvalds's avatar
Linus Torvalds committed
		case ARPHRD_AX25:
#if IS_ENABLED(CONFIG_NETROM)
Linus Torvalds's avatar
Linus Torvalds committed
		case ARPHRD_NETROM:
#endif
			neigh->ops = &arp_broken_ops;
			neigh->output = neigh->ops->output;
			return 0;
Changli Gao's avatar
Changli Gao committed
#else
			break;
Linus Torvalds's avatar
Linus Torvalds committed
#endif
Changli Gao's avatar
Changli Gao committed
		}
Linus Torvalds's avatar
Linus Torvalds committed
#endif
		if (neigh->type == RTN_MULTICAST) {
			neigh->nud_state = NUD_NOARP;
			arp_mc_map(addr, neigh->ha, dev, 1);
Changli Gao's avatar
Changli Gao committed
		} else if (dev->flags & (IFF_NOARP | IFF_LOOPBACK)) {
Linus Torvalds's avatar
Linus Torvalds committed
			neigh->nud_state = NUD_NOARP;
			memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
Changli Gao's avatar
Changli Gao committed
		} else if (neigh->type == RTN_BROADCAST ||
			   (dev->flags & IFF_POINTOPOINT)) {
Linus Torvalds's avatar
Linus Torvalds committed
			neigh->nud_state = NUD_NOARP;
			memcpy(neigh->ha, dev->broadcast, dev->addr_len);
		}
Linus Torvalds's avatar
Linus Torvalds committed
			neigh->ops = &arp_hh_ops;
		else
			neigh->ops = &arp_generic_ops;
Changli Gao's avatar
Changli Gao committed
		if (neigh->nud_state & NUD_VALID)
Linus Torvalds's avatar
Linus Torvalds committed
			neigh->output = neigh->ops->connected_output;
		else
			neigh->output = neigh->ops->output;
	}
	return 0;
}

static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb)
{
	dst_link_failure(skb);
	kfree_skb(skb);
}

static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
{
	__be32 saddr = 0;
	u8 dst_ha[MAX_ADDR_LEN];
Linus Torvalds's avatar
Linus Torvalds committed
	struct net_device *dev = neigh->dev;
Changli Gao's avatar
Changli Gao committed
	__be32 target = *(__be32 *)neigh->primary_key;
Linus Torvalds's avatar
Linus Torvalds committed
	int probes = atomic_read(&neigh->probes);
	struct in_device *in_dev;
Linus Torvalds's avatar
Linus Torvalds committed

	rcu_read_lock();
	in_dev = __in_dev_get_rcu(dev);
	if (!in_dev) {
		rcu_read_unlock();
Linus Torvalds's avatar
Linus Torvalds committed
		return;
Linus Torvalds's avatar
Linus Torvalds committed
	switch (IN_DEV_ARP_ANNOUNCE(in_dev)) {
	default:
	case 0:		/* By default announce any local IP */
Changli Gao's avatar
Changli Gao committed
		if (skb && inet_addr_type(dev_net(dev),
					  ip_hdr(skb)->saddr) == RTN_LOCAL)
			saddr = ip_hdr(skb)->saddr;
Linus Torvalds's avatar
Linus Torvalds committed
		break;
	case 1:		/* Restrict announcements of saddr in same subnet */
		if (!skb)
			break;
		saddr = ip_hdr(skb)->saddr;
		if (inet_addr_type(dev_net(dev), saddr) == RTN_LOCAL) {
Linus Torvalds's avatar
Linus Torvalds committed
			/* saddr should be known to target */
			if (inet_addr_onlink(in_dev, target, saddr))
				break;
		}
		saddr = 0;
		break;
	case 2:		/* Avoid secondary IPs, get a primary/preferred one */
		break;
	}
	rcu_read_unlock();
Linus Torvalds's avatar
Linus Torvalds committed

	if (!saddr)
		saddr = inet_select_addr(dev, target, RT_SCOPE_LINK);

Changli Gao's avatar
Changli Gao committed
	probes -= neigh->parms->ucast_probes;
	if (probes < 0) {
		if (!(neigh->nud_state & NUD_VALID))
			pr_debug("trying to ucast probe in NUD_INVALID\n");
		neigh_ha_snapshot(dst_ha, neigh, dev);
Changli Gao's avatar
Changli Gao committed
	} else {
		memset(dst_ha, 0, dev->addr_len);
Changli Gao's avatar
Changli Gao committed
		probes -= neigh->parms->app_probes;
		if (probes < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
#ifdef CONFIG_ARPD
Changli Gao's avatar
Changli Gao committed
			neigh_app_ns(neigh);
Linus Torvalds's avatar
Linus Torvalds committed
#endif
Changli Gao's avatar
Changli Gao committed
			return;
		}
Linus Torvalds's avatar
Linus Torvalds committed
	}

	arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,
		 dst_ha, dev->dev_addr, NULL);
}

static int arp_ignore(struct in_device *in_dev, __be32 sip, __be32 tip)
Linus Torvalds's avatar
Linus Torvalds committed
{
	int scope;

	switch (IN_DEV_ARP_IGNORE(in_dev)) {
	case 0:	/* Reply, the tip is already validated */
		return 0;
	case 1:	/* Reply only if tip is configured on the incoming interface */
		sip = 0;
		scope = RT_SCOPE_HOST;
		break;
	case 2:	/*
		 * Reply only if tip is configured on the incoming interface
		 * and is in same subnet as sip
		 */
		scope = RT_SCOPE_HOST;
		break;
	case 3:	/* Do not reply for scope host addresses */
		sip = 0;
		scope = RT_SCOPE_LINK;
		break;
	case 4:	/* Reserved */
	case 5:
	case 6:
	case 7:
		return 0;
	case 8:	/* Do not reply */
		return 1;
	default:
		return 0;
	}
	return !inet_confirm_addr(in_dev, sip, tip, scope);
static int arp_filter(__be32 sip, __be32 tip, struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct rtable *rt;
	int flag = 0;
Linus Torvalds's avatar
Linus Torvalds committed
	/*unsigned long now; */
	struct net *net = dev_net(dev);
Linus Torvalds's avatar
Linus Torvalds committed

	rt = ip_route_output(net, sip, tip, 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
		return 1;
	if (rt->dst.dev != dev) {
		NET_INC_STATS_BH(net, LINUX_MIB_ARPFILTER);
Linus Torvalds's avatar
Linus Torvalds committed
		flag = 1;
	}
	ip_rt_put(rt);
	return flag;
}
Linus Torvalds's avatar
Linus Torvalds committed

/* OBSOLETE FUNCTIONS */

/*
 *	Find an arp mapping in the cache. If not found, post a request.
 *
 *	It is very UGLY routine: it DOES NOT use skb->dst->neighbour,
 *	even if it exists. It is supposed that skb->dev was mangled
 *	by a virtual device (eql, shaper). Nobody but broken devices
 *	is allowed to use this function, it is scheduled to be removed. --ANK
 */

Changli Gao's avatar
Changli Gao committed
static int arp_set_predefined(int addr_hint, unsigned char *haddr,
			      __be32 paddr, struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
{
	switch (addr_hint) {
	case RTN_LOCAL:
		pr_debug("arp called for own IP address\n");
Linus Torvalds's avatar
Linus Torvalds committed
		memcpy(haddr, dev->dev_addr, dev->addr_len);
		return 1;
	case RTN_MULTICAST:
		arp_mc_map(paddr, haddr, dev, 1);
		return 1;
	case RTN_BROADCAST:
		memcpy(haddr, dev->broadcast, dev->addr_len);
		return 1;
	}
	return 0;
}


int arp_find(unsigned char *haddr, struct sk_buff *skb)
{
	struct net_device *dev = skb->dev;
	__be32 paddr;
Linus Torvalds's avatar
Linus Torvalds committed
	struct neighbour *n;

Eric Dumazet's avatar
Eric Dumazet committed
	if (!skb_dst(skb)) {
		pr_debug("arp_find is called with dst==NULL\n");
Linus Torvalds's avatar
Linus Torvalds committed
		kfree_skb(skb);
		return 1;
	}

	paddr = rt_nexthop(skb_rtable(skb), ip_hdr(skb)->daddr);
Changli Gao's avatar
Changli Gao committed
	if (arp_set_predefined(inet_addr_type(dev_net(dev), paddr), haddr,
			       paddr, dev))
Linus Torvalds's avatar
Linus Torvalds committed
		return 0;

	n = __neigh_lookup(&arp_tbl, &paddr, dev, 1);

	if (n) {
		n->used = jiffies;
		if (n->nud_state & NUD_VALID || neigh_event_send(n, skb) == 0) {
			neigh_ha_snapshot(haddr, n, dev);
Linus Torvalds's avatar
Linus Torvalds committed
			neigh_release(n);
			return 0;
		}
		neigh_release(n);
	} else
		kfree_skb(skb);
	return 1;
}
EXPORT_SYMBOL(arp_find);
Linus Torvalds's avatar
Linus Torvalds committed

/* END OF OBSOLETE FUNCTIONS */

/*
 * Check if we can use proxy ARP for this path
 */
static inline int arp_fwd_proxy(struct in_device *in_dev,
				struct net_device *dev,	struct rtable *rt)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct in_device *out_dev;
	int imi, omi = -1;

	if (rt->dst.dev == dev)
Linus Torvalds's avatar
Linus Torvalds committed
	if (!IN_DEV_PROXY_ARP(in_dev))
		return 0;
Changli Gao's avatar
Changli Gao committed
	imi = IN_DEV_MEDIUM_ID(in_dev);
	if (imi == 0)
Linus Torvalds's avatar
Linus Torvalds committed
		return 1;
	if (imi == -1)
		return 0;

	/* place to check for proxy_arp for routes */

	out_dev = __in_dev_get_rcu(rt->dst.dev);
Eric Dumazet's avatar
Eric Dumazet committed
	if (out_dev)
Linus Torvalds's avatar
Linus Torvalds committed
		omi = IN_DEV_MEDIUM_ID(out_dev);
Eric Dumazet's avatar
Eric Dumazet committed

	return omi != imi && omi != -1;
/*
 * Check for RFC3069 proxy arp private VLAN (allow to send back to same dev)
 *
 * RFC3069 supports proxy arp replies back to the same interface.  This
 * is done to support (ethernet) switch features, like RFC 3069, where
 * the individual ports are not allowed to communicate with each
 * other, BUT they are allowed to talk to the upstream router.  As
 * described in RFC 3069, it is possible to allow these hosts to
 * communicate through the upstream router, by proxy_arp'ing.
 *
 * RFC 3069: "VLAN Aggregation for Efficient IP Address Allocation"
 *
 *  This technology is known by different names:
 *    In RFC 3069 it is called VLAN Aggregation.
 *    Cisco and Allied Telesyn call it Private VLAN.
 *    Hewlett-Packard call it Source-Port filtering or port-isolation.
 *    Ericsson call it MAC-Forced Forwarding (RFC Draft).
 *
 */
static inline int arp_fwd_pvlan(struct in_device *in_dev,
				struct net_device *dev,	struct rtable *rt,
				__be32 sip, __be32 tip)
{
	/* Private VLAN is only concerned about the same ethernet segment */
	if (rt->dst.dev != dev)
		return 0;

	/* Don't reply on self probes (often done by windowz boxes)*/
	if (sip == tip)
		return 0;

	if (IN_DEV_PROXY_ARP_PVLAN(in_dev))
		return 1;
	else
		return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
/*
 *	Interface to link layer: send routine and receive handler.
 */

/*
 *	Create an arp packet. If (dest_hw == NULL), we create a broadcast
 *	message.
 */
struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip,
			   struct net_device *dev, __be32 src_ip,
			   const unsigned char *dest_hw,
			   const unsigned char *src_hw,
			   const unsigned char *target_hw)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct sk_buff *skb;
	struct arphdr *arp;
	unsigned char *arp_ptr;
	int hlen = LL_RESERVED_SPACE(dev);
	int tlen = dev->needed_tailroom;
Linus Torvalds's avatar
Linus Torvalds committed

	/*
	 *	Allocate a buffer
	 */
	skb = alloc_skb(arp_hdr_len(dev) + hlen + tlen, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
	if (skb == NULL)
		return NULL;

	skb_reserve(skb, hlen);
	skb_reset_network_header(skb);
	arp = (struct arphdr *) skb_put(skb, arp_hdr_len(dev));
Linus Torvalds's avatar
Linus Torvalds committed
	skb->dev = dev;
	skb->protocol = htons(ETH_P_ARP);
	if (src_hw == NULL)
		src_hw = dev->dev_addr;
	if (dest_hw == NULL)
		dest_hw = dev->broadcast;

	/*
	 *	Fill the device header for the ARP frame
	 */
	if (dev_hard_header(skb, dev, ptype, dest_hw, src_hw, skb->len) < 0)
Linus Torvalds's avatar
Linus Torvalds committed
		goto out;

	/*
	 * Fill out the arp protocol part.
	 *
	 * The arp hardware type should match the device type, except for FDDI,
	 * which (according to RFC 1390) should always equal 1 (Ethernet).
	 */
	/*
	 *	Exceptions everywhere. AX.25 uses the AX.25 PID value not the
	 *	DIX code for the protocol. Make these device structure fields.
	 */
	switch (dev->type) {
	default:
		arp->ar_hrd = htons(dev->type);
		arp->ar_pro = htons(ETH_P_IP);
		break;

#if IS_ENABLED(CONFIG_AX25)
Linus Torvalds's avatar
Linus Torvalds committed
	case ARPHRD_AX25:
		arp->ar_hrd = htons(ARPHRD_AX25);
		arp->ar_pro = htons(AX25_P_IP);
		break;

#if IS_ENABLED(CONFIG_NETROM)
Linus Torvalds's avatar
Linus Torvalds committed
	case ARPHRD_NETROM:
		arp->ar_hrd = htons(ARPHRD_NETROM);
		arp->ar_pro = htons(AX25_P_IP);
		break;
#endif
#endif

#if IS_ENABLED(CONFIG_FDDI)
Linus Torvalds's avatar
Linus Torvalds committed
	case ARPHRD_FDDI:
		arp->ar_hrd = htons(ARPHRD_ETHER);
		arp->ar_pro = htons(ETH_P_IP);
		break;
#endif
	}

	arp->ar_hln = dev->addr_len;
	arp->ar_pln = 4;
	arp->ar_op = htons(type);

Changli Gao's avatar
Changli Gao committed
	arp_ptr = (unsigned char *)(arp + 1);
Linus Torvalds's avatar
Linus Torvalds committed

	memcpy(arp_ptr, src_hw, dev->addr_len);
	arp_ptr += dev->addr_len;
	memcpy(arp_ptr, &src_ip, 4);
	arp_ptr += 4;
Linus Torvalds's avatar
Linus Torvalds committed
	if (target_hw != NULL)
		memcpy(arp_ptr, target_hw, dev->addr_len);
	else
		memset(arp_ptr, 0, dev->addr_len);
	arp_ptr += dev->addr_len;
Linus Torvalds's avatar
Linus Torvalds committed
	memcpy(arp_ptr, &dest_ip, 4);

	return skb;

out:
	kfree_skb(skb);
	return NULL;
}
EXPORT_SYMBOL(arp_create);
Linus Torvalds's avatar
Linus Torvalds committed

/*
 *	Send an arp packet.
 */
void arp_xmit(struct sk_buff *skb)
{
	/* Send it off, maybe filter it using firewalling first.  */
	NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, skb, NULL, skb->dev, dev_queue_xmit);
Linus Torvalds's avatar
Linus Torvalds committed
}
EXPORT_SYMBOL(arp_xmit);
Linus Torvalds's avatar
Linus Torvalds committed

/*
 *	Create and send an arp packet.
 */
void arp_send(int type, int ptype, __be32 dest_ip,
	      struct net_device *dev, __be32 src_ip,
	      const unsigned char *dest_hw, const unsigned char *src_hw,
	      const unsigned char *target_hw)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct sk_buff *skb;

	/*
	 *	No arp on this interface.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
	if (dev->flags&IFF_NOARP)
		return;

	skb = arp_create(type, ptype, dest_ip, dev, src_ip,
			 dest_hw, src_hw, target_hw);
Changli Gao's avatar
Changli Gao committed
	if (skb == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
		return;

	arp_xmit(skb);
}
EXPORT_SYMBOL(arp_send);
Linus Torvalds's avatar
Linus Torvalds committed

/*
 *	Process an arp request.
 */

static int arp_process(struct sk_buff *skb)
{
	struct net_device *dev = skb->dev;
Eric Dumazet's avatar
Eric Dumazet committed
	struct in_device *in_dev = __in_dev_get_rcu(dev);
Linus Torvalds's avatar
Linus Torvalds committed
	struct arphdr *arp;
	unsigned char *arp_ptr;
	struct rtable *rt;
	unsigned char *sha;
	__be32 sip, tip;
Linus Torvalds's avatar
Linus Torvalds committed
	u16 dev_type = dev->type;
	int addr_type;
	struct neighbour *n;
	struct net *net = dev_net(dev);
Linus Torvalds's avatar
Linus Torvalds committed

	/* arp_rcv below verifies the ARP header and verifies the device
	 * is ARP'able.
	 */

	if (in_dev == NULL)
		goto out;

Linus Torvalds's avatar
Linus Torvalds committed

	switch (dev_type) {
Linus Torvalds's avatar
Linus Torvalds committed
		if (arp->ar_pro != htons(ETH_P_IP) ||
		    htons(dev_type) != arp->ar_hrd)
			goto out;
		break;
	case ARPHRD_ETHER:
	case ARPHRD_FDDI:
	case ARPHRD_IEEE802:
		/*
		 * ETHERNET, and Fibre Channel (which are IEEE 802
Linus Torvalds's avatar
Linus Torvalds committed
		 * devices, according to RFC 2625) devices will accept ARP
		 * hardware types of either 1 (Ethernet) or 6 (IEEE 802.2).
		 * This is the case also of FDDI, where the RFC 1390 says that
		 * FDDI devices should accept ARP hardware of (1) Ethernet,
		 * however, to be more robust, we'll accept both 1 (Ethernet)
		 * or 6 (IEEE 802.2)
		 */
		if ((arp->ar_hrd != htons(ARPHRD_ETHER) &&
		     arp->ar_hrd != htons(ARPHRD_IEEE802)) ||
		    arp->ar_pro != htons(ETH_P_IP))
			goto out;
		break;
	case ARPHRD_AX25:
		if (arp->ar_pro != htons(AX25_P_IP) ||
		    arp->ar_hrd != htons(ARPHRD_AX25))
			goto out;
		break;
	case ARPHRD_NETROM:
		if (arp->ar_pro != htons(AX25_P_IP) ||
		    arp->ar_hrd != htons(ARPHRD_NETROM))
			goto out;
		break;
	}

	/* Understand only these message types */

	if (arp->ar_op != htons(ARPOP_REPLY) &&
	    arp->ar_op != htons(ARPOP_REQUEST))
		goto out;

/*
 *	Extract fields
 */
Changli Gao's avatar
Changli Gao committed
	arp_ptr = (unsigned char *)(arp + 1);
Linus Torvalds's avatar
Linus Torvalds committed
	sha	= arp_ptr;
	arp_ptr += dev->addr_len;
	memcpy(&sip, arp_ptr, 4);
	arp_ptr += 4;
	arp_ptr += dev->addr_len;
	memcpy(&tip, arp_ptr, 4);
Linus Torvalds's avatar
Linus Torvalds committed
 *	Check for bad requests for 127.x.x.x and requests for multicast
 *	addresses.  If this is one such, delete it.
 */
	if (ipv4_is_multicast(tip) ||
	    (!IN_DEV_ROUTE_LOCALNET(in_dev) && ipv4_is_loopback(tip)))
Linus Torvalds's avatar
Linus Torvalds committed
		goto out;

/*
 *     Special case: We must set Frame Relay source Q.922 address
 */
	if (dev_type == ARPHRD_DLCI)
		sha = dev->broadcast;

/*
 *  Process entry.  The idea here is we want to send a reply if it is a
 *  request for us or if it is a request for someone else that we hold
 *  a proxy for.  We want to add an entry to our cache if it is a reply
 *  to us or if it is a request for our address.
 *  (The assumption for this last is that if someone is requesting our
 *  address, they are probably intending to talk to us, so it saves time
 *  if we cache their address.  Their address is also probably not in
Linus Torvalds's avatar
Linus Torvalds committed
 *  our cache, since ours is not in their cache.)
Linus Torvalds's avatar
Linus Torvalds committed
 *  Putting this another way, we only care about replies if they are to
 *  us, in which case we add them to the cache.  For requests, we care
 *  about those for us and those for our proxies.  We reply to both,
 *  and in the case of requests for us we add the requester to the arp
Linus Torvalds's avatar
Linus Torvalds committed
 *  cache.
 */

	/* Special case: IPv4 duplicate address detection packet (RFC2131) */
	if (sip == 0) {
Linus Torvalds's avatar
Linus Torvalds committed
		if (arp->ar_op == htons(ARPOP_REQUEST) &&
		    inet_addr_type(net, tip) == RTN_LOCAL &&
		    !arp_ignore(in_dev, sip, tip))
			arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha,
				 dev->dev_addr, sha);
Linus Torvalds's avatar
Linus Torvalds committed
		goto out;
	}

	if (arp->ar_op == htons(ARPOP_REQUEST) &&
	    ip_route_input_noref(skb, tip, sip, 0, dev) == 0) {
Linus Torvalds's avatar
Linus Torvalds committed

Eric Dumazet's avatar
Eric Dumazet committed
		rt = skb_rtable(skb);
Linus Torvalds's avatar
Linus Torvalds committed
		addr_type = rt->rt_type;

		if (addr_type == RTN_LOCAL) {
Changli Gao's avatar
Changli Gao committed
			int dont_send;
Changli Gao's avatar
Changli Gao committed
			dont_send = arp_ignore(in_dev, sip, tip);
			if (!dont_send && IN_DEV_ARPFILTER(in_dev))
Changli Gao's avatar
Changli Gao committed
				dont_send = arp_filter(sip, tip, dev);
			if (!dont_send) {
				n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
				if (n) {
Changli Gao's avatar
Changli Gao committed
					arp_send(ARPOP_REPLY, ETH_P_ARP, sip,
						 dev, tip, sha, dev->dev_addr,
						 sha);
					neigh_release(n);
				}
Linus Torvalds's avatar
Linus Torvalds committed
			}
			goto out;
		} else if (IN_DEV_FORWARD(in_dev)) {
			if (addr_type == RTN_UNICAST  &&
			    (arp_fwd_proxy(in_dev, dev, rt) ||
			     arp_fwd_pvlan(in_dev, dev, rt, sip, tip) ||
			     (rt->dst.dev != dev &&
			      pneigh_lookup(&arp_tbl, net, &tip, dev, 0)))) {
Linus Torvalds's avatar
Linus Torvalds committed
				n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
				if (n)
					neigh_release(n);

				if (NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED ||
Linus Torvalds's avatar
Linus Torvalds committed
				    skb->pkt_type == PACKET_HOST ||
				    in_dev->arp_parms->proxy_delay == 0) {
Changli Gao's avatar
Changli Gao committed
					arp_send(ARPOP_REPLY, ETH_P_ARP, sip,
						 dev, tip, sha, dev->dev_addr,
						 sha);
Linus Torvalds's avatar
Linus Torvalds committed
				} else {
Changli Gao's avatar
Changli Gao committed
					pneigh_enqueue(&arp_tbl,
						       in_dev->arp_parms, skb);
Linus Torvalds's avatar
Linus Torvalds committed
					return 0;
				}
				goto out;
			}
		}
	}

	/* Update our ARP tables */

	n = __neigh_lookup(&arp_tbl, &sip, dev, 0);

	if (IN_DEV_ARP_ACCEPT(in_dev)) {
		/* 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) ||
		     (arp->ar_op == htons(ARPOP_REQUEST) && tip == sip)) &&
		    inet_addr_type(net, sip) == RTN_UNICAST)
			n = __neigh_lookup(&arp_tbl, &sip, dev, 1);
Linus Torvalds's avatar
Linus Torvalds committed

	if (n) {
		int state = NUD_REACHABLE;
		int override;

		/* If several different ARP replies follows back-to-back,
		   use the FIRST one. It is possible, if several proxy
		   agents are active. Taking the first reply prevents
		   arp trashing and chooses the fastest router.
		 */
		override = time_after(jiffies, n->updated + n->parms->locktime);

		/* Broadcast replies and request packets
		   do not assert neighbour reachability.
		 */
		if (arp->ar_op != htons(ARPOP_REPLY) ||
		    skb->pkt_type != PACKET_HOST)
			state = NUD_STALE;
Changli Gao's avatar
Changli Gao committed
		neigh_update(n, sha, state,
			     override ? NEIGH_UPDATE_F_OVERRIDE : 0);
Linus Torvalds's avatar
Linus Torvalds committed
		neigh_release(n);
	}

out:
Linus Torvalds's avatar
Linus Torvalds committed
	return 0;
}

static void parp_redo(struct sk_buff *skb)
{
	arp_process(skb);
}

Linus Torvalds's avatar
Linus Torvalds committed

/*
 *	Receive an arp request from the device layer.
 */

Adrian Bunk's avatar
Adrian Bunk committed
static int arp_rcv(struct sk_buff *skb, struct net_device *dev,
		   struct packet_type *pt, struct net_device *orig_dev)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct arphdr *arp;

	/* ARP header, plus 2 device addresses, plus 2 IP addresses.  */
	if (!pskb_may_pull(skb, arp_hdr_len(dev)))
Linus Torvalds's avatar
Linus Torvalds committed
		goto freeskb;

Linus Torvalds's avatar
Linus Torvalds committed
	if (arp->ar_hln != dev->addr_len ||
	    dev->flags & IFF_NOARP ||
	    skb->pkt_type == PACKET_OTHERHOST ||
	    skb->pkt_type == PACKET_LOOPBACK ||
	    arp->ar_pln != 4)
		goto freeskb;

Changli Gao's avatar
Changli Gao committed
	skb = skb_share_check(skb, GFP_ATOMIC);
	if (skb == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
		goto out_of_mem;

	memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));

	return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, skb, dev, NULL, arp_process);
Linus Torvalds's avatar
Linus Torvalds committed

freeskb:
	kfree_skb(skb);
out_of_mem:
	return 0;
}

/*
 *	User level interface (ioctl)
 */

/*
 *	Set (create) an ARP cache entry.
 */

static int arp_req_set_proxy(struct net *net, struct net_device *dev, int on)
		IPV4_DEVCONF_ALL(net, PROXY_ARP) = on;
	if (__in_dev_get_rtnl(dev)) {
		IN_DEV_CONF_SET(__in_dev_get_rtnl(dev), PROXY_ARP, on);
static int arp_req_set_public(struct net *net, struct arpreq *r,
		struct net_device *dev)
{
	__be32 ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
	__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_rcu(net, r->arp_ha.sa_family,
Changli Gao's avatar
Changli Gao committed
				      r->arp_ha.sa_data);
		if (!dev)
			return -ENODEV;
	}
	if (mask) {
		if (pneigh_lookup(&arp_tbl, net, &ip, dev, 1) == NULL)
	return arp_req_set_proxy(net, dev, 1);