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