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.6 2003/02/05 13:45:25 charnier 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 */
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.
154 main(int argc, char **argv)
156 struct timeval *timeout;
158 struct servent *servp;
161 int n, ba_len, ra_len;
165 progname = strrchr(argv[0], '/');
166 if (progname) progname++;
167 else progname = argv[0];
170 * Initialize logging.
172 report_init(0); /* uses progname */
177 report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
179 /* Debugging for compilers with struct padding. */
180 assert(sizeof(struct bootp) == BP_MINPKTSZ);
182 /* Get space for receiving packets and composing replies. */
183 pktbuf = malloc(MAX_MSG_SIZE);
185 report(LOG_ERR, "malloc failed");
188 bp = (struct bootp *) pktbuf;
191 * Check to see if a socket was passed to us from inetd.
193 * Use getsockname() to determine if descriptor 0 is indeed a socket
194 * (and thus we are probably a child of inetd) or if it is instead
195 * something else and we are running standalone.
198 ba_len = sizeof(bind_addr);
199 bzero((char *) &bind_addr, ba_len);
202 if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
204 * Descriptor 0 is a socket. Assume we are a child of inetd.
206 if (bind_addr.sin_family == AF_INET) {
208 bootps_port = ntohs(bind_addr.sin_port);
210 /* Some other type of socket? */
211 report(LOG_INFO, "getsockname: not an INET socket");
215 * Set defaults that might be changed by option switches.
218 timeout = &actualtimeout;
220 if (uname(&my_uname) < 0)
221 errx(1, "can't get hostname");
222 hostname = my_uname.nodename;
224 hep = gethostbyname(hostname);
226 printf("Can not get my IP address\n");
229 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
234 for (argc--, argv++; argc > 0; argc--, argv++) {
235 if (argv[0][0] != '-')
237 switch (argv[0][1]) {
239 case 'd': /* debug level */
241 stmp = &(argv[0][2]);
242 } else if (argv[1] && argv[1][0] == '-') {
244 * Backwards-compatible behavior:
245 * no parameter, so just increment the debug flag.
254 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
255 warnx("invalid debug level");
261 case 'h': /* hop count limit */
263 stmp = &(argv[0][2]);
269 if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
272 warnx("invalid hop count limit");
278 case 'i': /* inetd mode */
282 case 's': /* standalone mode */
286 case 't': /* timeout */
288 stmp = &(argv[0][2]);
294 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
295 warnx("invalid timeout specification");
298 actualtimeout.tv_sec = (int32) (60 * n);
300 * If the actual timeout is zero, pass a NULL pointer
301 * to select so it blocks indefinitely, otherwise,
302 * point to the actual timeout value.
304 timeout = (n > 0) ? &actualtimeout : NULL;
307 case 'w': /* wait time */
309 stmp = &(argv[0][2]);
315 if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
318 warnx("invalid wait time");
325 warnx("unknown switch: -%c", argv[0][1]);
332 /* Make sure server name argument is suplied. */
333 servername = argv[0];
335 warnx("missing server name");
339 * Get address of real bootp server.
341 if (isdigit(servername[0]))
342 server_ipa = inet_addr(servername);
344 hep = gethostbyname(servername);
346 errx(1, "can't get addr for %s", servername);
347 bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa));
352 * Go into background and disassociate from controlling terminal.
353 * XXX - This is not the POSIX way (Should use setsid). -gwr
361 n = open(_PATH_TTY, O_RDWR);
363 ioctl(n, TIOCNOTTY, NULL);
366 #endif /* TIOCNOTTY */
373 * Nuke any timeout value
378 * Here, bootpd would do:
388 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
389 report(LOG_ERR, "socket: %s", get_network_errmsg());
393 * Get server's listening port number
395 servp = getservbyname("bootps", "udp");
397 bootps_port = ntohs((u_short) servp->s_port);
399 bootps_port = (u_short) IPPORT_BOOTPS;
401 "bootps/udp: unknown service -- using port %d",
406 * Bind socket to BOOTPS port.
408 bind_addr.sin_family = AF_INET;
409 bind_addr.sin_port = htons(bootps_port);
410 bind_addr.sin_addr.s_addr = INADDR_ANY;
411 if (bind(s, (struct sockaddr *) &bind_addr,
412 sizeof(bind_addr)) < 0)
414 report(LOG_ERR, "bind: %s", get_network_errmsg());
417 } /* if standalone */
419 * Get destination port number so we can reply to client
421 servp = getservbyname("bootpc", "udp");
423 bootpc_port = ntohs(servp->s_port);
426 "bootpc/udp: unknown service -- using port %d",
428 bootpc_port = (u_short) IPPORT_BOOTPC;
431 /* no signal catchers */
434 * Process incoming requests.
443 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
444 (timeout) ? &tv : NULL);
446 if (errno != EINTR) {
447 report(LOG_ERR, "select: %s", get_errmsg());
451 if (!(readfds & (1 << s))) {
452 report(LOG_INFO, "exiting after %ld minutes of inactivity",
453 actualtimeout.tv_sec / 60);
456 ra_len = sizeof(recv_addr);
457 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
458 (struct sockaddr *) &recv_addr, &ra_len);
463 report(LOG_INFO, "recvd pkt from IP addr %s",
464 inet_ntoa(recv_addr.sin_addr));
466 if (n < sizeof(struct bootp)) {
468 report(LOG_INFO, "received short packet");
490 * Print "usage" message and exit
497 "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n");
498 fprintf(stderr, "\t -d n\tset debug level\n");
499 fprintf(stderr, "\t -h n\tset max hop count\n");
500 fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
501 fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
502 fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
503 fprintf(stderr, "\t -w n\tset min wait time (secs)\n");
510 * Process BOOTREQUEST packet.
512 * Note, this just forwards the request to a real server.
517 struct bootp *bp = (struct bootp *) pktbuf;
521 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
524 report(LOG_INFO, "request from %s",
525 inet_ntoa(recv_addr.sin_addr));
527 /* Has the client been waiting long enough? */
528 secs = ntohs(bp->bp_secs);
532 /* Has this packet hopped too many times? */
534 if (++hops > maxhops) {
535 report(LOG_NOTICE, "reqest from %s reached hop limit",
536 inet_ntoa(recv_addr.sin_addr));
542 * Here one might discard a request from the same subnet as the
543 * real server, but we can assume that the real server will send
544 * a reply to the client before it waits for minwait seconds.
547 /* If gateway address is not set, put in local interface addr. */
548 if (bp->bp_giaddr.s_addr == 0) {
550 struct sockaddr_in *sip;
553 * XXX - This picks the wrong interface when the receive addr
554 * is the broadcast address. There is no portable way to
555 * find out which interface a broadcast was received on. -gwr
556 * (Thanks to <walker@zk3.dec.com> for finding this bug!)
558 ifr = getif(s, &recv_addr.sin_addr);
560 report(LOG_NOTICE, "no interface for request from %s",
561 inet_ntoa(recv_addr.sin_addr));
564 sip = (struct sockaddr_in *) &(ifr->ifr_addr);
565 bp->bp_giaddr = sip->sin_addr;
568 * XXX - Just set "giaddr" to our "official" IP address.
569 * RFC 1532 says giaddr MUST be set to the address of the
570 * interface on which the request was received. Setting
571 * it to our "default" IP address is not strictly correct,
572 * but is good enough to allow the real BOOTP server to
573 * get the reply back here. Then, before we forward the
574 * reply to the client, the giaddr field is corrected.
575 * (In case the client uses giaddr, which it should not.)
578 bp->bp_giaddr = my_ip_addr;
582 * XXX - DHCP says to insert a subnet mask option into the
583 * options area of the request (if vendor magic == std).
586 /* Set up socket address for send. */
587 send_addr.sin_family = AF_INET;
588 send_addr.sin_port = htons(bootps_port);
589 send_addr.sin_addr.s_addr = server_ipa;
591 /* Send reply with same size packet as request used. */
592 if (sendto(s, pktbuf, pktlen, 0,
593 (struct sockaddr *) &send_addr,
594 sizeof(send_addr)) < 0)
596 report(LOG_ERR, "sendto: %s", get_network_errmsg());
603 * Process BOOTREPLY packet.
608 struct bootp *bp = (struct bootp *) pktbuf;
610 struct sockaddr_in *sip;
615 report(LOG_INFO, " reply for %s",
616 inet_ntoa(bp->bp_yiaddr));
618 /* Make sure client is directly accessible. */
619 ifr = getif(s, &(bp->bp_yiaddr));
621 report(LOG_NOTICE, "no interface for reply to %s",
622 inet_ntoa(bp->bp_yiaddr));
625 #if 1 /* Experimental (see BUG above) */
626 /* #ifdef CATER_TO_OLD_CLIENTS ? */
628 * The giaddr field has been set to our "default" IP address
629 * which might not be on the same interface as the client.
630 * In case the client looks at giaddr, (which it should not)
631 * giaddr is now set to the address of the correct interface.
633 sip = (struct sockaddr_in *) &(ifr->ifr_addr);
634 bp->bp_giaddr = sip->sin_addr;
637 /* Set up socket address for send to client. */
638 send_addr.sin_family = AF_INET;
639 send_addr.sin_addr = bp->bp_yiaddr;
640 send_addr.sin_port = htons(bootpc_port);
642 /* Create an ARP cache entry for the client. */
645 if (len > MAXHADDRLEN)
647 haf = (int) bp->bp_htype;
649 haf = HTYPE_ETHERNET;
652 report(LOG_INFO, "setarp %s - %s",
653 inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len));
654 setarp(s, &bp->bp_yiaddr, haf, ha, len);
656 /* Send reply with same size packet as request used. */
657 if (sendto(s, pktbuf, pktlen, 0,
658 (struct sockaddr *) &send_addr,
659 sizeof(send_addr)) < 0)
661 report(LOG_ERR, "sendto: %s", get_network_errmsg());
669 * c-argdecl-indent: 4
670 * c-continued-statement-offset: 4
671 * c-continued-brace-offset: -4