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