diff -urp v3.7/linux/include/uapi/linux/rtnetlink.h linux/include/uapi/linux/rtnetlink.h
--- v3.7/linux/include/uapi/linux/rtnetlink.h	2012-12-12 11:55:50.000000000 +0200
+++ linux/include/uapi/linux/rtnetlink.h	2012-12-12 23:36:04.611377209 +0200
@@ -120,6 +120,13 @@ enum {
 	RTM_SETDCB,
 #define RTM_SETDCB RTM_SETDCB
 
+	RTM_NEWARPRULE	= 80,
+#define	RTM_NEWARPRULE	RTM_NEWARPRULE
+	RTM_DELARPRULE,
+#define	RTM_DELARPRULE	RTM_DELARPRULE
+	RTM_GETARPRULE,
+#define	RTM_GETARPRULE	RTM_GETARPRULE
+
 	__RTM_MAX,
 #define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
 };
@@ -516,6 +523,54 @@ enum {
 
 #define NDUSEROPT_MAX	(__NDUSEROPT_MAX - 1)
 
+/******************************************************************************
+ *		Definitions used in ARP tables administration
+ ****/
+
+#define ARPA_TABLE_INPUT	0
+#define ARPA_TABLE_OUTPUT	1
+#define ARPA_TABLE_FORWARD	2
+#define ARPA_TABLE_ALL		-1
+
+#define ARPM_F_PREFSRC		0x0001
+#define ARPM_F_WILDIIF		0x0002
+#define ARPM_F_WILDOIF		0x0004
+#define ARPM_F_BROADCAST	0x0008
+#define ARPM_F_UNICAST		0x0010
+
+struct arpmsg
+{
+	unsigned char		arpm_family;
+	unsigned char		arpm_table;
+	unsigned char		arpm_action;
+	unsigned char		arpm_from_len;
+	unsigned char		arpm_to_len;
+	unsigned char		arpm__pad1;
+	unsigned short		arpm__pad2;
+	unsigned		arpm_pref;
+	unsigned		arpm_flags;
+};
+
+enum
+{
+	ARPA_UNSPEC,
+	ARPA_FROM,			/* FROM IP prefix	*/
+	ARPA_TO,			/* TO IP prefix		*/
+	ARPA_LLFROM,			/* FROM LL prefix	*/
+	ARPA_LLTO,			/* TO LL prefix		*/
+	ARPA_LLSRC,			/* New SRC lladdr	*/
+	ARPA_LLDST,			/* New DST lladdr	*/
+	ARPA_IIF,			/* In interface prefix	*/
+	ARPA_OIF,			/* Out interface prefix	*/
+	ARPA_SRC,			/* New IP SRC		*/
+	ARPA_DST,			/* New IP DST, not used	*/
+	ARPA_PACKETS,			/* Packets		*/
+};
+
+#define ARPA_MAX	ARPA_PACKETS
+
+#define ARPA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct arpmsg))))
+
 #ifndef __KERNEL__
 /* RTnetlink multicast groups - backwards compatibility for userspace */
 #define RTMGRP_LINK		1
@@ -536,6 +591,8 @@ enum {
 #define RTMGRP_DECnet_IFADDR    0x1000
 #define RTMGRP_DECnet_ROUTE     0x4000
 
+#define RTMGRP_ARP		0x00010000
+
 #define RTMGRP_IPV6_PREFIX	0x20000
 #endif
 
@@ -587,6 +644,8 @@ enum rtnetlink_groups {
 #define RTNLGRP_PHONET_ROUTE	RTNLGRP_PHONET_ROUTE
 	RTNLGRP_DCB,
 #define RTNLGRP_DCB		RTNLGRP_DCB
+	RTNLGRP_ARP,
+#define RTNLGRP_ARP		RTNLGRP_ARP
 	__RTNLGRP_MAX
 };
 #define RTNLGRP_MAX	(__RTNLGRP_MAX - 1)
diff -urp v3.7/linux/net/core/rtnetlink.c linux/net/core/rtnetlink.c
--- v3.7/linux/net/core/rtnetlink.c	2012-12-12 11:55:55.000000000 +0200
+++ linux/net/core/rtnetlink.c	2012-12-12 23:36:04.613377160 +0200
@@ -527,6 +527,7 @@ static const int rtm_min[RTM_NR_FAMILIES
 	[RTM_FAM(RTM_NEWACTION)]    = NLMSG_LENGTH(sizeof(struct tcamsg)),
 	[RTM_FAM(RTM_GETMULTICAST)] = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
 	[RTM_FAM(RTM_GETANYCAST)]   = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
+	[RTM_FAM(RTM_GETARPRULE)]   = NLMSG_LENGTH(sizeof(struct arpmsg)),
 };
 
 static const int rta_max[RTM_NR_FAMILIES] =
@@ -539,6 +540,7 @@ static const int rta_max[RTM_NR_FAMILIES
 	[RTM_FAM(RTM_NEWTCLASS)]    = TCA_MAX,
 	[RTM_FAM(RTM_NEWTFILTER)]   = TCA_MAX,
 	[RTM_FAM(RTM_NEWACTION)]    = TCAA_MAX,
+	[RTM_FAM(RTM_GETARPRULE)]   = ARPA_MAX,
 };
 
 int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned int group, int echo)
diff -urp v3.7/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v3.7/linux/net/ipv4/arp.c	2012-10-01 10:46:02.000000000 +0300
+++ linux/net/ipv4/arp.c	2012-12-12 23:36:57.331379185 +0200
@@ -71,6 +71,7 @@
  *					sending (e.g. insert 8021q tag).
  *		Harald Welte	:	convert to make use of jenkins hash
  *		Jesper D. Brouer:       Proxy ARP PVLAN RFC 3069 support.
+ *		Julian Anastasov:	ARP filtering via netlink
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -95,6 +96,7 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/stat.h>
+#include <net/netlink.h>
 #include <linux/init.h>
 #include <linux/net.h>
 #include <linux/rcupdate.h>
@@ -186,6 +188,47 @@ struct neigh_table arp_tbl = {
 };
 EXPORT_SYMBOL(arp_tbl);
 
+struct arpf_node {
+	struct arpf_node *	at_next;
+	u32			at_pref;
+	u32			at_from;
+	u32			at_from_mask;
+	u32			at_to;
+	u32			at_to_mask;
+	u32			at_src;
+	atomic_t		at_packets;
+	atomic_t		at_refcnt;
+	unsigned		at_flags;
+	unsigned char		at_from_len;
+	unsigned char		at_to_len;
+	unsigned char		at_action;
+	char			at_dead;
+	unsigned char		at_llfrom_len;
+	unsigned char		at_llto_len;
+	unsigned char		at_llsrc_len;
+	unsigned char		at_lldst_len;
+	unsigned char		at_iif_len;
+	unsigned char		at_oif_len;
+	unsigned short		at__pad1;
+	unsigned char		at_llfrom[MAX_ADDR_LEN];
+	unsigned char		at_llto[MAX_ADDR_LEN];
+	unsigned char		at_llsrc[MAX_ADDR_LEN];
+	unsigned char		at_lldst[MAX_ADDR_LEN];
+	char			at_iif[IFNAMSIZ];
+	char			at_oif[IFNAMSIZ];
+};
+
+static struct arpf_node *arp_tabs[3];
+
+static struct kmem_cache *arpf_cachep;
+
+static DEFINE_RWLOCK(arpf_lock);
+
+static void
+arpf_send(int table, struct net *net, struct sk_buff *skb, u32 sip, u32 tip,
+	  unsigned char *from_hw, unsigned char *to_hw,
+	  struct net_device *idev, struct net_device *odev);
+
 int arp_mc_map(__be32 addr, u8 *haddr, struct net_device *dev, int dir)
 {
 	switch (dev->type) {
@@ -326,6 +369,7 @@ static void arp_solicit(struct neighbour
 	__be32 target = *(__be32 *)neigh->primary_key;
 	int probes = atomic_read(&neigh->probes);
 	struct in_device *in_dev;
+	unsigned char tha[MAX_ADDR_LEN];
 
 	rcu_read_lock();
 	in_dev = __in_dev_get_rcu(dev);
@@ -363,8 +407,10 @@ static void arp_solicit(struct neighbour
 	if (probes < 0) {
 		if (!(neigh->nud_state & NUD_VALID))
 			pr_debug("trying to ucast probe in NUD_INVALID\n");
-		dst_ha = neigh->ha;
+		dst_ha = tha;
 		read_lock_bh(&neigh->lock);
+		memcpy(dst_ha, neigh->ha, dev->addr_len);
+		read_unlock_bh(&neigh->lock);
 	} else {
 		probes -= neigh->parms->app_probes;
 		if (probes < 0) {
@@ -375,10 +421,7 @@ static void arp_solicit(struct neighbour
 		}
 	}
 
-	arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,
-		 dst_ha, dev->dev_addr, NULL);
-	if (dst_ha)
-		read_unlock_bh(&neigh->lock);
+	arpf_send(ARPA_TABLE_OUTPUT,dev_net(dev),skb,saddr,target,NULL,dst_ha,NULL,dev);
 }
 
 static int arp_ignore(struct in_device *in_dev, __be32 sip, __be32 tip)
@@ -718,7 +761,7 @@ static int arp_process(struct sk_buff *s
 	struct arphdr *arp;
 	unsigned char *arp_ptr;
 	struct rtable *rt;
-	unsigned char *sha;
+	unsigned char *sha, *tha;
 	__be32 sip, tip;
 	u16 dev_type = dev->type;
 	int addr_type;
@@ -783,6 +826,7 @@ static int arp_process(struct sk_buff *s
 	arp_ptr += dev->addr_len;
 	memcpy(&sip, arp_ptr, 4);
 	arp_ptr += 4;
+	tha	= arp_ptr;
 	arp_ptr += dev->addr_len;
 	memcpy(&tip, arp_ptr, 4);
 /*
@@ -821,8 +865,8 @@ static int arp_process(struct sk_buff *s
 		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);
+			arpf_send(ARPA_TABLE_INPUT,net,
+				  skb,sip,tip,sha,tha,dev,NULL);
 		goto out;
 	}
 
@@ -841,9 +885,8 @@ static int arp_process(struct sk_buff *s
 			if (!dont_send) {
 				n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
 				if (n) {
-					arp_send(ARPOP_REPLY, ETH_P_ARP, sip,
-						 dev, tip, sha, dev->dev_addr,
-						 sha);
+					arpf_send(ARPA_TABLE_INPUT,net,
+						  skb,sip,tip,sha,tha,dev,NULL);
 					neigh_release(n);
 				}
 			}
@@ -861,9 +904,9 @@ static int arp_process(struct sk_buff *s
 				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);
+					arpf_send(ARPA_TABLE_FORWARD,net,
+						  skb,sip,tip,sha,tha,dev,
+						  rt->dst.dev);
 				} else {
 					pneigh_enqueue(&arp_tbl,
 						       in_dev->arp_parms, skb);
@@ -1248,6 +1291,558 @@ void arp_ifdown(struct net_device *dev)
 }
 
 
+static void arpf_destroy(struct arpf_node *afp)
+{
+	if (!afp->at_dead) {
+		printk(KERN_ERR "Destroying alive arp table node %p from %08lx\n", afp,
+		       *(((unsigned long*)&afp)-1));
+		return;
+	}
+	kmem_cache_free(arpf_cachep, afp);
+}
+
+static inline void arpf_put(struct arpf_node *afp)
+{
+	if (atomic_dec_and_test(&afp->at_refcnt))
+		arpf_destroy(afp);
+}
+
+static inline struct arpf_node *
+arpf_lookup(int table, struct sk_buff *skb, u32 sip, u32 tip,
+	    unsigned char *from_hw, unsigned char *to_hw,
+	    struct net_device *idev, struct net_device *odev)
+{
+	int sz_iif = idev? strlen(idev->name) : 0;
+	int sz_oif = odev? strlen(odev->name) : 0;
+	int alen;
+	struct arpf_node *afp;
+
+	if (ARPA_TABLE_OUTPUT != table) {
+		alen = idev->addr_len;
+	} else {
+		if (!from_hw) from_hw = odev->dev_addr;
+		if (!to_hw) to_hw = odev->broadcast;
+		alen = odev->addr_len;
+	}
+
+	read_lock(&arpf_lock);
+	for (afp = arp_tabs[table]; afp; afp = afp->at_next) {
+		if ((tip ^ afp->at_to) & afp->at_to_mask)
+			continue;
+		if ((sip ^ afp->at_from) & afp->at_from_mask)
+			continue;
+		if (afp->at_llfrom_len &&
+		    (afp->at_llfrom_len > alen ||
+		     memcmp(from_hw, afp->at_llfrom, afp->at_llfrom_len)))
+			continue;
+		if (afp->at_llto_len &&
+		    (afp->at_llto_len > alen ||
+		     memcmp(to_hw, afp->at_llto, afp->at_llto_len)))
+			continue;
+		if (afp->at_iif_len &&
+		    (afp->at_iif_len > sz_iif ||
+		     memcmp(afp->at_iif, idev->name, afp->at_iif_len) ||
+		     (sz_iif != afp->at_iif_len &&
+		      !(afp->at_flags & ARPM_F_WILDIIF))))
+			continue;
+		if (afp->at_oif_len &&
+		    (afp->at_oif_len > sz_oif ||
+		     memcmp(afp->at_oif, odev->name, afp->at_oif_len) ||
+		     (sz_oif != afp->at_oif_len &&
+		      !(afp->at_flags & ARPM_F_WILDOIF))))
+			continue;
+		if (afp->at_flags & ARPM_F_BROADCAST &&
+		    skb->pkt_type == PACKET_HOST)
+			continue;
+		if (afp->at_flags & ARPM_F_UNICAST &&
+		    skb->pkt_type != PACKET_HOST)
+			continue;
+		if (afp->at_llsrc_len && afp->at_llsrc_len != alen)
+			continue;
+		if (afp->at_lldst_len && afp->at_lldst_len != alen)
+			continue;
+		atomic_inc(&afp->at_packets);
+		break;
+	}
+	read_unlock(&arpf_lock);
+	return afp;
+}
+
+static void
+arpf_send(int table, struct net *net, struct sk_buff *skb, u32 sip, u32 tip,
+	  unsigned char *from_hw, unsigned char *to_hw,
+	  struct net_device *idev, struct net_device *odev)
+{
+	struct arpf_node *afp = NULL;
+
+	if (!arp_tabs[table] ||
+	    net != &init_net ||
+	    !(afp = arpf_lookup(table, skb, sip, tip,
+				from_hw, to_hw, idev, odev))) {
+		switch (table) {
+		case ARPA_TABLE_INPUT:
+		case ARPA_TABLE_FORWARD:
+			arp_send(ARPOP_REPLY, ETH_P_ARP, sip, idev, tip,
+				 from_hw, idev->dev_addr, from_hw);
+			break;
+		case ARPA_TABLE_OUTPUT:
+			arp_send(ARPOP_REQUEST, ETH_P_ARP, tip, odev, sip,
+				 to_hw, odev->dev_addr, NULL);
+			break;
+		}
+		return;
+	}
+
+	/* deny? */
+	if (!afp->at_action) goto out;
+
+	switch (table) {
+	case ARPA_TABLE_INPUT:
+	case ARPA_TABLE_FORWARD:
+		arp_send(ARPOP_REPLY, ETH_P_ARP, sip, idev, tip,
+			 afp->at_lldst_len?afp->at_lldst:from_hw,
+			 afp->at_llsrc_len?afp->at_llsrc:idev->dev_addr,
+			 afp->at_lldst_len?afp->at_lldst:from_hw);
+		break;
+	case ARPA_TABLE_OUTPUT:
+		if (afp->at_flags & ARPM_F_PREFSRC && afp->at_src == 0) {
+			struct rtable *rt;
+			struct flowi4 fl4 = { .daddr = tip,
+					      .flowi4_oif = odev->ifindex };
+
+			rt = ip_route_output_key(net, &fl4);
+			if (IS_ERR(rt))
+				break;
+			sip = fl4.saddr;
+			ip_rt_put(rt);
+			if (!sip)
+				break;
+		}
+		arp_send(ARPOP_REQUEST, ETH_P_ARP, tip, odev, afp->at_src?:sip,
+			 afp->at_lldst_len?afp->at_lldst:to_hw,
+			 afp->at_llsrc_len?afp->at_llsrc:odev->dev_addr,
+			 NULL);
+		break;
+	}
+
+out:
+	arpf_put(afp);
+}
+
+static int
+arpf_fill_node(struct sk_buff *skb, u32 portid, u32 seq, unsigned flags,
+	       int event, int table, struct arpf_node *afp)
+{
+	struct arpmsg	*am;
+	struct nlmsghdr	*nlh;
+	u32 packets = atomic_read(&afp->at_packets);
+
+	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*am), 0);
+	if (nlh == NULL)
+		return -ENOBUFS;
+	nlh->nlmsg_flags = flags;
+	am = nlmsg_data(nlh);
+	am->arpm_family = AF_UNSPEC;
+	am->arpm_table = table;
+	am->arpm_action = afp->at_action;
+	am->arpm_from_len = afp->at_from_len;
+	am->arpm_to_len = afp->at_to_len;
+	am->arpm_pref = afp->at_pref;
+	am->arpm_flags = afp->at_flags;
+	if (afp->at_from_len &&
+		nla_put(skb, ARPA_FROM, 4, &afp->at_from))
+		goto nla_put_failure;
+	if (afp->at_to_len &&
+		nla_put(skb, ARPA_TO, 4, &afp->at_to))
+		goto nla_put_failure;
+	if ((afp->at_src || afp->at_flags & ARPM_F_PREFSRC) &&
+		nla_put(skb, ARPA_SRC, 4, &afp->at_src))
+		goto nla_put_failure;
+	if (afp->at_iif[0] &&
+		nla_put(skb, ARPA_IIF, sizeof(afp->at_iif), afp->at_iif))
+		goto nla_put_failure;
+	if (afp->at_oif[0] &&
+		nla_put(skb, ARPA_OIF, sizeof(afp->at_oif), afp->at_oif))
+		goto nla_put_failure;
+	if (afp->at_llfrom_len &&
+		nla_put(skb, ARPA_LLFROM, afp->at_llfrom_len, afp->at_llfrom))
+		goto nla_put_failure;
+	if (afp->at_llto_len &&
+		nla_put(skb, ARPA_LLTO, afp->at_llto_len, afp->at_llto))
+		goto nla_put_failure;
+	if (afp->at_llsrc_len &&
+		nla_put(skb, ARPA_LLSRC, afp->at_llsrc_len, afp->at_llsrc))
+		goto nla_put_failure;
+	if (afp->at_lldst_len &&
+		nla_put(skb, ARPA_LLDST, afp->at_lldst_len, afp->at_lldst))
+		goto nla_put_failure;
+	if (nla_put(skb, ARPA_PACKETS, 4, &packets))
+		goto nla_put_failure;
+	return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+	nlmsg_cancel(skb, nlh);
+	return -EMSGSIZE;
+}
+
+static void
+arpmsg_notify(struct sk_buff *oskb, struct nlmsghdr *nlh, int table,
+	      struct arpf_node *afp, int event)
+{
+	struct sk_buff *skb;
+	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
+	int payload = sizeof(struct arpmsg) + 256;
+	int err = -ENOBUFS;
+
+	skb = nlmsg_new(nlmsg_total_size(payload), GFP_KERNEL);
+	if (!skb)
+		goto errout;
+
+	err = arpf_fill_node(skb, portid, nlh->nlmsg_seq, 0, event, table, afp);
+	if (err < 0) {
+		kfree_skb(skb);
+		goto errout;
+	}
+
+	rtnl_notify(skb, &init_net, portid, RTNLGRP_ARP, nlh, GFP_KERNEL);
+	return;
+errout:
+	if (err < 0)
+		rtnl_set_sk_err(&init_net, RTNLGRP_ARP, err);
+}
+
+static inline int
+arpf_str_size(int a, struct rtattr **rta, int maxlen)
+{
+	int size = 0;
+
+	if (rta[a-1] && (size = RTA_PAYLOAD(rta[a-1]))) {
+		if (size > maxlen)
+			size = maxlen;
+	}
+	return size;
+}
+
+static inline int
+arpf_get_str(int a, struct rtattr **rta, unsigned char *p,
+	     int maxlen, unsigned char *l)
+{
+	int size = arpf_str_size(a, rta, maxlen);
+
+	if (size) {
+		memcpy(p, RTA_DATA(rta[a-1]), size);
+		*l = size;
+	}
+	return size;
+}
+
+#define ARPF_MATCH_U32(ind, field)	(			\
+	(!rta[ind-1] && r->at_ ## field == 0) ||		\
+	(rta[ind-1] &&						\
+	 *(u32*) RTA_DATA(rta[ind-1]) == r->at_ ## field))
+
+#define ARPF_MATCH_STR(ind, field)	(			\
+	(!rta[ind-1] && r->at_ ## field ## _len == 0) ||	\
+	(rta[ind-1] && r->at_ ## field ## _len &&		\
+	 r->at_ ## field ## _len < RTA_PAYLOAD(rta[ind-1]) &&	\
+	 strcmp(RTA_DATA(rta[ind-1]), r->at_ ## field) == 0))
+
+#define ARPF_MATCH_DATA(ind, field)	(			\
+	(!rta[ind-1] && r->at_ ## field ## _len == 0) ||	\
+	(rta[ind-1] && r->at_ ## field ## _len &&		\
+	 r->at_ ## field ## _len == RTA_PAYLOAD(rta[ind-1]) &&	\
+	 memcmp(RTA_DATA(rta[ind-1]), &r->at_ ## field,		\
+		r->at_ ## field ## _len) == 0))
+
+/* RTM_NEWARPRULE/RTM_DELARPRULE/RTM_GETARPRULE */
+
+int arpf_rule_ctl(struct sk_buff *skb, struct nlmsghdr* n, void *arg)
+{
+	struct rtattr **rta = arg;
+	struct arpmsg *am = NLMSG_DATA(n);
+	struct arpf_node *r, **rp, **prevp = 0, **delp = 0, *newp = 0;
+	unsigned pref = 1;
+	int size, ret = -EINVAL;
+
+	if (am->arpm_table >= sizeof(arp_tabs)/sizeof(arp_tabs[0]))
+		goto out;
+	if (!((~am->arpm_flags) & (ARPM_F_BROADCAST|ARPM_F_UNICAST)))
+		goto out;
+	if (am->arpm_action > 1)
+		goto out;
+	if (am->arpm_to_len > 32 || am->arpm_from_len > 32)
+		goto out;
+	if (am->arpm_flags & ARPM_F_WILDIIF &&
+	    (!rta[ARPA_IIF-1] || !RTA_PAYLOAD(rta[ARPA_IIF-1]) ||
+	    !*(char*)RTA_DATA(rta[ARPA_IIF-1])))
+		am->arpm_flags &= ~ARPM_F_WILDIIF;
+	if (am->arpm_flags & ARPM_F_WILDOIF &&
+	    (!rta[ARPA_OIF-1] || !RTA_PAYLOAD(rta[ARPA_OIF-1]) ||
+	    !*(char*)RTA_DATA(rta[ARPA_OIF-1])))
+		am->arpm_flags &= ~ARPM_F_WILDOIF;
+	switch (am->arpm_table) {
+	case ARPA_TABLE_INPUT:
+		if (rta[ARPA_SRC-1] || rta[ARPA_OIF-1])
+			goto out;
+		break;
+	case ARPA_TABLE_OUTPUT:
+		if (rta[ARPA_IIF-1])
+			goto out;
+		if (am->arpm_flags & (ARPM_F_BROADCAST|ARPM_F_UNICAST))
+			goto out;
+		break;
+	case ARPA_TABLE_FORWARD:
+		if (rta[ARPA_SRC-1])
+			goto out;
+		break;
+	}
+	if (rta[ARPA_SRC-1] && !*(u32*) RTA_DATA(rta[ARPA_SRC-1]))
+		am->arpm_flags |= ARPM_F_PREFSRC;
+	else
+		am->arpm_flags &= ~ARPM_F_PREFSRC;
+
+	for (rp = &arp_tabs[am->arpm_table]; (r=*rp) != NULL; rp=&r->at_next) {
+		if (pref < r->at_pref)
+			prevp = rp;
+		if (am->arpm_pref == r->at_pref ||
+		    (!am->arpm_pref &&
+		     am->arpm_to_len == r->at_to_len &&
+		     am->arpm_from_len == r->at_from_len &&
+		     !((am->arpm_flags ^ r->at_flags) &
+		       (ARPM_F_BROADCAST | ARPM_F_UNICAST |
+		        ARPM_F_WILDIIF | ARPM_F_WILDOIF)) &&
+		     ARPF_MATCH_U32(ARPA_TO, to) &&
+		     ARPF_MATCH_U32(ARPA_FROM, from) &&
+		     ARPF_MATCH_DATA(ARPA_LLFROM, llfrom) &&
+		     ARPF_MATCH_DATA(ARPA_LLTO, llto) &&
+		     ARPF_MATCH_STR(ARPA_IIF, iif) &&
+		     ARPF_MATCH_STR(ARPA_OIF, oif) &&
+		     (n->nlmsg_type != RTM_DELARPRULE ||
+		      /* DEL matches more keys */
+		      (am->arpm_flags == r->at_flags &&
+		       am->arpm_action == r->at_action &&
+		       ARPF_MATCH_U32(ARPA_SRC, src) &&
+		       ARPF_MATCH_DATA(ARPA_LLSRC, llsrc) &&
+		       ARPF_MATCH_DATA(ARPA_LLDST, lldst)
+		      )
+		     )
+		    )
+		   )
+			break;
+		if (am->arpm_pref && r->at_pref > am->arpm_pref) {
+			r = NULL;
+			break;
+		}
+		pref = r->at_pref+1;
+	}
+
+	/*
+	 * r=NULL:	*rp != NULL (stopped before next pref), pref: not valid
+	 *		*rp == NULL (not found), pref: ready to use
+	 * r!=NULL:	found, pref: not valid
+	 *
+	 * prevp=NULL:	no free slot
+	 * prevp!=NULL:	free slot for rule
+	 */
+
+	if (n->nlmsg_type == RTM_DELARPRULE) {
+		if (!r)
+			return -ESRCH;
+		delp = rp;
+		goto dequeue;
+	}
+
+	if (r) {
+		/* Existing rule */
+		ret = -EEXIST;
+		if (n->nlmsg_flags&NLM_F_EXCL)
+			goto out;
+
+		if (n->nlmsg_flags&NLM_F_REPLACE) {
+			pref = r->at_pref;
+			prevp = delp = rp;
+			goto replace;
+		}
+	}
+
+	if (n->nlmsg_flags&NLM_F_APPEND) {
+		if (r) {
+			pref = r->at_pref+1;
+			for (rp=&r->at_next; (r=*rp) != NULL; rp=&r->at_next) {
+				if (pref != r->at_pref)
+					break;
+				pref ++;
+			}
+			ret = -EBUSY;
+			if (!pref)
+				goto out;
+		} else if (am->arpm_pref)
+			pref = am->arpm_pref;
+		prevp = rp;
+	}
+
+	if (!(n->nlmsg_flags&NLM_F_CREATE)) {
+		ret = -ENOENT;
+		if (n->nlmsg_flags&NLM_F_EXCL || r)
+			ret = 0;
+		goto out;
+	}
+
+	if (!(n->nlmsg_flags&NLM_F_APPEND)) {
+		if (!prevp) {
+			ret = -EBUSY;
+			if (r || *rp ||
+			    (!am->arpm_pref && arp_tabs[am->arpm_table]))
+				goto out;
+			prevp = rp;
+			pref = am->arpm_pref? : 99;
+		} else {
+			if (r || !am->arpm_pref) {
+				pref = (*prevp)->at_pref - 1;
+				if (am->arpm_pref && am->arpm_pref < pref)
+					pref = am->arpm_pref;
+			} else {
+				prevp = rp;
+				pref = am->arpm_pref;
+			}
+		}
+	}
+
+replace:
+
+	ret = -ENOMEM;
+	r = kmem_cache_alloc(arpf_cachep, GFP_KERNEL);
+	if (!r)
+		return ret;
+	memset(r, 0, sizeof(*r));
+
+	arpf_get_str(ARPA_LLFROM, rta, r->at_llfrom, MAX_ADDR_LEN,
+		     &r->at_llfrom_len);
+	arpf_get_str(ARPA_LLTO, rta, r->at_llto, MAX_ADDR_LEN,
+		     &r->at_llto_len);
+	arpf_get_str(ARPA_LLSRC, rta, r->at_llsrc, MAX_ADDR_LEN,
+		     &r->at_llsrc_len);
+	arpf_get_str(ARPA_LLDST, rta, r->at_lldst, MAX_ADDR_LEN,
+		     &r->at_lldst_len);
+
+	if (delp)
+		r->at_next = (*delp)->at_next;
+	else if (*prevp)
+		r->at_next = *prevp;
+
+	r->at_pref = pref;
+	r->at_from_len = am->arpm_from_len;
+	r->at_from_mask = inet_make_mask(r->at_from_len);
+	if (rta[ARPA_FROM-1])
+		r->at_from = *(u32*) RTA_DATA(rta[ARPA_FROM-1]);
+	r->at_from &= r->at_from_mask;
+	r->at_to_len = am->arpm_to_len;
+	r->at_to_mask = inet_make_mask(r->at_to_len);
+	if (rta[ARPA_TO-1])
+		r->at_to = *(u32*) RTA_DATA(rta[ARPA_TO-1]);
+	r->at_to &= r->at_to_mask;
+	if (rta[ARPA_SRC-1])
+		r->at_src = *(u32*) RTA_DATA(rta[ARPA_SRC-1]);
+	if (rta[ARPA_PACKETS-1]) {
+		u32 packets = *(u32*) RTA_DATA(rta[ARPA_PACKETS-1]);
+		atomic_set(&r->at_packets, packets);
+	}
+	atomic_set(&r->at_refcnt, 1);
+	r->at_flags = am->arpm_flags;
+	r->at_action = am->arpm_action;
+
+	if (rta[ARPA_IIF-1] && (size = RTA_PAYLOAD(rta[ARPA_IIF-1]))) {
+		if (size >= sizeof(r->at_iif))
+			size = sizeof(r->at_iif)-1;
+		memcpy(r->at_iif, RTA_DATA(rta[ARPA_IIF-1]), size);
+		r->at_iif_len = strlen(r->at_iif);
+	}
+	if (rta[ARPA_OIF-1] && (size = RTA_PAYLOAD(rta[ARPA_OIF-1]))) {
+		if (size >= sizeof(r->at_oif))
+			size = sizeof(r->at_oif)-1;
+		memcpy(r->at_oif, RTA_DATA(rta[ARPA_OIF-1]), size);
+		r->at_oif_len = strlen(r->at_oif);
+	}
+
+	newp = r;
+
+dequeue:
+
+	if (delp) {
+		r = *delp;
+		write_lock_bh(&arpf_lock);
+		if (newp) {
+			if (!rta[ARPA_PACKETS-1])
+				atomic_set(&newp->at_packets,
+					atomic_read(&r->at_packets));
+			*delp = newp;
+		} else {
+			*delp = r->at_next;
+		}
+		r->at_dead = 1;
+		write_unlock_bh(&arpf_lock);
+		arpmsg_notify(skb, n, am->arpm_table, r, RTM_DELARPRULE);
+		arpf_put(r);
+		prevp = 0;
+	}
+
+	if (newp) {
+		if (prevp) {
+			write_lock_bh(&arpf_lock);
+			*prevp = newp;
+			write_unlock_bh(&arpf_lock);
+		}
+		arpmsg_notify(skb, n, am->arpm_table, newp, RTM_NEWARPRULE);
+	}
+
+	ret = 0;
+
+out:
+	return ret;
+}
+
+int arpf_dump_table(int t, struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int idx, ret = -1;
+	struct arpf_node *afp;
+	int s_idx = cb->args[1];
+
+	for (idx=0, afp = arp_tabs[t]; afp; afp = afp->at_next, idx++) {
+		if (idx < s_idx)
+			continue;
+		if (arpf_fill_node(skb, NETLINK_CB(cb->skb).portid,
+		    cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWARPRULE, t, afp) < 0)
+			goto out;
+	}
+
+	ret = skb->len;
+
+out:
+	cb->args[1] = idx;
+
+	return ret;
+}
+
+int arpf_dump_rules(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	int idx;
+	int s_idx = cb->args[0];
+
+	read_lock_bh(&arpf_lock);
+	for (idx = 0; idx < sizeof(arp_tabs)/sizeof(arp_tabs[0]); idx++) {
+		if (idx < s_idx)
+			continue;
+		if (idx > s_idx)
+			memset(&cb->args[1], 0, sizeof(cb->args)-1*sizeof(cb->args[0]));
+		if (arpf_dump_table(idx, skb, cb) < 0)
+			break;
+	}
+	read_unlock_bh(&arpf_lock);
+	cb->args[0] = idx;
+
+	return skb->len;
+}
+
 /*
  *	Called once on startup.
  */
@@ -1261,6 +1856,16 @@ static int arp_proc_init(void);
 
 void __init arp_init(void)
 {
+	arpf_cachep = kmem_cache_create("ip_arpf_cache",
+					sizeof(struct arpf_node), 0,
+					SLAB_HWCACHE_ALIGN, NULL);
+	if (!arpf_cachep)
+		panic("IP: failed to allocate ip_arpf_cache\n");
+
+	rtnl_register(PF_UNSPEC, RTM_NEWARPRULE, arpf_rule_ctl, NULL, NULL);
+	rtnl_register(PF_UNSPEC, RTM_DELARPRULE, arpf_rule_ctl, NULL, NULL);
+	rtnl_register(PF_UNSPEC, RTM_GETARPRULE, NULL, arpf_dump_rules, NULL);
+
 	neigh_table_init(&arp_tbl);
 
 	dev_add_pack(&arp_packet_type);
