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 #include <sys/cdefs.h> 29 #ifndef lint 30 __RCSID("$NetBSD: bootpgw.c,v 1.8 1998/07/06 07:02:17 mrg Exp $"); 31 #endif 32 33 /* 34 * BOOTPGW is typically used to forward BOOTP client requests from 35 * one subnet to a BOOTP server on a different subnet. 36 */ 37 38 #include <sys/types.h> 39 #include <sys/param.h> 40 #include <sys/socket.h> 41 #include <sys/ioctl.h> 42 #include <sys/file.h> 43 #include <sys/time.h> 44 #include <sys/stat.h> 45 46 #include <net/if.h> 47 #include <netinet/in.h> 48 #include <arpa/inet.h> /* inet_ntoa */ 49 50 #ifndef NO_UNISTD 51 #include <unistd.h> 52 #endif 53 #include <stdlib.h> 54 #include <signal.h> 55 #include <stdio.h> 56 #include <string.h> 57 #include <errno.h> 58 #include <ctype.h> 59 #include <netdb.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 int main P((int, char **)); 103 104 #undef P 105 106 /* 107 * IP port numbers for client and server obtained from /etc/services 108 */ 109 110 u_short bootps_port, bootpc_port; 111 112 113 /* 114 * Internet socket and interface config structures 115 */ 116 117 struct sockaddr_in bind_addr; /* Listening */ 118 struct sockaddr_in clnt_addr; /* client address */ 119 struct sockaddr_in serv_addr; /* server address */ 120 121 122 /* 123 * option defaults 124 */ 125 int debug = 0; /* Debugging flag (level) */ 126 struct timeval actualtimeout = 127 { /* fifteen minutes */ 128 15 * 60L, /* tv_sec */ 129 0 /* tv_usec */ 130 }; 131 u_int maxhops = 4; /* Number of hops allowed for requests. */ 132 u_int minwait = 3; /* Number of seconds client must wait before 133 its bootrequest packets are forwarded. */ 134 135 /* 136 * General 137 */ 138 139 int s; /* Socket file descriptor */ 140 char *pktbuf; /* Receive packet buffer */ 141 int pktlen; 142 char *progname; 143 char *servername; 144 145 char myhostname[MAXHOSTNAMELEN + 1]; 146 struct in_addr my_ip_addr; 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 gethostname(myhostname, sizeof(myhostname)); 225 myhostname[sizeof(myhostname) - 1] = '\0'; 226 hep = gethostbyname(myhostname); 227 if (!hep) { 228 printf("Can not get my IP address\n"); 229 exit(1); 230 } 231 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); 232 233 /* 234 * Read switches. 235 */ 236 for (argc--, argv++; argc > 0; argc--, argv++) { 237 if (argv[0][0] != '-') 238 break; 239 switch (argv[0][1]) { 240 241 case 'd': /* debug level */ 242 if (argv[0][2]) { 243 stmp = &(argv[0][2]); 244 } else if (argv[1] && argv[1][0] == '-') { 245 /* 246 * Backwards-compatible behavior: 247 * no parameter, so just increment the debug flag. 248 */ 249 debug++; 250 break; 251 } else { 252 argc--; 253 argv++; 254 stmp = argv[0]; 255 } 256 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 257 fprintf(stderr, 258 "%s: invalid debug level\n", progname); 259 break; 260 } 261 debug = n; 262 break; 263 264 case 'h': /* hop count limit */ 265 if (argv[0][2]) { 266 stmp = &(argv[0][2]); 267 } else { 268 argc--; 269 argv++; 270 stmp = argv[0]; 271 } 272 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || 273 (n < 0) || (n > 16)) 274 { 275 fprintf(stderr, 276 "bootpgw: invalid hop count limit\n"); 277 break; 278 } 279 maxhops = (u_int)n; 280 break; 281 282 case 'i': /* inetd mode */ 283 standalone = FALSE; 284 break; 285 286 case 's': /* standalone mode */ 287 standalone = TRUE; 288 break; 289 290 case 't': /* timeout */ 291 if (argv[0][2]) { 292 stmp = &(argv[0][2]); 293 } else { 294 argc--; 295 argv++; 296 stmp = argv[0]; 297 } 298 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 299 fprintf(stderr, 300 "%s: invalid timeout specification\n", progname); 301 break; 302 } 303 actualtimeout.tv_sec = (int32) (60 * n); 304 /* 305 * If the actual timeout is zero, pass a NULL pointer 306 * to select so it blocks indefinitely, otherwise, 307 * point to the actual timeout value. 308 */ 309 timeout = (n > 0) ? &actualtimeout : NULL; 310 break; 311 312 case 'w': /* wait time */ 313 if (argv[0][2]) { 314 stmp = &(argv[0][2]); 315 } else { 316 argc--; 317 argv++; 318 stmp = argv[0]; 319 } 320 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || 321 (n < 0) || (n > 60)) 322 { 323 fprintf(stderr, 324 "bootpgw: invalid wait time\n"); 325 break; 326 } 327 minwait = (u_int)n; 328 break; 329 330 default: 331 fprintf(stderr, "%s: unknown switch: -%c\n", 332 progname, argv[0][1]); 333 usage(); 334 break; 335 336 } /* switch */ 337 } /* for args */ 338 339 /* Make sure server name argument is suplied. */ 340 servername = argv[0]; 341 if (!servername) { 342 fprintf(stderr, "bootpgw: missing server name\n"); 343 usage(); 344 } 345 /* 346 * Get address of real bootp server. 347 */ 348 if (inet_aton(servername, &serv_addr.sin_addr) == 0) { 349 hep = gethostbyname(servername); 350 if (!hep) { 351 fprintf(stderr, "bootpgw: can't get addr for %s\n", servername); 352 exit(1); 353 } 354 memcpy(&serv_addr.sin_addr, hep->h_addr, 355 sizeof(serv_addr.sin_addr)); 356 } 357 358 if (standalone) { 359 /* 360 * Go into background and disassociate from controlling terminal. 361 * XXX - This is not the POSIX way (Should use setsid). -gwr 362 */ 363 if (debug < 3) { 364 if (fork()) 365 exit(0); 366 #ifdef NO_SETSID 367 setpgrp(0,0); 368 #ifdef TIOCNOTTY 369 n = open("/dev/tty", O_RDWR); 370 if (n >= 0) { 371 ioctl(n, TIOCNOTTY, (char *) 0); 372 (void) close(n); 373 } 374 #endif /* TIOCNOTTY */ 375 #else /* SETSID */ 376 if (setsid() < 0) 377 perror("setsid"); 378 #endif /* SETSID */ 379 } /* if debug < 3 */ 380 /* 381 * Nuke any timeout value 382 */ 383 timeout = NULL; 384 385 /* 386 * Here, bootpd would do: 387 * chdir 388 * tzone_init 389 * rdtab_init 390 * readtab 391 */ 392 393 /* 394 * Create a socket. 395 */ 396 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 397 report(LOG_ERR, "socket: %s", get_network_errmsg()); 398 exit(1); 399 } 400 /* 401 * Get server's listening port number 402 */ 403 servp = getservbyname("bootps", "udp"); 404 if (servp) { 405 bootps_port = ntohs((u_short) servp->s_port); 406 } else { 407 bootps_port = (u_short) IPPORT_BOOTPS; 408 report(LOG_ERR, 409 "udp/bootps: unknown service -- assuming port %d", 410 bootps_port); 411 } 412 413 /* 414 * Bind socket to BOOTPS port. 415 */ 416 bind_addr.sin_family = AF_INET; 417 bind_addr.sin_port = htons(bootps_port); 418 bind_addr.sin_addr.s_addr = INADDR_ANY; 419 if (bind(s, (struct sockaddr *) &bind_addr, 420 sizeof(bind_addr)) < 0) 421 { 422 report(LOG_ERR, "bind: %s", get_network_errmsg()); 423 exit(1); 424 } 425 } /* if standalone */ 426 /* 427 * Get destination port number so we can reply to client 428 */ 429 servp = getservbyname("bootpc", "udp"); 430 if (servp) { 431 bootpc_port = ntohs(servp->s_port); 432 } else { 433 report(LOG_ERR, 434 "udp/bootpc: unknown service -- assuming port %d", 435 IPPORT_BOOTPC); 436 bootpc_port = (u_short) IPPORT_BOOTPC; 437 } 438 439 /* no signal catchers */ 440 441 /* 442 * Process incoming requests. 443 */ 444 for (;;) { 445 readfds = 1 << s; 446 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, timeout); 447 if (nfound < 0) { 448 if (errno != EINTR) { 449 report(LOG_ERR, "select: %s", get_errmsg()); 450 } 451 continue; 452 } 453 if (!(readfds & (1 << s))) { 454 report(LOG_INFO, "exiting after %ld minutes of inactivity", 455 actualtimeout.tv_sec / 60); 456 exit(0); 457 } 458 ra_len = sizeof(clnt_addr); 459 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, 460 (struct sockaddr *) &clnt_addr, &ra_len); 461 if (n <= 0) { 462 continue; 463 } 464 if (debug > 3) { 465 report(LOG_INFO, "recvd pkt from IP addr %s", 466 inet_ntoa(clnt_addr.sin_addr)); 467 } 468 if (n < sizeof(struct bootp)) { 469 if (debug) { 470 report(LOG_INFO, "received short packet"); 471 } 472 continue; 473 } 474 pktlen = n; 475 476 switch (bp->bp_op) { 477 case BOOTREQUEST: 478 handle_request(); 479 break; 480 case BOOTREPLY: 481 handle_reply(); 482 break; 483 } 484 } 485 } 486 487 488 489 490 /* 491 * Print "usage" message and exit 492 */ 493 494 static void 495 usage() 496 { 497 fprintf(stderr, 498 "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n"); 499 fprintf(stderr, "\t -d n\tset debug level\n"); 500 fprintf(stderr, "\t -h n\tset max hop count\n"); 501 fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); 502 fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); 503 fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); 504 fprintf(stderr, "\t -w n\tset min wait time (secs)\n"); 505 exit(1); 506 } 507 508 509 510 /* 511 * Process BOOTREQUEST packet. 512 * 513 * Note, this just forwards the request to a real server. 514 */ 515 static void 516 handle_request() 517 { 518 struct bootp *bp = (struct bootp *) pktbuf; 519 #if 0 520 struct ifreq *ifr; 521 #endif 522 u_short secs, hops; 523 524 /* XXX - SLIP init: Set bp_ciaddr = clnt_addr here? */ 525 526 if (debug) { 527 report(LOG_INFO, "request from %s", 528 inet_ntoa(clnt_addr.sin_addr)); 529 } 530 /* Has the client been waiting long enough? */ 531 secs = ntohs(bp->bp_secs); 532 if (secs < minwait) 533 return; 534 535 /* Has this packet hopped too many times? */ 536 hops = ntohs(bp->bp_hops); 537 if (++hops > maxhops) { 538 report(LOG_NOTICE, "request from %s reached hop limit", 539 inet_ntoa(clnt_addr.sin_addr)); 540 return; 541 } 542 bp->bp_hops = htons(hops); 543 544 /* 545 * Here one might discard a request from the same subnet as the 546 * real server, but we can assume that the real server will send 547 * a reply to the client before it waits for minwait seconds. 548 */ 549 550 /* If gateway address is not set, put in local interface addr. */ 551 if (bp->bp_giaddr.s_addr == 0) { 552 #if 0 /* BUG */ 553 struct sockaddr_in *sip; 554 /* 555 * XXX - This picks the wrong interface when the receive addr 556 * is the broadcast address. There is no portable way to 557 * find out which interface a broadcast was received on. -gwr 558 * (Thanks to <walker@zk3.dec.com> for finding this bug!) 559 */ 560 ifr = getif(s, &clnt_addr.sin_addr); 561 if (!ifr) { 562 report(LOG_NOTICE, "no interface for request from %s", 563 inet_ntoa(clnt_addr.sin_addr)); 564 return; 565 } 566 sip = (struct sockaddr_in *) &(ifr->ifr_addr); 567 bp->bp_giaddr = sip->sin_addr; 568 #else /* BUG */ 569 /* 570 * XXX - Just set "giaddr" to our "official" IP address. 571 * RFC 1532 says giaddr MUST be set to the address of the 572 * interface on which the request was received. Setting 573 * it to our "default" IP address is not strictly correct, 574 * but is good enough to allow the real BOOTP server to 575 * get the reply back here. Then, before we forward the 576 * reply to the client, the giaddr field is corrected. 577 * (In case the client uses giaddr, which it should not.) 578 * See handle_reply() 579 */ 580 bp->bp_giaddr = my_ip_addr; 581 #endif /* BUG */ 582 583 /* 584 * XXX - DHCP says to insert a subnet mask option into the 585 * options area of the request (if vendor magic == std). 586 */ 587 } 588 /* Set up socket address for send. */ 589 serv_addr.sin_family = AF_INET; 590 serv_addr.sin_port = htons(bootps_port); 591 592 /* Send reply with same size packet as request used. */ 593 if (sendto(s, pktbuf, pktlen, 0, 594 (struct sockaddr *) &serv_addr, 595 sizeof(serv_addr)) < 0) 596 { 597 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 598 } 599 } 600 601 602 603 /* 604 * Process BOOTREPLY packet. 605 */ 606 static void 607 handle_reply() 608 { 609 struct bootp *bp = (struct bootp *) pktbuf; 610 struct ifreq *ifr; 611 struct sockaddr_in *sip; 612 u_char canon_haddr[MAXHADDRLEN]; 613 unsigned char *ha; 614 int len; 615 616 if (debug) { 617 report(LOG_INFO, " reply for %s", 618 inet_ntoa(bp->bp_yiaddr)); 619 } 620 /* Make sure client is directly accessible. */ 621 ifr = getif(s, &(bp->bp_yiaddr)); 622 if (!ifr) { 623 report(LOG_NOTICE, "no interface for reply to %s", 624 inet_ntoa(bp->bp_yiaddr)); 625 return; 626 } 627 #if 1 /* Experimental (see BUG above) */ 628 /* #ifdef CATER_TO_OLD_CLIENTS ? */ 629 /* 630 * The giaddr field has been set to our "default" IP address 631 * which might not be on the same interface as the client. 632 * In case the client looks at giaddr, (which it should not) 633 * giaddr is now set to the address of the correct interface. 634 */ 635 sip = (struct sockaddr_in *) &(ifr->ifr_addr); 636 bp->bp_giaddr = sip->sin_addr; 637 #endif 638 639 /* Set up socket address for send to client. */ 640 clnt_addr.sin_family = AF_INET; 641 clnt_addr.sin_addr = bp->bp_yiaddr; 642 clnt_addr.sin_port = htons(bootpc_port); 643 644 /* Create an ARP cache entry for the client. */ 645 ha = bp->bp_chaddr; 646 len = bp->bp_hlen; 647 if (len > MAXHADDRLEN) 648 len = MAXHADDRLEN; 649 if (bp->bp_htype == HTYPE_IEEE802) { 650 haddr_conv802(ha, canon_haddr, len); 651 ha = canon_haddr; 652 } 653 if (debug > 1) 654 report(LOG_INFO, "setarp %s - %s", 655 inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len)); 656 setarp(s, &bp->bp_yiaddr, ha, len); 657 658 /* Send reply with same size packet as request used. */ 659 if (sendto(s, pktbuf, pktlen, 0, 660 (struct sockaddr *) &clnt_addr, 661 sizeof(clnt_addr)) < 0) 662 { 663 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 664 } 665 } 666 667 /* 668 * Local Variables: 669 * tab-width: 4 670 * c-indent-level: 4 671 * c-argdecl-indent: 4 672 * c-continued-statement-offset: 4 673 * c-continued-brace-offset: -4 674 * c-label-offset: -4 675 * c-brace-offset: 0 676 * End: 677 */ 678