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