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