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