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