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