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
94 static void usage(void);
95 static void handle_reply(void);
96 static void handle_request(void);
99 * IP port numbers for client and server obtained from /etc/services
102 u_short bootps_port
, bootpc_port
;
106 * Internet socket and interface config structures
109 struct sockaddr_in bind_addr
; /* Listening */
110 struct sockaddr_in recv_addr
; /* Packet source */
111 struct sockaddr_in send_addr
; /* destination */
117 int debug
= 0; /* Debugging flag (level) */
118 struct timeval actualtimeout
=
119 { /* fifteen minutes */
120 15 * 60L, /* tv_sec */
123 u_char maxhops
= 4; /* Number of hops allowed for requests. */
124 u_int minwait
= 3; /* Number of seconds client must wait before
125 its bootrequest packets are forwarded. */
131 int s
; /* Socket file descriptor */
132 char *pktbuf
; /* Receive packet buffer */
136 int32 server_ipa
; /* Real server IP address, network order. */
138 struct in_addr my_ip_addr
;
140 struct utsname my_uname
;
148 * Initialization such as command-line processing is done and then the
149 * main server loop is started.
153 main(int argc
, char **argv
)
155 struct timeval
*timeout
;
157 struct servent
*servp
;
160 int n
, ba_len
, ra_len
;
164 progname
= strrchr(argv
[0], '/');
165 if (progname
) progname
++;
166 else progname
= argv
[0];
169 * Initialize logging.
171 report_init(0); /* uses progname */
176 report(LOG_INFO
, "version %s.%d", VERSION
, PATCHLEVEL
);
178 /* Debugging for compilers with struct padding. */
179 assert(sizeof(struct bootp
) == BP_MINPKTSZ
);
181 /* Get space for receiving packets and composing replies. */
182 pktbuf
= malloc(MAX_MSG_SIZE
);
184 report(LOG_ERR
, "malloc failed");
187 bp
= (struct bootp
*) pktbuf
;
190 * Check to see if a socket was passed to us from inetd.
192 * Use getsockname() to determine if descriptor 0 is indeed a socket
193 * (and thus we are probably a child of inetd) or if it is instead
194 * something else and we are running standalone.
197 ba_len
= sizeof(bind_addr
);
198 bzero((char *) &bind_addr
, ba_len
);
201 if (getsockname(s
, (struct sockaddr
*) &bind_addr
, &ba_len
) == 0) {
203 * Descriptor 0 is a socket. Assume we are a child of inetd.
205 if (bind_addr
.sin_family
== AF_INET
) {
207 bootps_port
= ntohs(bind_addr
.sin_port
);
209 /* Some other type of socket? */
210 report(LOG_INFO
, "getsockname: not an INET socket");
214 * Set defaults that might be changed by option switches.
217 timeout
= &actualtimeout
;
219 if (uname(&my_uname
) < 0) {
220 fprintf(stderr
, "bootpgw: can't get hostname\n");
223 hostname
= my_uname
.nodename
;
225 hep
= gethostbyname(hostname
);
227 printf("Can not get my IP address\n");
230 bcopy(hep
->h_addr
, (char *)&my_ip_addr
, sizeof(my_ip_addr
));
235 for (argc
--, argv
++; argc
> 0; argc
--, argv
++) {
236 if (argv
[0][0] != '-')
238 switch (argv
[0][1]) {
240 case 'd': /* debug level */
242 stmp
= &(argv
[0][2]);
243 } else if (argv
[1] && argv
[1][0] == '-') {
245 * Backwards-compatible behavior:
246 * no parameter, so just increment the debug flag.
255 if (!stmp
|| (sscanf(stmp
, "%d", &n
) != 1) || (n
< 0)) {
257 "%s: invalid debug level\n", progname
);
263 case 'h': /* hop count limit */
265 stmp
= &(argv
[0][2]);
271 if (!stmp
|| (sscanf(stmp
, "%d", &n
) != 1) ||
275 "bootpgw: invalid hop count limit\n");
281 case 'i': /* inetd mode */
285 case 's': /* standalone mode */
289 case 't': /* timeout */
291 stmp
= &(argv
[0][2]);
297 if (!stmp
|| (sscanf(stmp
, "%d", &n
) != 1) || (n
< 0)) {
299 "%s: invalid timeout specification\n", progname
);
302 actualtimeout
.tv_sec
= (int32
) (60 * n
);
304 * If the actual timeout is zero, pass a NULL pointer
305 * to select so it blocks indefinitely, otherwise,
306 * point to the actual timeout value.
308 timeout
= (n
> 0) ? &actualtimeout
: NULL
;
311 case 'w': /* wait time */
313 stmp
= &(argv
[0][2]);
319 if (!stmp
|| (sscanf(stmp
, "%d", &n
) != 1) ||
323 "bootpgw: invalid wait time\n");
330 fprintf(stderr
, "%s: unknown switch: -%c\n",
331 progname
, argv
[0][1]);
338 /* Make sure server name argument is suplied. */
339 servername
= argv
[0];
341 fprintf(stderr
, "bootpgw: missing server name\n");
345 * Get address of real bootp server.
347 if (isdigit(servername
[0]))
348 server_ipa
= inet_addr(servername
);
350 hep
= gethostbyname(servername
);
352 fprintf(stderr
, "bootpgw: can't get addr for %s\n", servername
);
355 bcopy(hep
->h_addr
, (char *)&server_ipa
, sizeof(server_ipa
));
360 * Go into background and disassociate from controlling terminal.
361 * XXX - This is not the POSIX way (Should use setsid). -gwr
369 n
= open(_PATH_TTY
, O_RDWR
);
371 ioctl(n
, TIOCNOTTY
, NULL
);
374 #endif /* TIOCNOTTY */
381 * Nuke any timeout value
386 * Here, bootpd would do:
396 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) {
397 report(LOG_ERR
, "socket: %s", get_network_errmsg());
401 * Get server's listening port number
403 servp
= getservbyname("bootps", "udp");
405 bootps_port
= ntohs((u_short
) servp
->s_port
);
407 bootps_port
= (u_short
) IPPORT_BOOTPS
;
409 "udp/bootps: unknown service -- assuming port %d",
414 * Bind socket to BOOTPS port.
416 bind_addr
.sin_family
= AF_INET
;
417 bind_addr
.sin_port
= htons(bootps_port
);
418 bind_addr
.sin_addr
.s_addr
= INADDR_ANY
;
419 if (bind(s
, (struct sockaddr
*) &bind_addr
,
420 sizeof(bind_addr
)) < 0)
422 report(LOG_ERR
, "bind: %s", get_network_errmsg());
425 } /* if standalone */
427 * Get destination port number so we can reply to client
429 servp
= getservbyname("bootpc", "udp");
431 bootpc_port
= ntohs(servp
->s_port
);
434 "udp/bootpc: unknown service -- assuming port %d",
436 bootpc_port
= (u_short
) IPPORT_BOOTPC
;
439 /* no signal catchers */
442 * Process incoming requests.
451 nfound
= select(s
+ 1, (fd_set
*)&readfds
, NULL
, NULL
,
452 (timeout
) ? &tv
: NULL
);
454 if (errno
!= EINTR
) {
455 report(LOG_ERR
, "select: %s", get_errmsg());
459 if (!(readfds
& (1 << s
))) {
460 report(LOG_INFO
, "exiting after %ld minutes of inactivity",
461 actualtimeout
.tv_sec
/ 60);
464 ra_len
= sizeof(recv_addr
);
465 n
= recvfrom(s
, pktbuf
, MAX_MSG_SIZE
, 0,
466 (struct sockaddr
*) &recv_addr
, &ra_len
);
471 report(LOG_INFO
, "recvd pkt from IP addr %s",
472 inet_ntoa(recv_addr
.sin_addr
));
474 if (n
< sizeof(struct bootp
)) {
476 report(LOG_INFO
, "received short packet");
498 * Print "usage" message and exit
505 "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\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");
518 * Process BOOTREQUEST packet.
520 * Note, this just forwards the request to a real server.
525 struct bootp
*bp
= (struct bootp
*) pktbuf
;
529 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
532 report(LOG_INFO
, "request from %s",
533 inet_ntoa(recv_addr
.sin_addr
));
535 /* Has the client been waiting long enough? */
536 secs
= ntohs(bp
->bp_secs
);
540 /* Has this packet hopped too many times? */
542 if (++hops
> maxhops
) {
543 report(LOG_NOTICE
, "reqest from %s reached hop limit",
544 inet_ntoa(recv_addr
.sin_addr
));
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.
555 /* If gateway address is not set, put in local interface addr. */
556 if (bp
->bp_giaddr
.s_addr
== 0) {
558 struct sockaddr_in
*sip
;
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!)
566 ifr
= getif(s
, &recv_addr
.sin_addr
);
568 report(LOG_NOTICE
, "no interface for request from %s",
569 inet_ntoa(recv_addr
.sin_addr
));
572 sip
= (struct sockaddr_in
*) &(ifr
->ifr_addr
);
573 bp
->bp_giaddr
= sip
->sin_addr
;
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.)
586 bp
->bp_giaddr
= my_ip_addr
;
590 * XXX - DHCP says to insert a subnet mask option into the
591 * options area of the request (if vendor magic == std).
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
;
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)
604 report(LOG_ERR
, "sendto: %s", get_network_errmsg());
611 * Process BOOTREPLY packet.
616 struct bootp
*bp
= (struct bootp
*) pktbuf
;
618 struct sockaddr_in
*sip
;
623 report(LOG_INFO
, " reply for %s",
624 inet_ntoa(bp
->bp_yiaddr
));
626 /* Make sure client is directly accessible. */
627 ifr
= getif(s
, &(bp
->bp_yiaddr
));
629 report(LOG_NOTICE
, "no interface for reply to %s",
630 inet_ntoa(bp
->bp_yiaddr
));
633 #if 1 /* Experimental (see BUG above) */
634 /* #ifdef CATER_TO_OLD_CLIENTS ? */
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.
641 sip
= (struct sockaddr_in
*) &(ifr
->ifr_addr
);
642 bp
->bp_giaddr
= sip
->sin_addr
;
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
);
650 /* Create an ARP cache entry for the client. */
653 if (len
> MAXHADDRLEN
)
655 haf
= (int) bp
->bp_htype
;
657 haf
= HTYPE_ETHERNET
;
660 report(LOG_INFO
, "setarp %s - %s",
661 inet_ntoa(bp
->bp_yiaddr
), haddrtoa(ha
, len
));
662 setarp(s
, &bp
->bp_yiaddr
, haf
, ha
, len
);
664 /* Send reply with same size packet as request used. */
665 if (sendto(s
, pktbuf
, pktlen
, 0,
666 (struct sockaddr
*) &send_addr
,
667 sizeof(send_addr
)) < 0)
669 report(LOG_ERR
, "sendto: %s", get_network_errmsg());
677 * c-argdecl-indent: 4
678 * c-continued-statement-offset: 4
679 * c-continued-brace-offset: -4