diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index e7ebd45ca345..a503865fabed 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4662,6 +4662,28 @@ static int sock_has_perm(struct sock *sk, u32 perms)
 			    &ad);
 }
 
+static int nlmsg_sock_has_extended_perms(struct sock *sk, u32 perms, u16 nlmsg_type)
+{
+	struct sk_security_struct *sksec = sk->sk_security;
+	struct common_audit_data ad;
+	struct lsm_network_audit net = {0,};
+	u8 xperm;
+
+	if (sksec->sid == SECINITSID_KERNEL)
+		return 0;
+
+	ad.type = LSM_AUDIT_DATA_NET;
+	ad.u.net = &net;
+	ad.u.net->sk = sk;
+
+	// nlmsg_types comfortably fit into a single driver, see RTM_MAX in uapi/linux/rtnetlink.h
+	xperm = nlmsg_type & 0xff;
+
+	return avc_has_extended_perms(&selinux_state,
+			    current_sid(), sksec->sid, sksec->sclass, perms, 0, xperm,
+			    &ad);
+}
+
 static int selinux_socket_create(int family, int type,
 				 int protocol, int kern)
 {
@@ -6037,7 +6059,7 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
 
 		rc = selinux_nlmsg_lookup(sclass, nlh->nlmsg_type, &perm);
 		if (rc == 0) {
-			rc = sock_has_perm(sk, perm);
+			rc = nlmsg_sock_has_extended_perms(sk, perm, nlh->nlmsg_type);
 			if (rc)
 				return rc;
 		} else if (rc == -EINVAL) {
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index d3ebea8d146f..f69aa7bc3dc3 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -55,6 +55,7 @@ struct avtab_extended_perms {
 /* These are not flags. All 256 values may be used */
 #define AVTAB_XPERMS_IOCTLFUNCTION	0x01
 #define AVTAB_XPERMS_IOCTLDRIVER	0x02
+#define AVTAB_XPERMS_NLMSG		0x03
 	/* extension of the avtab_key specified */
 	u8 specified; /* ioctl, netfilter, ... */
 	/*
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index e5f1b2757a83..7bbb070e9ff5 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -591,7 +591,7 @@ static void type_attribute_bounds_av(struct policydb *policydb,
 
 /*
  * flag which drivers have permissions
- * only looking for ioctl based extended permssions
+ * only looking for ioctl/netlink based extended permissions
  */
 void services_compute_xperms_drivers(
 		struct extended_perms *xperms,
@@ -607,6 +607,9 @@ void services_compute_xperms_drivers(
 		/* if allowing permissions within a driver */
 		security_xperm_set(xperms->drivers.p,
 					node->datum.u.xperms->driver);
+	} else if (node->datum.u.xperms->specified == AVTAB_XPERMS_NLMSG) {
+		/* all netlink permissions are included in driver 0 */
+		xperms->drivers.p[0] |= 1;
 	}
 
 	xperms->len = 1;
@@ -970,6 +973,9 @@ void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
 		if (!security_xperm_test(node->datum.u.xperms->perms.p,
 					xpermd->driver))
 			return;
+	} else if (node->datum.u.xperms->specified == AVTAB_XPERMS_NLMSG) {
+		if (xpermd->driver != node->datum.u.xperms->driver)
+			return;
 	} else {
 		BUG();
 	}
@@ -985,6 +991,11 @@ void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
 				xpermd->allowed->p[i] |=
 					node->datum.u.xperms->perms.p[i];
 		}
+		if (node->datum.u.xperms->specified == AVTAB_XPERMS_NLMSG) {
+			for (i = 0; i < ARRAY_SIZE(xpermd->allowed->p); i++)
+				xpermd->allowed->p[i] |=
+					node->datum.u.xperms->perms.p[i];
+		}
 	} else if (node->key.specified == AVTAB_XPERMS_AUDITALLOW) {
 		xpermd->used |= XPERMS_AUDITALLOW;
 		if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
@@ -996,6 +1007,11 @@ void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
 				xpermd->auditallow->p[i] |=
 					node->datum.u.xperms->perms.p[i];
 		}
+		if (node->datum.u.xperms->specified == AVTAB_XPERMS_NLMSG) {
+			for (i = 0; i < ARRAY_SIZE(xpermd->auditallow->p); i++)
+				xpermd->auditallow->p[i] |=
+					node->datum.u.xperms->perms.p[i];
+		}
 	} else if (node->key.specified == AVTAB_XPERMS_DONTAUDIT) {
 		xpermd->used |= XPERMS_DONTAUDIT;
 		if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
@@ -1007,6 +1023,11 @@ void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
 				xpermd->dontaudit->p[i] |=
 					node->datum.u.xperms->perms.p[i];
 		}
+		if (node->datum.u.xperms->specified == AVTAB_XPERMS_NLMSG) {
+			for (i = 0; i < ARRAY_SIZE(xpermd->dontaudit->p); i++)
+				xpermd->dontaudit->p[i] |=
+					node->datum.u.xperms->perms.p[i];
+		}
 	} else {
 		BUG();
 	}
