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