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