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