diff --git a/include/net/resolver.h b/include/net/resolver.h
index f38c7e9f1205..307938ad91a6 100644
--- a/include/net/resolver.h
+++ b/include/net/resolver.h
@@ -14,12 +14,21 @@
 
 #include <linux/rhashtable.h>
 #include <linux/types.h>
+#include <net/genetlink.h>
+#include <uapi/linux/genetlink.h>
 
 struct net_rslv;
 
 typedef int (*net_rslv_cmpfn)(struct net_rslv *nrslv, const void *key,
 			      const void *object);
 
+struct net_rslv_netlink_map {
+	int dst_attr;
+	int timo_attr;
+	int get_cmd;
+	struct genl_family *genl_family;
+};
+
 struct net_rslv {
 	struct rhashtable rhash_table;
 	struct rhashtable_params params;
@@ -28,10 +37,12 @@ struct net_rslv {
 	spinlock_t *locks;
 	unsigned int locks_mask;
 	unsigned int hash_rnd;
+	const struct net_rslv_netlink_map *nlmap;
 };
 
 struct net_rslv *net_rslv_create(size_t obj_size, size_t key_len,
-				 size_t max_size, net_rslv_cmpfn cmp_fn);
+				 size_t max_size, net_rslv_cmpfn cmp_fn,
+				 const struct net_rslv_netlink_map *nlmap);
 
 void net_rslv_destroy(struct net_rslv *nrslv);
 
@@ -40,4 +51,17 @@ int net_rslv_lookup_and_create(struct net_rslv *nrslv, void *key,
 
 void net_rslv_resolved(struct net_rslv *nrslv, void *key);
 
+int net_rslv_nl_cmd_add(struct net_rslv *nrslv, struct sk_buff *skb,
+			struct genl_info *info);
+int net_rslv_nl_cmd_del(struct net_rslv *nrslv, struct sk_buff *skb,
+			struct genl_info *info);
+int net_rslv_nl_cmd_get(struct net_rslv *nrslv, struct sk_buff *skb,
+			struct genl_info *info);
+int net_rslv_nl_cmd_flush(struct net_rslv *nrslv, struct sk_buff *skb,
+			  struct genl_info *info);
+int net_rslv_nl_dump_start(struct net_rslv *nrslv, struct netlink_callback *cb);
+int net_rslv_nl_dump_done(struct net_rslv *nrslv, struct netlink_callback *cb);
+int net_rslv_nl_dump(struct net_rslv *nrslv, struct sk_buff *skb,
+		     struct netlink_callback *cb);
+
 #endif /* __NET_RESOLVER_H */
diff --git a/net/ipv6/ila/ila_resolver.c b/net/ipv6/ila/ila_resolver.c
index 8b9a3c5305a4..2aebc0526221 100644
--- a/net/ipv6/ila/ila_resolver.c
+++ b/net/ipv6/ila/ila_resolver.c
@@ -215,7 +215,8 @@ int ila_rslv_init_net(struct net *net)
 	struct net_rslv *nrslv;
 
 	nrslv = net_rslv_create(sizeof(struct ila_addr),
-				sizeof(struct ila_addr), ILA_MAX_SIZE, NULL);
+				sizeof(struct ila_addr), ILA_MAX_SIZE, NULL,
+				NULL);
 
 	if (IS_ERR(nrslv))
 		return PTR_ERR(nrslv);
diff --git a/net/resolver/resolver.c b/net/resolver/resolver.c
index 32a915ed8f93..e2496b0bf852 100644
--- a/net/resolver/resolver.c
+++ b/net/resolver/resolver.c
@@ -19,11 +19,13 @@
 #include <linux/types.h>
 #include <linux/vmalloc.h>
 #include <net/checksum.h>
+#include <net/genetlink.h>
 #include <net/ip.h>
 #include <net/ip6_fib.h>
 #include <net/lwtunnel.h>
 #include <net/protocol.h>
 #include <net/resolver.h>
+#include <uapi/linux/genetlink.h>
 
 struct net_rslv_ent {
 	struct rhash_head node;
@@ -192,8 +194,8 @@ static int net_rslv_cmp(struct rhashtable_compare_arg *arg,
 #define MAX_LOCKS 1024
 
 struct net_rslv *net_rslv_create(size_t obj_size, size_t key_len,
-				 size_t max_size,
-				 net_rslv_cmpfn cmp_fn)
+				 size_t max_size, net_rslv_cmpfn cmp_fn,
+				 const struct net_rslv_netlink_map *nlmap)
 {
 	struct net_rslv *nrslv;
 	int err;
@@ -212,6 +214,7 @@ struct net_rslv *net_rslv_create(size_t obj_size, size_t key_len,
 
 	nrslv->obj_size = obj_size;
 	nrslv->rslv_cmp = cmp_fn;
+	nrslv->nlmap = nlmap;
 	get_random_bytes(&nrslv->hash_rnd, sizeof(nrslv->hash_rnd));
 
 	nrslv->params.head_offset = offsetof(struct net_rslv_ent, node);
@@ -278,6 +281,279 @@ void net_rslv_destroy(struct net_rslv *nrslv)
 }
 EXPORT_SYMBOL_GPL(net_rslv_destroy);
 
+/* Netlink access utility functions and structures. */
+
+struct net_rslv_params {
+	unsigned int timeout;
+	__u8 key[MAX_ADDR_LEN];
+	size_t keysize;
+};
+
+static int parse_nl_config(struct net_rslv *nrslv, struct genl_info *info,
+			   struct net_rslv_params *np)
+{
+	if (!info->attrs[nrslv->nlmap->dst_attr] ||
+	    nla_len(info->attrs[nrslv->nlmap->dst_attr]) !=
+					nrslv->params.key_len)
+		return -EINVAL;
+
+	memset(np, 0, sizeof(*np));
+
+	memcpy(np->key, nla_data(info->attrs[nrslv->nlmap->dst_attr]),
+	       nla_len(info->attrs[nrslv->nlmap->dst_attr]));
+
+	if (info->attrs[nrslv->nlmap->timo_attr])
+		np->timeout = nla_get_u32(info->attrs[nrslv->nlmap->timo_attr]);
+
+	return 0;
+}
+
+int net_rslv_nl_cmd_add(struct net_rslv *nrslv, struct sk_buff *skb,
+			struct genl_info *info)
+{
+	struct net_rslv_params p;
+	int err;
+
+	err = parse_nl_config(nrslv, info, &p);
+	if (err)
+		return err;
+
+	return net_rslv_lookup_and_create(nrslv, p.key, p.timeout);
+}
+EXPORT_SYMBOL_GPL(net_rslv_nl_cmd_add);
+
+int net_rslv_nl_cmd_del(struct net_rslv *nrslv, struct sk_buff *skb,
+			struct genl_info *info)
+{
+	struct net_rslv_params p;
+	int err;
+
+	err = parse_nl_config(nrslv, info, &p);
+	if (err)
+		return err;
+
+	/* Treat removal as being resolved */
+	net_rslv_resolved(nrslv, p.key);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(net_rslv_nl_cmd_del);
+
+static int net_rslv_fill_info(struct net_rslv *nrslv,
+			      struct net_rslv_ent *nrent,
+			      struct sk_buff *msg)
+{
+	int from_now = 0;
+
+	if (delayed_work_pending(&nrent->timeout_work)) {
+		unsigned long expires = nrent->timeout_work.timer.expires;
+
+		from_now = jiffies_to_msecs(expires - jiffies);
+
+		if (from_now < 0)
+			from_now = 0;
+	}
+
+	if (nla_put(msg, nrslv->nlmap->dst_attr, nrslv->params.key_len,
+		    nrent->object) ||
+	    nla_put_s32(msg, nrslv->nlmap->timo_attr, from_now))
+		return -1;
+
+	return 0;
+}
+
+static int net_rslv_dump_info(struct net_rslv *nrslv,
+			      struct net_rslv_ent *nrent, u32 portid, u32 seq,
+			      u32 flags, struct sk_buff *skb, u8 cmd)
+{
+	void *hdr;
+
+	hdr = genlmsg_put(skb, portid, seq, nrslv->nlmap->genl_family, flags,
+			  cmd);
+	if (!hdr)
+		return -ENOMEM;
+
+	if (net_rslv_fill_info(nrslv, nrent, skb) < 0)
+		goto nla_put_failure;
+
+	genlmsg_end(skb, hdr);
+	return 0;
+
+nla_put_failure:
+	genlmsg_cancel(skb, hdr);
+	return -EMSGSIZE;
+}
+
+int net_rslv_nl_cmd_get(struct net_rslv *nrslv, struct sk_buff *skb,
+			struct genl_info *info)
+{
+	struct net_rslv_ent *nrent;
+	struct net_rslv_params p;
+	struct sk_buff *msg;
+	int err;
+
+	err = parse_nl_config(nrslv, info, &p);
+	if (err)
+		return err;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	rcu_read_lock();
+
+	nrent = rhashtable_lookup_fast(&nrslv->rhash_table, p.key,
+				       nrslv->params);
+	if (nrent)
+		err = net_rslv_dump_info(nrslv, nrent, info->snd_portid,
+					 info->snd_seq, 0, msg,
+					 info->genlhdr->cmd);
+
+	rcu_read_unlock();
+
+	if (err < 0)
+		goto out_free;
+
+	return genlmsg_reply(msg, info);
+
+out_free:
+	nlmsg_free(msg);
+	return err;
+}
+EXPORT_SYMBOL_GPL(net_rslv_nl_cmd_get);
+
+int net_rslv_nl_cmd_flush(struct net_rslv *nrslv, struct sk_buff *skb,
+			  struct genl_info *info)
+{
+	struct rhashtable_iter iter;
+	struct net_rslv_ent *nrent;
+	spinlock_t *lock;
+	int ret;
+
+	ret = rhashtable_walk_init(&nrslv->rhash_table, &iter, GFP_KERNEL);
+	if (ret)
+		return ret;
+
+	rhashtable_walk_start(&iter);
+
+	for (;;) {
+		nrent = rhashtable_walk_next(&iter);
+
+		if (IS_ERR(nrent)) {
+			if (PTR_ERR(nrent) == -EAGAIN) {
+				/* New table, we're okay to continue */
+				continue;
+			}
+			ret = PTR_ERR(nrent);
+			break;
+		} else if (!nrent) {
+			break;
+		}
+
+		lock = net_rslv_get_lock(nrslv, nrent->object);
+
+		spin_lock(lock);
+		ret = rhashtable_remove_fast(&nrslv->rhash_table, &nrent->node,
+					     nrslv->params);
+		spin_unlock(lock);
+
+		if (ret)
+			break;
+	}
+
+	rhashtable_walk_stop(&iter);
+	rhashtable_walk_exit(&iter);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(net_rslv_nl_cmd_flush);
+
+int net_rslv_nl_dump_start(struct net_rslv *nrslv, struct netlink_callback *cb)
+{
+	struct rhashtable_iter *iter;
+	int ret;
+
+	iter = kmalloc(sizeof(*iter), GFP_KERNEL);
+	if (!iter)
+		return -ENOMEM;
+
+	ret = rhashtable_walk_init(&nrslv->rhash_table, iter, GFP_KERNEL);
+	if (ret) {
+		kfree(iter);
+		return ret;
+	}
+
+	cb->args[0] = (long)iter;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(net_rslv_nl_dump_start);
+
+int net_rslv_nl_dump_done(struct net_rslv *nrslv, struct netlink_callback *cb)
+{
+	struct rhashtable_iter *iter =
+				(struct rhashtable_iter *)cb->args[0];
+
+	rhashtable_walk_exit(iter);
+
+	kfree(iter);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(net_rslv_nl_dump_done);
+
+int net_rslv_nl_dump(struct net_rslv *nrslv, struct sk_buff *skb,
+		     struct netlink_callback *cb)
+{
+	struct rhashtable_iter *iter =
+				(struct rhashtable_iter *)cb->args[0];
+	struct net_rslv_ent *nrent;
+	int ret;
+
+	ret = rhashtable_walk_start_check(iter);
+	if (ret)
+		goto done;
+
+	/* Get first entty */
+	nrent = rhashtable_walk_peek(iter);
+
+	for (;;) {
+		if (IS_ERR(nrent)) {
+			ret = PTR_ERR(nrent);
+			if (ret == -EAGAIN) {
+				/* Table has changed and iter has reset. Return
+				 * -EAGAIN to the application even if we have
+				 * written data to the skb. The application
+				 * needs to deal with this.
+				 */
+
+				goto done;
+			}
+			break;
+		} else if (!nrent) {
+			break;
+		}
+
+		ret =  net_rslv_dump_info(nrslv, nrent,
+					  NETLINK_CB(cb->skb).portid,
+					  cb->nlh->nlmsg_seq,
+					  NLM_F_MULTI, skb,
+					  nrslv->nlmap->get_cmd);
+		if (ret)
+			break;
+
+		/* Get next and advance the iter */
+		nrent = rhashtable_walk_next(iter);
+	}
+
+	ret = (skb->len ? : ret);
+
+done:
+	rhashtable_walk_stop(iter);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(net_rslv_nl_dump);
+
 MODULE_AUTHOR("Tom Herbert <tom@quantonium.net>");
 MODULE_LICENSE("GPL");
 
