Skip to content
Snippets Groups Projects
arp.c 34.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* 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
     */
    
    #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/trdevice.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_IEEE802_TR:
    		ip_tr_mc_map(addr, haddr);
    		return 0;
    	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;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	u8  *dst_ha = NULL;
    	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))
    			printk(KERN_DEBUG
    			       "trying to ucast probe in NUD_INVALID\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		dst_ha = neigh->ha;
    
    		read_lock_bh(&neigh->lock);
    
    Changli Gao's avatar
    Changli Gao committed
    	} else {
    		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);
    
    	if (dst_ha)
    		read_unlock_bh(&neigh->lock);
    
    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:
    		printk(KERN_DEBUG "ARP: arp called for own IP address\n");
    		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)) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		printk(KERN_DEBUG "arp_find is called with dst==NULL\n");
    		kfree_skb(skb);
    		return 1;
    	}
    
    
    Eric Dumazet's avatar
    Eric Dumazet committed
    	paddr = skb_rtable(skb)->rt_gateway;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    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
    
    #if IS_ENABLED(CONFIG_TR)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	case ARPHRD_IEEE802_TR:
    		arp->ar_hrd = htons(ARPHRD_IEEE802);
    		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_IEEE802_TR:
    	case ARPHRD_FDDI:
    	case ARPHRD_IEEE802:
    		/*
    		 * ETHERNET, Token Ring and Fibre Channel (which are IEEE 802
    		 * 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_loopback(tip) || ipv4_is_multicast(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)) {