diff --git a/include/linux/in.h b/include/linux/in.h
index ba355384016afa440a492b25c3fd6149f0603a3b..94f557fa46369ec94ec718d25616ceb0b73fd2d2 100644
--- a/include/linux/in.h
+++ b/include/linux/in.h
@@ -72,6 +72,7 @@ struct in_addr {
 #define IP_FREEBIND	15
 #define IP_IPSEC_POLICY	16
 #define IP_XFRM_POLICY	17
+#define IP_PASSSEC	18
 
 /* BSD compatibility */
 #define IP_RECVRETOPTS	IP_RETOPTS
diff --git a/include/linux/security.h b/include/linux/security.h
index 7cbef482e13aac4b74d66e86afa65ba1ece22d8d..b18eb8cfa6391e12c395dee2758a7633be762669 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1286,7 +1286,8 @@ struct security_operations {
 	int (*socket_setsockopt) (struct socket * sock, int level, int optname);
 	int (*socket_shutdown) (struct socket * sock, int how);
 	int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb);
-	int (*socket_getpeersec) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
+	int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
+	int (*socket_getpeersec_dgram) (struct sk_buff *skb, char **secdata, u32 *seclen);
 	int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);
 	void (*sk_free_security) (struct sock *sk);
 	unsigned int (*sk_getsid) (struct sock *sk, struct flowi *fl, u8 dir);
@@ -2741,10 +2742,16 @@ static inline int security_sock_rcv_skb (struct sock * sk,
 	return security_ops->socket_sock_rcv_skb (sk, skb);
 }
 
-static inline int security_socket_getpeersec(struct socket *sock, char __user *optval,
-					     int __user *optlen, unsigned len)
+static inline int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
+						    int __user *optlen, unsigned len)
 {
-	return security_ops->socket_getpeersec(sock, optval, optlen, len);
+	return security_ops->socket_getpeersec_stream(sock, optval, optlen, len);
+}
+
+static inline int security_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata,
+						   u32 *seclen)
+{
+	return security_ops->socket_getpeersec_dgram(skb, secdata, seclen);
 }
 
 static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
@@ -2863,8 +2870,14 @@ static inline int security_sock_rcv_skb (struct sock * sk,
 	return 0;
 }
 
-static inline int security_socket_getpeersec(struct socket *sock, char __user *optval,
-					     int __user *optlen, unsigned len)
+static inline int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
+						    int __user *optlen, unsigned len)
+{
+	return -ENOPROTOOPT;
+}
+
+static inline int security_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata,
+						   u32 *seclen)
 {
 	return -ENOPROTOOPT;
 }
diff --git a/include/linux/socket.h b/include/linux/socket.h
index b02dda4ee83d113f6e4783ba02a68629593fe5d9..9ab2ddd802212667c06c48b317f757ae2677225d 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -150,6 +150,7 @@ __KINLINE struct cmsghdr * cmsg_nxthdr (struct msghdr *__msg, struct cmsghdr *__
 
 #define	SCM_RIGHTS	0x01		/* rw: access rights (array of int) */
 #define SCM_CREDENTIALS 0x02		/* rw: struct ucred		*/
+#define SCM_SECURITY	0x03		/* rw: security label		*/
 
 struct ucred {
 	__u32	pid;
diff --git a/net/core/sock.c b/net/core/sock.c
index 6e00811d44bc810a8e7d6cf64585a520dc60fce2..5038a5a7bd842d60131e337517c96a02d247b2fa 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -616,7 +616,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
 			break;
 
 		case SO_PEERSEC:
-			return security_socket_getpeersec(sock, optval, optlen, len);
+			return security_socket_getpeersec_stream(sock, optval, optlen, len);
 
 		default:
 			return(-ENOPROTOOPT);
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 2bf8d782f678b6add07c5d193902667006ccd96d..b5c4f61518e85c302c116b08fc979d324b95805f 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -50,6 +50,7 @@
 #define IP_CMSG_TOS		4
 #define IP_CMSG_RECVOPTS	8
 #define IP_CMSG_RETOPTS		16
+#define IP_CMSG_PASSSEC		32
 
 /*
  *	SOL_IP control messages.
@@ -109,6 +110,19 @@ static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
 	put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
 }
 
+static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
+{
+	char *secdata;
+	u32 seclen;
+	int err;
+
+	err = security_socket_getpeersec_dgram(skb, &secdata, &seclen);
+	if (err)
+		return;
+
+	put_cmsg(msg, SOL_IP, SCM_SECURITY, seclen, secdata);
+}
+
 
 void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
 {
@@ -138,6 +152,11 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
 
 	if (flags & 1)
 		ip_cmsg_recv_retopts(msg, skb);
+	if ((flags>>=1) == 0)
+		return;
+
+	if (flags & 1)
+		ip_cmsg_recv_security(msg, skb);
 }
 
 int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc)
@@ -393,7 +412,8 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
 			    (1<<IP_RETOPTS) | (1<<IP_TOS) | 
 			    (1<<IP_TTL) | (1<<IP_HDRINCL) | 
 			    (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) | 
-			    (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND))) || 
+			    (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) |
+			    (1<<IP_PASSSEC))) ||
 				optname == IP_MULTICAST_TTL || 
 				optname == IP_MULTICAST_LOOP) { 
 		if (optlen >= sizeof(int)) {
@@ -478,6 +498,12 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
 			else
 				inet->cmsg_flags &= ~IP_CMSG_RETOPTS;
 			break;
+		case IP_PASSSEC:
+			if (val)
+				inet->cmsg_flags |= IP_CMSG_PASSSEC;
+			else
+				inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
+			break;
 		case IP_TOS:	/* This sets both TOS and Precedence */
 			if (sk->sk_type == SOCK_STREAM) {
 				val &= ~3;
@@ -932,6 +958,9 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
 		case IP_RETOPTS:
 			val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0;
 			break;
+		case IP_PASSSEC:
+			val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
+			break;
 		case IP_TOS:
 			val = inet->tos;
 			break;
diff --git a/security/dummy.c b/security/dummy.c
index f1a5bd98bf10c3ed56d82ddea1ce33cf5a0bc905..a326d6e0b6d79848955d2d41eabef9074030dffd 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -763,8 +763,14 @@ static int dummy_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb)
 	return 0;
 }
 
-static int dummy_socket_getpeersec(struct socket *sock, char __user *optval,
-				   int __user *optlen, unsigned len)
+static int dummy_socket_getpeersec_stream(struct socket *sock, char __user *optval,
+					  int __user *optlen, unsigned len)
+{
+	return -ENOPROTOOPT;
+}
+
+static int dummy_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata,
+					 u32 *seclen)
 {
 	return -ENOPROTOOPT;
 }
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index b65c201e9ff50386ea6cb43ed2c0c91f63a4cfe7..5b16196f28233eddba2ed445469355923020a72b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3318,24 +3318,38 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
 	return err;
 }
 
-static int selinux_socket_getpeersec(struct socket *sock, char __user *optval,
-				     int __user *optlen, unsigned len)
+static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval,
+					    int __user *optlen, unsigned len)
 {
 	int err = 0;
 	char *scontext;
 	u32 scontext_len;
 	struct sk_security_struct *ssec;
 	struct inode_security_struct *isec;
+	u32 peer_sid = 0;
 
 	isec = SOCK_INODE(sock)->i_security;
-	if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) {
+
+	/* if UNIX_STREAM check peer_sid, if TCP check dst for labelled sa */
+	if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET) {
+		ssec = sock->sk->sk_security;
+		peer_sid = ssec->peer_sid;
+	}
+	else if (isec->sclass == SECCLASS_TCP_SOCKET) {
+		peer_sid = selinux_socket_getpeer_stream(sock->sk);
+
+		if (peer_sid == SECSID_NULL) {
+			err = -ENOPROTOOPT;
+			goto out;
+		}
+	}
+	else {
 		err = -ENOPROTOOPT;
 		goto out;
 	}
 
-	ssec = sock->sk->sk_security;
-	
-	err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len);
+	err = security_sid_to_context(peer_sid, &scontext, &scontext_len);
+
 	if (err)
 		goto out;
 
@@ -3356,6 +3370,23 @@ static int selinux_socket_getpeersec(struct socket *sock, char __user *optval,
 	return err;
 }
 
+static int selinux_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, u32 *seclen)
+{
+	int err = 0;
+	u32 peer_sid = selinux_socket_getpeer_dgram(skb);
+
+	if (peer_sid == SECSID_NULL)
+		return -EINVAL;
+
+	err = security_sid_to_context(peer_sid, secdata, seclen);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+
+
 static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
 {
 	return sk_alloc_security(sk, family, priority);
@@ -4344,7 +4375,8 @@ static struct security_operations selinux_ops = {
 	.socket_setsockopt =		selinux_socket_setsockopt,
 	.socket_shutdown =		selinux_socket_shutdown,
 	.socket_sock_rcv_skb =		selinux_socket_sock_rcv_skb,
-	.socket_getpeersec =		selinux_socket_getpeersec,
+	.socket_getpeersec_stream =	selinux_socket_getpeersec_stream,
+	.socket_getpeersec_dgram =	selinux_socket_getpeersec_dgram,
 	.sk_alloc_security =		selinux_sk_alloc_security,
 	.sk_free_security =		selinux_sk_free_security,
 	.sk_getsid = 			selinux_sk_getsid_security,
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
index 8e87996c6dd58bd6abadfc68e6cf95a6d1c1970e..a7f388bff3f2e4a703fc4f3c2e8cbffa65c19565 100644
--- a/security/selinux/include/xfrm.h
+++ b/security/selinux/include/xfrm.h
@@ -39,6 +39,8 @@ static inline u32 selinux_no_sk_sid(struct flowi *fl)
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb);
 int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb);
+u32 selinux_socket_getpeer_stream(struct sock *sk);
+u32 selinux_socket_getpeer_dgram(struct sk_buff *skb);
 #else
 static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
 {
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index b2af7ca496c1c925aa75c678565aa07b22559461..dfab6c886698d97f6c7d278125fd878422e8b515 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -224,6 +224,74 @@ void selinux_xfrm_state_free(struct xfrm_state *x)
 		kfree(ctx);
 }
 
+/*
+ * SELinux internal function to retrieve the context of a connected
+ * (sk->sk_state == TCP_ESTABLISHED) TCP socket based on its security
+ * association used to connect to the remote socket.
+ *
+ * Retrieve via getsockopt SO_PEERSEC.
+ */
+u32 selinux_socket_getpeer_stream(struct sock *sk)
+{
+	struct dst_entry *dst, *dst_test;
+	u32 peer_sid = SECSID_NULL;
+
+	if (sk->sk_state != TCP_ESTABLISHED)
+		goto out;
+
+	dst = sk_dst_get(sk);
+	if (!dst)
+		goto out;
+
+ 	for (dst_test = dst; dst_test != 0;
+      	     dst_test = dst_test->child) {
+		struct xfrm_state *x = dst_test->xfrm;
+
+ 		if (x && selinux_authorizable_xfrm(x)) {
+	 	 	struct xfrm_sec_ctx *ctx = x->security;
+			peer_sid = ctx->ctx_sid;
+			break;
+		}
+	}
+	dst_release(dst);
+
+out:
+	return peer_sid;
+}
+
+/*
+ * SELinux internal function to retrieve the context of a UDP packet
+ * based on its security association used to connect to the remote socket.
+ *
+ * Retrieve via setsockopt IP_PASSSEC and recvmsg with control message
+ * type SCM_SECURITY.
+ */
+u32 selinux_socket_getpeer_dgram(struct sk_buff *skb)
+{
+	struct sec_path *sp;
+
+	if (skb == NULL)
+		return SECSID_NULL;
+
+	if (skb->sk->sk_protocol != IPPROTO_UDP)
+		return SECSID_NULL;
+
+	sp = skb->sp;
+	if (sp) {
+		int i;
+
+		for (i = sp->len-1; i >= 0; i--) {
+			struct xfrm_state *x = sp->x[i].xvec;
+			if (selinux_authorizable_xfrm(x)) {
+				struct xfrm_sec_ctx *ctx = x->security;
+				return ctx->ctx_sid;
+			}
+		}
+	}
+
+	return SECSID_NULL;
+}
+
 /*
  * LSM hook that controls access to unlabelled packets.  If
  * a xfrm_state is authorizable (defined by macro) then it was