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