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