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