enum {
                LOAD_REJECT,
                ATTACH_REJECT,
+               ATTACH_OKAY,
                SYSCALL_EPERM,
                SYSCALL_ENOTSUPP,
                SUCCESS,
 static int connect6_prog_load(const struct sock_addr_test *test);
 static int sendmsg_allow_prog_load(const struct sock_addr_test *test);
 static int sendmsg_deny_prog_load(const struct sock_addr_test *test);
+static int recvmsg_allow_prog_load(const struct sock_addr_test *test);
+static int recvmsg_deny_prog_load(const struct sock_addr_test *test);
 static int sendmsg4_rw_asm_prog_load(const struct sock_addr_test *test);
+static int recvmsg4_rw_asm_prog_load(const struct sock_addr_test *test);
 static int sendmsg4_rw_c_prog_load(const struct sock_addr_test *test);
 static int sendmsg6_rw_asm_prog_load(const struct sock_addr_test *test);
+static int recvmsg6_rw_asm_prog_load(const struct sock_addr_test *test);
 static int sendmsg6_rw_c_prog_load(const struct sock_addr_test *test);
 static int sendmsg6_rw_v4mapped_prog_load(const struct sock_addr_test *test);
 static int sendmsg6_rw_wildcard_prog_load(const struct sock_addr_test *test);
                SRC6_REWRITE_IP,
                SYSCALL_EPERM,
        },
+
+       /* recvmsg */
+       {
+               "recvmsg4: return code ok",
+               recvmsg_allow_prog_load,
+               BPF_CGROUP_UDP4_RECVMSG,
+               BPF_CGROUP_UDP4_RECVMSG,
+               AF_INET,
+               SOCK_DGRAM,
+               NULL,
+               0,
+               NULL,
+               0,
+               NULL,
+               ATTACH_OKAY,
+       },
+       {
+               "recvmsg4: return code !ok",
+               recvmsg_deny_prog_load,
+               BPF_CGROUP_UDP4_RECVMSG,
+               BPF_CGROUP_UDP4_RECVMSG,
+               AF_INET,
+               SOCK_DGRAM,
+               NULL,
+               0,
+               NULL,
+               0,
+               NULL,
+               LOAD_REJECT,
+       },
+       {
+               "recvmsg6: return code ok",
+               recvmsg_allow_prog_load,
+               BPF_CGROUP_UDP6_RECVMSG,
+               BPF_CGROUP_UDP6_RECVMSG,
+               AF_INET6,
+               SOCK_DGRAM,
+               NULL,
+               0,
+               NULL,
+               0,
+               NULL,
+               ATTACH_OKAY,
+       },
+       {
+               "recvmsg6: return code !ok",
+               recvmsg_deny_prog_load,
+               BPF_CGROUP_UDP6_RECVMSG,
+               BPF_CGROUP_UDP6_RECVMSG,
+               AF_INET6,
+               SOCK_DGRAM,
+               NULL,
+               0,
+               NULL,
+               0,
+               NULL,
+               LOAD_REJECT,
+       },
+       {
+               "recvmsg4: rewrite IP & port (asm)",
+               recvmsg4_rw_asm_prog_load,
+               BPF_CGROUP_UDP4_RECVMSG,
+               BPF_CGROUP_UDP4_RECVMSG,
+               AF_INET,
+               SOCK_DGRAM,
+               SERV4_REWRITE_IP,
+               SERV4_REWRITE_PORT,
+               SERV4_REWRITE_IP,
+               SERV4_REWRITE_PORT,
+               SERV4_IP,
+               SUCCESS,
+       },
+       {
+               "recvmsg6: rewrite IP & port (asm)",
+               recvmsg6_rw_asm_prog_load,
+               BPF_CGROUP_UDP6_RECVMSG,
+               BPF_CGROUP_UDP6_RECVMSG,
+               AF_INET6,
+               SOCK_DGRAM,
+               SERV6_REWRITE_IP,
+               SERV6_REWRITE_PORT,
+               SERV6_REWRITE_IP,
+               SERV6_REWRITE_PORT,
+               SERV6_IP,
+               SUCCESS,
+       },
 };
 
 static int mk_sockaddr(int domain, const char *ip, unsigned short port,
        return load_path(test, CONNECT6_PROG_PATH);
 }
 
-static int sendmsg_ret_only_prog_load(const struct sock_addr_test *test,
-                                     int32_t rc)
+static int xmsg_ret_only_prog_load(const struct sock_addr_test *test,
+                                  int32_t rc)
 {
        struct bpf_insn insns[] = {
                /* return rc */
 
 static int sendmsg_allow_prog_load(const struct sock_addr_test *test)
 {
-       return sendmsg_ret_only_prog_load(test, /*rc*/ 1);
+       return xmsg_ret_only_prog_load(test, /*rc*/ 1);
 }
 
 static int sendmsg_deny_prog_load(const struct sock_addr_test *test)
 {
-       return sendmsg_ret_only_prog_load(test, /*rc*/ 0);
+       return xmsg_ret_only_prog_load(test, /*rc*/ 0);
+}
+
+static int recvmsg_allow_prog_load(const struct sock_addr_test *test)
+{
+       return xmsg_ret_only_prog_load(test, /*rc*/ 1);
+}
+
+static int recvmsg_deny_prog_load(const struct sock_addr_test *test)
+{
+       return xmsg_ret_only_prog_load(test, /*rc*/ 0);
 }
 
 static int sendmsg4_rw_asm_prog_load(const struct sock_addr_test *test)
        return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn));
 }
 
+static int recvmsg4_rw_asm_prog_load(const struct sock_addr_test *test)
+{
+       struct sockaddr_in src4_rw_addr;
+
+       if (mk_sockaddr(AF_INET, SERV4_IP, SERV4_PORT,
+                       (struct sockaddr *)&src4_rw_addr,
+                       sizeof(src4_rw_addr)) == -1)
+               return -1;
+
+       struct bpf_insn insns[] = {
+               BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+
+               /* if (sk.family == AF_INET && */
+               BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
+                           offsetof(struct bpf_sock_addr, family)),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET, 6),
+
+               /*     sk.type == SOCK_DGRAM)  { */
+               BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
+                           offsetof(struct bpf_sock_addr, type)),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_DGRAM, 4),
+
+               /*      user_ip4 = src4_rw_addr.sin_addr */
+               BPF_MOV32_IMM(BPF_REG_7, src4_rw_addr.sin_addr.s_addr),
+               BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7,
+                           offsetof(struct bpf_sock_addr, user_ip4)),
+
+               /*      user_port = src4_rw_addr.sin_port */
+               BPF_MOV32_IMM(BPF_REG_7, src4_rw_addr.sin_port),
+               BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7,
+                           offsetof(struct bpf_sock_addr, user_port)),
+               /* } */
+
+               /* return 1 */
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       };
+
+       return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn));
+}
+
 static int sendmsg4_rw_c_prog_load(const struct sock_addr_test *test)
 {
        return load_path(test, SENDMSG4_PROG_PATH);
        return sendmsg6_rw_dst_asm_prog_load(test, SERV6_REWRITE_IP);
 }
 
+static int recvmsg6_rw_asm_prog_load(const struct sock_addr_test *test)
+{
+       struct sockaddr_in6 src6_rw_addr;
+
+       if (mk_sockaddr(AF_INET6, SERV6_IP, SERV6_PORT,
+                       (struct sockaddr *)&src6_rw_addr,
+                       sizeof(src6_rw_addr)) == -1)
+               return -1;
+
+       struct bpf_insn insns[] = {
+               BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+
+               /* if (sk.family == AF_INET6) { */
+               BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
+                           offsetof(struct bpf_sock_addr, family)),
+               BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET6, 10),
+
+               STORE_IPV6(user_ip6, src6_rw_addr.sin6_addr.s6_addr32),
+
+               /*      user_port = dst6_rw_addr.sin6_port */
+               BPF_MOV32_IMM(BPF_REG_7, src6_rw_addr.sin6_port),
+               BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7,
+                           offsetof(struct bpf_sock_addr, user_port)),
+               /* } */
+
+               /* return 1 */
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       };
+
+       return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn));
+}
+
 static int sendmsg6_rw_v4mapped_prog_load(const struct sock_addr_test *test)
 {
        return sendmsg6_rw_dst_asm_prog_load(test, SERV6_V4MAPPED_IP);
        return err;
 }
 
-static int run_sendmsg_test_case(const struct sock_addr_test *test)
+static int run_xmsg_test_case(const struct sock_addr_test *test, int max_cmsg)
 {
        socklen_t addr_len = sizeof(struct sockaddr_storage);
-       struct sockaddr_storage expected_src_addr;
-       struct sockaddr_storage requested_addr;
        struct sockaddr_storage expected_addr;
-       struct sockaddr_storage real_src_addr;
+       struct sockaddr_storage server_addr;
+       struct sockaddr_storage sendmsg_addr;
+       struct sockaddr_storage recvmsg_addr;
        int clientfd = -1;
        int servfd = -1;
        int set_cmsg;
        if (test->type != SOCK_DGRAM)
                goto err;
 
-       if (init_addrs(test, &requested_addr, &expected_addr,
-                      &expected_src_addr))
+       if (init_addrs(test, &sendmsg_addr, &server_addr, &expected_addr))
                goto err;
 
        /* Prepare server to sendmsg to */
-       servfd = start_server(test->type, &expected_addr, addr_len);
+       servfd = start_server(test->type, &server_addr, addr_len);
        if (servfd == -1)
                goto err;
 
-       for (set_cmsg = 0; set_cmsg <= 1; ++set_cmsg) {
+       for (set_cmsg = 0; set_cmsg <= max_cmsg; ++set_cmsg) {
                if (clientfd >= 0)
                        close(clientfd);
 
-               clientfd = sendmsg_to_server(test->type, &requested_addr,
+               clientfd = sendmsg_to_server(test->type, &sendmsg_addr,
                                             addr_len, set_cmsg, /*flags*/0,
                                             &err);
                if (err)
                 * specific packet may differ from the one used by default and
                 * returned by getsockname(2).
                 */
-               if (recvmsg_from_client(servfd, &real_src_addr) == -1)
+               if (recvmsg_from_client(servfd, &recvmsg_addr) == -1)
                        goto err;
 
-               if (cmp_addr(&real_src_addr, &expected_src_addr, /*cmp_port*/0))
+               if (cmp_addr(&recvmsg_addr, &expected_addr, /*cmp_port*/0))
                        goto err;
        }
 
                goto out;
        } else if (test->expected_result == ATTACH_REJECT || err) {
                goto err;
+       } else if (test->expected_result == ATTACH_OKAY) {
+               err = 0;
+               goto out;
        }
 
        switch (test->attach_type) {
                break;
        case BPF_CGROUP_UDP4_SENDMSG:
        case BPF_CGROUP_UDP6_SENDMSG:
-               err = run_sendmsg_test_case(test);
+               err = run_xmsg_test_case(test, 1);
+               break;
+       case BPF_CGROUP_UDP4_RECVMSG:
+       case BPF_CGROUP_UDP6_RECVMSG:
+               err = run_xmsg_test_case(test, 0);
                break;
        default:
                goto err;