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