2 * bootpgw.c - BOOTP GateWay
3 * This program forwards BOOTP Request packets to a BOOTP server.
6 /************************************************************************
7 Copyright 1988, 1991 by Carnegie Mellon University
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.
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
26 ************************************************************************/
28 /* $FreeBSD: src/libexec/bootpd/bootpgw/bootpgw.c,v 1.3.2.1 2000/12/11 01:03:21 obrien Exp $ */
29 /* $DragonFly: src/libexec/bootpd/bootpgw/bootpgw.c,v 1.2 2003/06/17 04:27:07 dillon Exp $ */
32 * BOOTPGW is typically used to forward BOOTP client requests from
33 * one subnet to a BOOTP server on a different subnet.
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/socket.h>
39 #include <sys/ioctl.h>
43 #include <sys/utsname.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h> /* inet_ntoa */
65 # include <fcntl.h> /* for O_RDONLY, etc */
70 /* Yes, memcpy is OK here (no overlapped copies). */
71 # define bcopy(a,b,c) memcpy(b,a,c)
72 # define bzero(p,l) memset(p,0,l)
73 # define bcmp(a,b,c) memcmp(a,b,c)
80 #include "patchlevel.h"
82 /* Local definitions: */
83 #define MAX_MSG_SIZE (3*512) /* Maximum packet size */
86 #define get_network_errmsg get_errmsg
91 * Externals, forward declarations, and global variables
100 static void usage
P((void));
101 static void handle_reply
P((void));
102 static void handle_request
P((void));
107 * IP port numbers for client and server obtained from /etc/services
110 u_short bootps_port
, bootpc_port
;
114 * Internet socket and interface config structures
117 struct sockaddr_in bind_addr
; /* Listening */
118 struct sockaddr_in recv_addr
; /* Packet source */
119 struct sockaddr_in send_addr
; /* destination */
125 int debug
= 0; /* Debugging flag (level) */
126 struct timeval actualtimeout
=
127 { /* fifteen minutes */
128 15 * 60L, /* tv_sec */
131 u_char maxhops
= 4; /* Number of hops allowed for requests. */
132 u_int minwait
= 3; /* Number of seconds client must wait before
133 its bootrequest packets are forwarded. */
139 int s
; /* Socket file descriptor */
140 char *pktbuf
; /* Receive packet buffer */
144 int32 server_ipa
; /* Real server IP address, network order. */
146 struct in_addr my_ip_addr
;
148 struct utsname my_uname
;
156 * Initialization such as command-line processing is done and then the
157 * main server loop is started.
165 struct timeval
*timeout
;
167 struct servent
*servp
;
170 int n
, ba_len
, ra_len
;
174 progname
= strrchr(argv
[0], '/');
175 if (progname
) progname
++;
176 else progname
= argv
[0];
179 * Initialize logging.
181 report_init(0); /* uses progname */
186 report(LOG_INFO
, "version %s.%d", VERSION
, PATCHLEVEL
);
188 /* Debugging for compilers with struct padding. */
189 assert(sizeof(struct bootp
) == BP_MINPKTSZ
);
191 /* Get space for receiving packets and composing replies. */
192 pktbuf
= malloc(MAX_MSG_SIZE
);
194 report(LOG_ERR
, "malloc failed");
197 bp
= (struct bootp
*) pktbuf
;
200 * Check to see if a socket was passed to us from inetd.
202 * Use getsockname() to determine if descriptor 0 is indeed a socket
203 * (and thus we are probably a child of inetd) or if it is instead
204 * something else and we are running standalone.
207 ba_len
= sizeof(bind_addr
);
208 bzero((char *) &bind_addr
, ba_len
);
211 if (getsockname(s
, (struct sockaddr
*) &bind_addr
, &ba_len
) == 0) {
213 * Descriptor 0 is a socket. Assume we are a child of inetd.
215 if (bind_addr
.sin_family
== AF_INET
) {
217 bootps_port
= ntohs(bind_addr
.sin_port
);
219 /* Some other type of socket? */
220 report(LOG_INFO
, "getsockname: not an INET socket");
224 * Set defaults that might be changed by option switches.
227 timeout
= &actualtimeout
;
229 if (uname(&my_uname
) < 0) {
230 fprintf(stderr
, "bootpgw: can't get hostname\n");
233 hostname
= my_uname
.nodename
;
235 hep
= gethostbyname(hostname
);
237 printf("Can not get my IP address\n");
240 bcopy(hep
->h_addr
, (char *)&my_ip_addr
, sizeof(my_ip_addr
));
245 for (argc
--, argv
++; argc
> 0; argc
--, argv
++) {
246 if (argv
[0][0] != '-')
248 switch (argv
[0][1]) {
250 case 'd': /* debug level */
252 stmp
= &(argv
[0][2]);
253 } else if (argv
[1] && argv
[1][0] == '-') {
255 * Backwards-compatible behavior:
256 * no parameter, so just increment the debug flag.
265 if (!stmp
|| (sscanf(stmp
, "%d", &n
) != 1) || (n
< 0)) {
267 "%s: invalid debug level\n", progname
);
273 case 'h': /* hop count limit */
275 stmp
= &(argv
[0][2]);
281 if (!stmp
|| (sscanf(stmp
, "%d", &n
) != 1) ||
285 "bootpgw: invalid hop count limit\n");
291 case 'i': /* inetd mode */
295 case 's': /* standalone mode */
299 case 't': /* timeout */
301 stmp
= &(argv
[0][2]);
307 if (!stmp
|| (sscanf(stmp
, "%d", &n
) != 1) || (n
< 0)) {
309 "%s: invalid timeout specification\n", progname
);
312 actualtimeout
.tv_sec
= (int32
) (60 * n
);
314 * If the actual timeout is zero, pass a NULL pointer
315 * to select so it blocks indefinitely, otherwise,
316 * point to the actual timeout value.
318 timeout
= (n
> 0) ? &actualtimeout
: NULL
;
321 case 'w': /* wait time */
323 stmp
= &(argv
[0][2]);
329 if (!stmp
|| (sscanf(stmp
, "%d", &n
) != 1) ||
333 "bootpgw: invalid wait time\n");
340 fprintf(stderr
, "%s: unknown switch: -%c\n",
341 progname
, argv
[0][1]);
348 /* Make sure server name argument is suplied. */
349 servername
= argv
[0];
351 fprintf(stderr
, "bootpgw: missing server name\n");
355 * Get address of real bootp server.
357 if (isdigit(servername
[0]))
358 server_ipa
= inet_addr(servername
);
360 hep
= gethostbyname(servername
);
362 fprintf(stderr
, "bootpgw: can't get addr for %s\n", servername
);
365 bcopy(hep
->h_addr
, (char *)&server_ipa
, sizeof(server_ipa
));
370 * Go into background and disassociate from controlling terminal.
371 * XXX - This is not the POSIX way (Should use setsid). -gwr
379 n
= open(_PATH_TTY
, O_RDWR
);
381 ioctl(n
, TIOCNOTTY
, (char *) 0);
384 #endif /* TIOCNOTTY */
391 * Nuke any timeout value
396 * Here, bootpd would do:
406 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) {
407 report(LOG_ERR
, "socket: %s", get_network_errmsg());
411 * Get server's listening port number
413 servp
= getservbyname("bootps", "udp");
415 bootps_port
= ntohs((u_short
) servp
->s_port
);
417 bootps_port
= (u_short
) IPPORT_BOOTPS
;
419 "udp/bootps: unknown service -- assuming port %d",
424 * Bind socket to BOOTPS port.
426 bind_addr
.sin_family
= AF_INET
;
427 bind_addr
.sin_port
= htons(bootps_port
);
428 bind_addr
.sin_addr
.s_addr
= INADDR_ANY
;
429 if (bind(s
, (struct sockaddr
*) &bind_addr
,
430 sizeof(bind_addr
)) < 0)
432 report(LOG_ERR
, "bind: %s", get_network_errmsg());
435 } /* if standalone */
437 * Get destination port number so we can reply to client
439 servp
= getservbyname("bootpc", "udp");
441 bootpc_port
= ntohs(servp
->s_port
);
444 "udp/bootpc: unknown service -- assuming port %d",
446 bootpc_port
= (u_short
) IPPORT_BOOTPC
;
449 /* no signal catchers */
452 * Process incoming requests.
461 nfound
= select(s
+ 1, (fd_set
*)&readfds
, NULL
, NULL
,
462 (timeout
) ? &tv
: NULL
);
464 if (errno
!= EINTR
) {
465 report(LOG_ERR
, "select: %s", get_errmsg());
469 if (!(readfds
& (1 << s
))) {
470 report(LOG_INFO
, "exiting after %ld minutes of inactivity",
471 actualtimeout
.tv_sec
/ 60);
474 ra_len
= sizeof(recv_addr
);
475 n
= recvfrom(s
, pktbuf
, MAX_MSG_SIZE
, 0,
476 (struct sockaddr
*) &recv_addr
, &ra_len
);
481 report(LOG_INFO
, "recvd pkt from IP addr %s",
482 inet_ntoa(recv_addr
.sin_addr
));
484 if (n
< sizeof(struct bootp
)) {
486 report(LOG_INFO
, "received short packet");
508 * Print "usage" message and exit
515 "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n");
516 fprintf(stderr
, "\t -d n\tset debug level\n");
517 fprintf(stderr
, "\t -h n\tset max hop count\n");
518 fprintf(stderr
, "\t -i\tforce inetd mode (run as child of inetd)\n");
519 fprintf(stderr
, "\t -s\tforce standalone mode (run without inetd)\n");
520 fprintf(stderr
, "\t -t n\tset inetd exit timeout to n minutes\n");
521 fprintf(stderr
, "\t -w n\tset min wait time (secs)\n");
528 * Process BOOTREQUEST packet.
530 * Note, this just forwards the request to a real server.
535 struct bootp
*bp
= (struct bootp
*) pktbuf
;
539 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
542 report(LOG_INFO
, "request from %s",
543 inet_ntoa(recv_addr
.sin_addr
));
545 /* Has the client been waiting long enough? */
546 secs
= ntohs(bp
->bp_secs
);
550 /* Has this packet hopped too many times? */
552 if (++hops
> maxhops
) {
553 report(LOG_NOTICE
, "reqest from %s reached hop limit",
554 inet_ntoa(recv_addr
.sin_addr
));
560 * Here one might discard a request from the same subnet as the
561 * real server, but we can assume that the real server will send
562 * a reply to the client before it waits for minwait seconds.
565 /* If gateway address is not set, put in local interface addr. */
566 if (bp
->bp_giaddr
.s_addr
== 0) {
568 struct sockaddr_in
*sip
;
571 * XXX - This picks the wrong interface when the receive addr
572 * is the broadcast address. There is no portable way to
573 * find out which interface a broadcast was received on. -gwr
574 * (Thanks to <walker@zk3.dec.com> for finding this bug!)
576 ifr
= getif(s
, &recv_addr
.sin_addr
);
578 report(LOG_NOTICE
, "no interface for request from %s",
579 inet_ntoa(recv_addr
.sin_addr
));
582 sip
= (struct sockaddr_in
*) &(ifr
->ifr_addr
);
583 bp
->bp_giaddr
= sip
->sin_addr
;
586 * XXX - Just set "giaddr" to our "official" IP address.
587 * RFC 1532 says giaddr MUST be set to the address of the
588 * interface on which the request was received. Setting
589 * it to our "default" IP address is not strictly correct,
590 * but is good enough to allow the real BOOTP server to
591 * get the reply back here. Then, before we forward the
592 * reply to the client, the giaddr field is corrected.
593 * (In case the client uses giaddr, which it should not.)
596 bp
->bp_giaddr
= my_ip_addr
;
600 * XXX - DHCP says to insert a subnet mask option into the
601 * options area of the request (if vendor magic == std).
604 /* Set up socket address for send. */
605 send_addr
.sin_family
= AF_INET
;
606 send_addr
.sin_port
= htons(bootps_port
);
607 send_addr
.sin_addr
.s_addr
= server_ipa
;
609 /* Send reply with same size packet as request used. */
610 if (sendto(s
, pktbuf
, pktlen
, 0,
611 (struct sockaddr
*) &send_addr
,
612 sizeof(send_addr
)) < 0)
614 report(LOG_ERR
, "sendto: %s", get_network_errmsg());
621 * Process BOOTREPLY packet.
626 struct bootp
*bp
= (struct bootp
*) pktbuf
;
628 struct sockaddr_in
*sip
;
633 report(LOG_INFO
, " reply for %s",
634 inet_ntoa(bp
->bp_yiaddr
));
636 /* Make sure client is directly accessible. */
637 ifr
= getif(s
, &(bp
->bp_yiaddr
));
639 report(LOG_NOTICE
, "no interface for reply to %s",
640 inet_ntoa(bp
->bp_yiaddr
));
643 #if 1 /* Experimental (see BUG above) */
644 /* #ifdef CATER_TO_OLD_CLIENTS ? */
646 * The giaddr field has been set to our "default" IP address
647 * which might not be on the same interface as the client.
648 * In case the client looks at giaddr, (which it should not)
649 * giaddr is now set to the address of the correct interface.
651 sip
= (struct sockaddr_in
*) &(ifr
->ifr_addr
);
652 bp
->bp_giaddr
= sip
->sin_addr
;
655 /* Set up socket address for send to client. */
656 send_addr
.sin_family
= AF_INET
;
657 send_addr
.sin_addr
= bp
->bp_yiaddr
;
658 send_addr
.sin_port
= htons(bootpc_port
);
660 /* Create an ARP cache entry for the client. */
663 if (len
> MAXHADDRLEN
)
665 haf
= (int) bp
->bp_htype
;
667 haf
= HTYPE_ETHERNET
;
670 report(LOG_INFO
, "setarp %s - %s",
671 inet_ntoa(bp
->bp_yiaddr
), haddrtoa(ha
, len
));
672 setarp(s
, &bp
->bp_yiaddr
, haf
, ha
, len
);
674 /* Send reply with same size packet as request used. */
675 if (sendto(s
, pktbuf
, pktlen
, 0,
676 (struct sockaddr
*) &send_addr
,
677 sizeof(send_addr
)) < 0)
679 report(LOG_ERR
, "sendto: %s", get_network_errmsg());
687 * c-argdecl-indent: 4
688 * c-continued-statement-offset: 4
689 * c-continued-brace-offset: -4