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