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