#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
 
        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);
        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)
        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;
 }
 
 
 #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;
 
                        break;
 
                case SO_PEERSEC:
-                       return security_socket_getpeersec(sock, optval, optlen, len);
+                       return security_socket_getpeersec_stream(sock, optval, optlen, len);
 
                default:
                        return(-ENOPROTOOPT);
 
 #define IP_CMSG_TOS            4
 #define IP_CMSG_RECVOPTS       8
 #define IP_CMSG_RETOPTS                16
+#define IP_CMSG_PASSSEC                32
 
 /*
  *     SOL_IP control messages.
        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)
 {
 
        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)
                            (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)) {
                        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;
                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;
 
        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;
 }
 
        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;
 
        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);
        .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,
 
 #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)
 {
 
                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