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