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 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 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