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 #include "bootp.h" 70 #include "getif.h" 71 #include "hwaddr.h" 72 #include "report.h" 73 #include "patchlevel.h" 74 75 /* Local definitions: */ 76 #define MAX_MSG_SIZE (3*512) /* Maximum packet size */ 77 #define TRUE 1 78 #define FALSE 0 79 #define get_network_errmsg get_errmsg 80 81 82 83 /* 84 * Externals, forward declarations, and global variables 85 */ 86 87 static void usage(void); 88 static void handle_reply(void); 89 static void handle_request(void); 90 91 /* 92 * IP port numbers for client and server obtained from /etc/services 93 */ 94 95 u_short bootps_port, bootpc_port; 96 97 98 /* 99 * Internet socket and interface config structures 100 */ 101 102 struct sockaddr_in bind_addr; /* Listening */ 103 struct sockaddr_in recv_addr; /* Packet source */ 104 struct sockaddr_in send_addr; /* destination */ 105 106 107 /* 108 * option defaults 109 */ 110 int debug = 0; /* Debugging flag (level) */ 111 struct timeval actualtimeout = 112 { /* fifteen minutes */ 113 15 * 60L, /* tv_sec */ 114 0 /* tv_usec */ 115 }; 116 u_char maxhops = 4; /* Number of hops allowed for requests. */ 117 u_int minwait = 3; /* Number of seconds client must wait before 118 its bootrequest packets are forwarded. */ 119 int arpmod = TRUE; /* modify the ARP table */ 120 121 /* 122 * General 123 */ 124 125 int s; /* Socket file descriptor */ 126 char *pktbuf; /* Receive packet buffer */ 127 int pktlen; 128 char *progname; 129 char *servername; 130 int32 server_ipa; /* Real server IP address, network order. */ 131 132 struct in_addr my_ip_addr; 133 134 struct utsname my_uname; 135 char *hostname; 136 137 138 139 140 141 /* 142 * Initialization such as command-line processing is done and then the 143 * main server loop is started. 144 */ 145 146 int 147 main(argc, argv) 148 int argc; 149 char **argv; 150 { 151 struct timeval *timeout; 152 struct bootp *bp; 153 struct servent *servp; 154 struct hostent *hep; 155 char *stmp; 156 int n, ba_len, ra_len; 157 int nfound, readfds; 158 int standalone; 159 160 progname = strrchr(argv[0], '/'); 161 if (progname) progname++; 162 else progname = argv[0]; 163 164 /* 165 * Initialize logging. 166 */ 167 report_init(0); /* uses progname */ 168 169 /* 170 * Log startup 171 */ 172 report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); 173 174 /* Debugging for compilers with struct padding. */ 175 assert(sizeof(struct bootp) == BP_MINPKTSZ); 176 177 /* Get space for receiving packets and composing replies. */ 178 pktbuf = malloc(MAX_MSG_SIZE); 179 if (!pktbuf) { 180 report(LOG_ERR, "malloc failed"); 181 exit(1); 182 } 183 bp = (struct bootp *) pktbuf; 184 185 /* 186 * Check to see if a socket was passed to us from inetd. 187 * 188 * Use getsockname() to determine if descriptor 0 is indeed a socket 189 * (and thus we are probably a child of inetd) or if it is instead 190 * something else and we are running standalone. 191 */ 192 s = 0; 193 ba_len = sizeof(bind_addr); 194 bzero((char *) &bind_addr, ba_len); 195 errno = 0; 196 standalone = TRUE; 197 if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { 198 /* 199 * Descriptor 0 is a socket. Assume we are a child of inetd. 200 */ 201 if (bind_addr.sin_family == AF_INET) { 202 standalone = FALSE; 203 bootps_port = ntohs(bind_addr.sin_port); 204 } else { 205 /* Some other type of socket? */ 206 report(LOG_INFO, "getsockname: not an INET socket"); 207 } 208 } 209 /* 210 * Set defaults that might be changed by option switches. 211 */ 212 stmp = NULL; 213 timeout = &actualtimeout; 214 215 if (uname(&my_uname) < 0) 216 errx(1, "can't get hostname"); 217 hostname = my_uname.nodename; 218 219 hep = gethostbyname(hostname); 220 if (!hep) { 221 printf("Can not get my IP address\n"); 222 exit(1); 223 } 224 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); 225 226 /* 227 * Read switches. 228 */ 229 for (argc--, argv++; argc > 0; argc--, argv++) { 230 if (argv[0][0] != '-') 231 break; 232 switch (argv[0][1]) { 233 234 case 'a': /* don't modify the ARP table */ 235 arpmod = FALSE; 236 break; 237 case 'd': /* debug level */ 238 if (argv[0][2]) { 239 stmp = &(argv[0][2]); 240 } else if (argv[1] && argv[1][0] == '-') { 241 /* 242 * Backwards-compatible behavior: 243 * no parameter, so just increment the debug flag. 244 */ 245 debug++; 246 break; 247 } else { 248 argc--; 249 argv++; 250 stmp = argv[0]; 251 } 252 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 253 warnx("invalid debug level"); 254 break; 255 } 256 debug = n; 257 break; 258 259 case 'h': /* hop count limit */ 260 if (argv[0][2]) { 261 stmp = &(argv[0][2]); 262 } else { 263 argc--; 264 argv++; 265 stmp = argv[0]; 266 } 267 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || 268 (n < 0) || (n > 16)) 269 { 270 warnx("invalid hop count limit"); 271 break; 272 } 273 maxhops = (u_char)n; 274 break; 275 276 case 'i': /* inetd mode */ 277 standalone = FALSE; 278 break; 279 280 case 's': /* standalone mode */ 281 standalone = TRUE; 282 break; 283 284 case 't': /* timeout */ 285 if (argv[0][2]) { 286 stmp = &(argv[0][2]); 287 } else { 288 argc--; 289 argv++; 290 stmp = argv[0]; 291 } 292 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 293 warnx("invalid timeout specification"); 294 break; 295 } 296 actualtimeout.tv_sec = (int32) (60 * n); 297 /* 298 * If the actual timeout is zero, pass a NULL pointer 299 * to select so it blocks indefinitely, otherwise, 300 * point to the actual timeout value. 301 */ 302 timeout = (n > 0) ? &actualtimeout : NULL; 303 break; 304 305 case 'w': /* wait time */ 306 if (argv[0][2]) { 307 stmp = &(argv[0][2]); 308 } else { 309 argc--; 310 argv++; 311 stmp = argv[0]; 312 } 313 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || 314 (n < 0) || (n > 60)) 315 { 316 warnx("invalid wait time"); 317 break; 318 } 319 minwait = (u_int)n; 320 break; 321 322 default: 323 warnx("unknown switch: -%c", argv[0][1]); 324 usage(); 325 break; 326 327 } /* switch */ 328 } /* for args */ 329 330 /* Make sure server name argument is suplied. */ 331 servername = argv[0]; 332 if (!servername) { 333 warnx("missing server name"); 334 usage(); 335 } 336 /* 337 * Get address of real bootp server. 338 */ 339 if (isdigit(servername[0])) 340 server_ipa = inet_addr(servername); 341 else { 342 hep = gethostbyname(servername); 343 if (!hep) 344 errx(1, "can't get addr for %s", servername); 345 bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa)); 346 } 347 348 if (standalone) { 349 /* 350 * Go into background and disassociate from controlling terminal. 351 * XXX - This is not the POSIX way (Should use setsid). -gwr 352 */ 353 if (debug < 3) { 354 if (fork()) 355 exit(0); 356 #ifdef NO_SETSID 357 setpgrp(0,0); 358 #ifdef TIOCNOTTY 359 n = open(_PATH_TTY, O_RDWR); 360 if (n >= 0) { 361 ioctl(n, TIOCNOTTY, (char *) 0); 362 (void) close(n); 363 } 364 #endif /* TIOCNOTTY */ 365 #else /* SETSID */ 366 if (setsid() < 0) 367 perror("setsid"); 368 #endif /* SETSID */ 369 } /* if debug < 3 */ 370 /* 371 * Nuke any timeout value 372 */ 373 timeout = NULL; 374 375 /* 376 * Here, bootpd would do: 377 * chdir 378 * tzone_init 379 * rdtab_init 380 * readtab 381 */ 382 383 /* 384 * Create a socket. 385 */ 386 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 387 report(LOG_ERR, "socket: %s", get_network_errmsg()); 388 exit(1); 389 } 390 /* 391 * Get server's listening port number 392 */ 393 servp = getservbyname("bootps", "udp"); 394 if (servp) { 395 bootps_port = ntohs((u_short) servp->s_port); 396 } else { 397 bootps_port = (u_short) IPPORT_BOOTPS; 398 report(LOG_ERR, 399 "bootps/udp: unknown service -- using port %d", 400 bootps_port); 401 } 402 403 /* 404 * Bind socket to BOOTPS port. 405 */ 406 bind_addr.sin_family = AF_INET; 407 bind_addr.sin_port = htons(bootps_port); 408 bind_addr.sin_addr.s_addr = INADDR_ANY; 409 if (bind(s, (struct sockaddr *) &bind_addr, 410 sizeof(bind_addr)) < 0) 411 { 412 report(LOG_ERR, "bind: %s", get_network_errmsg()); 413 exit(1); 414 } 415 } /* if standalone */ 416 /* 417 * Get destination port number so we can reply to client 418 */ 419 servp = getservbyname("bootpc", "udp"); 420 if (servp) { 421 bootpc_port = ntohs(servp->s_port); 422 } else { 423 report(LOG_ERR, 424 "bootpc/udp: unknown service -- using port %d", 425 IPPORT_BOOTPC); 426 bootpc_port = (u_short) IPPORT_BOOTPC; 427 } 428 429 /* no signal catchers */ 430 431 /* 432 * Process incoming requests. 433 */ 434 for (;;) { 435 struct timeval tv; 436 437 readfds = 1 << s; 438 if (timeout) 439 tv = *timeout; 440 441 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, 442 (timeout) ? &tv : NULL); 443 if (nfound < 0) { 444 if (errno != EINTR) { 445 report(LOG_ERR, "select: %s", get_errmsg()); 446 } 447 continue; 448 } 449 if (!(readfds & (1 << s))) { 450 report(LOG_INFO, "exiting after %ld minutes of inactivity", 451 (long)(actualtimeout.tv_sec / 60)); 452 exit(0); 453 } 454 ra_len = sizeof(recv_addr); 455 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, 456 (struct sockaddr *) &recv_addr, &ra_len); 457 if (n <= 0) { 458 continue; 459 } 460 if (debug > 3) { 461 report(LOG_INFO, "recvd pkt from IP addr %s", 462 inet_ntoa(recv_addr.sin_addr)); 463 } 464 if (n < sizeof(struct bootp)) { 465 if (debug) { 466 report(LOG_INFO, "received short packet"); 467 } 468 continue; 469 } 470 pktlen = n; 471 472 switch (bp->bp_op) { 473 case BOOTREQUEST: 474 handle_request(); 475 break; 476 case BOOTREPLY: 477 handle_reply(); 478 break; 479 } 480 } 481 return 0; 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 [-a] [-i | -s] [-d level] [-h count] [-t timeout]\n" 496 " [-w time] server\n"); 497 fprintf(stderr, "\t -a\tdon't modify ARP table\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 u_short secs; 519 u_char 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 = bp->bp_hops; 534 if (++hops > maxhops) { 535 report(LOG_NOTICE, "request from %s reached hop limit", 536 inet_ntoa(recv_addr.sin_addr)); 537 return; 538 } 539 bp->bp_hops = 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 struct ifreq *ifr; 552 /* 553 * XXX - This picks the wrong interface when the receive addr 554 * is the broadcast address. There is no portable way to 555 * find out which interface a broadcast was received on. -gwr 556 * (Thanks to <walker@zk3.dec.com> for finding this bug!) 557 */ 558 ifr = getif(s, &recv_addr.sin_addr); 559 if (!ifr) { 560 report(LOG_NOTICE, "no interface for request from %s", 561 inet_ntoa(recv_addr.sin_addr)); 562 return; 563 } 564 sip = (struct sockaddr_in *) &(ifr->ifr_addr); 565 bp->bp_giaddr = sip->sin_addr; 566 #else /* BUG */ 567 /* 568 * XXX - Just set "giaddr" to our "official" IP address. 569 * RFC 1532 says giaddr MUST be set to the address of the 570 * interface on which the request was received. Setting 571 * it to our "default" IP address is not strictly correct, 572 * but is good enough to allow the real BOOTP server to 573 * get the reply back here. Then, before we forward the 574 * reply to the client, the giaddr field is corrected. 575 * (In case the client uses giaddr, which it should not.) 576 * See handle_reply() 577 */ 578 bp->bp_giaddr = my_ip_addr; 579 #endif /* BUG */ 580 581 /* 582 * XXX - DHCP says to insert a subnet mask option into the 583 * options area of the request (if vendor magic == std). 584 */ 585 } 586 /* Set up socket address for send. */ 587 send_addr.sin_family = AF_INET; 588 send_addr.sin_port = htons(bootps_port); 589 send_addr.sin_addr.s_addr = server_ipa; 590 591 /* Send reply with same size packet as request used. */ 592 if (sendto(s, pktbuf, pktlen, 0, 593 (struct sockaddr *) &send_addr, 594 sizeof(send_addr)) < 0) 595 { 596 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 597 } 598 } 599 600 601 602 /* 603 * Process BOOTREPLY packet. 604 */ 605 static void 606 handle_reply() 607 { 608 struct bootp *bp = (struct bootp *) pktbuf; 609 struct ifreq *ifr; 610 struct sockaddr_in *sip; 611 unsigned char *ha; 612 int len, haf; 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 if (arpmod) { 643 /* Create an ARP cache entry for the client. */ 644 ha = bp->bp_chaddr; 645 len = bp->bp_hlen; 646 struct in_addr dst; 647 648 if (len > MAXHADDRLEN) 649 len = MAXHADDRLEN; 650 haf = (int) bp->bp_htype; 651 if (haf == 0) 652 haf = HTYPE_ETHERNET; 653 654 if (debug > 1) 655 report(LOG_INFO, "setarp %s - %s", 656 inet_ntoa(dst), haddrtoa(ha, len)); 657 setarp(s, &dst, haf, ha, len); 658 } 659 660 /* Send reply with same size packet as request used. */ 661 if (sendto(s, pktbuf, pktlen, 0, 662 (struct sockaddr *) &send_addr, 663 sizeof(send_addr)) < 0) 664 { 665 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 666 } 667 } 668 669 /* 670 * Local Variables: 671 * tab-width: 4 672 * c-indent-level: 4 673 * c-argdecl-indent: 4 674 * c-continued-statement-offset: 4 675 * c-continued-brace-offset: -4 676 * c-label-offset: -4 677 * c-brace-offset: 0 678 * End: 679 */ 680