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