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