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/param.h>
32#include <sys/types.h>
33#include <sys/ioctl.h>
34#include <sys/socket.h>
35
36#include <net/if.h>
37#include <net/if_dl.h>
38#include <netinet/in.h>
39#include <arpa/inet.h>
40
41#include <signal.h>
42#include <stddef.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <time.h>
47
48#include <err.h>
49#include <errno.h>
50#include <getopt.h>
51#include <pwd.h>
52#include <unistd.h>
53#include <netdb.h>
54#include <libgen.h>
55
56#ifndef IP_SENDIF
57#define IP_SENDIF	24		/* XXX */
58#endif
59
60#ifndef IPPROTO_ZEROHOP
61#define IPPROTO_ZEROHOP	114		/* any 0-hop protocol */
62#endif
63
64#define DEFAULT_PORT		6698
65#define DEFAULT_PAYLOAD_SIZE	24
66#define DEFAULT_TTL		1
67
68#define MY_CMSG_SIZE				\
69	CMSG_SPACE(sizeof(struct in_addr)) +	\
70	CMSG_SPACE(sizeof(struct sockaddr_dl))
71
72static char *progname = NULL;
73
74static void
75usage(void)
76{
77
78	fprintf(stderr, "IPv4 broadcast test program. Sends a %d byte UDP "
79	        "datagram to <dest>:<port>.\n\n", DEFAULT_PAYLOAD_SIZE);
80	fprintf(stderr,
81"usage: %s [-1] [-A laddr] [-b] [-B] [-d] [-i iface] [-l len]\n"
82"                   [-p port] [-R] [-s srcaddr] [-t ttl] <dest>\n",
83	    progname);
84	fprintf(stderr, "-1: Set IP_ONESBCAST\n");
85	fprintf(stderr, "-A: specify laddr (default: INADDR_ANY)\n");
86	fprintf(stderr, "-b: bind socket to <laddr>:<lport>\n");
87	fprintf(stderr, "-B: Set SO_BROADCAST\n");
88	fprintf(stderr, "-d: Set SO_DONTROUTE\n");
89	fprintf(stderr, "-i: Set IP_SENDIF <iface> (if supported)\n");
90	fprintf(stderr, "-l: Set payload size to <len>\n");
91	fprintf(stderr, "-p: Set local and remote port (default: %d)\n",
92	    DEFAULT_PORT);
93	fprintf(stderr, "-R: Use raw IP (protocol %d)\n", IPPROTO_ZEROHOP);
94#if 0
95	fprintf(stderr, "-r: Fill datagram with random bytes\n");
96#endif
97	fprintf(stderr, "-s: Set IP_SENDSRCADDR to <srcaddr>\n");
98	fprintf(stderr, "-t: Set IP_TTL to <ttl>\n");
99
100	exit(EXIT_FAILURE);
101}
102
103int
104main(int argc, char *argv[])
105{
106	char			*buf;
107	char			 cmsgbuf[MY_CMSG_SIZE];
108	struct iovec		 iov[1];
109	struct msghdr		 msg;
110	struct sockaddr_in	 dsin;
111	struct sockaddr_in	 laddr;
112	struct sockaddr_dl	*sdl;
113	struct cmsghdr		*cmsgp;
114	struct in_addr		 dstaddr;
115	struct in_addr		*srcaddrp;
116	char			*ifname;
117	char			*laddr_s;
118	char			*srcaddr_s;
119	int			 ch;
120	int			 dobind;
121	int			 dobroadcast;
122	int			 dontroute;
123	int			 doonesbcast;
124	int			 dorandom;
125	int			 dorawip;
126	size_t			 buflen;
127	ssize_t			 nbytes;
128	int			 portno;
129	int			 ret;
130	int			 s;
131	socklen_t		 soptlen;
132	int			 soptval;
133	int			 ttl;
134
135	dobind = 0;
136	dobroadcast = 0;
137	dontroute = 0;
138	doonesbcast = 0;
139	dorandom = 0;
140	dorawip = 0;
141
142	ifname = NULL;
143	dstaddr.s_addr = INADDR_ANY;
144	laddr_s = NULL;
145	srcaddr_s = NULL;
146	portno = DEFAULT_PORT;
147	ttl = DEFAULT_TTL;
148
149	buf = NULL;
150	buflen = DEFAULT_PAYLOAD_SIZE;
151
152	progname = basename(argv[0]);
153	while ((ch = getopt(argc, argv, "1A:bBdi:l:p:Rrs:t:")) != -1) {
154		switch (ch) {
155		case '1':
156			doonesbcast = 1;
157			break;
158		case 'A':
159			laddr_s = optarg;
160			break;
161		case 'b':
162			dobind = 1;
163			break;
164		case 'B':
165			dobroadcast = 1;
166			break;
167		case 'd':
168			dontroute = 1;
169			break;
170		case 'i':
171			ifname = optarg;
172			break;
173		case 'l':
174			buflen = atoi(optarg);
175			break;
176		case 'p':
177			portno = atoi(optarg);
178			break;
179		case 'R':
180			dorawip = 1;
181			break;
182		case 'r':
183			dorandom = 1;
184			break;
185		case 's':
186			srcaddr_s = optarg;
187			break;
188		case 't':
189			ttl = atoi(optarg);
190			break;
191		default:
192			usage();
193			break;
194		}
195	}
196	argc -= optind;
197	argv += optind;
198
199	if (argc != 1)
200		usage();
201	if (argv[0] == NULL || inet_aton(argv[0], &dstaddr) == 0)
202		usage();
203	/* IP_SENDSRCADDR and IP_SENDIF are mutually exclusive just now. */
204	if (srcaddr_s != NULL && ifname != NULL)
205		usage();
206	if (dorawip) {
207		if (geteuid() != 0)
208			fprintf(stderr, "WARNING: not running as root.\n");
209		s = socket(PF_INET, SOCK_RAW, IPPROTO_ZEROHOP);
210	} else {
211		s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
212	}
213	if (s == -1) {
214		perror("socket");
215		exit(EXIT_FAILURE);
216	}
217
218	if (dontroute) {
219		soptval = 1;
220		soptlen = sizeof(soptval);
221		ret = setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &soptval,
222		    soptlen);
223		if (ret == -1) {
224			perror("setsockopt SO_DONTROUTE");
225			close(s);
226			exit(EXIT_FAILURE);
227		}
228	}
229
230	if (dobroadcast) {
231		soptval = 1;
232		soptlen = sizeof(soptval);
233		ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &soptval,
234		    soptlen);
235		if (ret == -1) {
236			perror("setsockopt SO_BROADCAST");
237			close(s);
238			exit(EXIT_FAILURE);
239		}
240	}
241
242	soptval = ttl;
243	soptlen = sizeof(soptval);
244	ret = setsockopt(s, IPPROTO_IP, IP_TTL, &soptval, soptlen);
245	if (ret == -1) {
246		perror("setsockopt IPPROTO_IP IP_TTL");
247		close(s);
248		exit(EXIT_FAILURE);
249	}
250
251	if (doonesbcast) {
252		soptval = 1;
253		soptlen = sizeof(soptval);
254		ret = setsockopt(s, IPPROTO_IP, IP_ONESBCAST, &soptval,
255		    soptlen);
256		if (ret == -1) {
257			perror("setsockopt IP_ONESBCAST");
258			close(s);
259			exit(EXIT_FAILURE);
260		}
261	}
262
263	if (dobind) {
264		memset(&laddr, 0, sizeof(struct sockaddr_in));
265		laddr.sin_family = AF_INET;
266		laddr.sin_len = sizeof(struct sockaddr_in);
267		if (laddr_s != NULL) {
268			laddr.sin_addr.s_addr = inet_addr(laddr_s);
269		} else
270			laddr.sin_addr.s_addr = INADDR_ANY;
271		laddr.sin_port = htons(portno);
272		ret = bind(s, (struct sockaddr *)&laddr, sizeof(laddr));
273		if (ret == -1) {
274			perror("bind");
275			close(s);
276			exit(EXIT_FAILURE);
277		}
278	}
279
280	memset(&dsin, 0, sizeof(struct sockaddr_in));
281	dsin.sin_family = AF_INET;
282	dsin.sin_len = sizeof(struct sockaddr_in);
283	dsin.sin_addr.s_addr = dstaddr.s_addr;
284	dsin.sin_port = htons(portno);
285
286	buf = malloc(buflen);
287	if (buf == NULL) {
288		perror("malloc");
289		close(s);
290		exit(EXIT_FAILURE);
291	}
292	memset(iov, 0, sizeof(iov));
293	iov[0].iov_base = buf;
294	iov[0].iov_len = buflen;
295
296	memset(&msg, 0, sizeof(struct msghdr));
297	msg.msg_name = &dsin;
298	msg.msg_namelen = sizeof(dsin);
299	msg.msg_iov = iov;
300	msg.msg_iovlen = 1;
301
302	/* Assume we fill out a control msg; macros need to see buf ptr */
303	msg.msg_control = cmsgbuf;
304	msg.msg_controllen = 0;
305	memset(cmsgbuf, 0, MY_CMSG_SIZE);
306
307	/* IP_SENDSRCADDR and IP_SENDIF are mutually exclusive just now. */
308	if (srcaddr_s != NULL) {
309		msg.msg_controllen += CMSG_SPACE(sizeof(struct in_addr));
310		cmsgp = CMSG_FIRSTHDR(&msg);
311		cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
312		cmsgp->cmsg_level = IPPROTO_IP;
313		cmsgp->cmsg_type = IP_SENDSRCADDR;
314		srcaddrp = (struct in_addr *)CMSG_DATA(cmsgp);
315		srcaddrp->s_addr = inet_addr(srcaddr_s);
316	}
317
318	if (ifname != NULL) {
319#ifdef IP_SENDIF
320		msg.msg_controllen += CMSG_SPACE(sizeof(struct sockaddr_dl));
321		cmsgp = CMSG_FIRSTHDR(&msg);
322		cmsgp->cmsg_len = CMSG_LEN(sizeof(struct sockaddr_dl));
323		cmsgp->cmsg_level = IPPROTO_IP;
324		cmsgp->cmsg_type = IP_SENDIF;
325
326#ifdef DIAGNOSTIC
327		fprintf(stderr, "DEBUG: cmsgp->cmsg_len is %d\n",
328		    cmsgp->cmsg_len);
329#endif
330
331		sdl = (struct sockaddr_dl *)CMSG_DATA(cmsgp);
332		memset(sdl, 0, sizeof(struct sockaddr_dl));
333		sdl->sdl_family = AF_LINK;
334		sdl->sdl_len = sizeof(struct sockaddr_dl);
335		sdl->sdl_index = if_nametoindex(ifname);
336
337#ifdef DIAGNOSTIC
338		fprintf(stderr, "DEBUG: sdl->sdl_family is %d\n",
339		    sdl->sdl_family);
340		fprintf(stderr, "DEBUG: sdl->sdl_len is %d\n",
341		    sdl->sdl_len);
342		fprintf(stderr, "DEBUG: sdl->sdl_index is %d\n",
343		    sdl->sdl_index);
344#endif
345#else
346		fprintf(stderr, "WARNING: IP_SENDIF not supported, ignored.\n");
347#endif
348	}
349
350	if (msg.msg_controllen == 0)
351		msg.msg_control = NULL;
352
353	nbytes = sendmsg(s, &msg, (dontroute ? MSG_DONTROUTE : 0));
354	if (nbytes == -1) {
355		perror("sendmsg");
356		close(s);
357		exit(EXIT_FAILURE);
358	}
359
360	close(s);
361
362	exit(EXIT_SUCCESS);
363}
364