xref: /linux/tools/testing/selftests/net/so_rcv_listener.c (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <errno.h>
4 #include <netdb.h>
5 #include <stdbool.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <linux/types.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 
15 #ifndef SO_RCVPRIORITY
16 #define SO_RCVPRIORITY 82
17 #endif
18 
19 struct options {
20 	__u32 val;
21 	int name;
22 	int rcvname;
23 	const char *host;
24 	const char *service;
25 } opt;
26 
usage(const char * bin)27 static void __attribute__((noreturn)) usage(const char *bin)
28 {
29 	printf("Usage: %s [opts] <dst host> <dst port / service>\n", bin);
30 	printf("Options:\n"
31 		"\t\t-M val  Test SO_RCVMARK\n"
32 		"\t\t-P val  Test SO_RCVPRIORITY\n"
33 		"");
34 	exit(EXIT_FAILURE);
35 }
36 
parse_args(int argc,char * argv[])37 static void parse_args(int argc, char *argv[])
38 {
39 	int o;
40 
41 	while ((o = getopt(argc, argv, "M:P:")) != -1) {
42 		switch (o) {
43 		case 'M':
44 			opt.val = atoi(optarg);
45 			opt.name = SO_MARK;
46 			opt.rcvname = SO_RCVMARK;
47 			break;
48 		case 'P':
49 			opt.val = atoi(optarg);
50 			opt.name = SO_PRIORITY;
51 			opt.rcvname = SO_RCVPRIORITY;
52 			break;
53 		default:
54 			usage(argv[0]);
55 			break;
56 		}
57 	}
58 
59 	if (optind != argc - 2)
60 		usage(argv[0]);
61 
62 	opt.host = argv[optind];
63 	opt.service = argv[optind + 1];
64 }
65 
main(int argc,char * argv[])66 int main(int argc, char *argv[])
67 {
68 	int err = 0;
69 	int recv_fd = -1;
70 	int ret_value = 0;
71 	__u32 recv_val;
72 	struct cmsghdr *cmsg;
73 	char cbuf[CMSG_SPACE(sizeof(__u32))];
74 	char recv_buf[CMSG_SPACE(sizeof(__u32))];
75 	struct iovec iov[1];
76 	struct msghdr msg;
77 	struct sockaddr_in recv_addr4;
78 	struct sockaddr_in6 recv_addr6;
79 
80 	parse_args(argc, argv);
81 
82 	int family = strchr(opt.host, ':') ? AF_INET6 : AF_INET;
83 
84 	recv_fd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
85 	if (recv_fd < 0) {
86 		perror("Can't open recv socket");
87 		ret_value = -errno;
88 		goto cleanup;
89 	}
90 
91 	err = setsockopt(recv_fd, SOL_SOCKET, opt.rcvname, &opt.val, sizeof(opt.val));
92 	if (err < 0) {
93 		perror("Recv setsockopt error");
94 		ret_value = -errno;
95 		goto cleanup;
96 	}
97 
98 	if (family == AF_INET) {
99 		memset(&recv_addr4, 0, sizeof(recv_addr4));
100 		recv_addr4.sin_family = family;
101 		recv_addr4.sin_port = htons(atoi(opt.service));
102 
103 		if (inet_pton(family, opt.host, &recv_addr4.sin_addr) <= 0) {
104 			perror("Invalid IPV4 address");
105 			ret_value = -errno;
106 			goto cleanup;
107 		}
108 
109 		err = bind(recv_fd, (struct sockaddr *)&recv_addr4, sizeof(recv_addr4));
110 	} else {
111 		memset(&recv_addr6, 0, sizeof(recv_addr6));
112 		recv_addr6.sin6_family = family;
113 		recv_addr6.sin6_port = htons(atoi(opt.service));
114 
115 		if (inet_pton(family, opt.host, &recv_addr6.sin6_addr) <= 0) {
116 			perror("Invalid IPV6 address");
117 			ret_value = -errno;
118 			goto cleanup;
119 		}
120 
121 		err = bind(recv_fd, (struct sockaddr *)&recv_addr6, sizeof(recv_addr6));
122 	}
123 
124 	if (err < 0) {
125 		perror("Recv bind error");
126 		ret_value = -errno;
127 		goto cleanup;
128 	}
129 
130 	iov[0].iov_base = recv_buf;
131 	iov[0].iov_len = sizeof(recv_buf);
132 
133 	memset(&msg, 0, sizeof(msg));
134 	msg.msg_iov = iov;
135 	msg.msg_iovlen = 1;
136 	msg.msg_control = cbuf;
137 	msg.msg_controllen = sizeof(cbuf);
138 
139 	err = recvmsg(recv_fd, &msg, 0);
140 	if (err < 0) {
141 		perror("Message receive error");
142 		ret_value = -errno;
143 		goto cleanup;
144 	}
145 
146 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
147 		if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == opt.name) {
148 			recv_val = *(__u32 *)CMSG_DATA(cmsg);
149 			printf("Received value: %u\n", recv_val);
150 
151 			if (recv_val != opt.val) {
152 				fprintf(stderr, "Error: expected value: %u, got: %u\n",
153 					opt.val, recv_val);
154 				ret_value = -EINVAL;
155 			}
156 			goto cleanup;
157 		}
158 	}
159 
160 	fprintf(stderr, "Error: No matching cmsg received\n");
161 	ret_value = -ENOMSG;
162 
163 cleanup:
164 	if (recv_fd >= 0)
165 		close(recv_fd);
166 
167 	return ret_value;
168 }
169