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. */
127 int arpmod = TRUE; /* modify the ARP table */
133 int s; /* Socket file descriptor */
134 char *pktbuf; /* Receive packet buffer */
138 int32 server_ipa; /* Real server IP address, network order. */
140 struct in_addr my_ip_addr;
142 struct utsname my_uname;
150 * Initialization such as command-line processing is done and then the
151 * main server loop is started.
159 struct timeval *timeout;
161 struct servent *servp;
164 int n, ba_len, ra_len;
168 progname = strrchr(argv[0], '/');
169 if (progname) progname++;
170 else progname = argv[0];
173 * Initialize logging.
175 report_init(0); /* uses progname */
180 report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
182 /* Debugging for compilers with struct padding. */
183 assert(sizeof(struct bootp) == BP_MINPKTSZ);
185 /* Get space for receiving packets and composing replies. */
186 pktbuf = malloc(MAX_MSG_SIZE);
188 report(LOG_ERR, "malloc failed");
191 bp = (struct bootp *) pktbuf;
194 * Check to see if a socket was passed to us from inetd.
196 * Use getsockname() to determine if descriptor 0 is indeed a socket
197 * (and thus we are probably a child of inetd) or if it is instead
198 * something else and we are running standalone.
201 ba_len = sizeof(bind_addr);
202 bzero((char *) &bind_addr, ba_len);
205 if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
207 * Descriptor 0 is a socket. Assume we are a child of inetd.
209 if (bind_addr.sin_family == AF_INET) {
211 bootps_port = ntohs(bind_addr.sin_port);
213 /* Some other type of socket? */
214 report(LOG_INFO, "getsockname: not an INET socket");
218 * Set defaults that might be changed by option switches.
221 timeout = &actualtimeout;
223 if (uname(&my_uname) < 0)
224 errx(1, "can't get hostname");
225 hostname = my_uname.nodename;
227 hep = gethostbyname(hostname);
229 printf("Can not get my IP address\n");
232 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
237 for (argc--, argv++; argc > 0; argc--, argv++) {
238 if (argv[0][0] != '-')
240 switch (argv[0][1]) {
242 case 'a': /* don't modify the ARP table */
245 case 'd': /* debug level */
247 stmp = &(argv[0][2]);
248 } else if (argv[1] && argv[1][0] == '-') {
250 * Backwards-compatible behavior:
251 * no parameter, so just increment the debug flag.
260 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
261 warnx("invalid debug level");
267 case 'h': /* hop count limit */
269 stmp = &(argv[0][2]);
275 if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
278 warnx("invalid hop count limit");
284 case 'i': /* inetd mode */
288 case 's': /* standalone mode */
292 case 't': /* timeout */
294 stmp = &(argv[0][2]);
300 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
301 warnx("invalid timeout specification");
304 actualtimeout.tv_sec = (int32) (60 * n);
306 * If the actual timeout is zero, pass a NULL pointer
307 * to select so it blocks indefinitely, otherwise,
308 * point to the actual timeout value.
310 timeout = (n > 0) ? &actualtimeout : NULL;
313 case 'w': /* wait time */
315 stmp = &(argv[0][2]);
321 if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
324 warnx("invalid wait time");
331 warnx("unknown switch: -%c", argv[0][1]);
338 /* Make sure server name argument is suplied. */
339 servername = argv[0];
341 warnx("missing server name");
345 * Get address of real bootp server.
347 if (isdigit(servername[0]))
348 server_ipa = inet_addr(servername);
350 hep = gethostbyname(servername);
352 errx(1, "can't get addr for %s", servername);
353 bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa));
358 * Go into background and disassociate from controlling terminal.
359 * XXX - This is not the POSIX way (Should use setsid). -gwr
367 n = open(_PATH_TTY, O_RDWR);
369 ioctl(n, TIOCNOTTY, (char *) 0);
372 #endif /* TIOCNOTTY */
379 * Nuke any timeout value
384 * Here, bootpd would do:
394 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
395 report(LOG_ERR, "socket: %s", get_network_errmsg());
399 * Get server's listening port number
401 servp = getservbyname("bootps", "udp");
403 bootps_port = ntohs((u_short) servp->s_port);
405 bootps_port = (u_short) IPPORT_BOOTPS;
407 "bootps/udp: unknown service -- using port %d",
412 * Bind socket to BOOTPS port.
414 bind_addr.sin_family = AF_INET;
415 bind_addr.sin_port = htons(bootps_port);
416 bind_addr.sin_addr.s_addr = INADDR_ANY;
417 if (bind(s, (struct sockaddr *) &bind_addr,
418 sizeof(bind_addr)) < 0)
420 report(LOG_ERR, "bind: %s", get_network_errmsg());
423 } /* if standalone */
425 * Get destination port number so we can reply to client
427 servp = getservbyname("bootpc", "udp");
429 bootpc_port = ntohs(servp->s_port);
432 "bootpc/udp: unknown service -- using port %d",
434 bootpc_port = (u_short) IPPORT_BOOTPC;
437 /* no signal catchers */
440 * Process incoming requests.
449 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
450 (timeout) ? &tv : NULL);
452 if (errno != EINTR) {
453 report(LOG_ERR, "select: %s", get_errmsg());
457 if (!(readfds & (1 << s))) {
458 report(LOG_INFO, "exiting after %ld minutes of inactivity",
459 (long)(actualtimeout.tv_sec / 60));
462 ra_len = sizeof(recv_addr);
463 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
464 (struct sockaddr *) &recv_addr, &ra_len);
469 report(LOG_INFO, "recvd pkt from IP addr %s",
470 inet_ntoa(recv_addr.sin_addr));
472 if (n < sizeof(struct bootp)) {
474 report(LOG_INFO, "received short packet");
496 * Print "usage" message and exit
503 "usage: \nbootpgw [-a] [-d level] [-h count] [-i] [-s] [-t timeout] [-w time] server\n");
504 fprintf(stderr, "\t -a\tdon't modify ARP table\n");
505 fprintf(stderr, "\t -d n\tset debug level\n");
506 fprintf(stderr, "\t -h n\tset max hop count\n");
507 fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
508 fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
509 fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
510 fprintf(stderr, "\t -w n\tset min wait time (secs)\n");
517 * Process BOOTREQUEST packet.
519 * Note, this just forwards the request to a real server.
524 struct bootp *bp = (struct bootp *) pktbuf;
528 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
531 report(LOG_INFO, "request from %s",
532 inet_ntoa(recv_addr.sin_addr));
534 /* Has the client been waiting long enough? */
535 secs = ntohs(bp->bp_secs);
539 /* Has this packet hopped too many times? */
541 if (++hops > maxhops) {
542 report(LOG_NOTICE, "request from %s reached hop limit",
543 inet_ntoa(recv_addr.sin_addr));
549 * Here one might discard a request from the same subnet as the
550 * real server, but we can assume that the real server will send
551 * a reply to the client before it waits for minwait seconds.
554 /* If gateway address is not set, put in local interface addr. */
555 if (bp->bp_giaddr.s_addr == 0) {
557 struct sockaddr_in *sip;
560 * XXX - This picks the wrong interface when the receive addr
561 * is the broadcast address. There is no portable way to
562 * find out which interface a broadcast was received on. -gwr
563 * (Thanks to <walker@zk3.dec.com> for finding this bug!)
565 ifr = getif(s, &recv_addr.sin_addr);
567 report(LOG_NOTICE, "no interface for request from %s",
568 inet_ntoa(recv_addr.sin_addr));
571 sip = (struct sockaddr_in *) &(ifr->ifr_addr);
572 bp->bp_giaddr = sip->sin_addr;
575 * XXX - Just set "giaddr" to our "official" IP address.
576 * RFC 1532 says giaddr MUST be set to the address of the
577 * interface on which the request was received. Setting
578 * it to our "default" IP address is not strictly correct,
579 * but is good enough to allow the real BOOTP server to
580 * get the reply back here. Then, before we forward the
581 * reply to the client, the giaddr field is corrected.
582 * (In case the client uses giaddr, which it should not.)
585 bp->bp_giaddr = my_ip_addr;
589 * XXX - DHCP says to insert a subnet mask option into the
590 * options area of the request (if vendor magic == std).
593 /* Set up socket address for send. */
594 send_addr.sin_family = AF_INET;
595 send_addr.sin_port = htons(bootps_port);
596 send_addr.sin_addr.s_addr = server_ipa;
598 /* Send reply with same size packet as request used. */
599 if (sendto(s, pktbuf, pktlen, 0,
600 (struct sockaddr *) &send_addr,
601 sizeof(send_addr)) < 0)
603 report(LOG_ERR, "sendto: %s", get_network_errmsg());
610 * Process BOOTREPLY packet.
615 struct bootp *bp = (struct bootp *) pktbuf;
617 struct sockaddr_in *sip;
622 report(LOG_INFO, " reply for %s",
623 inet_ntoa(bp->bp_yiaddr));
625 /* Make sure client is directly accessible. */
626 ifr = getif(s, &(bp->bp_yiaddr));
628 report(LOG_NOTICE, "no interface for reply to %s",
629 inet_ntoa(bp->bp_yiaddr));
632 #if 1 /* Experimental (see BUG above) */
633 /* #ifdef CATER_TO_OLD_CLIENTS ? */
635 * The giaddr field has been set to our "default" IP address
636 * which might not be on the same interface as the client.
637 * In case the client looks at giaddr, (which it should not)
638 * giaddr is now set to the address of the correct interface.
640 sip = (struct sockaddr_in *) &(ifr->ifr_addr);
641 bp->bp_giaddr = sip->sin_addr;
644 /* Set up socket address for send to client. */
645 send_addr.sin_family = AF_INET;
646 send_addr.sin_addr = bp->bp_yiaddr;
647 send_addr.sin_port = htons(bootpc_port);
650 /* Create an ARP cache entry for the client. */
655 if (len > MAXHADDRLEN)
657 haf = (int) bp->bp_htype;
659 haf = HTYPE_ETHERNET;
662 report(LOG_INFO, "setarp %s - %s",
663 inet_ntoa(dst), haddrtoa(ha, len));
664 setarp(s, &dst, haf, ha, len);
667 /* Send reply with same size packet as request used. */
668 if (sendto(s, pktbuf, pktlen, 0,
669 (struct sockaddr *) &send_addr,
670 sizeof(send_addr)) < 0)
672 report(LOG_ERR, "sendto: %s", get_network_errmsg());
680 * c-argdecl-indent: 4
681 * c-continued-statement-offset: 4
682 * c-continued-brace-offset: -4