1/*
2 * This file is part of net-yy-netlink strace test.
3 *
4 * Copyright (c) 2014-2016 Dmitry V. Levin <ldv@altlinux.org>
5 * Copyright (c) 2016 Fabien Siron <fabien.siron@epita.fr>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "tests.h"
32#include <errno.h>
33#include <string.h>
34#include <unistd.h>
35#include <netinet/in.h>
36#include "netlink.h"
37#include <linux/sock_diag.h>
38#include <linux/netlink_diag.h>
39
40static void
41send_query(const int fd)
42{
43	struct sockaddr_nl nladdr = {
44		.nl_family = AF_NETLINK
45	};
46	struct {
47		struct nlmsghdr nlh;
48		struct netlink_diag_req ndr;
49	} req = {
50		.nlh = {
51			.nlmsg_len = sizeof(req),
52			.nlmsg_type = SOCK_DIAG_BY_FAMILY,
53			.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
54		},
55		.ndr = {
56			.sdiag_family = AF_NETLINK,
57			.sdiag_protocol = NDIAG_PROTO_ALL,
58			.ndiag_show = NDIAG_SHOW_MEMINFO
59		}
60	};
61	struct iovec iov = {
62		.iov_base = &req,
63		.iov_len = sizeof(req)
64	};
65	struct msghdr msg = {
66		.msg_name = (void *) &nladdr,
67		.msg_namelen = sizeof(nladdr),
68		.msg_iov = &iov,
69		.msg_iovlen = 1
70	};
71
72	if (sendmsg(fd, &msg, 0) <= 0)
73		perror_msg_and_skip("sendmsg");
74}
75
76static void
77check_responses(const int fd)
78{
79	static union {
80		struct nlmsghdr hdr;
81		long buf[8192 / sizeof(long)];
82	} hdr_buf;
83
84	struct sockaddr_nl nladdr = {
85		.nl_family = AF_NETLINK
86	};
87	struct iovec iov = {
88		.iov_base = hdr_buf.buf,
89		.iov_len = sizeof(hdr_buf.buf)
90	};
91	struct msghdr msg = {
92		.msg_name = (void *) &nladdr,
93		.msg_namelen = sizeof(nladdr),
94		.msg_iov = &iov,
95		.msg_iovlen = 1
96	};
97
98	ssize_t ret = recvmsg(fd, &msg, 0);
99	if (ret <= 0)
100		perror_msg_and_skip("recvmsg");
101
102	struct nlmsghdr *h = &hdr_buf.hdr;
103	if (!NLMSG_OK(h, ret))
104		error_msg_and_skip("!NLMSG_OK");
105	if (h->nlmsg_type == NLMSG_ERROR) {
106		const struct nlmsgerr *err = NLMSG_DATA(h);
107		if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
108			error_msg_and_skip("NLMSG_ERROR");
109		errno = -err->error;
110		perror_msg_and_skip("NLMSG_ERROR");
111	}
112	if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY)
113		error_msg_and_skip("unexpected nlmsg_type %u",
114				   (unsigned) h->nlmsg_type);
115
116	const struct netlink_diag_msg *diag = NLMSG_DATA(h);
117	if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*diag)))
118		error_msg_and_skip("short response");
119}
120
121int main(void)
122{
123	struct sockaddr_nl addr;
124	socklen_t len = sizeof(addr);
125
126	memset(&addr, 0, sizeof(addr));
127	addr.nl_family = AF_NETLINK;
128
129	close(0);
130	close(1);
131
132	if (socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG))
133		perror_msg_and_skip("socket AF_NETLINK");
134	if (bind(0, (struct sockaddr *) &addr, len))
135		perror_msg_and_skip("bind");
136
137	if (socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG) != 1)
138		perror_msg_and_skip("socket AF_NETLINK");
139
140	send_query(1);
141	check_responses(1);
142	return 0;
143}
144