1 /*-
2 * Copyright (c) 2007 Bruce M. Simpson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 /*
28 * Test utility for IPv4 broadcast sockets.
29 */
30
31 #include <sys/cdefs.h>
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <sys/socket.h>
36
37 #include <net/if.h>
38 #include <net/if_dl.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41
42 #include <signal.h>
43 #include <stddef.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <time.h>
48
49 #include <err.h>
50 #include <errno.h>
51 #include <getopt.h>
52 #include <pwd.h>
53 #include <unistd.h>
54 #include <netdb.h>
55 #include <libgen.h>
56
57 #ifndef IP_SENDIF
58 #define IP_SENDIF 24 /* XXX */
59 #endif
60
61 #ifndef IPPROTO_ZEROHOP
62 #define IPPROTO_ZEROHOP 114 /* any 0-hop protocol */
63 #endif
64
65 #define DEFAULT_PORT 6698
66 #define DEFAULT_PAYLOAD_SIZE 24
67 #define DEFAULT_TTL 1
68
69 #define MY_CMSG_SIZE \
70 CMSG_SPACE(sizeof(struct in_addr)) + \
71 CMSG_SPACE(sizeof(struct sockaddr_dl))
72
73 static char *progname = NULL;
74
75 static void
usage(void)76 usage(void)
77 {
78
79 fprintf(stderr, "IPv4 broadcast test program. Sends a %d byte UDP "
80 "datagram to <dest>:<port>.\n\n", DEFAULT_PAYLOAD_SIZE);
81 fprintf(stderr,
82 "usage: %s [-1] [-A laddr] [-b] [-B] [-d] [-i iface] [-l len]\n"
83 " [-p port] [-R] [-s srcaddr] [-t ttl] <dest>\n",
84 progname);
85 fprintf(stderr, "-1: Set IP_ONESBCAST\n");
86 fprintf(stderr, "-A: specify laddr (default: INADDR_ANY)\n");
87 fprintf(stderr, "-b: bind socket to <laddr>:<lport>\n");
88 fprintf(stderr, "-B: Set SO_BROADCAST\n");
89 fprintf(stderr, "-d: Set SO_DONTROUTE\n");
90 fprintf(stderr, "-i: Set IP_SENDIF <iface> (if supported)\n");
91 fprintf(stderr, "-l: Set payload size to <len>\n");
92 fprintf(stderr, "-p: Set local and remote port (default: %d)\n",
93 DEFAULT_PORT);
94 fprintf(stderr, "-R: Use raw IP (protocol %d)\n", IPPROTO_ZEROHOP);
95 #if 0
96 fprintf(stderr, "-r: Fill datagram with random bytes\n");
97 #endif
98 fprintf(stderr, "-s: Set IP_SENDSRCADDR to <srcaddr>\n");
99 fprintf(stderr, "-t: Set IP_TTL to <ttl>\n");
100
101 exit(EXIT_FAILURE);
102 }
103
104 int
main(int argc,char * argv[])105 main(int argc, char *argv[])
106 {
107 char *buf;
108 char cmsgbuf[MY_CMSG_SIZE];
109 struct iovec iov[1];
110 struct msghdr msg;
111 struct sockaddr_in dsin;
112 struct sockaddr_in laddr;
113 struct sockaddr_dl *sdl;
114 struct cmsghdr *cmsgp;
115 struct in_addr dstaddr;
116 struct in_addr *srcaddrp;
117 char *ifname;
118 char *laddr_s;
119 char *srcaddr_s;
120 int ch;
121 int dobind;
122 int dobroadcast;
123 int dontroute;
124 int doonesbcast;
125 int dorandom;
126 int dorawip;
127 size_t buflen;
128 ssize_t nbytes;
129 int portno;
130 int ret;
131 int s;
132 socklen_t soptlen;
133 int soptval;
134 int ttl;
135
136 dobind = 0;
137 dobroadcast = 0;
138 dontroute = 0;
139 doonesbcast = 0;
140 dorandom = 0;
141 dorawip = 0;
142
143 ifname = NULL;
144 dstaddr.s_addr = INADDR_ANY;
145 laddr_s = NULL;
146 srcaddr_s = NULL;
147 portno = DEFAULT_PORT;
148 ttl = DEFAULT_TTL;
149
150 buf = NULL;
151 buflen = DEFAULT_PAYLOAD_SIZE;
152
153 progname = basename(argv[0]);
154 while ((ch = getopt(argc, argv, "1A:bBdi:l:p:Rrs:t:")) != -1) {
155 switch (ch) {
156 case '1':
157 doonesbcast = 1;
158 break;
159 case 'A':
160 laddr_s = optarg;
161 break;
162 case 'b':
163 dobind = 1;
164 break;
165 case 'B':
166 dobroadcast = 1;
167 break;
168 case 'd':
169 dontroute = 1;
170 break;
171 case 'i':
172 ifname = optarg;
173 break;
174 case 'l':
175 buflen = atoi(optarg);
176 break;
177 case 'p':
178 portno = atoi(optarg);
179 break;
180 case 'R':
181 dorawip = 1;
182 break;
183 case 'r':
184 dorandom = 1;
185 break;
186 case 's':
187 srcaddr_s = optarg;
188 break;
189 case 't':
190 ttl = atoi(optarg);
191 break;
192 default:
193 usage();
194 break;
195 }
196 }
197 argc -= optind;
198 argv += optind;
199
200 if (argc != 1)
201 usage();
202 if (argv[0] == NULL || inet_aton(argv[0], &dstaddr) == 0)
203 usage();
204 /* IP_SENDSRCADDR and IP_SENDIF are mutually exclusive just now. */
205 if (srcaddr_s != NULL && ifname != NULL)
206 usage();
207 if (dorawip) {
208 if (geteuid() != 0)
209 fprintf(stderr, "WARNING: not running as root.\n");
210 s = socket(PF_INET, SOCK_RAW, IPPROTO_ZEROHOP);
211 } else {
212 s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
213 }
214 if (s == -1) {
215 perror("socket");
216 exit(EXIT_FAILURE);
217 }
218
219 if (dontroute) {
220 soptval = 1;
221 soptlen = sizeof(soptval);
222 ret = setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &soptval,
223 soptlen);
224 if (ret == -1) {
225 perror("setsockopt SO_DONTROUTE");
226 close(s);
227 exit(EXIT_FAILURE);
228 }
229 }
230
231 if (dobroadcast) {
232 soptval = 1;
233 soptlen = sizeof(soptval);
234 ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &soptval,
235 soptlen);
236 if (ret == -1) {
237 perror("setsockopt SO_BROADCAST");
238 close(s);
239 exit(EXIT_FAILURE);
240 }
241 }
242
243 soptval = ttl;
244 soptlen = sizeof(soptval);
245 ret = setsockopt(s, IPPROTO_IP, IP_TTL, &soptval, soptlen);
246 if (ret == -1) {
247 perror("setsockopt IPPROTO_IP IP_TTL");
248 close(s);
249 exit(EXIT_FAILURE);
250 }
251
252 if (doonesbcast) {
253 soptval = 1;
254 soptlen = sizeof(soptval);
255 ret = setsockopt(s, IPPROTO_IP, IP_ONESBCAST, &soptval,
256 soptlen);
257 if (ret == -1) {
258 perror("setsockopt IP_ONESBCAST");
259 close(s);
260 exit(EXIT_FAILURE);
261 }
262 }
263
264 if (dobind) {
265 memset(&laddr, 0, sizeof(struct sockaddr_in));
266 laddr.sin_family = AF_INET;
267 laddr.sin_len = sizeof(struct sockaddr_in);
268 if (laddr_s != NULL) {
269 laddr.sin_addr.s_addr = inet_addr(laddr_s);
270 } else
271 laddr.sin_addr.s_addr = INADDR_ANY;
272 laddr.sin_port = htons(portno);
273 ret = bind(s, (struct sockaddr *)&laddr, sizeof(laddr));
274 if (ret == -1) {
275 perror("bind");
276 close(s);
277 exit(EXIT_FAILURE);
278 }
279 }
280
281 memset(&dsin, 0, sizeof(struct sockaddr_in));
282 dsin.sin_family = AF_INET;
283 dsin.sin_len = sizeof(struct sockaddr_in);
284 dsin.sin_addr.s_addr = dstaddr.s_addr;
285 dsin.sin_port = htons(portno);
286
287 buf = malloc(buflen);
288 if (buf == NULL) {
289 perror("malloc");
290 close(s);
291 exit(EXIT_FAILURE);
292 }
293 memset(iov, 0, sizeof(iov));
294 iov[0].iov_base = buf;
295 iov[0].iov_len = buflen;
296
297 memset(&msg, 0, sizeof(struct msghdr));
298 msg.msg_name = &dsin;
299 msg.msg_namelen = sizeof(dsin);
300 msg.msg_iov = iov;
301 msg.msg_iovlen = 1;
302
303 /* Assume we fill out a control msg; macros need to see buf ptr */
304 msg.msg_control = cmsgbuf;
305 msg.msg_controllen = 0;
306 memset(cmsgbuf, 0, MY_CMSG_SIZE);
307
308 /* IP_SENDSRCADDR and IP_SENDIF are mutually exclusive just now. */
309 if (srcaddr_s != NULL) {
310 msg.msg_controllen += CMSG_SPACE(sizeof(struct in_addr));
311 cmsgp = CMSG_FIRSTHDR(&msg);
312 cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
313 cmsgp->cmsg_level = IPPROTO_IP;
314 cmsgp->cmsg_type = IP_SENDSRCADDR;
315 srcaddrp = (struct in_addr *)CMSG_DATA(cmsgp);
316 srcaddrp->s_addr = inet_addr(srcaddr_s);
317 }
318
319 if (ifname != NULL) {
320 #ifdef IP_SENDIF
321 msg.msg_controllen += CMSG_SPACE(sizeof(struct sockaddr_dl));
322 cmsgp = CMSG_FIRSTHDR(&msg);
323 cmsgp->cmsg_len = CMSG_LEN(sizeof(struct sockaddr_dl));
324 cmsgp->cmsg_level = IPPROTO_IP;
325 cmsgp->cmsg_type = IP_SENDIF;
326
327 #ifdef DIAGNOSTIC
328 fprintf(stderr, "DEBUG: cmsgp->cmsg_len is %d\n",
329 cmsgp->cmsg_len);
330 #endif
331
332 sdl = (struct sockaddr_dl *)CMSG_DATA(cmsgp);
333 memset(sdl, 0, sizeof(struct sockaddr_dl));
334 sdl->sdl_family = AF_LINK;
335 sdl->sdl_len = sizeof(struct sockaddr_dl);
336 sdl->sdl_index = if_nametoindex(ifname);
337
338 #ifdef DIAGNOSTIC
339 fprintf(stderr, "DEBUG: sdl->sdl_family is %d\n",
340 sdl->sdl_family);
341 fprintf(stderr, "DEBUG: sdl->sdl_len is %d\n",
342 sdl->sdl_len);
343 fprintf(stderr, "DEBUG: sdl->sdl_index is %d\n",
344 sdl->sdl_index);
345 #endif
346 #else
347 fprintf(stderr, "WARNING: IP_SENDIF not supported, ignored.\n");
348 #endif
349 }
350
351 if (msg.msg_controllen == 0)
352 msg.msg_control = NULL;
353
354 nbytes = sendmsg(s, &msg, (dontroute ? MSG_DONTROUTE : 0));
355 if (nbytes == -1) {
356 perror("sendmsg");
357 close(s);
358 exit(EXIT_FAILURE);
359 }
360
361 close(s);
362
363 exit(EXIT_SUCCESS);
364 }
365