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