dfb2e218ce7c1ff8a1f4da3705b71725f61d9174
[dragonfly.git] / libexec / bootpd / bootpgw / bootpgw.c
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.3.2.1 2000/12/11 01:03:21 obrien Exp $ */
29 /* $DragonFly: src/libexec/bootpd/bootpgw/bootpgw.c,v 1.2 2003/06/17 04:27:07 dillon Exp $ */
30
31 /*
32  * BOOTPGW is typically used to forward BOOTP client requests from
33  * one subnet to a BOOTP server on a different subnet.
34  */
35
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/socket.h>
39 #include <sys/ioctl.h>
40 #include <sys/file.h>
41 #include <sys/time.h>
42 #include <sys/stat.h>
43 #include <sys/utsname.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
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 \f
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 \f
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                 fprintf(stderr, "bootpgw: can't get hostname\n");
221                 exit(1);
222         }
223         hostname = my_uname.nodename;
224
225         hep = gethostbyname(hostname);
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_char)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 (isdigit(servername[0]))
348                 server_ipa = inet_addr(servername);
349         else {
350                 hep = gethostbyname(servername);
351                 if (!hep) {
352                         fprintf(stderr, "bootpgw: can't get addr for %s\n", servername);
353                         exit(1);
354                 }
355                 bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa));
356         }
357
358         if (standalone) {
359                 /*
360                  * Go into background and disassociate from controlling terminal.
361                  * XXX - This is not the POSIX way (Should use setsid). -gwr
362                  */
363                 if (debug < 3) {
364                         if (fork())
365                                 exit(0);
366 #ifdef  NO_SETSID
367                         setpgrp(0,0);
368 #ifdef TIOCNOTTY
369                         n = open(_PATH_TTY, O_RDWR);
370                         if (n >= 0) {
371                                 ioctl(n, TIOCNOTTY, NULL);
372                                 (void) close(n);
373                         }
374 #endif  /* TIOCNOTTY */
375 #else   /* SETSID */
376                         if (setsid() < 0)
377                                 perror("setsid");
378 #endif  /* SETSID */
379                 } /* if debug < 3 */
380                 /*
381                  * Nuke any timeout value
382                  */
383                 timeout = NULL;
384
385                 /*
386                  * Here, bootpd would do:
387                  *      chdir
388                  *      tzone_init
389                  *      rdtab_init
390                  *      readtab
391                  */
392
393                 /*
394                  * Create a socket.
395                  */
396                 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
397                         report(LOG_ERR, "socket: %s", get_network_errmsg());
398                         exit(1);
399                 }
400                 /*
401                  * Get server's listening port number
402                  */
403                 servp = getservbyname("bootps", "udp");
404                 if (servp) {
405                         bootps_port = ntohs((u_short) servp->s_port);
406                 } else {
407                         bootps_port = (u_short) IPPORT_BOOTPS;
408                         report(LOG_ERR,
409                                    "udp/bootps: unknown service -- assuming port %d",
410                                    bootps_port);
411                 }
412
413                 /*
414                  * Bind socket to BOOTPS port.
415                  */
416                 bind_addr.sin_family = AF_INET;
417                 bind_addr.sin_port = htons(bootps_port);
418                 bind_addr.sin_addr.s_addr = INADDR_ANY;
419                 if (bind(s, (struct sockaddr *) &bind_addr,
420                                  sizeof(bind_addr)) < 0)
421                 {
422                         report(LOG_ERR, "bind: %s", get_network_errmsg());
423                         exit(1);
424                 }
425         } /* if standalone */
426         /*
427          * Get destination port number so we can reply to client
428          */
429         servp = getservbyname("bootpc", "udp");
430         if (servp) {
431                 bootpc_port = ntohs(servp->s_port);
432         } else {
433                 report(LOG_ERR,
434                            "udp/bootpc: unknown service -- assuming port %d",
435                            IPPORT_BOOTPC);
436                 bootpc_port = (u_short) IPPORT_BOOTPC;
437         }
438
439         /* no signal catchers */
440
441         /*
442          * Process incoming requests.
443          */
444         for (;;) {
445                 struct timeval tv;
446
447                 readfds = 1 << s;
448                 if (timeout)
449                         tv = *timeout;
450
451                 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
452                                                 (timeout) ? &tv : NULL);
453                 if (nfound < 0) {
454                         if (errno != EINTR) {
455                                 report(LOG_ERR, "select: %s", get_errmsg());
456                         }
457                         continue;
458                 }
459                 if (!(readfds & (1 << s))) {
460                         report(LOG_INFO, "exiting after %ld minutes of inactivity",
461                                    actualtimeout.tv_sec / 60);
462                         exit(0);
463                 }
464                 ra_len = sizeof(recv_addr);
465                 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
466                                          (struct sockaddr *) &recv_addr, &ra_len);
467                 if (n <= 0) {
468                         continue;
469                 }
470                 if (debug > 3) {
471                         report(LOG_INFO, "recvd pkt from IP addr %s",
472                                    inet_ntoa(recv_addr.sin_addr));
473                 }
474                 if (n < sizeof(struct bootp)) {
475                         if (debug) {
476                                 report(LOG_INFO, "received short packet");
477                         }
478                         continue;
479                 }
480                 pktlen = n;
481
482                 switch (bp->bp_op) {
483                 case BOOTREQUEST:
484                         handle_request();
485                         break;
486                 case BOOTREPLY:
487                         handle_reply();
488                         break;
489                 }
490         }
491         return 0;
492 }
493 \f
494
495
496
497 /*
498  * Print "usage" message and exit
499  */
500
501 static void
502 usage(void)
503 {
504         fprintf(stderr,
505                         "usage:  bootpgw [-d level] [-i] [-s] [-t timeout] server\n");
506         fprintf(stderr, "\t -d n\tset debug level\n");
507         fprintf(stderr, "\t -h n\tset max hop count\n");
508         fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
509         fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
510         fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
511         fprintf(stderr, "\t -w n\tset min wait time (secs)\n");
512         exit(1);
513 }
514 \f
515
516
517 /*
518  * Process BOOTREQUEST packet.
519  *
520  * Note, this just forwards the request to a real server.
521  */
522 static void
523 handle_request(void)
524 {
525         struct bootp *bp = (struct bootp *) pktbuf;
526         u_short secs;
527         u_char hops;
528
529         /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
530
531         if (debug) {
532                 report(LOG_INFO, "request from %s",
533                            inet_ntoa(recv_addr.sin_addr));
534         }
535         /* Has the client been waiting long enough? */
536         secs = ntohs(bp->bp_secs);
537         if (secs < minwait)
538                 return;
539
540         /* Has this packet hopped too many times? */
541         hops = bp->bp_hops;
542         if (++hops > maxhops) {
543                 report(LOG_NOTICE, "reqest from %s reached hop limit",
544                            inet_ntoa(recv_addr.sin_addr));
545                 return;
546         }
547         bp->bp_hops = hops;
548
549         /*
550          * Here one might discard a request from the same subnet as the
551          * real server, but we can assume that the real server will send
552          * a reply to the client before it waits for minwait seconds.
553          */
554
555         /* If gateway address is not set, put in local interface addr. */
556         if (bp->bp_giaddr.s_addr == 0) {
557 #if 0   /* BUG */
558                 struct sockaddr_in *sip;
559                 struct ifreq *ifr;
560                 /*
561                  * XXX - This picks the wrong interface when the receive addr
562                  * is the broadcast address.  There is no  portable way to
563                  * find out which interface a broadcast was received on. -gwr
564                  * (Thanks to <walker@zk3.dec.com> for finding this bug!)
565                  */
566                 ifr = getif(s, &recv_addr.sin_addr);
567                 if (!ifr) {
568                         report(LOG_NOTICE, "no interface for request from %s",
569                                    inet_ntoa(recv_addr.sin_addr));
570                         return;
571                 }
572                 sip = (struct sockaddr_in *) &(ifr->ifr_addr);
573                 bp->bp_giaddr = sip->sin_addr;
574 #else   /* BUG */
575                 /*
576                  * XXX - Just set "giaddr" to our "official" IP address.
577                  * RFC 1532 says giaddr MUST be set to the address of the
578                  * interface on which the request was received.  Setting
579                  * it to our "default" IP address is not strictly correct,
580                  * but is good enough to allow the real BOOTP server to
581                  * get the reply back here.  Then, before we forward the
582                  * reply to the client, the giaddr field is corrected.
583                  * (In case the client uses giaddr, which it should not.)
584                  * See handle_reply()
585                  */
586                 bp->bp_giaddr = my_ip_addr;
587 #endif  /* BUG */
588
589                 /*
590                  * XXX - DHCP says to insert a subnet mask option into the
591                  * options area of the request (if vendor magic == std).
592                  */
593         }
594         /* Set up socket address for send. */
595         send_addr.sin_family = AF_INET;
596         send_addr.sin_port = htons(bootps_port);
597         send_addr.sin_addr.s_addr = server_ipa;
598
599         /* Send reply with same size packet as request used. */
600         if (sendto(s, pktbuf, pktlen, 0,
601                            (struct sockaddr *) &send_addr,
602                            sizeof(send_addr)) < 0)
603         {
604                 report(LOG_ERR, "sendto: %s", get_network_errmsg());
605         }
606 }
607 \f
608
609
610 /*
611  * Process BOOTREPLY packet.
612  */
613 static void
614 handle_reply(void)
615 {
616         struct bootp *bp = (struct bootp *) pktbuf;
617         struct ifreq *ifr;
618         struct sockaddr_in *sip;
619         unsigned char *ha;
620         int len, haf;
621
622         if (debug) {
623                 report(LOG_INFO, "   reply for %s",
624                            inet_ntoa(bp->bp_yiaddr));
625         }
626         /* Make sure client is directly accessible. */
627         ifr = getif(s, &(bp->bp_yiaddr));
628         if (!ifr) {
629                 report(LOG_NOTICE, "no interface for reply to %s",
630                            inet_ntoa(bp->bp_yiaddr));
631                 return;
632         }
633 #if 1   /* Experimental (see BUG above) */
634 /* #ifdef CATER_TO_OLD_CLIENTS ? */
635         /*
636          * The giaddr field has been set to our "default" IP address
637          * which might not be on the same interface as the client.
638          * In case the client looks at giaddr, (which it should not)
639          * giaddr is now set to the address of the correct interface.
640          */
641         sip = (struct sockaddr_in *) &(ifr->ifr_addr);
642         bp->bp_giaddr = sip->sin_addr;
643 #endif
644
645         /* Set up socket address for send to client. */
646         send_addr.sin_family = AF_INET;
647         send_addr.sin_addr = bp->bp_yiaddr;
648         send_addr.sin_port = htons(bootpc_port);
649
650         /* Create an ARP cache entry for the client. */
651         ha = bp->bp_chaddr;
652         len = bp->bp_hlen;
653         if (len > MAXHADDRLEN)
654                 len = MAXHADDRLEN;
655         haf = (int) bp->bp_htype;
656         if (haf == 0)
657                 haf = HTYPE_ETHERNET;
658
659         if (debug > 1)
660                 report(LOG_INFO, "setarp %s - %s",
661                            inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len));
662         setarp(s, &bp->bp_yiaddr, haf, ha, len);
663
664         /* Send reply with same size packet as request used. */
665         if (sendto(s, pktbuf, pktlen, 0,
666                            (struct sockaddr *) &send_addr,
667                            sizeof(send_addr)) < 0)
668         {
669                 report(LOG_ERR, "sendto: %s", get_network_errmsg());
670         }
671 }
672
673 /*
674  * Local Variables:
675  * tab-width: 4
676  * c-indent-level: 4
677  * c-argdecl-indent: 4
678  * c-continued-statement-offset: 4
679  * c-continued-brace-offset: -4
680  * c-label-offset: -4
681  * c-brace-offset: 0
682  * End:
683  */