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