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 void 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 } 498 499 500 501 502 /* 503 * Print "usage" message and exit 504 */ 505 506 static void 507 usage() 508 { 509 fprintf(stderr, 510 "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n"); 511 fprintf(stderr, "\t -d n\tset debug level\n"); 512 fprintf(stderr, "\t -h n\tset max hop count\n"); 513 fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); 514 fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); 515 fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); 516 fprintf(stderr, "\t -w n\tset min wait time (secs)\n"); 517 exit(1); 518 } 519 520 521 522 /* 523 * Process BOOTREQUEST packet. 524 * 525 * Note, this just forwards the request to a real server. 526 */ 527 static void 528 handle_request() 529 { 530 struct bootp *bp = (struct bootp *) pktbuf; 531 u_short secs; 532 u_char hops; 533 534 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ 535 536 if (debug) { 537 report(LOG_INFO, "request from %s", 538 inet_ntoa(recv_addr.sin_addr)); 539 } 540 /* Has the client been waiting long enough? */ 541 secs = ntohs(bp->bp_secs); 542 if (secs < minwait) 543 return; 544 545 /* Has this packet hopped too many times? */ 546 hops = bp->bp_hops; 547 if (++hops > maxhops) { 548 report(LOG_NOTICE, "reqest from %s reached hop limit", 549 inet_ntoa(recv_addr.sin_addr)); 550 return; 551 } 552 bp->bp_hops = hops; 553 554 /* 555 * Here one might discard a request from the same subnet as the 556 * real server, but we can assume that the real server will send 557 * a reply to the client before it waits for minwait seconds. 558 */ 559 560 /* If gateway address is not set, put in local interface addr. */ 561 if (bp->bp_giaddr.s_addr == 0) { 562 #if 0 /* BUG */ 563 struct sockaddr_in *sip; 564 struct ifreq *ifr; 565 /* 566 * XXX - This picks the wrong interface when the receive addr 567 * is the broadcast address. There is no portable way to 568 * find out which interface a broadcast was received on. -gwr 569 * (Thanks to <walker@zk3.dec.com> for finding this bug!) 570 */ 571 ifr = getif(s, &recv_addr.sin_addr); 572 if (!ifr) { 573 report(LOG_NOTICE, "no interface for request from %s", 574 inet_ntoa(recv_addr.sin_addr)); 575 return; 576 } 577 sip = (struct sockaddr_in *) &(ifr->ifr_addr); 578 bp->bp_giaddr = sip->sin_addr; 579 #else /* BUG */ 580 /* 581 * XXX - Just set "giaddr" to our "official" IP address. 582 * RFC 1532 says giaddr MUST be set to the address of the 583 * interface on which the request was received. Setting 584 * it to our "default" IP address is not strictly correct, 585 * but is good enough to allow the real BOOTP server to 586 * get the reply back here. Then, before we forward the 587 * reply to the client, the giaddr field is corrected. 588 * (In case the client uses giaddr, which it should not.) 589 * See handle_reply() 590 */ 591 bp->bp_giaddr = my_ip_addr; 592 #endif /* BUG */ 593 594 /* 595 * XXX - DHCP says to insert a subnet mask option into the 596 * options area of the request (if vendor magic == std). 597 */ 598 } 599 /* Set up socket address for send. */ 600 send_addr.sin_family = AF_INET; 601 send_addr.sin_port = htons(bootps_port); 602 send_addr.sin_addr.s_addr = server_ipa; 603 604 /* Send reply with same size packet as request used. */ 605 if (sendto(s, pktbuf, pktlen, 0, 606 (struct sockaddr *) &send_addr, 607 sizeof(send_addr)) < 0) 608 { 609 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 610 } 611 } 612 613 614 615 /* 616 * Process BOOTREPLY packet. 617 */ 618 static void 619 handle_reply() 620 { 621 struct bootp *bp = (struct bootp *) pktbuf; 622 struct ifreq *ifr; 623 struct sockaddr_in *sip; 624 unsigned char *ha; 625 int len, haf; 626 627 if (debug) { 628 report(LOG_INFO, " reply for %s", 629 inet_ntoa(bp->bp_yiaddr)); 630 } 631 /* Make sure client is directly accessible. */ 632 ifr = getif(s, &(bp->bp_yiaddr)); 633 if (!ifr) { 634 report(LOG_NOTICE, "no interface for reply to %s", 635 inet_ntoa(bp->bp_yiaddr)); 636 return; 637 } 638 #if 1 /* Experimental (see BUG above) */ 639 /* #ifdef CATER_TO_OLD_CLIENTS ? */ 640 /* 641 * The giaddr field has been set to our "default" IP address 642 * which might not be on the same interface as the client. 643 * In case the client looks at giaddr, (which it should not) 644 * giaddr is now set to the address of the correct interface. 645 */ 646 sip = (struct sockaddr_in *) &(ifr->ifr_addr); 647 bp->bp_giaddr = sip->sin_addr; 648 #endif 649 650 /* Set up socket address for send to client. */ 651 send_addr.sin_family = AF_INET; 652 send_addr.sin_addr = bp->bp_yiaddr; 653 send_addr.sin_port = htons(bootpc_port); 654 655 /* Create an ARP cache entry for the client. */ 656 ha = bp->bp_chaddr; 657 len = bp->bp_hlen; 658 if (len > MAXHADDRLEN) 659 len = MAXHADDRLEN; 660 haf = (int) bp->bp_htype; 661 if (haf == 0) 662 haf = HTYPE_ETHERNET; 663 664 if (debug > 1) 665 report(LOG_INFO, "setarp %s - %s", 666 inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len)); 667 setarp(s, &bp->bp_yiaddr, haf, ha, len); 668 669 /* Send reply with same size packet as request used. */ 670 if (sendto(s, pktbuf, pktlen, 0, 671 (struct sockaddr *) &send_addr, 672 sizeof(send_addr)) < 0) 673 { 674 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 675 } 676 } 677 678 /* 679 * Local Variables: 680 * tab-width: 4 681 * c-indent-level: 4 682 * c-argdecl-indent: 4 683 * c-continued-statement-offset: 4 684 * c-continued-brace-offset: -4 685 * c-label-offset: -4 686 * c-brace-offset: 0 687 * End: 688 */ 689