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