1167140Sbms/*-
2167140Sbms * Copyright (c) 2007 Bruce M. Simpson
3167140Sbms * All rights reserved.
4167140Sbms *
5167140Sbms * Redistribution and use in source and binary forms, with or without
6167140Sbms * modification, are permitted provided that the following conditions
7167140Sbms * are met:
8167140Sbms * 1. Redistributions of source code must retain the above copyright
9167140Sbms *    notice, this list of conditions and the following disclaimer.
10167140Sbms * 2. Redistributions in binary form must reproduce the above copyright
11167140Sbms *    notice, this list of conditions and the following disclaimer in the
12167140Sbms *    documentation and/or other materials provided with the distribution.
13167140Sbms *
14167140Sbms * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15167140Sbms * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16167140Sbms * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17167140Sbms * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18167140Sbms * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19167140Sbms * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20167140Sbms * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21167140Sbms * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22167140Sbms * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23167140Sbms * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24167140Sbms * SUCH DAMAGE.
25167140Sbms */
26167140Sbms
27167140Sbms/*
28167140Sbms * Test utility for IPv4 broadcast sockets.
29167140Sbms */
30167140Sbms
31167140Sbms#include <sys/cdefs.h>
32167140Sbms__FBSDID("$FreeBSD$");
33167140Sbms
34167140Sbms#include <sys/param.h>
35167140Sbms#include <sys/types.h>
36167140Sbms#include <sys/ioctl.h>
37167140Sbms#include <sys/socket.h>
38167140Sbms
39167340Sbms#include <net/if.h>
40167340Sbms#include <net/if_dl.h>
41167140Sbms#include <netinet/in.h>
42167140Sbms#include <arpa/inet.h>
43167140Sbms
44167140Sbms#include <signal.h>
45167140Sbms#include <stddef.h>
46167140Sbms#include <stdio.h>
47167140Sbms#include <stdlib.h>
48167140Sbms#include <string.h>
49167140Sbms#include <time.h>
50167140Sbms
51167140Sbms#include <err.h>
52167140Sbms#include <errno.h>
53167140Sbms#include <getopt.h>
54167140Sbms#include <pwd.h>
55167140Sbms#include <unistd.h>
56167140Sbms#include <netdb.h>
57167140Sbms#include <libgen.h>
58167140Sbms
59167340Sbms#ifndef IP_SENDIF
60167340Sbms#define IP_SENDIF	24		/* XXX */
61167340Sbms#endif
62167340Sbms
63167512Sbms#ifndef IPPROTO_ZEROHOP
64167512Sbms#define IPPROTO_ZEROHOP	114		/* any 0-hop protocol */
65167512Sbms#endif
66167512Sbms
67167140Sbms#define DEFAULT_PORT		6698
68167140Sbms#define DEFAULT_PAYLOAD_SIZE	24
69167149Sbms#define DEFAULT_TTL		1
70167140Sbms
71167340Sbms#define MY_CMSG_SIZE				\
72167340Sbms	CMSG_SPACE(sizeof(struct in_addr)) +	\
73167340Sbms	CMSG_SPACE(sizeof(struct sockaddr_dl))
74167340Sbms
75167140Sbmsstatic char *progname = NULL;
76167140Sbms
77167140Sbmsstatic void
78167140Sbmsusage(void)
79167140Sbms{
80167140Sbms
81167341Sbms	fprintf(stderr, "IPv4 broadcast test program. Sends a %d byte UDP "
82167341Sbms	        "datagram to <dest>:<port>.\n\n", DEFAULT_PAYLOAD_SIZE);
83167140Sbms	fprintf(stderr,
84167341Sbms"usage: %s [-1] [-A laddr] [-b] [-B] [-d] [-i iface] [-l len]\n"
85167512Sbms"                   [-p port] [-R] [-s srcaddr] [-t ttl] <dest>\n",
86167140Sbms	    progname);
87167140Sbms	fprintf(stderr, "-1: Set IP_ONESBCAST\n");
88167341Sbms	fprintf(stderr, "-A: specify laddr (default: INADDR_ANY)\n");
89167341Sbms	fprintf(stderr, "-b: bind socket to <laddr>:<lport>\n");
90167140Sbms	fprintf(stderr, "-B: Set SO_BROADCAST\n");
91167140Sbms	fprintf(stderr, "-d: Set SO_DONTROUTE\n");
92167341Sbms	fprintf(stderr, "-i: Set IP_SENDIF <iface> (if supported)\n");
93167340Sbms	fprintf(stderr, "-l: Set payload size to <len>\n");
94167341Sbms	fprintf(stderr, "-p: Set local and remote port (default: %d)\n",
95167340Sbms	    DEFAULT_PORT);
96167512Sbms	fprintf(stderr, "-R: Use raw IP (protocol %d)\n", IPPROTO_ZEROHOP);
97167140Sbms#if 0
98167140Sbms	fprintf(stderr, "-r: Fill datagram with random bytes\n");
99167140Sbms#endif
100167149Sbms	fprintf(stderr, "-s: Set IP_SENDSRCADDR to <srcaddr>\n");
101167149Sbms	fprintf(stderr, "-t: Set IP_TTL to <ttl>\n");
102167140Sbms
103167140Sbms	exit(EXIT_FAILURE);
104167140Sbms}
105167140Sbms
106167140Sbmsint
107167140Sbmsmain(int argc, char *argv[])
108167140Sbms{
109167140Sbms	char			*buf;
110167140Sbms	char			 cmsgbuf[MY_CMSG_SIZE];
111167140Sbms	struct iovec		 iov[1];
112167140Sbms	struct msghdr		 msg;
113167140Sbms	struct sockaddr_in	 dsin;
114167341Sbms	struct sockaddr_in	 laddr;
115167340Sbms	struct sockaddr_dl	*sdl;
116167140Sbms	struct cmsghdr		*cmsgp;
117167140Sbms	struct in_addr		 dstaddr;
118167140Sbms	struct in_addr		*srcaddrp;
119167340Sbms	char			*ifname;
120167341Sbms	char			*laddr_s;
121167140Sbms	char			*srcaddr_s;
122167140Sbms	int			 ch;
123167140Sbms	int			 dobind;
124167140Sbms	int			 dobroadcast;
125167140Sbms	int			 dontroute;
126167140Sbms	int			 doonesbcast;
127167140Sbms	int			 dorandom;
128167512Sbms	int			 dorawip;
129167140Sbms	size_t			 buflen;
130167140Sbms	ssize_t			 nbytes;
131167140Sbms	int			 portno;
132167140Sbms	int			 ret;
133167140Sbms	int			 s;
134167140Sbms	socklen_t		 soptlen;
135167140Sbms	int			 soptval;
136167149Sbms	int			 ttl;
137167140Sbms
138167140Sbms	dobind = 0;
139167140Sbms	dobroadcast = 0;
140167140Sbms	dontroute = 0;
141167140Sbms	doonesbcast = 0;
142167140Sbms	dorandom = 0;
143167512Sbms	dorawip = 0;
144167140Sbms
145167340Sbms	ifname = NULL;
146167140Sbms	dstaddr.s_addr = INADDR_ANY;
147167341Sbms	laddr_s = NULL;
148167140Sbms	srcaddr_s = NULL;
149167140Sbms	portno = DEFAULT_PORT;
150167149Sbms	ttl = DEFAULT_TTL;
151167140Sbms
152167140Sbms	buf = NULL;
153167140Sbms	buflen = DEFAULT_PAYLOAD_SIZE;
154167140Sbms
155167140Sbms	progname = basename(argv[0]);
156167512Sbms	while ((ch = getopt(argc, argv, "1A:bBdi:l:p:Rrs:t:")) != -1) {
157167140Sbms		switch (ch) {
158167140Sbms		case '1':
159167140Sbms			doonesbcast = 1;
160167140Sbms			break;
161167341Sbms		case 'A':
162167341Sbms			laddr_s = optarg;
163167341Sbms			break;
164167140Sbms		case 'b':
165167140Sbms			dobind = 1;
166167140Sbms			break;
167167140Sbms		case 'B':
168167140Sbms			dobroadcast = 1;
169167140Sbms			break;
170167140Sbms		case 'd':
171167140Sbms			dontroute = 1;
172167140Sbms			break;
173167340Sbms		case 'i':
174167340Sbms			ifname = optarg;
175167340Sbms			break;
176167140Sbms		case 'l':
177167140Sbms			buflen = atoi(optarg);
178167140Sbms			break;
179167140Sbms		case 'p':
180167140Sbms			portno = atoi(optarg);
181167140Sbms			break;
182167512Sbms		case 'R':
183167512Sbms			dorawip = 1;
184167512Sbms			break;
185167140Sbms		case 'r':
186167140Sbms			dorandom = 1;
187167140Sbms			break;
188167140Sbms		case 's':
189167140Sbms			srcaddr_s = optarg;
190167140Sbms			break;
191167149Sbms		case 't':
192167149Sbms			ttl = atoi(optarg);
193167149Sbms			break;
194167140Sbms		default:
195167140Sbms			usage();
196167140Sbms			break;
197167140Sbms		}
198167140Sbms	}
199167140Sbms	argc -= optind;
200167140Sbms	argv += optind;
201167140Sbms
202167140Sbms	if (argc != 1)
203167140Sbms		usage();
204167140Sbms	if (argv[0] == NULL || inet_aton(argv[0], &dstaddr) == 0)
205167140Sbms		usage();
206167340Sbms	/* IP_SENDSRCADDR and IP_SENDIF are mutually exclusive just now. */
207167340Sbms	if (srcaddr_s != NULL && ifname != NULL)
208167340Sbms		usage();
209167512Sbms	if (dorawip) {
210167512Sbms		if (geteuid() != 0)
211167512Sbms			fprintf(stderr, "WARNING: not running as root.\n");
212167512Sbms		s = socket(PF_INET, SOCK_RAW, IPPROTO_ZEROHOP);
213167512Sbms	} else {
214167512Sbms		s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
215167512Sbms	}
216167140Sbms	if (s == -1) {
217167140Sbms		perror("socket");
218167140Sbms		exit(EXIT_FAILURE);
219167140Sbms	}
220167140Sbms
221167140Sbms	if (dontroute) {
222167140Sbms		soptval = 1;
223167140Sbms		soptlen = sizeof(soptval);
224167140Sbms		ret = setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &soptval,
225167140Sbms		    soptlen);
226167140Sbms		if (ret == -1) {
227167140Sbms			perror("setsockopt SO_DONTROUTE");
228167140Sbms			close(s);
229167140Sbms			exit(EXIT_FAILURE);
230167140Sbms		}
231167140Sbms	}
232167140Sbms
233167140Sbms	if (dobroadcast) {
234167140Sbms		soptval = 1;
235167140Sbms		soptlen = sizeof(soptval);
236167140Sbms		ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &soptval,
237167140Sbms		    soptlen);
238167140Sbms		if (ret == -1) {
239167140Sbms			perror("setsockopt SO_BROADCAST");
240167140Sbms			close(s);
241167140Sbms			exit(EXIT_FAILURE);
242167140Sbms		}
243167140Sbms	}
244167140Sbms
245167149Sbms	soptval = ttl;
246167149Sbms	soptlen = sizeof(soptval);
247167149Sbms	ret = setsockopt(s, IPPROTO_IP, IP_TTL, &soptval, soptlen);
248167149Sbms	if (ret == -1) {
249167149Sbms		perror("setsockopt IPPROTO_IP IP_TTL");
250167149Sbms		close(s);
251167149Sbms		exit(EXIT_FAILURE);
252167149Sbms	}
253167149Sbms
254167140Sbms	if (doonesbcast) {
255167140Sbms		soptval = 1;
256167140Sbms		soptlen = sizeof(soptval);
257167140Sbms		ret = setsockopt(s, IPPROTO_IP, IP_ONESBCAST, &soptval,
258167140Sbms		    soptlen);
259167140Sbms		if (ret == -1) {
260167140Sbms			perror("setsockopt IP_ONESBCAST");
261167140Sbms			close(s);
262167140Sbms			exit(EXIT_FAILURE);
263167140Sbms		}
264167140Sbms	}
265167140Sbms
266167140Sbms	if (dobind) {
267167341Sbms		memset(&laddr, 0, sizeof(struct sockaddr_in));
268167341Sbms		laddr.sin_family = AF_INET;
269167341Sbms		laddr.sin_len = sizeof(struct sockaddr_in);
270167341Sbms		if (laddr_s != NULL) {
271167341Sbms			laddr.sin_addr.s_addr = inet_addr(laddr_s);
272167341Sbms		} else
273167341Sbms			laddr.sin_addr.s_addr = INADDR_ANY;
274167341Sbms		laddr.sin_port = htons(portno);
275167341Sbms		ret = bind(s, (struct sockaddr *)&laddr, sizeof(laddr));
276167140Sbms		if (ret == -1) {
277167140Sbms			perror("bind");
278167140Sbms			close(s);
279167140Sbms			exit(EXIT_FAILURE);
280167140Sbms		}
281167140Sbms	}
282167140Sbms
283167140Sbms	memset(&dsin, 0, sizeof(struct sockaddr_in));
284167140Sbms	dsin.sin_family = AF_INET;
285167140Sbms	dsin.sin_len = sizeof(struct sockaddr_in);
286167140Sbms	dsin.sin_addr.s_addr = dstaddr.s_addr;
287167140Sbms	dsin.sin_port = htons(portno);
288167140Sbms
289167140Sbms	buf = malloc(buflen);
290167140Sbms	if (buf == NULL) {
291167140Sbms		perror("malloc");
292167140Sbms		close(s);
293167140Sbms		exit(EXIT_FAILURE);
294167140Sbms	}
295167140Sbms	memset(iov, 0, sizeof(iov));
296167140Sbms	iov[0].iov_base = buf;
297167140Sbms	iov[0].iov_len = buflen;
298167140Sbms
299167140Sbms	memset(&msg, 0, sizeof(struct msghdr));
300167140Sbms	msg.msg_name = &dsin;
301167140Sbms	msg.msg_namelen = sizeof(dsin);
302167140Sbms	msg.msg_iov = iov;
303167140Sbms	msg.msg_iovlen = 1;
304167140Sbms
305167340Sbms	/* Assume we fill out a control msg; macros need to see buf ptr */
306167340Sbms	msg.msg_control = cmsgbuf;
307167340Sbms	msg.msg_controllen = 0;
308167340Sbms	memset(cmsgbuf, 0, MY_CMSG_SIZE);
309167340Sbms
310167340Sbms	/* IP_SENDSRCADDR and IP_SENDIF are mutually exclusive just now. */
311167140Sbms	if (srcaddr_s != NULL) {
312167340Sbms		msg.msg_controllen += CMSG_SPACE(sizeof(struct in_addr));
313167140Sbms		cmsgp = CMSG_FIRSTHDR(&msg);
314167140Sbms		cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
315167140Sbms		cmsgp->cmsg_level = IPPROTO_IP;
316167140Sbms		cmsgp->cmsg_type = IP_SENDSRCADDR;
317167140Sbms		srcaddrp = (struct in_addr *)CMSG_DATA(cmsgp);
318167140Sbms		srcaddrp->s_addr = inet_addr(srcaddr_s);
319167140Sbms	}
320167140Sbms
321167340Sbms	if (ifname != NULL) {
322167340Sbms#ifdef IP_SENDIF
323167340Sbms		msg.msg_controllen += CMSG_SPACE(sizeof(struct sockaddr_dl));
324167340Sbms		cmsgp = CMSG_FIRSTHDR(&msg);
325167340Sbms		cmsgp->cmsg_len = CMSG_LEN(sizeof(struct sockaddr_dl));
326167340Sbms		cmsgp->cmsg_level = IPPROTO_IP;
327167340Sbms		cmsgp->cmsg_type = IP_SENDIF;
328167340Sbms
329167340Sbms#ifdef DIAGNOSTIC
330167340Sbms		fprintf(stderr, "DEBUG: cmsgp->cmsg_len is %d\n",
331167340Sbms		    cmsgp->cmsg_len);
332167340Sbms#endif
333167340Sbms
334167340Sbms		sdl = (struct sockaddr_dl *)CMSG_DATA(cmsgp);
335167340Sbms		memset(sdl, 0, sizeof(struct sockaddr_dl));
336167340Sbms		sdl->sdl_family = AF_LINK;
337167340Sbms		sdl->sdl_len = sizeof(struct sockaddr_dl);
338167340Sbms		sdl->sdl_index = if_nametoindex(ifname);
339167340Sbms
340167340Sbms#ifdef DIAGNOSTIC
341167340Sbms		fprintf(stderr, "DEBUG: sdl->sdl_family is %d\n",
342167340Sbms		    sdl->sdl_family);
343167340Sbms		fprintf(stderr, "DEBUG: sdl->sdl_len is %d\n",
344167340Sbms		    sdl->sdl_len);
345167340Sbms		fprintf(stderr, "DEBUG: sdl->sdl_index is %d\n",
346167340Sbms		    sdl->sdl_index);
347167340Sbms#endif
348167340Sbms#else
349167340Sbms		fprintf(stderr, "WARNING: IP_SENDIF not supported, ignored.\n");
350167340Sbms#endif
351167340Sbms	}
352167340Sbms
353167340Sbms	if (msg.msg_controllen == 0)
354167340Sbms		msg.msg_control = NULL;
355167340Sbms
356167140Sbms	nbytes = sendmsg(s, &msg, (dontroute ? MSG_DONTROUTE : 0));
357167140Sbms	if (nbytes == -1) {
358167140Sbms		perror("sendmsg");
359167140Sbms		close(s);
360167140Sbms		exit(EXIT_FAILURE);
361167140Sbms	}
362167140Sbms
363167140Sbms	close(s);
364167140Sbms
365167140Sbms	exit(EXIT_SUCCESS);
366167140Sbms}
367