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 ************************************************************************/
29 * BOOTPGW is typically used to forward BOOTP client requests from
30 * one subnet to a BOOTP server on a different subnet.
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
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 */
66 # include <fcntl.h> /* for O_RDONLY, etc */
71 /* Yes, memcpy is OK here (no overlapped copies). */
72 # define bcopy(a,b,c) memcpy(b,a,c)
73 # define bzero(p,l) memset(p,0,l)
74 # define bcmp(a,b,c) memcmp(a,b,c)
81 #include "patchlevel.h"
83 /* Local definitions: */
84 #define MAX_MSG_SIZE (3*512) /* Maximum packet size */
87 #define get_network_errmsg get_errmsg
92 * Externals, forward declarations, and global variables
95 static void usage(void);
96 static void handle_reply(void);
97 static void handle_request(void);
100 * IP port numbers for client and server obtained from /etc/services
103 u_short bootps_port, bootpc_port;
107 * Internet socket and interface config structures
110 struct sockaddr_in bind_addr; /* Listening */
111 struct sockaddr_in recv_addr; /* Packet source */
112 struct sockaddr_in send_addr; /* destination */
118 int debug = 0; /* Debugging flag (level) */
119 struct timeval actualtimeout =
120 { /* fifteen minutes */
121 15 * 60L, /* tv_sec */
124 u_char maxhops = 4; /* Number of hops allowed for requests. */
125 u_int minwait = 3; /* Number of seconds client must wait before
126 its bootrequest packets are forwarded. */
132 int s; /* Socket file descriptor */
133 char *pktbuf; /* Receive packet buffer */
137 int32 server_ipa; /* Real server IP address, network order. */
139 struct in_addr my_ip_addr;
141 struct utsname my_uname;
149 * Initialization such as command-line processing is done and then the
150 * main server loop is started.
158 struct timeval *timeout;
160 struct servent *servp;
163 int n, ba_len, ra_len;
167 progname = strrchr(argv[0], '/');
168 if (progname) progname++;
169 else progname = argv[0];
172 * Initialize logging.
174 report_init(0); /* uses progname */
179 report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
181 /* Debugging for compilers with struct padding. */
182 assert(sizeof(struct bootp) == BP_MINPKTSZ);
184 /* Get space for receiving packets and composing replies. */
185 pktbuf = malloc(MAX_MSG_SIZE);
187 report(LOG_ERR, "malloc failed");
190 bp = (struct bootp *) pktbuf;
193 * Check to see if a socket was passed to us from inetd.
195 * Use getsockname() to determine if descriptor 0 is indeed a socket
196 * (and thus we are probably a child of inetd) or if it is instead
197 * something else and we are running standalone.
200 ba_len = sizeof(bind_addr);
201 bzero((char *) &bind_addr, ba_len);
204 if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
206 * Descriptor 0 is a socket. Assume we are a child of inetd.
208 if (bind_addr.sin_family == AF_INET) {
210 bootps_port = ntohs(bind_addr.sin_port);
212 /* Some other type of socket? */
213 report(LOG_INFO, "getsockname: not an INET socket");
217 * Set defaults that might be changed by option switches.
220 timeout = &actualtimeout;
222 if (uname(&my_uname) < 0)
223 errx(1, "can't get hostname");
224 hostname = my_uname.nodename;
226 hep = gethostbyname(hostname);
228 printf("Can not get my IP address\n");
231 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
236 for (argc--, argv++; argc > 0; argc--, argv++) {
237 if (argv[0][0] != '-')
239 switch (argv[0][1]) {
241 case 'd': /* debug level */
243 stmp = &(argv[0][2]);
244 } else if (argv[1] && argv[1][0] == '-') {
246 * Backwards-compatible behavior:
247 * no parameter, so just increment the debug flag.
256 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
257 warnx("invalid debug level");
263 case 'h': /* hop count limit */
265 stmp = &(argv[0][2]);
271 if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
274 warnx("invalid hop count limit");
280 case 'i': /* inetd mode */
284 case 's': /* standalone mode */
288 case 't': /* timeout */
290 stmp = &(argv[0][2]);
296 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
297 warnx("invalid timeout specification");
300 actualtimeout.tv_sec = (int32) (60 * n);
302 * If the actual timeout is zero, pass a NULL pointer
303 * to select so it blocks indefinitely, otherwise,
304 * point to the actual timeout value.
306 timeout = (n > 0) ? &actualtimeout : NULL;
309 case 'w': /* wait time */
311 stmp = &(argv[0][2]);
317 if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
320 warnx("invalid wait time");
327 warnx("unknown switch: -%c", argv[0][1]);
334 /* Make sure server name argument is suplied. */
335 servername = argv[0];
337 warnx("missing server name");
341 * Get address of real bootp server.
343 if (isdigit(servername[0]))
344 server_ipa = inet_addr(servername);
346 hep = gethostbyname(servername);
348 errx(1, "can't get addr for %s", servername);
349 bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa));
354 * Go into background and disassociate from controlling terminal.
355 * XXX - This is not the POSIX way (Should use setsid). -gwr
363 n = open(_PATH_TTY, O_RDWR);
365 ioctl(n, TIOCNOTTY, (char *) 0);
368 #endif /* TIOCNOTTY */
375 * Nuke any timeout value
380 * Here, bootpd would do:
390 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
391 report(LOG_ERR, "socket: %s", get_network_errmsg());
395 * Get server's listening port number
397 servp = getservbyname("bootps", "udp");
399 bootps_port = ntohs((u_short) servp->s_port);
401 bootps_port = (u_short) IPPORT_BOOTPS;
403 "bootps/udp: unknown service -- using port %d",
408 * Bind socket to BOOTPS port.
410 bind_addr.sin_family = AF_INET;
411 bind_addr.sin_port = htons(bootps_port);
412 bind_addr.sin_addr.s_addr = INADDR_ANY;
413 if (bind(s, (struct sockaddr *) &bind_addr,
414 sizeof(bind_addr)) < 0)
416 report(LOG_ERR, "bind: %s", get_network_errmsg());
419 } /* if standalone */
421 * Get destination port number so we can reply to client
423 servp = getservbyname("bootpc", "udp");
425 bootpc_port = ntohs(servp->s_port);
428 "bootpc/udp: unknown service -- using port %d",
430 bootpc_port = (u_short) IPPORT_BOOTPC;
433 /* no signal catchers */
436 * Process incoming requests.
445 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
446 (timeout) ? &tv : NULL);
448 if (errno != EINTR) {
449 report(LOG_ERR, "select: %s", get_errmsg());
453 if (!(readfds & (1 << s))) {
454 report(LOG_INFO, "exiting after %ld minutes of inactivity",
455 actualtimeout.tv_sec / 60);
458 ra_len = sizeof(recv_addr);
459 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
460 (struct sockaddr *) &recv_addr, &ra_len);
465 report(LOG_INFO, "recvd pkt from IP addr %s",
466 inet_ntoa(recv_addr.sin_addr));
468 if (n < sizeof(struct bootp)) {
470 report(LOG_INFO, "received short packet");
492 * Print "usage" message and exit
499 "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n");
500 fprintf(stderr, "\t -d n\tset debug level\n");
501 fprintf(stderr, "\t -h n\tset max hop count\n");
502 fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
503 fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
504 fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
505 fprintf(stderr, "\t -w n\tset min wait time (secs)\n");
512 * Process BOOTREQUEST packet.
514 * Note, this just forwards the request to a real server.
519 struct bootp *bp = (struct bootp *) pktbuf;
523 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
526 report(LOG_INFO, "request from %s",
527 inet_ntoa(recv_addr.sin_addr));
529 /* Has the client been waiting long enough? */
530 secs = ntohs(bp->bp_secs);
534 /* Has this packet hopped too many times? */
536 if (++hops > maxhops) {
537 report(LOG_NOTICE, "reqest from %s reached hop limit",
538 inet_ntoa(recv_addr.sin_addr));
544 * Here one might discard a request from the same subnet as the
545 * real server, but we can assume that the real server will send
546 * a reply to the client before it waits for minwait seconds.
549 /* If gateway address is not set, put in local interface addr. */
550 if (bp->bp_giaddr.s_addr == 0) {
552 struct sockaddr_in *sip;
555 * XXX - This picks the wrong interface when the receive addr
556 * is the broadcast address. There is no portable way to
557 * find out which interface a broadcast was received on. -gwr
558 * (Thanks to <walker@zk3.dec.com> for finding this bug!)
560 ifr = getif(s, &recv_addr.sin_addr);
562 report(LOG_NOTICE, "no interface for request from %s",
563 inet_ntoa(recv_addr.sin_addr));
566 sip = (struct sockaddr_in *) &(ifr->ifr_addr);
567 bp->bp_giaddr = sip->sin_addr;
570 * XXX - Just set "giaddr" to our "official" IP address.
571 * RFC 1532 says giaddr MUST be set to the address of the
572 * interface on which the request was received. Setting
573 * it to our "default" IP address is not strictly correct,
574 * but is good enough to allow the real BOOTP server to
575 * get the reply back here. Then, before we forward the
576 * reply to the client, the giaddr field is corrected.
577 * (In case the client uses giaddr, which it should not.)
580 bp->bp_giaddr = my_ip_addr;
584 * XXX - DHCP says to insert a subnet mask option into the
585 * options area of the request (if vendor magic == std).
588 /* Set up socket address for send. */
589 send_addr.sin_family = AF_INET;
590 send_addr.sin_port = htons(bootps_port);
591 send_addr.sin_addr.s_addr = server_ipa;
593 /* Send reply with same size packet as request used. */
594 if (sendto(s, pktbuf, pktlen, 0,
595 (struct sockaddr *) &send_addr,
596 sizeof(send_addr)) < 0)
598 report(LOG_ERR, "sendto: %s", get_network_errmsg());
605 * Process BOOTREPLY packet.
610 struct bootp *bp = (struct bootp *) pktbuf;
612 struct sockaddr_in *sip;
617 report(LOG_INFO, " reply for %s",
618 inet_ntoa(bp->bp_yiaddr));
620 /* Make sure client is directly accessible. */
621 ifr = getif(s, &(bp->bp_yiaddr));
623 report(LOG_NOTICE, "no interface for reply to %s",
624 inet_ntoa(bp->bp_yiaddr));
627 #if 1 /* Experimental (see BUG above) */
628 /* #ifdef CATER_TO_OLD_CLIENTS ? */
630 * The giaddr field has been set to our "default" IP address
631 * which might not be on the same interface as the client.
632 * In case the client looks at giaddr, (which it should not)
633 * giaddr is now set to the address of the correct interface.
635 sip = (struct sockaddr_in *) &(ifr->ifr_addr);
636 bp->bp_giaddr = sip->sin_addr;
639 /* Set up socket address for send to client. */
640 send_addr.sin_family = AF_INET;
641 send_addr.sin_addr = bp->bp_yiaddr;
642 send_addr.sin_port = htons(bootpc_port);
644 /* Create an ARP cache entry for the client. */
647 if (len > MAXHADDRLEN)
649 haf = (int) bp->bp_htype;
651 haf = HTYPE_ETHERNET;
654 report(LOG_INFO, "setarp %s - %s",
655 inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len));
656 setarp(s, &bp->bp_yiaddr, haf, ha, len);
658 /* Send reply with same size packet as request used. */
659 if (sendto(s, pktbuf, pktlen, 0,
660 (struct sockaddr *) &send_addr,
661 sizeof(send_addr)) < 0)
663 report(LOG_ERR, "sendto: %s", get_network_errmsg());
671 * c-argdecl-indent: 4
672 * c-continued-statement-offset: 4
673 * c-continued-brace-offset: -4