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