1/*
2 * Copyright (c) 2016 Cyril Hrubis <chrubis@suse.cz>
3 * Copyright (c) 2016 Michal Kubecek <mkubecek@suse.cz>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/*
20 * This is a regression test for kernel commit:
21 *
22 * 197c949e7798  udp: properly support MSG_PEEK with truncated buffers
23 *
24 * NOTE: The testcase will hang on upatched stable kernel.
25 */
26
27#include <string.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <netinet/in.h>
34
35#include "tst_test.h"
36#include "lapi/socket.h"
37
38static const char msg[] = "Michael Gilfix was here\341\210\264\r\n";
39static const unsigned msglen = ARRAY_SIZE(msg) - 1;
40static unsigned char buff[25];
41static const int bufflen = ARRAY_SIZE(buff);
42
43static int sdr, sdw;
44
45static void verify_recvmsg(void)
46{
47	struct sockaddr_in6 addr_init = {
48		.sin6_family	= AF_INET6,
49		.sin6_port	= htons(0),
50		.sin6_addr	= IN6ADDR_LOOPBACK_INIT,
51	};
52	struct sockaddr_in6 addr_r, addr_w, addr_f;
53	socklen_t addrlen_r, addrlen_w;
54	struct iovec iov = {
55		.iov_base	= buff,
56		.iov_len	= sizeof(buff),
57	};
58	struct msghdr msghdr = {
59		.msg_name	= &addr_f,
60		.msg_namelen	= sizeof(addr_f),
61		.msg_iov	= &iov,
62		.msg_iovlen	= 1,
63		.msg_control	= NULL,
64		.msg_controllen	= 0,
65		.msg_flags	= 0,
66	};
67	int R;
68
69	sdr = SAFE_SOCKET(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_IP);
70	SAFE_BIND(sdr, (struct sockaddr*)&addr_init, sizeof(addr_init));
71	addrlen_r = sizeof(addr_r);
72	SAFE_GETSOCKNAME(sdr, (struct sockaddr*)&addr_r, &addrlen_r);
73	sdw = SAFE_SOCKET(PF_INET6, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_IP);
74	SAFE_BIND(sdw, (struct sockaddr*)&addr_init, sizeof(addr_init));
75	addrlen_w = sizeof(addr_w);
76	SAFE_GETSOCKNAME(sdw, (struct sockaddr*)&addr_w, &addrlen_w);
77
78	R = sendto(sdw, msg, msglen, 0, (struct sockaddr*)&addr_r, addrlen_r);
79	if (R < 0)
80		tst_brk(TBROK | TERRNO, "sendto()");
81
82	R = recvmsg(sdr, &msghdr, MSG_PEEK);
83	if (R < 0) {
84		tst_res(TFAIL | TERRNO, "recvmsg(..., MSG_PEEK)");
85		return;
86	}
87
88	tst_res(TINFO, "received %d bytes", R);
89
90	if ((R == bufflen) && !memcmp(msg, buff, R))
91		tst_res(TPASS, "recvmsg(..., MSG_PEEK) works fine");
92	else
93		tst_res(TPASS, "recvmsg(..., MSG_PEEK) failed");
94
95	SAFE_CLOSE(sdw);
96	SAFE_CLOSE(sdr);
97}
98
99static void cleanup(void)
100{
101	if (sdw > 0 && close(sdw))
102		tst_res(TWARN | TERRNO, "close(sdw) failed");
103
104	if (sdr > 0 && close(sdr))
105		tst_res(TWARN | TERRNO, "close(sdr) failed");
106}
107
108static struct tst_test test = {
109	.tid = "recvmsg02",
110	.min_kver = "2.6.27",
111	.test_all = verify_recvmsg,
112	.cleanup = cleanup,
113};
114