#define SCM_TIMESTAMPING_PKTINFO       58
 
+#define SO_PEERGROUPS          59
+
 #endif /* _UAPI_ASM_SOCKET_H */
 
 
 #define SCM_TIMESTAMPING_PKTINFO       58
 
+#define SO_PEERGROUPS          59
+
 #endif /* _ASM_SOCKET_H */
 
 
 
 #define SCM_TIMESTAMPING_PKTINFO       58
 
+#define SO_PEERGROUPS          59
+
 #endif /* _ASM_IA64_SOCKET_H */
 
 
 #define SCM_TIMESTAMPING_PKTINFO       58
 
+#define SO_PEERGROUPS          59
+
 #endif /* _ASM_M32R_SOCKET_H */
 
 
 #define SCM_TIMESTAMPING_PKTINFO       58
 
+#define SO_PEERGROUPS          59
+
 #endif /* _UAPI_ASM_SOCKET_H */
 
 
 #define SCM_TIMESTAMPING_PKTINFO       58
 
+#define SO_PEERGROUPS          59
+
 #endif /* _ASM_SOCKET_H */
 
 
 #define SCM_TIMESTAMPING_PKTINFO       0x4033
 
+#define SO_PEERGROUPS          0x4034
+
 #endif /* _UAPI_ASM_SOCKET_H */
 
 
 #define SCM_TIMESTAMPING_PKTINFO       58
 
+#define SO_PEERGROUPS          59
+
 #endif /* _ASM_SOCKET_H */
 
 
 #define SCM_TIMESTAMPING_PKTINFO       0x003c
 
+#define SO_PEERGROUPS          0x003d
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       0x5002
 
 
 #define SCM_TIMESTAMPING_PKTINFO       58
 
+#define SO_PEERGROUPS          59
+
 #endif /* _XTENSA_SOCKET_H */
 
 
 #define SCM_TIMESTAMPING_PKTINFO       58
 
+#define SO_PEERGROUPS          59
+
 #endif /* __ASM_GENERIC_SOCKET_H */
 
        }
 }
 
+static int groups_to_user(gid_t __user *dst, const struct group_info *src)
+{
+       struct user_namespace *user_ns = current_user_ns();
+       int i;
+
+       for (i = 0; i < src->ngroups; i++)
+               if (put_user(from_kgid_munged(user_ns, src->gid[i]), dst + i))
+                       return -EFAULT;
+
+       return 0;
+}
+
 int sock_getsockopt(struct socket *sock, int level, int optname,
                    char __user *optval, int __user *optlen)
 {
                goto lenout;
        }
 
+       case SO_PEERGROUPS:
+       {
+               int ret, n;
+
+               if (!sk->sk_peer_cred)
+                       return -ENODATA;
+
+               n = sk->sk_peer_cred->group_info->ngroups;
+               if (len < n * sizeof(gid_t)) {
+                       len = n * sizeof(gid_t);
+                       return put_user(len, optlen) ? -EFAULT : -ERANGE;
+               }
+               len = n * sizeof(gid_t);
+
+               ret = groups_to_user((gid_t __user *)optval,
+                                    sk->sk_peer_cred->group_info);
+               if (ret)
+                       return ret;
+               goto lenout;
+       }
+
        case SO_PEERNAME:
        {
                char address[128];