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