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 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: bootpgw.c,v 1.13 2007/05/27 16:31:42 tls Exp $");
34 * BOOTPGW is typically used to forward BOOTP client requests from
35 * one subnet to a BOOTP server on a different subnet.
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/socket.h>
41 #include <sys/ioctl.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h> /* inet_ntoa */
66 # include <fcntl.h> /* for O_RDONLY, etc */
73 #include "patchlevel.h"
75 /* Local definitions: */
76 #define MAX_MSG_SIZE (3*512) /* Maximum packet size */
79 #define get_network_errmsg get_errmsg
84 * Externals, forward declarations, and global variables
87 static void usage(void);
88 static void handle_reply(void);
89 static void handle_request(void);
90 int main(int, char **);
93 * IP port numbers for client and server obtained from /etc/services
96 u_short bootps_port
, bootpc_port
;
100 * Internet socket and interface config structures
103 struct sockaddr_in bind_addr
; /* Listening */
104 struct sockaddr_in clnt_addr
; /* client address */
105 struct sockaddr_in serv_addr
; /* server address */
111 int debug
= 0; /* Debugging flag (level) */
112 int actualtimeout
= 15 * 60000; /* fifteen minutes */
113 u_int maxhops
= 4; /* Number of hops allowed for requests. */
114 u_int minwait
= 3; /* Number of seconds client must wait before
115 its bootrequest packets are forwarded. */
121 int s
; /* Socket file descriptor */
122 char *pktbuf
; /* Receive packet buffer */
127 char myhostname
[MAXHOSTNAMELEN
+ 1];
128 struct in_addr my_ip_addr
;
134 * Initialization such as command-line processing is done and then the
135 * main server loop is started.
139 main(int argc
, char **argv
)
143 struct servent
*servp
;
146 socklen_t ba_len
, ra_len
;
149 struct pollfd set
[1];
152 progname
= strrchr(argv
[0], '/');
153 if (progname
) progname
++;
154 else progname
= argv
[0];
157 * Initialize logging.
159 report_init(0); /* uses progname */
164 report(LOG_INFO
, "version %s.%d", VERSION
, PATCHLEVEL
);
166 /* Debugging for compilers with struct padding. */
167 assert(sizeof(struct bootp
) == BP_MINPKTSZ
);
169 /* Get space for receiving packets and composing replies. */
170 pktbuf
= malloc(MAX_MSG_SIZE
);
172 report(LOG_ERR
, "malloc failed");
175 bp
= (struct bootp
*) pktbuf
;
178 * Check to see if a socket was passed to us from inetd.
180 * Use getsockname() to determine if descriptor 0 is indeed a socket
181 * (and thus we are probably a child of inetd) or if it is instead
182 * something else and we are running standalone.
185 ba_len
= sizeof(bind_addr
);
186 bzero((char *) &bind_addr
, ba_len
);
189 if (getsockname(s
, (struct sockaddr
*) &bind_addr
, &ba_len
) == 0) {
191 * Descriptor 0 is a socket. Assume we are a child of inetd.
193 if (bind_addr
.sin_family
== AF_INET
) {
195 bootps_port
= ntohs(bind_addr
.sin_port
);
197 /* Some other type of socket? */
198 report(LOG_INFO
, "getsockname: not an INET socket");
202 * Set defaults that might be changed by option switches.
205 timeout
= actualtimeout
;
206 gethostname(myhostname
, sizeof(myhostname
));
207 myhostname
[sizeof(myhostname
) - 1] = '\0';
208 hep
= gethostbyname(myhostname
);
210 printf("Can not get my IP address\n");
213 bcopy(hep
->h_addr
, (char *)&my_ip_addr
, sizeof(my_ip_addr
));
218 for (argc
--, argv
++; argc
> 0; argc
--, argv
++) {
219 if (argv
[0][0] != '-')
221 switch (argv
[0][1]) {
223 case 'd': /* debug level */
225 stmp
= &(argv
[0][2]);
226 } else if (argv
[1] && argv
[1][0] == '-') {
228 * Backwards-compatible behavior:
229 * no parameter, so just increment the debug flag.
238 if (!stmp
|| (sscanf(stmp
, "%d", &n
) != 1) || (n
< 0)) {
240 "%s: invalid debug level\n", progname
);
246 case 'h': /* hop count limit */
248 stmp
= &(argv
[0][2]);
254 if (!stmp
|| (sscanf(stmp
, "%d", &n
) != 1) ||
258 "bootpgw: invalid hop count limit\n");
264 case 'i': /* inetd mode */
268 case 's': /* standalone mode */
272 case 't': /* timeout */
274 stmp
= &(argv
[0][2]);
280 if (!stmp
|| (sscanf(stmp
, "%d", &n
) != 1) || (n
< 0)) {
282 "%s: invalid timeout specification\n", progname
);
285 actualtimeout
= n
* 60000;
287 * If the actual timeout is zero, pass INFTIM
288 * to poll so it blocks indefinitely, otherwise,
289 * use the actual timeout value.
291 timeout
= (n
> 0) ? actualtimeout
: INFTIM
;
294 case 'w': /* wait time */
296 stmp
= &(argv
[0][2]);
302 if (!stmp
|| (sscanf(stmp
, "%d", &n
) != 1) ||
306 "bootpgw: invalid wait time\n");
313 fprintf(stderr
, "%s: unknown switch: -%c\n",
314 progname
, argv
[0][1]);
321 /* Make sure server name argument is suplied. */
322 servername
= argv
[0];
324 fprintf(stderr
, "bootpgw: missing server name\n");
328 * Get address of real bootp server.
330 if (inet_aton(servername
, &serv_addr
.sin_addr
) == 0) {
331 hep
= gethostbyname(servername
);
333 fprintf(stderr
, "bootpgw: can't get addr for %s\n", servername
);
336 memcpy(&serv_addr
.sin_addr
, hep
->h_addr
,
337 sizeof(serv_addr
.sin_addr
));
342 * Go into background and disassociate from controlling terminal.
343 * XXX - This is not the POSIX way (Should use setsid). -gwr
351 n
= open("/dev/tty", O_RDWR
);
353 ioctl(n
, TIOCNOTTY
, (char *) 0);
356 #endif /* TIOCNOTTY */
363 * Nuke any timeout value
368 * Here, bootpd would do:
378 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) {
379 report(LOG_ERR
, "socket: %s", get_network_errmsg());
383 * Get server's listening port number
385 servp
= getservbyname("bootps", "udp");
387 bootps_port
= ntohs((u_short
) servp
->s_port
);
389 bootps_port
= (u_short
) IPPORT_BOOTPS
;
391 "udp/bootps: unknown service -- assuming port %d",
396 * Bind socket to BOOTPS port.
398 bind_addr
.sin_family
= AF_INET
;
399 bind_addr
.sin_port
= htons(bootps_port
);
400 bind_addr
.sin_addr
.s_addr
= INADDR_ANY
;
401 if (bind(s
, (struct sockaddr
*) &bind_addr
,
402 sizeof(bind_addr
)) < 0)
404 report(LOG_ERR
, "bind: %s", get_network_errmsg());
407 } /* if standalone */
409 * Get destination port number so we can reply to client
411 servp
= getservbyname("bootpc", "udp");
413 bootpc_port
= ntohs(servp
->s_port
);
416 "udp/bootpc: unknown service -- assuming port %d",
418 bootpc_port
= (u_short
) IPPORT_BOOTPC
;
421 /* no signal catchers */
424 * Process incoming requests.
427 set
[0].events
= POLLIN
;
429 nfound
= poll(set
, 1, timeout
);
431 if (errno
!= EINTR
) {
432 report(LOG_ERR
, "poll: %s", get_errmsg());
437 report(LOG_INFO
, "exiting after %d minute%s of inactivity",
438 actualtimeout
/ 60000,
439 actualtimeout
== 60000 ? "" : "s");
442 ra_len
= sizeof(clnt_addr
);
443 n
= recvfrom(s
, pktbuf
, MAX_MSG_SIZE
, 0,
444 (struct sockaddr
*) &clnt_addr
, &ra_len
);
449 report(LOG_INFO
, "recvd pkt from IP addr %s",
450 inet_ntoa(clnt_addr
.sin_addr
));
452 if (n
< (int)sizeof(struct bootp
)) {
454 report(LOG_INFO
, "received short packet");
475 * Print "usage" message and exit
482 "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n");
483 fprintf(stderr
, "\t -d n\tset debug level\n");
484 fprintf(stderr
, "\t -h n\tset max hop count\n");
485 fprintf(stderr
, "\t -i\tforce inetd mode (run as child of inetd)\n");
486 fprintf(stderr
, "\t -s\tforce standalone mode (run without inetd)\n");
487 fprintf(stderr
, "\t -t n\tset inetd exit timeout to n minutes\n");
488 fprintf(stderr
, "\t -w n\tset min wait time (secs)\n");
495 * Process BOOTREQUEST packet.
497 * Note, this just forwards the request to a real server.
502 struct bootp
*bp
= (struct bootp
*) pktbuf
;
508 /* XXX - SLIP init: Set bp_ciaddr = clnt_addr here? */
511 report(LOG_INFO
, "request from %s",
512 inet_ntoa(clnt_addr
.sin_addr
));
514 /* Has the client been waiting long enough? */
515 secs
= ntohs(bp
->bp_secs
);
519 /* Has this packet hopped too many times? */
520 hops
= ntohs(bp
->bp_hops
);
521 if (++hops
> maxhops
) {
522 report(LOG_NOTICE
, "request from %s reached hop limit",
523 inet_ntoa(clnt_addr
.sin_addr
));
526 bp
->bp_hops
= htons(hops
);
529 * Here one might discard a request from the same subnet as the
530 * real server, but we can assume that the real server will send
531 * a reply to the client before it waits for minwait seconds.
534 /* If gateway address is not set, put in local interface addr. */
535 if (bp
->bp_giaddr
.s_addr
== 0) {
537 struct sockaddr_in
*sip
;
539 * XXX - This picks the wrong interface when the receive addr
540 * is the broadcast address. There is no portable way to
541 * find out which interface a broadcast was received on. -gwr
542 * (Thanks to <walker@zk3.dec.com> for finding this bug!)
544 ifr
= getif(s
, &clnt_addr
.sin_addr
);
546 report(LOG_NOTICE
, "no interface for request from %s",
547 inet_ntoa(clnt_addr
.sin_addr
));
550 sip
= (struct sockaddr_in
*) &(ifr
->ifr_addr
);
551 bp
->bp_giaddr
= sip
->sin_addr
;
554 * XXX - Just set "giaddr" to our "official" IP address.
555 * RFC 1532 says giaddr MUST be set to the address of the
556 * interface on which the request was received. Setting
557 * it to our "default" IP address is not strictly correct,
558 * but is good enough to allow the real BOOTP server to
559 * get the reply back here. Then, before we forward the
560 * reply to the client, the giaddr field is corrected.
561 * (In case the client uses giaddr, which it should not.)
564 bp
->bp_giaddr
= my_ip_addr
;
568 * XXX - DHCP says to insert a subnet mask option into the
569 * options area of the request (if vendor magic == std).
572 /* Set up socket address for send. */
573 serv_addr
.sin_family
= AF_INET
;
574 serv_addr
.sin_port
= htons(bootps_port
);
576 /* Send reply with same size packet as request used. */
577 if (sendto(s
, pktbuf
, pktlen
, 0,
578 (struct sockaddr
*) &serv_addr
,
579 sizeof(serv_addr
)) < 0)
581 report(LOG_ERR
, "sendto: %s", get_network_errmsg());
588 * Process BOOTREPLY packet.
593 struct bootp
*bp
= (struct bootp
*) pktbuf
;
595 struct sockaddr_in
*sip
;
596 u_char canon_haddr
[MAXHADDRLEN
];
601 report(LOG_INFO
, " reply for %s",
602 inet_ntoa(bp
->bp_yiaddr
));
604 /* Make sure client is directly accessible. */
605 ifr
= getif(s
, &(bp
->bp_yiaddr
));
607 report(LOG_NOTICE
, "no interface for reply to %s",
608 inet_ntoa(bp
->bp_yiaddr
));
611 #if 1 /* Experimental (see BUG above) */
612 /* #ifdef CATER_TO_OLD_CLIENTS ? */
614 * The giaddr field has been set to our "default" IP address
615 * which might not be on the same interface as the client.
616 * In case the client looks at giaddr, (which it should not)
617 * giaddr is now set to the address of the correct interface.
619 sip
= (struct sockaddr_in
*) &(ifr
->ifr_addr
);
620 bp
->bp_giaddr
= sip
->sin_addr
;
623 /* Set up socket address for send to client. */
624 clnt_addr
.sin_family
= AF_INET
;
625 clnt_addr
.sin_addr
= bp
->bp_yiaddr
;
626 clnt_addr
.sin_port
= htons(bootpc_port
);
628 /* Create an ARP cache entry for the client. */
631 if (len
> MAXHADDRLEN
)
633 if (bp
->bp_htype
== HTYPE_IEEE802
) {
634 haddr_conv802(ha
, canon_haddr
, len
);
638 report(LOG_INFO
, "setarp %s - %s",
639 inet_ntoa(bp
->bp_yiaddr
), haddrtoa(ha
, len
));
640 setarp(s
, &bp
->bp_yiaddr
, ha
, len
);
642 /* Send reply with same size packet as request used. */
643 if (sendto(s
, pktbuf
, pktlen
, 0,
644 (struct sockaddr
*) &clnt_addr
,
645 sizeof(clnt_addr
)) < 0)
647 report(LOG_ERR
, "sendto: %s", get_network_errmsg());
655 * c-argdecl-indent: 4
656 * c-continued-statement-offset: 4
657 * c-continued-brace-offset: -4