1 /* 2 * bootpgw.c - BOOTP GateWay 3 * This program forwards BOOTP Request packets to a BOOTP server. 4 */ 5 6 /************************************************************************ 7 Copyright 1988, 1991 by Carnegie Mellon University 8 9 All Rights Reserved 10 11 Permission to use, copy, modify, and distribute this software and its 12 documentation for any purpose and without fee is hereby granted, provided 13 that the above copyright notice appear in all copies and that both that 14 copyright notice and this permission notice appear in supporting 15 documentation, and that the name of Carnegie Mellon University not be used 16 in advertising or publicity pertaining to distribution of the software 17 without specific, written prior permission. 18 19 CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 20 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 21 IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 22 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 23 PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 24 ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 25 SOFTWARE. 26 ************************************************************************/ 27 28 /* 29 * BOOTPGW is typically used to forward BOOTP client requests from 30 * one subnet to a BOOTP server on a different subnet. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/types.h> 37 #include <sys/param.h> 38 #include <sys/socket.h> 39 #include <sys/ioctl.h> 40 #include <sys/file.h> 41 #include <sys/time.h> 42 #include <sys/stat.h> 43 #include <sys/utsname.h> 44 45 #include <net/if.h> 46 #include <netinet/in.h> 47 #include <arpa/inet.h> /* inet_ntoa */ 48 49 #ifndef NO_UNISTD 50 #include <unistd.h> 51 #endif 52 53 #include <err.h> 54 #include <stdlib.h> 55 #include <signal.h> 56 #include <stdio.h> 57 #include <string.h> 58 #include <errno.h> 59 #include <ctype.h> 60 #include <netdb.h> 61 #include <paths.h> 62 #include <syslog.h> 63 #include <assert.h> 64 65 #ifdef NO_SETSID 66 # include <fcntl.h> /* for O_RDONLY, etc */ 67 #endif 68 69 #ifndef USE_BFUNCS 70 # include <memory.h> 71 /* Yes, memcpy is OK here (no overlapped copies). */ 72 # define bcopy(a,b,c) memcpy(b,a,c) 73 # define bzero(p,l) memset(p,0,l) 74 # define bcmp(a,b,c) memcmp(a,b,c) 75 #endif 76 77 #include "bootp.h" 78 #include "getif.h" 79 #include "hwaddr.h" 80 #include "report.h" 81 #include "patchlevel.h" 82 83 /* Local definitions: */ 84 #define MAX_MSG_SIZE (3*512) /* Maximum packet size */ 85 #define TRUE 1 86 #define FALSE 0 87 #define get_network_errmsg get_errmsg 88 89 90 91 /* 92 * Externals, forward declarations, and global variables 93 */ 94 95 static void usage(void); 96 static void handle_reply(void); 97 static void handle_request(void); 98 99 /* 100 * IP port numbers for client and server obtained from /etc/services 101 */ 102 103 u_short bootps_port, bootpc_port; 104 105 106 /* 107 * Internet socket and interface config structures 108 */ 109 110 struct sockaddr_in bind_addr; /* Listening */ 111 struct sockaddr_in recv_addr; /* Packet source */ 112 struct sockaddr_in send_addr; /* destination */ 113 114 115 /* 116 * option defaults 117 */ 118 int debug = 0; /* Debugging flag (level) */ 119 struct timeval actualtimeout = 120 { /* fifteen minutes */ 121 15 * 60L, /* tv_sec */ 122 0 /* tv_usec */ 123 }; 124 u_char maxhops = 4; /* Number of hops allowed for requests. */ 125 u_int minwait = 3; /* Number of seconds client must wait before 126 its bootrequest packets are forwarded. */ 127 128 /* 129 * General 130 */ 131 132 int s; /* Socket file descriptor */ 133 char *pktbuf; /* Receive packet buffer */ 134 int pktlen; 135 char *progname; 136 char *servername; 137 int32 server_ipa; /* Real server IP address, network order. */ 138 139 struct in_addr my_ip_addr; 140 141 struct utsname my_uname; 142 char *hostname; 143 144 145 146 147 148 /* 149 * Initialization such as command-line processing is done and then the 150 * main server loop is started. 151 */ 152 153 int 154 main(argc, argv) 155 int argc; 156 char **argv; 157 { 158 struct timeval *timeout; 159 struct bootp *bp; 160 struct servent *servp; 161 struct hostent *hep; 162 char *stmp; 163 int n, ba_len, ra_len; 164 int nfound, readfds; 165 int standalone; 166 167 progname = strrchr(argv[0], '/'); 168 if (progname) progname++; 169 else progname = argv[0]; 170 171 /* 172 * Initialize logging. 173 */ 174 report_init(0); /* uses progname */ 175 176 /* 177 * Log startup 178 */ 179 report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); 180 181 /* Debugging for compilers with struct padding. */ 182 assert(sizeof(struct bootp) == BP_MINPKTSZ); 183 184 /* Get space for receiving packets and composing replies. */ 185 pktbuf = malloc(MAX_MSG_SIZE); 186 if (!pktbuf) { 187 report(LOG_ERR, "malloc failed"); 188 exit(1); 189 } 190 bp = (struct bootp *) pktbuf; 191 192 /* 193 * Check to see if a socket was passed to us from inetd. 194 * 195 * Use getsockname() to determine if descriptor 0 is indeed a socket 196 * (and thus we are probably a child of inetd) or if it is instead 197 * something else and we are running standalone. 198 */ 199 s = 0; 200 ba_len = sizeof(bind_addr); 201 bzero((char *) &bind_addr, ba_len); 202 errno = 0; 203 standalone = TRUE; 204 if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { 205 /* 206 * Descriptor 0 is a socket. Assume we are a child of inetd. 207 */ 208 if (bind_addr.sin_family == AF_INET) { 209 standalone = FALSE; 210 bootps_port = ntohs(bind_addr.sin_port); 211 } else { 212 /* Some other type of socket? */ 213 report(LOG_INFO, "getsockname: not an INET socket"); 214 } 215 } 216 /* 217 * Set defaults that might be changed by option switches. 218 */ 219 stmp = NULL; 220 timeout = &actualtimeout; 221 222 if (uname(&my_uname) < 0) 223 errx(1, "can't get hostname"); 224 hostname = my_uname.nodename; 225 226 hep = gethostbyname(hostname); 227 if (!hep) { 228 printf("Can not get my IP address\n"); 229 exit(1); 230 } 231 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); 232 233 /* 234 * Read switches. 235 */ 236 for (argc--, argv++; argc > 0; argc--, argv++) { 237 if (argv[0][0] != '-') 238 break; 239 switch (argv[0][1]) { 240 241 case 'd': /* debug level */ 242 if (argv[0][2]) { 243 stmp = &(argv[0][2]); 244 } else if (argv[1] && argv[1][0] == '-') { 245 /* 246 * Backwards-compatible behavior: 247 * no parameter, so just increment the debug flag. 248 */ 249 debug++; 250 break; 251 } else { 252 argc--; 253 argv++; 254 stmp = argv[0]; 255 } 256 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 257 warnx("invalid debug level"); 258 break; 259 } 260 debug = n; 261 break; 262 263 case 'h': /* hop count limit */ 264 if (argv[0][2]) { 265 stmp = &(argv[0][2]); 266 } else { 267 argc--; 268 argv++; 269 stmp = argv[0]; 270 } 271 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || 272 (n < 0) || (n > 16)) 273 { 274 warnx("invalid hop count limit"); 275 break; 276 } 277 maxhops = (u_char)n; 278 break; 279 280 case 'i': /* inetd mode */ 281 standalone = FALSE; 282 break; 283 284 case 's': /* standalone mode */ 285 standalone = TRUE; 286 break; 287 288 case 't': /* timeout */ 289 if (argv[0][2]) { 290 stmp = &(argv[0][2]); 291 } else { 292 argc--; 293 argv++; 294 stmp = argv[0]; 295 } 296 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 297 warnx("invalid timeout specification"); 298 break; 299 } 300 actualtimeout.tv_sec = (int32) (60 * n); 301 /* 302 * If the actual timeout is zero, pass a NULL pointer 303 * to select so it blocks indefinitely, otherwise, 304 * point to the actual timeout value. 305 */ 306 timeout = (n > 0) ? &actualtimeout : NULL; 307 break; 308 309 case 'w': /* wait time */ 310 if (argv[0][2]) { 311 stmp = &(argv[0][2]); 312 } else { 313 argc--; 314 argv++; 315 stmp = argv[0]; 316 } 317 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || 318 (n < 0) || (n > 60)) 319 { 320 warnx("invalid wait time"); 321 break; 322 } 323 minwait = (u_int)n; 324 break; 325 326 default: 327 warnx("unknown switch: -%c", argv[0][1]); 328 usage(); 329 break; 330 331 } /* switch */ 332 } /* for args */ 333 334 /* Make sure server name argument is suplied. */ 335 servername = argv[0]; 336 if (!servername) { 337 warnx("missing server name"); 338 usage(); 339 } 340 /* 341 * Get address of real bootp server. 342 */ 343 if (isdigit(servername[0])) 344 server_ipa = inet_addr(servername); 345 else { 346 hep = gethostbyname(servername); 347 if (!hep) 348 errx(1, "can't get addr for %s", servername); 349 bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa)); 350 } 351 352 if (standalone) { 353 /* 354 * Go into background and disassociate from controlling terminal. 355 * XXX - This is not the POSIX way (Should use setsid). -gwr 356 */ 357 if (debug < 3) { 358 if (fork()) 359 exit(0); 360 #ifdef NO_SETSID 361 setpgrp(0,0); 362 #ifdef TIOCNOTTY 363 n = open(_PATH_TTY, O_RDWR); 364 if (n >= 0) { 365 ioctl(n, TIOCNOTTY, (char *) 0); 366 (void) close(n); 367 } 368 #endif /* TIOCNOTTY */ 369 #else /* SETSID */ 370 if (setsid() < 0) 371 perror("setsid"); 372 #endif /* SETSID */ 373 } /* if debug < 3 */ 374 /* 375 * Nuke any timeout value 376 */ 377 timeout = NULL; 378 379 /* 380 * Here, bootpd would do: 381 * chdir 382 * tzone_init 383 * rdtab_init 384 * readtab 385 */ 386 387 /* 388 * Create a socket. 389 */ 390 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 391 report(LOG_ERR, "socket: %s", get_network_errmsg()); 392 exit(1); 393 } 394 /* 395 * Get server's listening port number 396 */ 397 servp = getservbyname("bootps", "udp"); 398 if (servp) { 399 bootps_port = ntohs((u_short) servp->s_port); 400 } else { 401 bootps_port = (u_short) IPPORT_BOOTPS; 402 report(LOG_ERR, 403 "bootps/udp: unknown service -- using port %d", 404 bootps_port); 405 } 406 407 /* 408 * Bind socket to BOOTPS port. 409 */ 410 bind_addr.sin_family = AF_INET; 411 bind_addr.sin_port = htons(bootps_port); 412 bind_addr.sin_addr.s_addr = INADDR_ANY; 413 if (bind(s, (struct sockaddr *) &bind_addr, 414 sizeof(bind_addr)) < 0) 415 { 416 report(LOG_ERR, "bind: %s", get_network_errmsg()); 417 exit(1); 418 } 419 } /* if standalone */ 420 /* 421 * Get destination port number so we can reply to client 422 */ 423 servp = getservbyname("bootpc", "udp"); 424 if (servp) { 425 bootpc_port = ntohs(servp->s_port); 426 } else { 427 report(LOG_ERR, 428 "bootpc/udp: unknown service -- using port %d", 429 IPPORT_BOOTPC); 430 bootpc_port = (u_short) IPPORT_BOOTPC; 431 } 432 433 /* no signal catchers */ 434 435 /* 436 * Process incoming requests. 437 */ 438 for (;;) { 439 struct timeval tv; 440 441 readfds = 1 << s; 442 if (timeout) 443 tv = *timeout; 444 445 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, 446 (timeout) ? &tv : NULL); 447 if (nfound < 0) { 448 if (errno != EINTR) { 449 report(LOG_ERR, "select: %s", get_errmsg()); 450 } 451 continue; 452 } 453 if (!(readfds & (1 << s))) { 454 report(LOG_INFO, "exiting after %ld minutes of inactivity", 455 (long)(actualtimeout.tv_sec / 60)); 456 exit(0); 457 } 458 ra_len = sizeof(recv_addr); 459 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, 460 (struct sockaddr *) &recv_addr, &ra_len); 461 if (n <= 0) { 462 continue; 463 } 464 if (debug > 3) { 465 report(LOG_INFO, "recvd pkt from IP addr %s", 466 inet_ntoa(recv_addr.sin_addr)); 467 } 468 if (n < sizeof(struct bootp)) { 469 if (debug) { 470 report(LOG_INFO, "received short packet"); 471 } 472 continue; 473 } 474 pktlen = n; 475 476 switch (bp->bp_op) { 477 case BOOTREQUEST: 478 handle_request(); 479 break; 480 case BOOTREPLY: 481 handle_reply(); 482 break; 483 } 484 } 485 return 0; 486 } 487 488 489 490 491 /* 492 * Print "usage" message and exit 493 */ 494 495 static void 496 usage() 497 { 498 fprintf(stderr, 499 "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n"); 500 fprintf(stderr, "\t -d n\tset debug level\n"); 501 fprintf(stderr, "\t -h n\tset max hop count\n"); 502 fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); 503 fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); 504 fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); 505 fprintf(stderr, "\t -w n\tset min wait time (secs)\n"); 506 exit(1); 507 } 508 509 510 511 /* 512 * Process BOOTREQUEST packet. 513 * 514 * Note, this just forwards the request to a real server. 515 */ 516 static void 517 handle_request() 518 { 519 struct bootp *bp = (struct bootp *) pktbuf; 520 u_short secs; 521 u_char hops; 522 523 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ 524 525 if (debug) { 526 report(LOG_INFO, "request from %s", 527 inet_ntoa(recv_addr.sin_addr)); 528 } 529 /* Has the client been waiting long enough? */ 530 secs = ntohs(bp->bp_secs); 531 if (secs < minwait) 532 return; 533 534 /* Has this packet hopped too many times? */ 535 hops = bp->bp_hops; 536 if (++hops > maxhops) { 537 report(LOG_NOTICE, "request from %s reached hop limit", 538 inet_ntoa(recv_addr.sin_addr)); 539 return; 540 } 541 bp->bp_hops = hops; 542 543 /* 544 * Here one might discard a request from the same subnet as the 545 * real server, but we can assume that the real server will send 546 * a reply to the client before it waits for minwait seconds. 547 */ 548 549 /* If gateway address is not set, put in local interface addr. */ 550 if (bp->bp_giaddr.s_addr == 0) { 551 #if 0 /* BUG */ 552 struct sockaddr_in *sip; 553 struct ifreq *ifr; 554 /* 555 * XXX - This picks the wrong interface when the receive addr 556 * is the broadcast address. There is no portable way to 557 * find out which interface a broadcast was received on. -gwr 558 * (Thanks to <walker@zk3.dec.com> for finding this bug!) 559 */ 560 ifr = getif(s, &recv_addr.sin_addr); 561 if (!ifr) { 562 report(LOG_NOTICE, "no interface for request from %s", 563 inet_ntoa(recv_addr.sin_addr)); 564 return; 565 } 566 sip = (struct sockaddr_in *) &(ifr->ifr_addr); 567 bp->bp_giaddr = sip->sin_addr; 568 #else /* BUG */ 569 /* 570 * XXX - Just set "giaddr" to our "official" IP address. 571 * RFC 1532 says giaddr MUST be set to the address of the 572 * interface on which the request was received. Setting 573 * it to our "default" IP address is not strictly correct, 574 * but is good enough to allow the real BOOTP server to 575 * get the reply back here. Then, before we forward the 576 * reply to the client, the giaddr field is corrected. 577 * (In case the client uses giaddr, which it should not.) 578 * See handle_reply() 579 */ 580 bp->bp_giaddr = my_ip_addr; 581 #endif /* BUG */ 582 583 /* 584 * XXX - DHCP says to insert a subnet mask option into the 585 * options area of the request (if vendor magic == std). 586 */ 587 } 588 /* Set up socket address for send. */ 589 send_addr.sin_family = AF_INET; 590 send_addr.sin_port = htons(bootps_port); 591 send_addr.sin_addr.s_addr = server_ipa; 592 593 /* Send reply with same size packet as request used. */ 594 if (sendto(s, pktbuf, pktlen, 0, 595 (struct sockaddr *) &send_addr, 596 sizeof(send_addr)) < 0) 597 { 598 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 599 } 600 } 601 602 603 604 /* 605 * Process BOOTREPLY packet. 606 */ 607 static void 608 handle_reply() 609 { 610 struct bootp *bp = (struct bootp *) pktbuf; 611 struct ifreq *ifr; 612 struct sockaddr_in *sip; 613 unsigned char *ha; 614 int len, haf; 615 616 if (debug) { 617 report(LOG_INFO, " reply for %s", 618 inet_ntoa(bp->bp_yiaddr)); 619 } 620 /* Make sure client is directly accessible. */ 621 ifr = getif(s, &(bp->bp_yiaddr)); 622 if (!ifr) { 623 report(LOG_NOTICE, "no interface for reply to %s", 624 inet_ntoa(bp->bp_yiaddr)); 625 return; 626 } 627 #if 1 /* Experimental (see BUG above) */ 628 /* #ifdef CATER_TO_OLD_CLIENTS ? */ 629 /* 630 * The giaddr field has been set to our "default" IP address 631 * which might not be on the same interface as the client. 632 * In case the client looks at giaddr, (which it should not) 633 * giaddr is now set to the address of the correct interface. 634 */ 635 sip = (struct sockaddr_in *) &(ifr->ifr_addr); 636 bp->bp_giaddr = sip->sin_addr; 637 #endif 638 639 /* Set up socket address for send to client. */ 640 send_addr.sin_family = AF_INET; 641 send_addr.sin_addr = bp->bp_yiaddr; 642 send_addr.sin_port = htons(bootpc_port); 643 644 /* Create an ARP cache entry for the client. */ 645 ha = bp->bp_chaddr; 646 len = bp->bp_hlen; 647 if (len > MAXHADDRLEN) 648 len = MAXHADDRLEN; 649 haf = (int) bp->bp_htype; 650 if (haf == 0) 651 haf = HTYPE_ETHERNET; 652 653 if (debug > 1) 654 report(LOG_INFO, "setarp %s - %s", 655 inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len)); 656 setarp(s, &bp->bp_yiaddr, haf, ha, len); 657 658 /* Send reply with same size packet as request used. */ 659 if (sendto(s, pktbuf, pktlen, 0, 660 (struct sockaddr *) &send_addr, 661 sizeof(send_addr)) < 0) 662 { 663 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 664 } 665 } 666 667 /* 668 * Local Variables: 669 * tab-width: 4 670 * c-indent-level: 4 671 * c-argdecl-indent: 4 672 * c-continued-statement-offset: 4 673 * c-continued-brace-offset: -4 674 * c-label-offset: -4 675 * c-brace-offset: 0 676 * End: 677 */ 678