1 1.22 andvar /* $NetBSD: bootptest.c,v 1.22 2022/12/24 15:23:03 andvar Exp $ */ 2 1.4 perry 3 1.1 gwr /* 4 1.1 gwr * bootptest.c - Test out a bootp server. 5 1.1 gwr * 6 1.1 gwr * This simple program was put together from pieces taken from 7 1.1 gwr * various places, including the CMU BOOTP client and server. 8 1.1 gwr * The packet printing routine is from the Berkeley "tcpdump" 9 1.1 gwr * program with some enhancements I added. The print-bootp.c 10 1.2 gwr * file was shared with my copy of "tcpdump" and therefore uses 11 1.1 gwr * some unusual utility routines that would normally be provided 12 1.2 gwr * by various parts of the tcpdump program. Gordon W. Ross 13 1.1 gwr * 14 1.1 gwr * Boilerplate: 15 1.1 gwr * 16 1.1 gwr * This program includes software developed by the University of 17 1.1 gwr * California, Lawrence Berkeley Laboratory and its contributors. 18 1.1 gwr * (See the copyright notice in print-bootp.c) 19 1.1 gwr * 20 1.1 gwr * The remainder of this program is public domain. You may do 21 1.1 gwr * whatever you like with it except claim that you wrote it. 22 1.1 gwr * 23 1.1 gwr * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 24 1.1 gwr * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 25 1.1 gwr * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 26 1.1 gwr * 27 1.1 gwr * HISTORY: 28 1.1 gwr * 29 1.1 gwr * 12/02/93 Released version 1.4 (with bootp-2.3.2) 30 1.1 gwr * 11/05/93 Released version 1.3 31 1.1 gwr * 10/14/93 Released version 1.2 32 1.1 gwr * 10/11/93 Released version 1.1 33 1.1 gwr * 09/28/93 Released version 1.0 34 1.1 gwr * 09/93 Original developed by Gordon W. Ross <gwr (at) mc.com> 35 1.1 gwr */ 36 1.1 gwr 37 1.5 lukem #include <sys/cdefs.h> 38 1.5 lukem #ifndef lint 39 1.22 andvar __RCSID("$NetBSD: bootptest.c,v 1.22 2022/12/24 15:23:03 andvar Exp $"); 40 1.5 lukem #endif 41 1.5 lukem 42 1.20 christos static const char usage[] = 43 1.20 christos "Usage: %s [-f bootfile] [-h] [-m magic_number] server-name\n" 44 1.20 christos "\t[vendor-data-template-file]\n"; 45 1.1 gwr 46 1.7 kleink #include <sys/param.h> 47 1.1 gwr #include <sys/socket.h> 48 1.1 gwr #include <sys/ioctl.h> 49 1.1 gwr #include <sys/file.h> 50 1.1 gwr #include <sys/time.h> 51 1.1 gwr #include <sys/stat.h> 52 1.10 mycroft #include <sys/poll.h> 53 1.1 gwr 54 1.1 gwr #include <net/if.h> 55 1.1 gwr #include <netinet/in.h> 56 1.1 gwr #include <arpa/inet.h> /* inet_ntoa */ 57 1.1 gwr 58 1.1 gwr #include <stdlib.h> 59 1.1 gwr #include <signal.h> 60 1.1 gwr #include <stdio.h> 61 1.1 gwr #include <string.h> 62 1.16 tls #include <strings.h> 63 1.1 gwr #include <errno.h> 64 1.1 gwr #include <ctype.h> 65 1.1 gwr #include <netdb.h> 66 1.1 gwr #include <assert.h> 67 1.5 lukem #include <unistd.h> 68 1.1 gwr 69 1.1 gwr #include "bootp.h" 70 1.1 gwr #include "bootptest.h" 71 1.1 gwr #include "getif.h" 72 1.5 lukem #include "report.h" 73 1.1 gwr #include "patchlevel.h" 74 1.1 gwr 75 1.1 gwr #define LOG_ERR 1 76 1.1 gwr #define BUFLEN 1024 77 1.1 gwr #define WAITSECS 1 78 1.1 gwr #define MAXWAIT 10 79 1.1 gwr 80 1.1 gwr int vflag = 1; 81 1.1 gwr int tflag = 0; 82 1.1 gwr int thiszone; 83 1.1 gwr char *progname; 84 1.1 gwr unsigned char *packetp; 85 1.1 gwr unsigned char *snapend; 86 1.1 gwr int snaplen; 87 1.1 gwr 88 1.1 gwr 89 1.1 gwr /* 90 1.1 gwr * IP port numbers for client and server obtained from /etc/services 91 1.1 gwr */ 92 1.1 gwr 93 1.1 gwr u_short bootps_port, bootpc_port; 94 1.1 gwr 95 1.1 gwr 96 1.1 gwr /* 97 1.1 gwr * Internet socket and interface config structures 98 1.1 gwr */ 99 1.1 gwr 100 1.1 gwr struct sockaddr_in sin_server; /* where to send requests */ 101 1.1 gwr struct sockaddr_in sin_client; /* for bind and listen */ 102 1.1 gwr struct sockaddr_in sin_from; /* Packet source */ 103 1.1 gwr u_char eaddr[16]; /* Ethernet address */ 104 1.1 gwr 105 1.1 gwr /* 106 1.1 gwr * General 107 1.1 gwr */ 108 1.1 gwr 109 1.1 gwr int debug = 1; /* Debugging flag (level) */ 110 1.6 mrg char hostname[MAXHOSTNAMELEN + 1]; 111 1.1 gwr char *sndbuf; /* Send packet buffer */ 112 1.1 gwr char *rcvbuf; /* Receive packet buffer */ 113 1.1 gwr 114 1.1 gwr /* 115 1.1 gwr * Vendor magic cookies for CMU and RFC1048 116 1.1 gwr */ 117 1.1 gwr 118 1.1 gwr unsigned char vm_cmu[4] = VM_CMU; 119 1.1 gwr unsigned char vm_rfc1048[4] = VM_RFC1048; 120 1.1 gwr short secs; /* How long client has waited */ 121 1.1 gwr 122 1.5 lukem 123 1.8 wiz extern int getether(char *, char *); 124 1.8 wiz void send_request(int); 125 1.1 gwr 126 1.1 gwr /* 127 1.1 gwr * Initialization such as command-line processing is done, then 128 1.1 gwr * the receiver loop is started. Die when interrupted. 129 1.1 gwr */ 130 1.1 gwr 131 1.5 lukem int 132 1.8 wiz main(int argc, char **argv) 133 1.1 gwr { 134 1.1 gwr struct bootp *bp; 135 1.1 gwr struct servent *sep; 136 1.1 gwr struct hostent *hep; 137 1.1 gwr 138 1.1 gwr char *servername = NULL; 139 1.1 gwr char *vendor_file = NULL; 140 1.1 gwr char *bp_file = NULL; 141 1.15 mrg socklen_t fromlen; 142 1.3 mycroft int s; /* Socket file descriptor */ 143 1.15 mrg int n, recvcnt; 144 1.1 gwr int use_hwa = 0; 145 1.2 gwr int32 vend_magic; 146 1.1 gwr int32 xid; 147 1.11 mycroft struct pollfd set[1]; 148 1.1 gwr 149 1.1 gwr progname = strrchr(argv[0], '/'); 150 1.1 gwr if (progname) 151 1.1 gwr progname++; 152 1.1 gwr else 153 1.1 gwr progname = argv[0]; 154 1.1 gwr argc--; 155 1.1 gwr argv++; 156 1.1 gwr 157 1.1 gwr if (debug) 158 1.1 gwr printf("%s: version %s.%d\n", progname, VERSION, PATCHLEVEL); 159 1.1 gwr 160 1.2 gwr /* 161 1.2 gwr * Verify that "struct bootp" has the correct official size. 162 1.2 gwr * (Catch evil compilers that do struct padding.) 163 1.2 gwr */ 164 1.1 gwr assert(sizeof(struct bootp) == BP_MINPKTSZ); 165 1.1 gwr 166 1.1 gwr sndbuf = malloc(BUFLEN); 167 1.1 gwr rcvbuf = malloc(BUFLEN); 168 1.1 gwr if (!sndbuf || !rcvbuf) { 169 1.1 gwr printf("malloc failed\n"); 170 1.1 gwr exit(1); 171 1.1 gwr } 172 1.2 gwr 173 1.2 gwr /* default magic number */ 174 1.2 gwr bcopy(vm_rfc1048, (char*)&vend_magic, 4); 175 1.2 gwr 176 1.1 gwr /* Handle option switches. */ 177 1.1 gwr while (argc > 0) { 178 1.1 gwr if (argv[0][0] != '-') 179 1.1 gwr break; 180 1.1 gwr switch (argv[0][1]) { 181 1.1 gwr 182 1.22 andvar case 'f': /* File name to request. */ 183 1.1 gwr if (argc < 2) 184 1.1 gwr goto error; 185 1.1 gwr argc--; argv++; 186 1.1 gwr bp_file = *argv; 187 1.1 gwr break; 188 1.1 gwr 189 1.1 gwr case 'h': /* Use hardware address. */ 190 1.1 gwr use_hwa = 1; 191 1.1 gwr break; 192 1.1 gwr 193 1.2 gwr case 'm': /* Magic number value. */ 194 1.2 gwr if (argc < 2) 195 1.2 gwr goto error; 196 1.2 gwr argc--; argv++; 197 1.2 gwr vend_magic = inet_addr(*argv); 198 1.2 gwr break; 199 1.2 gwr 200 1.1 gwr error: 201 1.1 gwr default: 202 1.14 hira (void)fprintf(stderr, usage, getprogname()); 203 1.1 gwr exit(1); 204 1.1 gwr 205 1.1 gwr } 206 1.1 gwr argc--; 207 1.1 gwr argv++; 208 1.1 gwr } 209 1.1 gwr 210 1.1 gwr /* Get server name (or address) for query. */ 211 1.1 gwr if (argc > 0) { 212 1.1 gwr servername = *argv; 213 1.1 gwr argc--; 214 1.1 gwr argv++; 215 1.1 gwr } 216 1.1 gwr /* Get optional vendor-data-template-file. */ 217 1.1 gwr if (argc > 0) { 218 1.1 gwr vendor_file = *argv; 219 1.1 gwr argc--; 220 1.1 gwr argv++; 221 1.1 gwr } 222 1.1 gwr if (!servername) { 223 1.1 gwr printf("missing server name.\n"); 224 1.14 hira (void)fprintf(stderr, usage, getprogname()); 225 1.1 gwr exit(1); 226 1.1 gwr } 227 1.1 gwr /* 228 1.1 gwr * Create a socket. 229 1.1 gwr */ 230 1.1 gwr if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 231 1.1 gwr perror("socket"); 232 1.1 gwr exit(1); 233 1.1 gwr } 234 1.1 gwr /* 235 1.1 gwr * Get server's listening port number 236 1.1 gwr */ 237 1.1 gwr sep = getservbyname("bootps", "udp"); 238 1.1 gwr if (sep) { 239 1.1 gwr bootps_port = ntohs((u_short) sep->s_port); 240 1.1 gwr } else { 241 1.1 gwr fprintf(stderr, "udp/bootps: unknown service -- using port %d\n", 242 1.1 gwr IPPORT_BOOTPS); 243 1.1 gwr bootps_port = (u_short) IPPORT_BOOTPS; 244 1.1 gwr } 245 1.1 gwr 246 1.1 gwr /* 247 1.1 gwr * Set up server socket address (for send) 248 1.1 gwr */ 249 1.1 gwr if (servername) { 250 1.3 mycroft if (inet_aton(servername, &sin_server.sin_addr) == 0) { 251 1.1 gwr hep = gethostbyname(servername); 252 1.1 gwr if (!hep) { 253 1.1 gwr fprintf(stderr, "%s: unknown host\n", servername); 254 1.1 gwr exit(1); 255 1.1 gwr } 256 1.3 mycroft memcpy(&sin_server.sin_addr, hep->h_addr, 257 1.3 mycroft sizeof(sin_server.sin_addr)); 258 1.1 gwr } 259 1.1 gwr } else { 260 1.1 gwr /* Get broadcast address */ 261 1.1 gwr /* XXX - not yet */ 262 1.3 mycroft sin_server.sin_addr.s_addr = INADDR_ANY; 263 1.1 gwr } 264 1.1 gwr sin_server.sin_family = AF_INET; 265 1.1 gwr sin_server.sin_port = htons(bootps_port); 266 1.1 gwr 267 1.1 gwr /* 268 1.1 gwr * Get client's listening port number 269 1.1 gwr */ 270 1.1 gwr sep = getservbyname("bootpc", "udp"); 271 1.1 gwr if (sep) { 272 1.1 gwr bootpc_port = ntohs(sep->s_port); 273 1.1 gwr } else { 274 1.1 gwr fprintf(stderr, "udp/bootpc: unknown service -- using port %d\n", 275 1.1 gwr IPPORT_BOOTPC); 276 1.1 gwr bootpc_port = (u_short) IPPORT_BOOTPC; 277 1.1 gwr } 278 1.1 gwr 279 1.1 gwr /* 280 1.1 gwr * Set up client socket address (for listen) 281 1.1 gwr */ 282 1.1 gwr sin_client.sin_family = AF_INET; 283 1.1 gwr sin_client.sin_port = htons(bootpc_port); 284 1.1 gwr sin_client.sin_addr.s_addr = INADDR_ANY; 285 1.1 gwr 286 1.1 gwr /* 287 1.1 gwr * Bind client socket to BOOTPC port. 288 1.1 gwr */ 289 1.1 gwr if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) { 290 1.1 gwr perror("bind BOOTPC port"); 291 1.1 gwr if (errno == EACCES) 292 1.1 gwr fprintf(stderr, "You need to run this as root\n"); 293 1.1 gwr exit(1); 294 1.1 gwr } 295 1.1 gwr /* 296 1.1 gwr * Build a request. 297 1.1 gwr */ 298 1.1 gwr bp = (struct bootp *) sndbuf; 299 1.1 gwr bzero(bp, sizeof(*bp)); 300 1.1 gwr bp->bp_op = BOOTREQUEST; 301 1.1 gwr xid = (int32) getpid(); 302 1.1 gwr bp->bp_xid = (u_int32) htonl(xid); 303 1.1 gwr if (bp_file) 304 1.12 itojun strlcpy(bp->bp_file, bp_file, sizeof(bp->bp_file)); 305 1.1 gwr 306 1.1 gwr /* 307 1.1 gwr * Fill in the hardware address (or client IP address) 308 1.1 gwr */ 309 1.1 gwr if (use_hwa) { 310 1.1 gwr struct ifreq *ifr; 311 1.1 gwr 312 1.1 gwr ifr = getif(s, &sin_server.sin_addr); 313 1.1 gwr if (!ifr) { 314 1.1 gwr printf("No interface for %s\n", servername); 315 1.1 gwr exit(1); 316 1.1 gwr } 317 1.15 mrg if (getether(ifr->ifr_name, (char *)eaddr)) { 318 1.1 gwr printf("Can not get ether addr for %s\n", ifr->ifr_name); 319 1.1 gwr exit(1); 320 1.1 gwr } 321 1.1 gwr /* Copy Ethernet address into request packet. */ 322 1.1 gwr bp->bp_htype = 1; 323 1.1 gwr bp->bp_hlen = 6; 324 1.1 gwr bcopy(eaddr, bp->bp_chaddr, bp->bp_hlen); 325 1.1 gwr } else { 326 1.1 gwr /* Fill in the client IP address. */ 327 1.1 gwr gethostname(hostname, sizeof(hostname)); 328 1.6 mrg hostname[sizeof(hostname) - 1] = '\0'; 329 1.1 gwr hep = gethostbyname(hostname); 330 1.1 gwr if (!hep) { 331 1.1 gwr printf("Can not get my IP address\n"); 332 1.1 gwr exit(1); 333 1.1 gwr } 334 1.1 gwr bcopy(hep->h_addr, &bp->bp_ciaddr, hep->h_length); 335 1.1 gwr } 336 1.1 gwr 337 1.1 gwr /* 338 1.1 gwr * Copy in the default vendor data. 339 1.1 gwr */ 340 1.2 gwr bcopy((char*)&vend_magic, bp->bp_vend, 4); 341 1.2 gwr if (vend_magic) 342 1.2 gwr bp->bp_vend[4] = TAG_END; 343 1.1 gwr 344 1.1 gwr /* 345 1.1 gwr * Read in the "options" part of the request. 346 1.1 gwr * This also determines the size of the packet. 347 1.1 gwr */ 348 1.1 gwr snaplen = sizeof(*bp); 349 1.1 gwr if (vendor_file) { 350 1.1 gwr int fd = open(vendor_file, 0); 351 1.1 gwr if (fd < 0) { 352 1.1 gwr perror(vendor_file); 353 1.1 gwr exit(1); 354 1.1 gwr } 355 1.1 gwr /* Compute actual space for options. */ 356 1.1 gwr n = BUFLEN - sizeof(*bp) + BP_VEND_LEN; 357 1.1 gwr n = read(fd, bp->bp_vend, n); 358 1.1 gwr close(fd); 359 1.1 gwr if (n < 0) { 360 1.1 gwr perror(vendor_file); 361 1.1 gwr exit(1); 362 1.1 gwr } 363 1.1 gwr printf("read %d bytes of vendor template\n", n); 364 1.1 gwr if (n > BP_VEND_LEN) { 365 1.1 gwr printf("warning: extended options in use (len > %d)\n", 366 1.1 gwr BP_VEND_LEN); 367 1.1 gwr snaplen += (n - BP_VEND_LEN); 368 1.1 gwr } 369 1.1 gwr } 370 1.1 gwr /* 371 1.1 gwr * Set globals needed by print_bootp 372 1.1 gwr * (called by send_request) 373 1.1 gwr */ 374 1.1 gwr packetp = (unsigned char *) eaddr; 375 1.1 gwr snapend = (unsigned char *) sndbuf + snaplen; 376 1.1 gwr 377 1.1 gwr /* Send a request once per second while waiting for replies. */ 378 1.1 gwr recvcnt = 0; 379 1.1 gwr bp->bp_secs = secs = 0; 380 1.1 gwr send_request(s); 381 1.11 mycroft set[0].fd = s; 382 1.11 mycroft set[0].events = POLLIN; 383 1.1 gwr while (1) { 384 1.10 mycroft n = poll(set, 1, WAITSECS * 1000); 385 1.1 gwr if (n < 0) { 386 1.10 mycroft perror("poll"); 387 1.1 gwr break; 388 1.1 gwr } 389 1.1 gwr if (n == 0) { 390 1.1 gwr /* 391 1.2 gwr * We have not received a response in the last second. 392 1.1 gwr * If we have ever received any responses, exit now. 393 1.1 gwr * Otherwise, bump the "wait time" field and re-send. 394 1.1 gwr */ 395 1.1 gwr if (recvcnt > 0) 396 1.1 gwr exit(0); 397 1.1 gwr secs += WAITSECS; 398 1.1 gwr if (secs > MAXWAIT) 399 1.1 gwr break; 400 1.1 gwr bp->bp_secs = htons(secs); 401 1.1 gwr send_request(s); 402 1.1 gwr continue; 403 1.1 gwr } 404 1.1 gwr fromlen = sizeof(sin_from); 405 1.1 gwr n = recvfrom(s, rcvbuf, BUFLEN, 0, 406 1.1 gwr (struct sockaddr *) &sin_from, &fromlen); 407 1.1 gwr if (n <= 0) { 408 1.1 gwr continue; 409 1.1 gwr } 410 1.18 lukem if (n < (int)sizeof(struct bootp)) { 411 1.1 gwr printf("received short packet\n"); 412 1.1 gwr continue; 413 1.1 gwr } 414 1.1 gwr recvcnt++; 415 1.1 gwr 416 1.1 gwr /* Print the received packet. */ 417 1.1 gwr printf("Recvd from %s", inet_ntoa(sin_from.sin_addr)); 418 1.1 gwr /* set globals needed by bootp_print() */ 419 1.1 gwr snaplen = n; 420 1.1 gwr snapend = (unsigned char *) rcvbuf + snaplen; 421 1.5 lukem bootp_print((struct bootp *)rcvbuf, n, sin_from.sin_port, 0); 422 1.1 gwr putchar('\n'); 423 1.1 gwr /* 424 1.1 gwr * This no longer exits immediately after receiving 425 1.1 gwr * one response because it is useful to know if the 426 1.1 gwr * client might get multiple responses. This code 427 1.1 gwr * will now listen for one second after a response. 428 1.1 gwr */ 429 1.1 gwr } 430 1.1 gwr fprintf(stderr, "no response from %s\n", servername); 431 1.1 gwr exit(1); 432 1.1 gwr } 433 1.1 gwr 434 1.5 lukem void 435 1.8 wiz send_request(int s) 436 1.1 gwr { 437 1.1 gwr /* Print the request packet. */ 438 1.1 gwr printf("Sending to %s", inet_ntoa(sin_server.sin_addr)); 439 1.5 lukem bootp_print((struct bootp *)sndbuf, snaplen, sin_from.sin_port, 0); 440 1.1 gwr putchar('\n'); 441 1.1 gwr 442 1.1 gwr /* Send the request packet. */ 443 1.1 gwr if (sendto(s, sndbuf, snaplen, 0, 444 1.1 gwr (struct sockaddr *) &sin_server, 445 1.1 gwr sizeof(sin_server)) < 0) 446 1.1 gwr { 447 1.1 gwr perror("sendto server"); 448 1.1 gwr exit(1); 449 1.1 gwr } 450 1.1 gwr } 451 1.1 gwr 452 1.1 gwr /* 453 1.1 gwr * Print out a filename (or other ascii string). 454 1.1 gwr * Return true if truncated. 455 1.1 gwr */ 456 1.1 gwr int 457 1.9 wiz printfn(u_char *s, u_char *ep) 458 1.1 gwr { 459 1.9 wiz u_char c; 460 1.1 gwr 461 1.1 gwr putchar('"'); 462 1.5 lukem while ((c = *s++) != 0) { 463 1.1 gwr if (s > ep) { 464 1.1 gwr putchar('"'); 465 1.1 gwr return (1); 466 1.1 gwr } 467 1.1 gwr if (!isascii(c)) { 468 1.1 gwr c = toascii(c); 469 1.1 gwr putchar('M'); 470 1.1 gwr putchar('-'); 471 1.1 gwr } 472 1.1 gwr if (!isprint(c)) { 473 1.1 gwr c ^= 0x40; /* DEL to ?, others to alpha */ 474 1.1 gwr putchar('^'); 475 1.1 gwr } 476 1.1 gwr putchar(c); 477 1.1 gwr } 478 1.1 gwr putchar('"'); 479 1.1 gwr return (0); 480 1.1 gwr } 481 1.1 gwr 482 1.1 gwr /* 483 1.1 gwr * Convert an IP addr to a string. 484 1.1 gwr * (like inet_ntoa, but ina is a pointer) 485 1.1 gwr */ 486 1.1 gwr char * 487 1.8 wiz ipaddr_string(struct in_addr *ina) 488 1.1 gwr { 489 1.1 gwr static char b[24]; 490 1.1 gwr u_char *p; 491 1.1 gwr 492 1.1 gwr p = (u_char *) ina; 493 1.13 itojun snprintf(b, sizeof(b), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); 494 1.1 gwr return (b); 495 1.1 gwr } 496 1.1 gwr 497 1.1 gwr /* 498 1.1 gwr * Local Variables: 499 1.1 gwr * tab-width: 4 500 1.1 gwr * c-indent-level: 4 501 1.1 gwr * c-argdecl-indent: 4 502 1.1 gwr * c-continued-statement-offset: 4 503 1.1 gwr * c-continued-brace-offset: -4 504 1.1 gwr * c-label-offset: -4 505 1.1 gwr * c-brace-offset: 0 506 1.1 gwr * End: 507 1.1 gwr */ 508