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