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