Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/bootpd/bootpgw/bootpgw.c
34923 views
1
/*
2
* bootpgw.c - BOOTP GateWay
3
* This program forwards BOOTP Request packets to a BOOTP server.
4
*/
5
6
/************************************************************************
7
Copyright 1988, 1991 by Carnegie Mellon University
8
9
All Rights Reserved
10
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.
18
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
25
SOFTWARE.
26
************************************************************************/
27
28
/*
29
* BOOTPGW is typically used to forward BOOTP client requests from
30
* one subnet to a BOOTP server on a different subnet.
31
*/
32
33
#include <sys/types.h>
34
#include <sys/param.h>
35
#include <sys/socket.h>
36
#include <sys/ioctl.h>
37
#include <sys/file.h>
38
#include <sys/time.h>
39
#include <sys/stat.h>
40
#include <sys/utsname.h>
41
42
#include <net/if.h>
43
#include <netinet/in.h>
44
#include <arpa/inet.h> /* inet_ntoa */
45
46
#ifndef NO_UNISTD
47
#include <unistd.h>
48
#endif
49
50
#include <err.h>
51
#include <stdlib.h>
52
#include <signal.h>
53
#include <stdio.h>
54
#include <string.h>
55
#include <errno.h>
56
#include <ctype.h>
57
#include <netdb.h>
58
#include <paths.h>
59
#include <syslog.h>
60
#include <assert.h>
61
62
#ifdef NO_SETSID
63
# include <fcntl.h> /* for O_RDONLY, etc */
64
#endif
65
66
#include "bootp.h"
67
#include "getif.h"
68
#include "hwaddr.h"
69
#include "report.h"
70
#include "patchlevel.h"
71
72
/* Local definitions: */
73
#define MAX_MSG_SIZE (3*512) /* Maximum packet size */
74
#define TRUE 1
75
#define FALSE 0
76
#define get_network_errmsg get_errmsg
77
78
79
80
/*
81
* Externals, forward declarations, and global variables
82
*/
83
84
static void usage(void) __dead2;
85
static void handle_reply(void);
86
static void handle_request(void);
87
88
/*
89
* IP port numbers for client and server obtained from /etc/services
90
*/
91
92
u_short bootps_port, bootpc_port;
93
94
95
/*
96
* Internet socket and interface config structures
97
*/
98
99
struct sockaddr_in bind_addr; /* Listening */
100
struct sockaddr_in recv_addr; /* Packet source */
101
struct sockaddr_in send_addr; /* destination */
102
103
104
/*
105
* option defaults
106
*/
107
int debug = 0; /* Debugging flag (level) */
108
struct timeval actualtimeout =
109
{ /* fifteen minutes */
110
15 * 60L, /* tv_sec */
111
0 /* tv_usec */
112
};
113
u_char 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. */
116
int arpmod = TRUE; /* modify the ARP table */
117
118
/*
119
* General
120
*/
121
122
int s; /* Socket file descriptor */
123
char *pktbuf; /* Receive packet buffer */
124
int pktlen;
125
char *progname;
126
char *servername;
127
int32 server_ipa; /* Real server IP address, network order. */
128
129
struct in_addr my_ip_addr;
130
131
struct utsname my_uname;
132
char *hostname;
133
134
135
136
137
138
/*
139
* Initialization such as command-line processing is done and then the
140
* main server loop is started.
141
*/
142
143
int
144
main(int argc, char **argv)
145
{
146
struct timeval *timeout;
147
struct bootp *bp;
148
struct servent *servp;
149
struct hostent *hep;
150
char *stmp;
151
int n, ba_len, ra_len;
152
int nfound, readfds;
153
int standalone;
154
155
progname = strrchr(argv[0], '/');
156
if (progname) progname++;
157
else progname = argv[0];
158
159
/*
160
* Initialize logging.
161
*/
162
report_init(0); /* uses progname */
163
164
/*
165
* Log startup
166
*/
167
report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
168
169
/* Debugging for compilers with struct padding. */
170
assert(sizeof(struct bootp) == BP_MINPKTSZ);
171
172
/* Get space for receiving packets and composing replies. */
173
pktbuf = malloc(MAX_MSG_SIZE);
174
if (!pktbuf) {
175
report(LOG_ERR, "malloc failed");
176
exit(1);
177
}
178
bp = (struct bootp *) pktbuf;
179
180
/*
181
* Check to see if a socket was passed to us from inetd.
182
*
183
* Use getsockname() to determine if descriptor 0 is indeed a socket
184
* (and thus we are probably a child of inetd) or if it is instead
185
* something else and we are running standalone.
186
*/
187
s = 0;
188
ba_len = sizeof(bind_addr);
189
bzero((char *) &bind_addr, ba_len);
190
errno = 0;
191
standalone = TRUE;
192
if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
193
/*
194
* Descriptor 0 is a socket. Assume we are a child of inetd.
195
*/
196
if (bind_addr.sin_family == AF_INET) {
197
standalone = FALSE;
198
bootps_port = ntohs(bind_addr.sin_port);
199
} else {
200
/* Some other type of socket? */
201
report(LOG_INFO, "getsockname: not an INET socket");
202
}
203
}
204
/*
205
* Set defaults that might be changed by option switches.
206
*/
207
stmp = NULL;
208
timeout = &actualtimeout;
209
210
if (uname(&my_uname) < 0)
211
errx(1, "can't get hostname");
212
hostname = my_uname.nodename;
213
214
hep = gethostbyname(hostname);
215
if (!hep) {
216
printf("Can not get my IP address\n");
217
exit(1);
218
}
219
bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
220
221
/*
222
* Read switches.
223
*/
224
for (argc--, argv++; argc > 0; argc--, argv++) {
225
if (argv[0][0] != '-')
226
break;
227
switch (argv[0][1]) {
228
229
case 'a': /* don't modify the ARP table */
230
arpmod = FALSE;
231
break;
232
case 'd': /* debug level */
233
if (argv[0][2]) {
234
stmp = &(argv[0][2]);
235
} else if (argv[1] && argv[1][0] == '-') {
236
/*
237
* Backwards-compatible behavior:
238
* no parameter, so just increment the debug flag.
239
*/
240
debug++;
241
break;
242
} else {
243
argc--;
244
argv++;
245
stmp = argv[0];
246
}
247
if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
248
warnx("invalid debug level");
249
break;
250
}
251
debug = n;
252
break;
253
254
case 'h': /* hop count limit */
255
if (argv[0][2]) {
256
stmp = &(argv[0][2]);
257
} else {
258
argc--;
259
argv++;
260
stmp = argv[0];
261
}
262
if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
263
(n < 0) || (n > 16))
264
{
265
warnx("invalid hop count limit");
266
break;
267
}
268
maxhops = (u_char)n;
269
break;
270
271
case 'i': /* inetd mode */
272
standalone = FALSE;
273
break;
274
275
case 's': /* standalone mode */
276
standalone = TRUE;
277
break;
278
279
case 't': /* timeout */
280
if (argv[0][2]) {
281
stmp = &(argv[0][2]);
282
} else {
283
argc--;
284
argv++;
285
stmp = argv[0];
286
}
287
if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
288
warnx("invalid timeout specification");
289
break;
290
}
291
actualtimeout.tv_sec = (int32) (60 * n);
292
/*
293
* If the actual timeout is zero, pass a NULL pointer
294
* to select so it blocks indefinitely, otherwise,
295
* point to the actual timeout value.
296
*/
297
timeout = (n > 0) ? &actualtimeout : NULL;
298
break;
299
300
case 'w': /* wait time */
301
if (argv[0][2]) {
302
stmp = &(argv[0][2]);
303
} else {
304
argc--;
305
argv++;
306
stmp = argv[0];
307
}
308
if (!stmp || (sscanf(stmp, "%d", &n) != 1) ||
309
(n < 0) || (n > 60))
310
{
311
warnx("invalid wait time");
312
break;
313
}
314
minwait = (u_int)n;
315
break;
316
317
default:
318
warnx("unknown switch: -%c", argv[0][1]);
319
usage();
320
break;
321
322
} /* switch */
323
} /* for args */
324
325
/* Make sure server name argument is suplied. */
326
servername = argv[0];
327
if (!servername) {
328
warnx("missing server name");
329
usage();
330
}
331
/*
332
* Get address of real bootp server.
333
*/
334
if (isdigit(servername[0]))
335
server_ipa = inet_addr(servername);
336
else {
337
hep = gethostbyname(servername);
338
if (!hep)
339
errx(1, "can't get addr for %s", servername);
340
bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa));
341
}
342
343
if (standalone) {
344
/*
345
* Go into background and disassociate from controlling terminal.
346
* XXX - This is not the POSIX way (Should use setsid). -gwr
347
*/
348
if (debug < 3) {
349
if (fork())
350
exit(0);
351
#ifdef NO_SETSID
352
setpgrp(0,0);
353
#ifdef TIOCNOTTY
354
n = open(_PATH_TTY, O_RDWR);
355
if (n >= 0) {
356
ioctl(n, TIOCNOTTY, (char *) 0);
357
(void) close(n);
358
}
359
#endif /* TIOCNOTTY */
360
#else /* SETSID */
361
if (setsid() < 0)
362
perror("setsid");
363
#endif /* SETSID */
364
} /* if debug < 3 */
365
/*
366
* Nuke any timeout value
367
*/
368
timeout = NULL;
369
370
/*
371
* Here, bootpd would do:
372
* chdir
373
* tzone_init
374
* rdtab_init
375
* readtab
376
*/
377
378
/*
379
* Create a socket.
380
*/
381
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
382
report(LOG_ERR, "socket: %s", get_network_errmsg());
383
exit(1);
384
}
385
/*
386
* Get server's listening port number
387
*/
388
servp = getservbyname("bootps", "udp");
389
if (servp) {
390
bootps_port = ntohs((u_short) servp->s_port);
391
} else {
392
bootps_port = (u_short) IPPORT_BOOTPS;
393
report(LOG_ERR,
394
"bootps/udp: unknown service -- using port %d",
395
bootps_port);
396
}
397
398
/*
399
* Bind socket to BOOTPS port.
400
*/
401
bind_addr.sin_family = AF_INET;
402
bind_addr.sin_port = htons(bootps_port);
403
bind_addr.sin_addr.s_addr = INADDR_ANY;
404
if (bind(s, (struct sockaddr *) &bind_addr,
405
sizeof(bind_addr)) < 0)
406
{
407
report(LOG_ERR, "bind: %s", get_network_errmsg());
408
exit(1);
409
}
410
} /* if standalone */
411
/*
412
* Get destination port number so we can reply to client
413
*/
414
servp = getservbyname("bootpc", "udp");
415
if (servp) {
416
bootpc_port = ntohs(servp->s_port);
417
} else {
418
report(LOG_ERR,
419
"bootpc/udp: unknown service -- using port %d",
420
IPPORT_BOOTPC);
421
bootpc_port = (u_short) IPPORT_BOOTPC;
422
}
423
424
/* no signal catchers */
425
426
/*
427
* Process incoming requests.
428
*/
429
for (;;) {
430
struct timeval tv;
431
432
readfds = 1 << s;
433
if (timeout)
434
tv = *timeout;
435
436
nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
437
(timeout) ? &tv : NULL);
438
if (nfound < 0) {
439
if (errno != EINTR) {
440
report(LOG_ERR, "select: %s", get_errmsg());
441
}
442
continue;
443
}
444
if (!(readfds & (1 << s))) {
445
report(LOG_INFO, "exiting after %ld minutes of inactivity",
446
(long)(actualtimeout.tv_sec / 60));
447
exit(0);
448
}
449
ra_len = sizeof(recv_addr);
450
n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
451
(struct sockaddr *) &recv_addr, &ra_len);
452
if (n <= 0) {
453
continue;
454
}
455
if (debug > 3) {
456
report(LOG_INFO, "recvd pkt from IP addr %s",
457
inet_ntoa(recv_addr.sin_addr));
458
}
459
if (n < sizeof(struct bootp)) {
460
if (debug) {
461
report(LOG_INFO, "received short packet");
462
}
463
continue;
464
}
465
pktlen = n;
466
467
switch (bp->bp_op) {
468
case BOOTREQUEST:
469
handle_request();
470
break;
471
case BOOTREPLY:
472
handle_reply();
473
break;
474
}
475
}
476
return 0;
477
}
478
479
480
481
482
/*
483
* Print "usage" message and exit
484
*/
485
486
static void
487
usage()
488
{
489
fprintf(stderr,
490
"usage: bootpgw [-a] [-i | -s] [-d level] [-h count] [-t timeout]\n"
491
" [-w time] server\n");
492
fprintf(stderr, "\t -a\tdon't modify ARP table\n");
493
fprintf(stderr, "\t -d n\tset debug level\n");
494
fprintf(stderr, "\t -h n\tset max hop count\n");
495
fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
496
fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
497
fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
498
fprintf(stderr, "\t -w n\tset min wait time (secs)\n");
499
exit(1);
500
}
501
502
503
504
/*
505
* Process BOOTREQUEST packet.
506
*
507
* Note, this just forwards the request to a real server.
508
*/
509
static void
510
handle_request()
511
{
512
struct bootp *bp = (struct bootp *) pktbuf;
513
u_short secs;
514
u_char hops;
515
516
/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
517
518
if (debug) {
519
report(LOG_INFO, "request from %s",
520
inet_ntoa(recv_addr.sin_addr));
521
}
522
/* Has the client been waiting long enough? */
523
secs = ntohs(bp->bp_secs);
524
if (secs < minwait)
525
return;
526
527
/* Has this packet hopped too many times? */
528
hops = bp->bp_hops;
529
if (++hops > maxhops) {
530
report(LOG_NOTICE, "request from %s reached hop limit",
531
inet_ntoa(recv_addr.sin_addr));
532
return;
533
}
534
bp->bp_hops = hops;
535
536
/*
537
* Here one might discard a request from the same subnet as the
538
* real server, but we can assume that the real server will send
539
* a reply to the client before it waits for minwait seconds.
540
*/
541
542
/* If gateway address is not set, put in local interface addr. */
543
if (bp->bp_giaddr.s_addr == 0) {
544
#if 0 /* BUG */
545
struct sockaddr_in *sip;
546
struct ifreq *ifr;
547
/*
548
* XXX - This picks the wrong interface when the receive addr
549
* is the broadcast address. There is no portable way to
550
* find out which interface a broadcast was received on. -gwr
551
* (Thanks to <[email protected]> for finding this bug!)
552
*/
553
ifr = getif(s, &recv_addr.sin_addr);
554
if (!ifr) {
555
report(LOG_NOTICE, "no interface for request from %s",
556
inet_ntoa(recv_addr.sin_addr));
557
return;
558
}
559
sip = (struct sockaddr_in *) &(ifr->ifr_addr);
560
bp->bp_giaddr = sip->sin_addr;
561
#else /* BUG */
562
/*
563
* XXX - Just set "giaddr" to our "official" IP address.
564
* RFC 1532 says giaddr MUST be set to the address of the
565
* interface on which the request was received. Setting
566
* it to our "default" IP address is not strictly correct,
567
* but is good enough to allow the real BOOTP server to
568
* get the reply back here. Then, before we forward the
569
* reply to the client, the giaddr field is corrected.
570
* (In case the client uses giaddr, which it should not.)
571
* See handle_reply()
572
*/
573
bp->bp_giaddr = my_ip_addr;
574
#endif /* BUG */
575
576
/*
577
* XXX - DHCP says to insert a subnet mask option into the
578
* options area of the request (if vendor magic == std).
579
*/
580
}
581
/* Set up socket address for send. */
582
send_addr.sin_family = AF_INET;
583
send_addr.sin_port = htons(bootps_port);
584
send_addr.sin_addr.s_addr = server_ipa;
585
586
/* Send reply with same size packet as request used. */
587
if (sendto(s, pktbuf, pktlen, 0,
588
(struct sockaddr *) &send_addr,
589
sizeof(send_addr)) < 0)
590
{
591
report(LOG_ERR, "sendto: %s", get_network_errmsg());
592
}
593
}
594
595
596
597
/*
598
* Process BOOTREPLY packet.
599
*/
600
static void
601
handle_reply()
602
{
603
struct bootp *bp = (struct bootp *) pktbuf;
604
struct ifreq *ifr;
605
struct sockaddr_in *sip;
606
unsigned char *ha;
607
int len, haf;
608
609
if (debug) {
610
report(LOG_INFO, " reply for %s",
611
inet_ntoa(bp->bp_yiaddr));
612
}
613
/* Make sure client is directly accessible. */
614
ifr = getif(s, &(bp->bp_yiaddr));
615
if (!ifr) {
616
report(LOG_NOTICE, "no interface for reply to %s",
617
inet_ntoa(bp->bp_yiaddr));
618
return;
619
}
620
#if 1 /* Experimental (see BUG above) */
621
/* #ifdef CATER_TO_OLD_CLIENTS ? */
622
/*
623
* The giaddr field has been set to our "default" IP address
624
* which might not be on the same interface as the client.
625
* In case the client looks at giaddr, (which it should not)
626
* giaddr is now set to the address of the correct interface.
627
*/
628
sip = (struct sockaddr_in *) &(ifr->ifr_addr);
629
bp->bp_giaddr = sip->sin_addr;
630
#endif
631
632
/* Set up socket address for send to client. */
633
send_addr.sin_family = AF_INET;
634
send_addr.sin_addr = bp->bp_yiaddr;
635
send_addr.sin_port = htons(bootpc_port);
636
637
if (arpmod) {
638
/* Create an ARP cache entry for the client. */
639
ha = bp->bp_chaddr;
640
len = bp->bp_hlen;
641
struct in_addr dst;
642
643
if (len > MAXHADDRLEN)
644
len = MAXHADDRLEN;
645
haf = (int) bp->bp_htype;
646
if (haf == 0)
647
haf = HTYPE_ETHERNET;
648
649
if (debug > 1)
650
report(LOG_INFO, "setarp %s - %s",
651
inet_ntoa(dst), haddrtoa(ha, len));
652
setarp(s, &dst, haf, ha, len);
653
}
654
655
/* Send reply with same size packet as request used. */
656
if (sendto(s, pktbuf, pktlen, 0,
657
(struct sockaddr *) &send_addr,
658
sizeof(send_addr)) < 0)
659
{
660
report(LOG_ERR, "sendto: %s", get_network_errmsg());
661
}
662
}
663
664
/*
665
* Local Variables:
666
* tab-width: 4
667
* c-indent-level: 4
668
* c-argdecl-indent: 4
669
* c-continued-statement-offset: 4
670
* c-continued-brace-offset: -4
671
* c-label-offset: -4
672
* c-brace-offset: 0
673
* End:
674
*/
675
676