diff --git a/ell/genl.c b/ell/genl.c
index d2c6ea15dfa9..5a8dd371a970 100644
--- a/ell/genl.c
+++ b/ell/genl.c
@@ -19,20 +19,15 @@
 #include "queue.h"
 #include "io.h"
 #include "private.h"
+#include "netlink.h"
 #include "netlink-private.h"
 #include "notifylist.h"
 #include "genl.h"
 
-#define MAX_NESTING_LEVEL 4
 #define GENL_DEBUG(fmt, args...)	\
 	l_util_debug(genl->debug_callback, genl->debug_data, "%s:%i " fmt, \
 			__func__, __LINE__, ## args)
 
-struct nest_info {
-	uint16_t type;
-	uint16_t offset;
-};
-
 struct unicast_watch {
 	struct l_notifylist_entry super;
 	char name[GENL_NAMSIZ];
@@ -89,11 +84,7 @@ struct l_genl_msg {
 	char *error_msg;
 	uint8_t cmd;
 	uint8_t version;
-	void *data;
-	uint32_t size;
-	uint32_t len;
-	struct nest_info nests[MAX_NESTING_LEVEL];
-	uint8_t nesting_level;
+	struct l_netlink_message *nlm;
 };
 
 struct genl_request {
@@ -714,44 +705,6 @@ static bool match_request_hid(const void *a, const void *b)
 	return request->handle_id == id;
 }
 
-static struct l_genl_msg *msg_alloc(uint8_t cmd, uint8_t version, uint32_t size)
-{
-	struct l_genl_msg *msg;
-
-	msg = l_new(struct l_genl_msg, 1);
-
-	msg->cmd = cmd;
-	msg->version = version;
-
-	msg->len = NLMSG_HDRLEN + GENL_HDRLEN;
-	msg->size = msg->len + NLMSG_ALIGN(size);
-
-	msg->data = l_realloc(NULL, msg->size);
-	memset(msg->data, 0, msg->size);
-	msg->nesting_level = 0;
-
-	return l_genl_msg_ref(msg);
-}
-
-static bool msg_grow(struct l_genl_msg *msg, uint32_t needed)
-{
-	uint32_t grow_by;
-
-	if (msg->size >= msg->len + needed)
-		return true;
-
-	grow_by = msg->len + needed - msg->size;
-
-	if (grow_by < 32)
-		grow_by = 128;
-
-	msg->data = l_realloc(msg->data, msg->size + grow_by);
-	memset(msg->data + msg->size, 0, grow_by);
-	msg->size += grow_by;
-
-	return true;
-}
-
 static struct l_genl_msg *msg_create(const struct nlmsghdr *nlmsg)
 {
 	struct l_genl_msg *msg;
@@ -771,13 +724,10 @@ static struct l_genl_msg *msg_create(const struct nlmsghdr *nlmsg)
 		goto done;
 	}
 
-	msg->data = l_memdup(nlmsg, nlmsg->nlmsg_len);
+	msg->nlm = netlink_message_from_nlmsg(nlmsg);
 
-	msg->len = nlmsg->nlmsg_len;
-	msg->size = nlmsg->nlmsg_len;
-
-	if (msg->len >= GENL_HDRLEN) {
-		struct genlmsghdr *genlmsg = msg->data + NLMSG_HDRLEN;
+	if (nlmsg->nlmsg_len >= NLMSG_LENGTH(GENL_HDRLEN)) {
+		struct genlmsghdr *genlmsg = msg->nlm->data + NLMSG_HDRLEN;
 
 		msg->cmd = genlmsg->cmd;
 		msg->version = genlmsg->version;
@@ -794,23 +744,23 @@ static const void *msg_as_bytes(struct l_genl_msg *msg, uint16_t type,
 	struct nlmsghdr *nlmsg;
 	struct genlmsghdr *genlmsg;
 
-	nlmsg = msg->data;
+	nlmsg = msg->nlm->data;
 
-	nlmsg->nlmsg_len = msg->len;
 	nlmsg->nlmsg_type = type;
 	nlmsg->nlmsg_flags = flags;
 	nlmsg->nlmsg_seq = seq;
 	nlmsg->nlmsg_pid = pid;
 
-	genlmsg = msg->data + NLMSG_HDRLEN;
+	genlmsg = msg->nlm->data + NLMSG_HDRLEN;
 
 	genlmsg->cmd = msg->cmd;
 	genlmsg->version = msg->version;
+	genlmsg->reserved = 0;
 
 	if (out_size)
-		*out_size = msg->len;
+		*out_size = nlmsg->nlmsg_len;
 
-	return msg->data;
+	return msg->nlm->data;
 }
 
 static void write_watch_destroy(void *user_data)
@@ -842,7 +792,7 @@ static bool can_write_data(struct l_io *io, void *user_data)
 		return false;
 	}
 
-	l_util_hexdump(false, request->msg->data, bytes_written,
+	l_util_hexdump(false, data, bytes_written,
 				genl->debug_callback, genl->debug_data);
 
 	l_queue_push_tail(genl->pending_list, request);
@@ -1472,7 +1422,14 @@ LIB_EXPORT struct l_genl_msg *l_genl_msg_new(uint8_t cmd)
 
 LIB_EXPORT struct l_genl_msg *l_genl_msg_new_sized(uint8_t cmd, uint32_t size)
 {
-	return msg_alloc(cmd, 0x00, size);
+	struct l_genl_msg *msg = l_new(struct l_genl_msg, 1);
+
+	msg->cmd = cmd;
+	msg->nlm = l_netlink_message_new_sized(0, 0, size + GENL_HDRLEN);
+	netlink_message_reserve_header(msg->nlm,
+					sizeof(struct genlmsghdr), NULL);
+
+	return l_genl_msg_ref(msg);
 }
 
 LIB_EXPORT struct l_genl_msg *l_genl_msg_new_from_data(const void *data,
@@ -1516,7 +1473,7 @@ LIB_EXPORT void l_genl_msg_unref(struct l_genl_msg *msg)
 		return;
 
 	l_free(msg->error_msg);
-	l_free(msg->data);
+	l_netlink_message_unref(msg->nlm);
 	l_free(msg);
 }
 
@@ -1555,23 +1512,12 @@ LIB_EXPORT const char *l_genl_msg_get_extended_error(struct l_genl_msg *msg)
 LIB_EXPORT bool l_genl_msg_append_attr(struct l_genl_msg *msg, uint16_t type,
 						uint16_t len, const void *data)
 {
-	struct nlattr *nla;
-
 	if (unlikely(!msg))
 		return false;
 
-	if (!msg_grow(msg, NLA_HDRLEN + NLA_ALIGN(len)))
+	if (l_netlink_message_append(msg->nlm, type, data, len) < 0)
 		return false;
 
-	nla = msg->data + msg->len;
-	nla->nla_len = NLA_HDRLEN + len;
-	nla->nla_type = type;
-
-	if (len)
-		memcpy(msg->data + msg->len + NLA_HDRLEN, data, len);
-
-	msg->len += NLA_HDRLEN + NLA_ALIGN(len);
-
 	return true;
 }
 
@@ -1579,32 +1525,12 @@ LIB_EXPORT bool l_genl_msg_append_attrv(struct l_genl_msg *msg, uint16_t type,
 					const struct iovec *iov,
 					size_t iov_len)
 {
-	struct nlattr *nla;
-	size_t len = 0;
-	unsigned int i;
-
 	if (unlikely(!msg))
 		return false;
 
-	for (i = 0; i < iov_len; i++)
-		len += iov[i].iov_len;
-
-	if (!msg_grow(msg, NLA_HDRLEN + NLA_ALIGN(len)))
+	if (l_netlink_message_appendv(msg->nlm, type, iov, iov_len) < 0)
 		return false;
 
-	nla = msg->data + msg->len;
-	nla->nla_len = NLA_HDRLEN + len;
-	nla->nla_type = type;
-
-	msg->len += NLA_HDRLEN;
-
-	for (i = 0; i < iov_len; i++, iov++) {
-		memcpy(msg->data + msg->len, iov->iov_base, iov->iov_len);
-		msg->len += iov->iov_len;
-	}
-
-	msg->len += NLA_ALIGN(len) - len;
-
 	return true;
 }
 
@@ -1613,37 +1539,20 @@ LIB_EXPORT bool l_genl_msg_enter_nested(struct l_genl_msg *msg, uint16_t type)
 	if (unlikely(!msg))
 		return false;
 
-	if (unlikely(msg->nesting_level == MAX_NESTING_LEVEL))
-		return false;
-
-	if (!msg_grow(msg, NLA_HDRLEN))
+	if (l_netlink_message_enter_nested(msg->nlm, type) < 0)
 		return false;
 
-	msg->nests[msg->nesting_level].type = type | NLA_F_NESTED;
-	msg->nests[msg->nesting_level].offset = msg->len;
-	msg->nesting_level += 1;
-
-	msg->len += NLA_HDRLEN;
-
 	return true;
 }
 
 LIB_EXPORT bool l_genl_msg_leave_nested(struct l_genl_msg *msg)
 {
-	struct nlattr *nla;
-
 	if (unlikely(!msg))
 		return false;
 
-	if (unlikely(msg->nesting_level == 0))
+	if (l_netlink_message_leave_nested(msg->nlm) < 0)
 		return false;
 
-	nla = msg->data + msg->nests[msg->nesting_level - 1].offset;
-	nla->nla_type = msg->nests[msg->nesting_level - 1].type;
-	nla->nla_len = msg->len - msg->nests[msg->nesting_level - 1].offset;
-
-	msg->nesting_level -= 1;
-
 	return true;
 }
 
@@ -1656,11 +1565,12 @@ LIB_EXPORT bool l_genl_attr_init(struct l_genl_attr *attr,
 	if (unlikely(!attr) || unlikely(!msg))
 		return false;
 
-	if (!msg->data || msg->len < NLMSG_HDRLEN + GENL_HDRLEN)
+	if (!msg->nlm ||
+			msg->nlm->hdr->nlmsg_len < NLMSG_HDRLEN + GENL_HDRLEN)
 		return false;
 
-	nla = msg->data + NLMSG_HDRLEN + GENL_HDRLEN;
-	len = msg->len - NLMSG_HDRLEN - GENL_HDRLEN;
+	nla = msg->nlm->data + NLMSG_HDRLEN + GENL_HDRLEN;
+	len = msg->nlm->hdr->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
 
 	if (!NLA_OK(nla, len))
 		return false;
