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