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 $ */
31 * BOOTPGW is typically used to forward BOOTP client requests from
32 * one subnet to a BOOTP server on a different subnet.
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/socket.h>
38 #include <sys/ioctl.h>
42 #include <sys/utsname.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h> /* inet_ntoa */
64 # include <fcntl.h> /* for O_RDONLY, etc */
69 /* Yes, memcpy is OK here (no overlapped copies). */
70 # define bcopy(a,b,c) memcpy(b,a,c)
71 # define bzero(p,l) memset(p,0,l)
72 # define bcmp(a,b,c) memcmp(a,b,c)
79 #include "patchlevel.h"
81 /* Local definitions: */
82 #define MAX_MSG_SIZE (3*512) /* Maximum packet size */
85 #define get_network_errmsg get_errmsg
90 * Externals, forward declarations, and global variables
99 static void usage P((void));
100 static void handle_reply P((void));
101 static void handle_request P((void));
106 * IP port numbers for client and server obtained from /etc/services
109 u_short bootps_port, bootpc_port;
113 * Internet socket and interface config structures
116 struct sockaddr_in bind_addr; /* Listening */
117 struct sockaddr_in recv_addr; /* Packet source */
118 struct sockaddr_in send_addr; /* destination */
124 int debug = 0; /* Debugging flag (level) */
125 struct timeval actualtimeout =
126 { /* fifteen minutes */
127 15 * 60L, /* tv_sec */
130 u_char maxhops = 4; /* Number of hops allowed for requests. */
131 u_int minwait = 3; /* Number of seconds client must wait before
132 its bootrequest packets are forwarded. */
138 int s; /* Socket file descriptor */
139 char *pktbuf; /* Receive packet buffer */
143 int32 server_ipa; /* Real server IP address, network order. */
145 struct in_addr my_ip_addr;
147 struct utsname my_uname;
155 * Initialization such as command-line processing is done and then the
156 * main server loop is started.
164 struct timeval *timeout;
166 struct servent *servp;
169 int n, ba_len, ra_len;
173 progname = strrchr(argv[0], '/');
174 if (progname) progname++;
175 else progname = argv[0];
178 * Initialize logging.
180 report_init(0); /* uses progname */
185 report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
187 /* Debugging for compilers with struct padding. */
188 assert(sizeof(struct bootp) == BP_MINPKTSZ);
190 /* Get space for receiving packets and composing replies. */
191 pktbuf = malloc(MAX_MSG_SIZE);
193 report(LOG_ERR, "malloc failed");
196 bp = (struct bootp *) pktbuf;
199 * Check to see if a socket was passed to us from inetd.
201 * Use getsockname() to determine if descriptor 0 is indeed a socket
202 * (and thus we are probably a child of inetd) or if it is instead
203 * something else and we are running standalone.
206 ba_len = sizeof(bind_addr);
207 bzero((char *) &bind_addr, ba_len);
210 if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
212 * Descriptor 0 is a socket. Assume we are a child of inetd.
214 if (bind_addr.sin_family == AF_INET) {
216 bootps_port = ntohs(bind_addr.sin_port);
218 /* Some other type of socket? */
219 report(LOG_INFO, "getsockname: not an INET socket");
223 * Set defaults that might be changed by option switches.
226 timeout = &actualtimeout;
228 if (uname(&my_uname) < 0) {
229 fprintf(stderr, "bootpgw: can't get hostname\n");
232 hostname = my_uname.nodename;
234 hep = gethostbyname(hostname);
236 printf("Can not get my IP address\n");
239 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
244 for (argc--, argv++; argc > 0; argc--, argv++) {
245 if (argv[0][0] != '-')
247 switch (argv[0][1]) {
249 case 'd': /* debug level */
251 stmp = &(argv[0][2]);
252 } else if (argv[1] && argv[1][0] == '-') {
254 * Backwards-compatible behavior:
255 * no parameter, so just increment the debug flag.
264 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
266 "%s: invalid debug level\n", progname);
272 case 'h': /* hop count limit */
274 stmp = &(argv[0][2]);
280 if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
284 "bootpgw: invalid hop count limit\n");
290 case 'i': /* inetd mode */
294 case 's': /* standalone mode */
298 case 't': /* timeout */
300 stmp = &(argv[0][2]);
306 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
308 "%s: invalid timeout specification\n", progname);
311 actualtimeout.tv_sec = (int32) (60 * n);
313 * If the actual timeout is zero, pass a NULL pointer
314 * to select so it blocks indefinitely, otherwise,
315 * point to the actual timeout value.
317 timeout = (n > 0) ? &actualtimeout : NULL;
320 case 'w': /* wait time */
322 stmp = &(argv[0][2]);
328 if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
332 "bootpgw: invalid wait time\n");
339 fprintf(stderr, "%s: unknown switch: -%c\n",
340 progname, argv[0][1]);
347 /* Make sure server name argument is suplied. */
348 servername = argv[0];
350 fprintf(stderr, "bootpgw: missing server name\n");
354 * Get address of real bootp server.
356 if (isdigit(servername[0]))
357 server_ipa = inet_addr(servername);
359 hep = gethostbyname(servername);
361 fprintf(stderr, "bootpgw: can't get addr for %s\n", servername);
364 bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa));
369 * Go into background and disassociate from controlling terminal.
370 * XXX - This is not the POSIX way (Should use setsid). -gwr
378 n = open(_PATH_TTY, O_RDWR);
380 ioctl(n, TIOCNOTTY, (char *) 0);
383 #endif /* TIOCNOTTY */
390 * Nuke any timeout value
395 * Here, bootpd would do:
405 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
406 report(LOG_ERR, "socket: %s", get_network_errmsg());
410 * Get server's listening port number
412 servp = getservbyname("bootps", "udp");
414 bootps_port = ntohs((u_short) servp->s_port);
416 bootps_port = (u_short) IPPORT_BOOTPS;
418 "udp/bootps: unknown service -- assuming port %d",
423 * Bind socket to BOOTPS port.
425 bind_addr.sin_family = AF_INET;
426 bind_addr.sin_port = htons(bootps_port);
427 bind_addr.sin_addr.s_addr = INADDR_ANY;
428 if (bind(s, (struct sockaddr *) &bind_addr,
429 sizeof(bind_addr)) < 0)
431 report(LOG_ERR, "bind: %s", get_network_errmsg());
434 } /* if standalone */
436 * Get destination port number so we can reply to client
438 servp = getservbyname("bootpc", "udp");
440 bootpc_port = ntohs(servp->s_port);
443 "udp/bootpc: unknown service -- assuming port %d",
445 bootpc_port = (u_short) IPPORT_BOOTPC;
448 /* no signal catchers */
451 * Process incoming requests.
460 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
461 (timeout) ? &tv : NULL);
463 if (errno != EINTR) {
464 report(LOG_ERR, "select: %s", get_errmsg());
468 if (!(readfds & (1 << s))) {
469 report(LOG_INFO, "exiting after %ld minutes of inactivity",
470 actualtimeout.tv_sec / 60);
473 ra_len = sizeof(recv_addr);
474 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
475 (struct sockaddr *) &recv_addr, &ra_len);
480 report(LOG_INFO, "recvd pkt from IP addr %s",
481 inet_ntoa(recv_addr.sin_addr));
483 if (n < sizeof(struct bootp)) {
485 report(LOG_INFO, "received short packet");
507 * Print "usage" message and exit
514 "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n");
515 fprintf(stderr, "\t -d n\tset debug level\n");
516 fprintf(stderr, "\t -h n\tset max hop count\n");
517 fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
518 fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
519 fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
520 fprintf(stderr, "\t -w n\tset min wait time (secs)\n");
527 * Process BOOTREQUEST packet.
529 * Note, this just forwards the request to a real server.
534 struct bootp *bp = (struct bootp *) pktbuf;
538 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
541 report(LOG_INFO, "request from %s",
542 inet_ntoa(recv_addr.sin_addr));
544 /* Has the client been waiting long enough? */
545 secs = ntohs(bp->bp_secs);
549 /* Has this packet hopped too many times? */
551 if (++hops > maxhops) {
552 report(LOG_NOTICE, "reqest from %s reached hop limit",
553 inet_ntoa(recv_addr.sin_addr));
559 * Here one might discard a request from the same subnet as the
560 * real server, but we can assume that the real server will send
561 * a reply to the client before it waits for minwait seconds.
564 /* If gateway address is not set, put in local interface addr. */
565 if (bp->bp_giaddr.s_addr == 0) {
567 struct sockaddr_in *sip;
570 * XXX - This picks the wrong interface when the receive addr
571 * is the broadcast address. There is no portable way to
572 * find out which interface a broadcast was received on. -gwr
573 * (Thanks to <walker@zk3.dec.com> for finding this bug!)
575 ifr = getif(s, &recv_addr.sin_addr);
577 report(LOG_NOTICE, "no interface for request from %s",
578 inet_ntoa(recv_addr.sin_addr));
581 sip = (struct sockaddr_in *) &(ifr->ifr_addr);
582 bp->bp_giaddr = sip->sin_addr;
585 * XXX - Just set "giaddr" to our "official" IP address.
586 * RFC 1532 says giaddr MUST be set to the address of the
587 * interface on which the request was received. Setting
588 * it to our "default" IP address is not strictly correct,
589 * but is good enough to allow the real BOOTP server to
590 * get the reply back here. Then, before we forward the
591 * reply to the client, the giaddr field is corrected.
592 * (In case the client uses giaddr, which it should not.)
595 bp->bp_giaddr = my_ip_addr;
599 * XXX - DHCP says to insert a subnet mask option into the
600 * options area of the request (if vendor magic == std).
603 /* Set up socket address for send. */
604 send_addr.sin_family = AF_INET;
605 send_addr.sin_port = htons(bootps_port);
606 send_addr.sin_addr.s_addr = server_ipa;
608 /* Send reply with same size packet as request used. */
609 if (sendto(s, pktbuf, pktlen, 0,
610 (struct sockaddr *) &send_addr,
611 sizeof(send_addr)) < 0)
613 report(LOG_ERR, "sendto: %s", get_network_errmsg());
620 * Process BOOTREPLY packet.
625 struct bootp *bp = (struct bootp *) pktbuf;
627 struct sockaddr_in *sip;
632 report(LOG_INFO, " reply for %s",
633 inet_ntoa(bp->bp_yiaddr));
635 /* Make sure client is directly accessible. */
636 ifr = getif(s, &(bp->bp_yiaddr));
638 report(LOG_NOTICE, "no interface for reply to %s",
639 inet_ntoa(bp->bp_yiaddr));
642 #if 1 /* Experimental (see BUG above) */
643 /* #ifdef CATER_TO_OLD_CLIENTS ? */
645 * The giaddr field has been set to our "default" IP address
646 * which might not be on the same interface as the client.
647 * In case the client looks at giaddr, (which it should not)
648 * giaddr is now set to the address of the correct interface.
650 sip = (struct sockaddr_in *) &(ifr->ifr_addr);
651 bp->bp_giaddr = sip->sin_addr;
654 /* Set up socket address for send to client. */
655 send_addr.sin_family = AF_INET;
656 send_addr.sin_addr = bp->bp_yiaddr;
657 send_addr.sin_port = htons(bootpc_port);
659 /* Create an ARP cache entry for the client. */
662 if (len > MAXHADDRLEN)
664 haf = (int) bp->bp_htype;
666 haf = HTYPE_ETHERNET;
669 report(LOG_INFO, "setarp %s - %s",
670 inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len));
671 setarp(s, &bp->bp_yiaddr, haf, ha, len);
673 /* Send reply with same size packet as request used. */
674 if (sendto(s, pktbuf, pktlen, 0,
675 (struct sockaddr *) &send_addr,
676 sizeof(send_addr)) < 0)
678 report(LOG_ERR, "sendto: %s", get_network_errmsg());
686 * c-argdecl-indent: 4
687 * c-continued-statement-offset: 4
688 * c-continued-brace-offset: -4