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