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