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