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