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