opendir change: refinement
[minix.git] / commands / dhcpd / dhcpd.c
blobb7f190e34d56b1518581ed120695bf94f057347b
1 /* dhcpd 1.15 - Dynamic Host Configuration Protocol daemon.
2 * Author: Kees J. Bot
3 * 11 Jun 1999
4 */
5 #include <sys/types.h>
6 #include <stdio.h>
7 #include <stddef.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <signal.h>
13 #include <string.h>
14 #include <time.h>
15 #include <limits.h>
16 #include <configfile.h>
17 #include <sys/ioctl.h>
18 #include <sys/asynchio.h>
19 #include <net/hton.h>
20 #include <net/gen/socket.h>
21 #include <net/gen/netdb.h>
22 #include <net/gen/in.h>
23 #include <net/gen/inet.h>
24 #include <net/gen/ether.h>
25 #include <net/gen/if_ether.h>
26 #include <net/gen/eth_hdr.h>
27 #include <net/gen/ip_hdr.h>
28 #include <net/gen/udp.h>
29 #include <net/gen/udp_hdr.h>
30 #include <net/gen/udp_io.h>
31 #include <net/gen/dhcp.h>
32 #include "arp.h"
33 #define EXTERN
34 #include "dhcpd.h"
36 char *configfile= PATH_DHCPCONF;
37 char *poolfile= PATH_DHCPPOOL;
38 static char *cachefile= PATH_DHCPCACHE;
39 static int qflag; /* True if printing cached DHCP data. */
40 static int aflag, rflag; /* True if adding or deleting pool addresses. */
42 #ifdef __NBSD_LIBC
43 #define BCAST_IP htonl(0xFFFFFFFFUL)
44 #else
45 #define BCAST_IP HTONL(0xFFFFFFFFUL)
46 #endif
48 /* We try to play with up to this many networks. */
49 #define N_NETS 32
50 static unsigned n_nets; /* Actual number of networks. */
52 int lwip;
54 void report(const char *label)
56 static FILE *logfp;
57 if(!logfp)
58 logfp = fopen("/usr/log/dhcp.log", "w");
59 if(logfp)
60 fprintf(logfp, "%s: %s: %s\n", program, label, strerror(errno));
63 void fatal(const char *label)
65 report(label);
66 exit(1);
69 void *allocate(size_t size)
71 void *mem;
73 if ((mem= malloc(size)) == nil) fatal("Can't allocate memory");
74 return mem;
77 /* Choose a DHCP xid based on the start time and network number. Not really
78 * random, but we don't have anything more random than the clock anyway.
80 #define XID(np) htonl(((u32_t) (np)->start << 8) | (np)->n)
82 static network_t *network[N_NETS];
84 int ifname2if(const char *name)
86 /* Translate an interface name to a number, -1 if bad. */
87 char *end;
88 unsigned long n;
90 if (*name++ != 'i' || *name++ != 'p') return -1;
91 n= strtoul(name, &end, 10);
92 if (end == name || *end != 0) return -1;
93 if (n >= N_NETS) return -1;
94 return n;
97 network_t *if2net(int n)
99 /* Translate an interface number to a network struct. */
100 int i;
102 for (i= 0; i < n_nets; i++) {
103 if (network[i]->n == n) return network[i];
105 return nil;
108 static ipaddr_t defaultmask(ipaddr_t ip)
110 /* Compute netmask by the oldfashioned Class rules. */
111 #ifdef __NBSD_LIBC
112 if (B(&ip)[0] < 0x80) return htonl(0xFF000000UL); /* Class A. */
113 if (B(&ip)[0] < 0xC0) return htonl(0xFFFF0000UL); /* Class B. */
114 if (B(&ip)[0] < 0xE0) return htonl(0xFFFFFF00UL); /* Class C. */
115 return htonl(0xFFFFFFFFUL); /* Multicast? Shouldn't happen... */
116 #else
117 if (B(&ip)[0] < 0x80) return HTONL(0xFF000000UL); /* Class A. */
118 if (B(&ip)[0] < 0xC0) return HTONL(0xFFFF0000UL); /* Class B. */
119 if (B(&ip)[0] < 0xE0) return HTONL(0xFFFFFF00UL); /* Class C. */
120 return HTONL(0xFFFFFFFFUL); /* Multicast? Shouldn't happen... */
121 #endif
124 #ifdef __NBSD_LIBC
125 #define POOL_MAGIC htonl(0x81F85D00UL)
126 #else
127 #define POOL_MAGIC HTONL(0x81F85D00UL)
128 #endif
130 typedef struct pool { /* Dynamic pool entry. */
131 u32_t magic; /* Pool file magic number. */
132 ipaddr_t ip; /* IP address. */
133 u32_t expire; /* When does/did the lease expire? */
134 u8_t len; /* Client ID length. */
135 u8_t unused[19]; /* Space for extensions. */
136 u8_t clid[CLID_MAX]; /* Client ID of current/last user. */
137 } pool_t;
139 static int openpool(int mode)
141 /* Open the dynamic pool and lock it, return fd on success or -1. */
142 int fd;
143 struct flock lck;
145 if ((fd= open(poolfile, mode, 0644)) < 0) {
146 if (errno != ENOENT) fatal(poolfile);
147 return -1;
149 if (mode != O_RDONLY) {
150 lck.l_type= F_WRLCK;
151 lck.l_whence= SEEK_SET;
152 lck.l_start= 0;
153 lck.l_len= 0;
154 if (fcntl(fd, F_SETLKW, &lck) < 0) fatal(poolfile);
156 return fd;
159 static int readpool(int fd, pool_t *entry)
161 /* Read one pool table entry, return true unless EOF. */
162 ssize_t r;
164 if ((r= read(fd, entry, sizeof(*entry))) < 0) fatal(poolfile);
165 if (r == 0) return 0;
167 if (r != sizeof(*entry) || entry->magic != POOL_MAGIC) {
168 fprintf(stderr, "%s: %s: Pool table is corrupt\n",
169 program, poolfile);
170 close(fd);
171 return 0;
173 return 1;
176 static void writepool(int fd, pool_t *entry)
178 /* (Over)write a pool table entry. */
179 if (write(fd, entry, sizeof(*entry)) < 0
180 || (entry->expire > now && fsync(fd) < 0)
182 fatal(poolfile);
186 static ipaddr_t findpool(u8_t *client, size_t len, ipaddr_t ifip)
188 /* Look for a client ID in the dynamic address pool within the same network
189 * as 'ifip'. Select an unused one for a new client if necessary. Return
190 * 0 if nothing is available, otherwise the IP address we can offer.
192 int fd, found;
193 pool_t entry, oldest;
194 dhcp_t dhcp;
195 u8_t *pmask;
196 ipaddr_t mask;
198 /* Any information available on the network the client is at? */
199 if (!makedhcp(&dhcp, nil, 0, nil, 0, ifip, ifip, nil)) return 0;
201 if ((fd= openpool(O_RDWR)) < 0) return 0;
203 (void) gettag(&dhcp, DHCP_TAG_NETMASK, &pmask, nil);
204 memcpy(&mask, pmask, sizeof(mask));
206 oldest.expire= NEVER;
207 while ((found= readpool(fd, &entry))) {
208 /* Deleted entry? */
209 if (entry.ip == 0) continue;
211 /* Correct network? */
212 if (((entry.ip ^ ifip) & mask) != 0) continue;
214 /* Client present? */
215 if (entry.len == len && memcmp(entry.clid, client, len) == 0) break;
217 /* Oldest candidate for a new lease? */
218 entry.expire= ntohl(entry.expire);
219 if (entry.expire < oldest.expire) oldest= entry;
221 close(fd);
223 if (found) return entry.ip;
224 if (oldest.expire <= now) return oldest.ip;
225 return 0;
228 static int commitpool(ipaddr_t ip, u8_t *client, size_t len, time_t expire)
230 /* Commit a new binding to stable storage, return true on success. */
231 int fd;
232 pool_t entry;
234 if ((fd= openpool(O_RDWR)) < 0) return 0;
236 do {
237 if (!readpool(fd, &entry)) {
238 close(fd);
239 return 0;
241 } while (entry.ip != ip);
243 entry.expire= htonl(expire);
244 entry.len= len;
245 memcpy(entry.clid, client, len);
246 if (lseek(fd, -(off_t)sizeof(entry), SEEK_CUR) == -1) fatal(poolfile);
247 writepool(fd, &entry);
248 close(fd);
249 return 1;
252 static void updatepool(int add, const char *name)
254 /* Add a new IP address to the dynamic pool. */
255 ipaddr_t ip;
256 int fd, i;
257 pool_t entry;
258 struct hostent *he;
259 off_t off, off0;
261 if ((he= gethostbyname(name)) == nil || he->h_addrtype != AF_INET) {
262 fprintf(stderr, "%s: %s: Unknown host\n", program, name);
263 exit(1);
265 for (i= 0; he->h_addr_list[i] != nil; i++) {}
266 if (i != 1) {
267 fprintf(stderr, "%s: %s has %d addresses\n", program, name, i);
268 exit(1);
270 memcpy(&ip, he->h_addr_list[0], sizeof(ip));
272 if ((fd= openpool(O_RDWR|O_CREAT)) < 0) fatal(poolfile);
274 off= 0;
275 off0= -1;
276 while (readpool(fd, &entry)) {
277 if (add) {
278 if (entry.ip == ip) {
279 fprintf(stderr, "%s: %s: %s is already present\n",
280 program, poolfile, name);
281 exit(1);
283 if (entry.ip == 0 && off0 == -1) off0= off;
284 } else {
285 if (entry.ip == ip) {
286 memset(&entry, 0, sizeof(entry));
287 entry.magic= POOL_MAGIC;
288 entry.ip= 0;
289 if (lseek(fd, off, SEEK_SET) == -1) fatal(poolfile);
290 writepool(fd, &entry);
293 off+= sizeof(entry);
296 if (add) {
297 if (off0 != -1 && lseek(fd, off0, SEEK_SET) == -1) fatal(poolfile);
298 memset(&entry, 0, sizeof(entry));
299 entry.magic= POOL_MAGIC;
300 entry.ip= ip;
301 writepool(fd, &entry);
303 close(fd);
306 static void cachedhcp(int n, dhcp_t *dp)
308 /* Store a DHCP packet in a cache where those who care can find it. */
309 static int inited;
310 FILE *fp;
311 int fd;
312 int mode;
314 if (test > 0) return;
316 if (!inited) {
317 /* First time, clear store and also save my pid. */
318 if ((fp= fopen(PATH_DHCPPID, "w")) != nil) {
319 if (fprintf(fp, "%d\n", getpid()) == EOF || fclose(fp) == EOF) {
320 fatal(PATH_DHCPPID);
323 inited= 1;
324 mode= O_WRONLY | O_CREAT | O_TRUNC;
325 } else {
326 mode= O_WRONLY;
329 dp->xid= htonl(now); /* To tell how old this data is. */
331 if ((fd= open(cachefile, mode, 0666)) < 0
332 || lseek(fd, (off_t) n * sizeof(*dp), SEEK_SET) == -1
333 || write(fd, dp, sizeof(*dp)) < 0
334 || close(fd) < 0
336 if (errno != ENOENT) fatal(cachefile);
340 static void printdata(void)
342 /* Show the contents of the cache and the dynamic pool. */
343 int fd;
344 dhcp_t d;
345 ssize_t r;
346 int i;
347 pool_t entry;
348 unsigned long expire;
349 char delta[3*sizeof(u32_t)];
351 initdhcpconf();
353 if ((fd= open(cachefile, O_RDONLY)) < 0) fatal(cachefile);
354 i= 0;
355 while ((r= read(fd, &d, sizeof(d))) == sizeof(d)) {
356 if (d.yiaddr != 0) {
357 printf("DHCP data for network %d:\n", i);
358 printdhcp(&d);
360 i++;
362 if (r < 0) fatal(cachefile);
363 close(fd);
365 if ((fd= openpool(O_RDONLY)) >= 0) {
366 printf("Dynamic address pool since %ld:\n", (long) now);
367 while (readpool(fd, &entry)) {
368 if (entry.ip == 0) continue;
369 expire= ntohl(entry.expire);
370 if (expire == 0) {
371 strcpy(delta, "unused");
372 } else
373 if (expire == 0xFFFFFFFFUL) {
374 strcpy(delta, "infinite");
375 } else
376 if (expire < now) {
377 sprintf(delta, "-%lu", now - expire);
378 } else {
379 sprintf(delta, "+%lu", expire - now);
381 printf("\t%-15s %8s ", inet_ntoa(entry.ip), delta);
382 for (i= 0; i < entry.len; i++) {
383 printf("%02X", entry.clid[i]);
385 fputc('\n', stdout);
387 close(fd);
391 static udpport_t portbyname(const char *name)
393 struct servent *se;
395 if ((se= getservbyname(name, "udp")) == nil) {
396 fprintf(stderr, "%s: Unknown port \"%s\"\n", program, name);
397 exit(1);
399 return se->s_port;
402 static int sendpacket(network_t *np, void *data, size_t len)
404 /* Send out a packet using a filedescriptor that is probably in async mode,
405 * so first dup() a sync version, then write. Return true on success.
407 int fd;
408 ssize_t r;
410 if ((fd= dup(np->fdp->fd)) < 0) fatal("Can't dup()");
411 if ((r= write(fd, data, len)) < 0) {
412 report(np->fdp->device);
413 sleep(10);
415 close(fd);
416 return r >= 0;
419 static size_t servdhcp(network_t *np, buf_t *bp, size_t dlen)
421 buf_t *abp= nil;
422 ipaddr_t cip, ifip;
423 u8_t defclid[1+sizeof(bp->dhcp->chaddr)];
424 u8_t *pdata, *client, *class, *server, *reqip, *lease;
425 u32_t expire;
426 size_t len, cilen, calen;
427 int type, dyn;
428 u8_t atype;
429 static char NAKMESS[] = "IP address requested isn't yours";
431 if (test > 0) return 0;
433 /* The IP address of the interface close to the client. */
434 ifip= bp->dhcp->giaddr != 0 ? bp->dhcp->giaddr : np->ip;
436 /* All kinds of DHCP tags. */
437 if (gettag(bp->dhcp, DHCP_TAG_TYPE, &pdata, nil)) {
438 type= *pdata;
439 } else {
440 type= -1; /* BOOTP? */
443 if (!gettag(bp->dhcp, DHCP_TAG_CLIENTID, &client, &cilen)) {
444 defclid[0]= bp->dhcp->htype;
445 memcpy(defclid+1, bp->dhcp->chaddr, bp->dhcp->hlen);
446 client= defclid;
447 cilen= 1+bp->dhcp->hlen;
450 if (!gettag(bp->dhcp, DHCP_TAG_CLASSID, &class, &calen)) {
451 calen= 0;
454 if (!gettag(bp->dhcp, DHCP_TAG_SERVERID, &server, nil)) {
455 server= B(&np->ip);
458 if (!gettag(bp->dhcp, DHCP_TAG_REQIP, &reqip, nil)) {
459 reqip= nil;
462 /* I'm a server? Then see if I know this client. */
463 if ((np->flags & NF_SERVING)
464 && bp->dhcp->op == DHCP_BOOTREQUEST
465 && between(1, bp->dhcp->hlen, sizeof(bp->dhcp->chaddr))
466 && (server == nil || memcmp(server, &np->ip, sizeof(np->ip)) == 0)
468 get_buf(&abp);
470 /* Is the client in my tables? */
471 (void) makedhcp(abp->dhcp, class, calen, client, cilen, 0, ifip, nil);
472 cip= abp->dhcp->yiaddr;
474 dyn= 0;
475 /* If not, do we have a dynamic address? */
476 if (cip == 0 && (cip= findpool(client, cilen, ifip)) != 0) dyn= 1;
478 if (type == DHCP_INFORM) {
479 /* The client already has an address, it just wants information.
480 * We only answer if we could answer a normal request (cip != 0),
481 * unless configured to answer anyone.
483 if (cip != 0 || (np->flags & NF_INFORM)) cip= bp->dhcp->ciaddr;
486 if (cip == 0 || !makedhcp(abp->dhcp, class, calen,
487 client, cilen, cip, ifip, nil)) {
488 put_buf(&abp);
491 if (abp != nil) {
492 if (gettag(abp->dhcp, DHCP_TAG_LEASE, &lease, nil)) {
493 memcpy(&expire, lease, sizeof(expire));
494 expire= now + ntohl(expire);
495 if (expire < now) expire= 0xFFFFFFFFUL;
496 } else {
497 if (dyn) {
498 /* A dynamic address must have a lease. */
499 fprintf(stderr, "%s: No lease set for address %s\n",
500 program, inet_ntoa(cip));
501 exit(1);
503 lease= nil;
504 expire= 0xFFFFFFFFUL;
507 /* What does our client want, and what do we say? */
508 switch (type) {
509 case DHCP_DISCOVER:
510 atype= DHCP_OFFER;
512 /* Assign this address for a short moment. */
513 if (dyn && !commitpool(cip, client, cilen, now + DELTA_FAST)) {
514 put_buf(&abp);
516 break;
518 case -1:/* BOOTP */
519 case DHCP_REQUEST:
520 case DHCP_INFORM:
521 atype= DHCP_ACK;
522 /* The address wanted must be the address we offer. */
523 if ((reqip != nil && memcmp(reqip, &cip, sizeof(cip)) != 0)
524 || (bp->dhcp->ciaddr != 0 && bp->dhcp->ciaddr != cip)
526 atype= DHCP_NAK;
527 } else
528 if (dyn && type == DHCP_REQUEST) {
529 /* Assign this address for the duration of the lease. */
530 if (!commitpool(cip, client, cilen, expire)) put_buf(&abp);
532 break;
534 case DHCP_DECLINE:
535 /* Our client doesn't want the offered address! */
536 if (dyn
537 && reqip != nil
538 && memcmp(reqip, &cip, sizeof(cip)) == 0
540 int i;
542 fprintf(stderr, "%s: ", program);
543 for (i= 0; i < cilen; i++) {
544 fprintf(stderr, "%02X", client[i]);
546 fprintf(stderr, " declines %s", inet_ntoa(cip));
547 if (gettag(bp->dhcp, DHCP_TAG_MESSAGE, &pdata, &len)) {
548 fprintf(stderr, " saying: \"%.*s\"", (int)len, pdata);
550 fputc('\n', stderr);
552 /* Disable address for the duration of the lease. */
553 (void) commitpool(cip, nil, 0, expire);
555 put_buf(&abp);
556 break;
558 case DHCP_RELEASE:
559 /* Our client is nice enough to return its address. */
560 if (dyn) (void) commitpool(cip, client, cilen, now);
561 put_buf(&abp);
562 break;
564 default: /* Anything else is ignored. */
565 put_buf(&abp);
569 if (abp != nil) {
570 /* Finish the return packet. */
571 abp->dhcp->htype= bp->dhcp->htype;
572 abp->dhcp->hlen= bp->dhcp->hlen;
573 abp->dhcp->hops= 0;
574 abp->dhcp->xid= bp->dhcp->xid;
575 abp->dhcp->secs= 0;
576 abp->dhcp->flags= bp->dhcp->flags;
577 abp->dhcp->ciaddr= 0;
578 abp->dhcp->yiaddr= atype == DHCP_NAK ? 0 : cip;
579 if (atype == DHCP_NAK) abp->dhcp->siaddr= 0;
580 abp->dhcp->giaddr= bp->dhcp->giaddr;
581 memcpy(abp->dhcp->chaddr,bp->dhcp->chaddr,sizeof(bp->dhcp->chaddr));
583 settag(abp->dhcp, DHCP_TAG_SERVERID, &np->ip, sizeof(np->ip));
585 if (lease == nil) {
586 /* No lease specified? Then give an infinite lease. */
587 settag(abp->dhcp, DHCP_TAG_LEASE, &expire, sizeof(expire));
590 if (type == DHCP_INFORM) {
591 /* Oops, this one has a fixed address, so no lease business. */
592 abp->dhcp->yiaddr= 0;
593 settag(abp->dhcp, DHCP_TAG_LEASE, nil, 0);
594 settag(abp->dhcp, DHCP_TAG_RENEWAL, nil, 0);
595 settag(abp->dhcp, DHCP_TAG_REBINDING, nil, 0);
598 if (atype == DHCP_NAK) {
599 /* A NAK doesn't need much. */
600 memset(abp->dhcp->sname, 0, sizeof(abp->dhcp->sname));
601 memset(abp->dhcp->file, 0, sizeof(abp->dhcp->file));
602 memset(abp->dhcp->options, 255, sizeof(abp->dhcp->options));
603 settag(abp->dhcp, DHCP_TAG_MESSAGE, NAKMESS, sizeof(NAKMESS));
606 settag(abp->dhcp, DHCP_TAG_TYPE, &atype, sizeof(atype));
608 /* Figure out where to send this to. */
609 abp->udpio->uih_src_addr= np->ip;
610 abp->udpio->uih_src_port= port_server;
611 if (bp->dhcp->giaddr != 0) {
612 abp->udpio->uih_dst_addr= bp->dhcp->giaddr;
613 abp->udpio->uih_dst_port= port_server;
614 } else
615 if (bp->dhcp->flags & DHCP_FLAGS_BCAST) {
616 abp->udpio->uih_dst_addr= BCAST_IP;
617 abp->udpio->uih_dst_port= port_client;
618 } else
619 if (bp->udpio->uih_src_addr != 0
620 && bp->udpio->uih_dst_addr == np->ip
622 abp->udpio->uih_dst_addr= bp->udpio->uih_src_addr;
623 abp->udpio->uih_dst_port= port_client;
624 } else {
625 abp->udpio->uih_dst_addr= BCAST_IP;
626 abp->udpio->uih_dst_port= port_client;
628 abp->udpio->uih_ip_opt_len= 0;
629 abp->udpio->uih_data_len= sizeof(dhcp_t);
631 /* Copy the packet to the input buffer, and return the new size. */
632 memcpy(bp->buf, abp->buf, sizeof(bp->buf));
633 put_buf(&abp);
634 return sizeof(udp_io_hdr_t) + sizeof(dhcp_t);
638 /* I'm a relay? If it is a not already a relayed request then relay. */
639 if ((np->flags & NF_RELAYING)
640 && bp->dhcp->op == DHCP_BOOTREQUEST
641 && bp->dhcp->giaddr == 0
643 bp->dhcp->giaddr= np->ip;
644 bp->udpio->uih_src_addr= np->ip;
645 bp->udpio->uih_src_port= port_server;
646 bp->udpio->uih_dst_addr= np->server;
647 bp->udpio->uih_dst_port= port_server;
648 return dlen;
651 /* I'm a relay? If the server sends a reply to me then relay back. */
652 if ((np->flags & NF_RELAYING)
653 && bp->dhcp->op == DHCP_BOOTREPLY
654 && bp->dhcp->giaddr == np->ip
656 bp->dhcp->giaddr= 0;
657 bp->udpio->uih_src_addr= np->ip;
658 bp->udpio->uih_src_port= port_server;
659 bp->udpio->uih_dst_addr= BCAST_IP;
660 bp->udpio->uih_dst_port= port_client;
661 return dlen;
664 /* Don't know what to do otherwise, so doing nothing seems wise. */
665 return 0;
668 static void onsig(int sig)
670 switch (sig) {
671 case SIGUSR1: debug++; break;
672 case SIGUSR2: debug= 0; break;
676 static void usage(void)
678 fprintf(stderr,
679 "Usage: %s [-qar] [-t[L]] [-d[L]] [-f config] [-c cache] [-p pool] [host ...]\n",
680 program);
681 exit(1);
684 int main(int argc, char **argv)
686 int i;
687 network_t *np;
688 struct sigaction sa;
689 ssize_t r;
690 buf_t *bp;
691 static struct timeval eventtv;
693 main:
694 r = -1;
695 bp = nil;
696 program= argv[0];
697 start= now= time(nil);
699 debug= 0;
700 i= 1;
701 while (i < argc && argv[i][0] == '-') {
702 char *opt= argv[i++]+1;
704 if (opt[0] == '-' && opt[1] == 0) break; /* -- */
706 if (strcmp(opt, "-lwip") == 0) {
707 lwip = 1;
708 continue;
711 while (*opt != 0) switch (*opt++) {
712 case 'f':
713 if (*opt == 0) {
714 if (i == argc) usage();
715 opt= argv[i++];
717 configfile= opt;
718 opt= "";
719 break;
720 case 'c':
721 if (*opt == 0) {
722 if (i == argc) usage();
723 opt= argv[i++];
725 cachefile= opt;
726 opt= "";
727 break;
728 case 'p':
729 if (*opt == 0) {
730 if (i == argc) usage();
731 opt= argv[i++];
733 poolfile= opt;
734 opt= "";
735 break;
736 case 't':
737 test= 1;
738 if (between('0', *opt, '9')) test= strtoul(opt, &opt, 10);
739 break;
740 case 'd':
741 debug= 1;
742 if (between('0', *opt, '9')) debug= strtoul(opt, &opt, 10);
743 break;
744 case 'q':
745 qflag= 1;
746 break;
747 case 'a':
748 aflag= 1;
749 break;
750 case 'r':
751 rflag= 1;
752 break;
753 default:
754 usage();
757 if (aflag + rflag + qflag > 1) usage();
759 if (aflag || rflag) {
760 /* Add or remove addresses from the dynamic pool. */
761 while (i < argc) updatepool(aflag, argv[i++]);
762 exit(0);
765 if (i != argc) usage();
767 if (qflag) {
768 /* Only show the contents of the cache and dynamic pool to the user. */
769 printdata();
770 exit(0);
773 /* BOOTP ports. */
774 port_server= portbyname("bootps");
775 port_client= portbyname("bootpc");
777 sa.sa_handler= onsig;
778 sigemptyset(&sa.sa_mask);
779 sa.sa_flags= 0;
780 sigaction(SIGUSR1, &sa, nil);
781 sigaction(SIGUSR2, &sa, nil);
783 /* Initial configuration. */
784 for (i= 0; i < N_NETS; i++) {
785 int fd;
786 ipaddr_t ip, mask;
788 /* Is there something there? */
789 if ((fd= open(ipdev(i), O_RDWR|O_NONBLOCK)) < 0) {
790 if (errno != ENOENT && errno != ENODEV && errno != ENXIO) {
791 fatal(ipdev(i));
793 continue;
795 close(fd);
797 network[n_nets++]= np= newnetwork();
798 np->n= i;
800 /* Ethernet? */
801 if (lwip) {
802 np->type = NT_ETHERNET;
803 } else if (opendev(np, FT_ETHERNET, 1)) {
804 np->type= B(&np->eth)[0] != 'Z' ? NT_ETHERNET : NT_SINK;
805 if (debug >= 1) {
806 printf("%s: Ethernet address is %s%s\n",
807 np->fdp->device, ether_ntoa(&np->eth),
808 np->type == NT_SINK ? " (sink)" : "");
810 closedev(np, FT_ETHERNET);
813 /* Only true Ethernets worry about DHCP. */
814 if (np->type != NT_ETHERNET) np->renew= np->rebind= np->lease= NEVER;
817 /* Try to find my interfaces in the DHCP table. */
818 for (i= 0; i < n_nets; i++) {
819 ipaddr_t cip;
820 u8_t clid[1+DHCP_HLEN_ETH];
821 size_t cilen;
823 np= network[i];
824 if (np->flags & NF_BOUND) continue;
826 if (np->type == NT_IP) {
827 cilen= 0;
828 } else {
829 ether2clid(clid, &np->eth);
830 cilen= 1+DHCP_HLEN_ETH;
833 /* Try to find an Ethernet address, or the IP address of an already
834 * configured network. If we have data we get an IP address back.
836 get_buf(&bp);
837 (void) makedhcp(bp->dhcp, (u8_t *) "Minix", 5,
838 clid, cilen, np->ip, 0, np);
839 cip= bp->dhcp->yiaddr;
841 /* Gather information on the interface. */
842 if (cip != 0
843 && makedhcp(bp->dhcp, (u8_t *) "Minix", 5,
844 clid, cilen, cip, cip, np)
845 && test < 2
847 u8_t *pdata;
848 u16_t mtu;
850 cachedhcp(np->n, bp->dhcp);
851 np->ip= cip;
852 (void) gettag(bp->dhcp, DHCP_TAG_NETMASK, &pdata, nil);
853 memcpy(&np->mask, pdata, sizeof(np->mask));
854 if (gettag(bp->dhcp, DHCP_TAG_GATEWAY, &pdata, nil)) {
855 memcpy(&np->gateway, pdata, sizeof(np->gateway));
856 } else {
857 np->gateway= 0;
859 if (gettag(bp->dhcp, DHCP_TAG_IPMTU, &pdata, nil)) {
860 memcpy(&mtu, pdata, sizeof(mtu));
861 mtu= ntohs(mtu);
862 } else {
863 mtu= 0;
865 set_ipconf(ipdev(np->n), np->ip, np->mask, mtu);
866 if (debug >= 1) {
867 printf("%s: IP address is %s\n",
868 ipdev(np->n), cidr_ntoa(np->ip, np->mask));
870 np->flags |= NF_BOUND;
871 np->renew= np->rebind= np->lease= NEVER;
872 np->sol_ct= N_SOLICITS;
873 np->solicit= 0;
875 /* Other (previous) interfaces may have been defined. */
876 i= 0;
878 put_buf(&bp);
881 for (;;) {
882 now= time(nil);
883 event= NEVER;
885 /* Is it time to request/renew a lease? */
886 for (i= 0; i < n_nets; i++) {
887 np= network[i];
889 if (np->renew <= now) {
890 u8_t type;
891 static u8_t taglist[] = {
892 DHCP_TAG_NETMASK, DHCP_TAG_GATEWAY, DHCP_TAG_DNS,
893 DHCP_TAG_HOSTNAME
895 u8_t ethclid[1+DHCP_HLEN_ETH];
897 /* We may have lost our binding or even our lease. */
898 if (np->rebind <= now) np->server= BCAST_IP;
900 if (np->lease <= now) {
901 if (np->flags & NF_BOUND) closedev(np, FT_ALL);
903 if ((np->flags & (NF_BOUND | NF_POSSESSIVE)) == NF_BOUND) {
904 set_ipconf(ipdev(np->n), np->ip= 0, np->mask= 0, 0);
905 if (debug >= 1) {
906 printf("%s: Interface disabled (lease expired)\n",
907 ipdev(np->n));
910 np->flags &= ~NF_BOUND;
913 /* See if we can open the network we need to send on. */
914 if (!(np->flags & NF_BOUND)) {
915 if (!opendev(np, FT_ETHERNET, 1)) continue;
916 } else {
917 if (!opendev(np, FT_BOOTPC, 1)) continue;
920 if (!(np->flags & NF_NEGOTIATING)) {
921 /* We need to start querying a DHCP server. */
922 np->start= now;
923 np->delta= DELTA_FIRST;
924 np->flags |= NF_NEGOTIATING;
927 /* Fill in a DHCP query packet. */
928 get_buf(&bp);
929 dhcp_init(bp->dhcp);
930 bp->dhcp->op= DHCP_BOOTREQUEST;
931 bp->dhcp->htype= DHCP_HTYPE_ETH;
932 bp->dhcp->hlen= DHCP_HLEN_ETH;
933 bp->dhcp->xid= XID(np);
934 bp->dhcp->secs= htons(now - np->start > 0xFFFF
935 ? 0xFFFF : now - np->start);
936 memcpy(bp->dhcp->chaddr, &np->eth, sizeof(np->eth));
938 if (np->lease <= now) {
939 /* First time, or my old server is unresponsive. */
940 type= DHCP_DISCOVER;
941 } else {
942 /* Request an offered address or renew an address. */
943 type= DHCP_REQUEST;
944 if (np->flags & NF_BOUND) {
945 /* A renewal, I claim my current address. */
946 bp->dhcp->ciaddr= np->ip;
947 } else {
948 /* Nicely ask for the address just offered. */
949 settag(bp->dhcp, DHCP_TAG_REQIP, &np->ip,
950 sizeof(np->ip));
951 settag(bp->dhcp, DHCP_TAG_SERVERID, &np->server,
952 sizeof(np->server));
955 settag(bp->dhcp, DHCP_TAG_TYPE, &type, 1);
957 /* My client ID. Simply use the default. */
958 ether2clid(ethclid, &np->eth);
959 settag(bp->dhcp, DHCP_TAG_CLIENTID, ethclid, sizeof(ethclid));
961 /* The Class ID may serve to recognize Minix hosts. */
962 settag(bp->dhcp, DHCP_TAG_CLASSID, "Minix", 5);
964 /* The few tags that Minix can make good use of. */
965 settag(bp->dhcp, DHCP_TAG_REQPAR, taglist, sizeof(taglist));
967 /* Some weird sites use a hostname, not a client ID. */
968 if (np->hostname != nil) {
969 settag(bp->dhcp, DHCP_TAG_HOSTNAME,
970 np->hostname, strlen(np->hostname));
973 bp->udpio->uih_src_addr= np->ip;
974 bp->udpio->uih_dst_addr= np->server;
975 bp->udpio->uih_src_port= port_client;
976 bp->udpio->uih_dst_port= port_server;
977 bp->udpio->uih_ip_opt_len= 0;
978 bp->udpio->uih_data_len= sizeof(dhcp_t);
980 if (!(np->flags & NF_BOUND)) {
981 /* Rebind over Ethernet. */
982 udp2ether(bp, np);
983 if (sendpacket(np, (lwip ? bp->ip : (void *) bp->eth),
984 (lwip ? 0 : sizeof(eth_hdr_t)) + sizeof(ip_hdr_t)
985 + sizeof(udp_hdr_t) + sizeof(dhcp_t))) {
986 if (debug >= 1) {
987 printf("%s: Broadcast DHCP %s\n",
988 np->fdp->device, dhcptypename(type));
989 if (debug >= 2) printdhcp(bp->dhcp);
992 } else {
993 /* Renew over UDP. */
994 if (sendpacket(np, bp->udpio, sizeof(udp_io_hdr_t)
995 + sizeof(dhcp_t))) {
996 if (debug >= 1) {
997 printf("%s: Sent DHCP %s to %s\n",
998 np->fdp->device,
999 dhcptypename(type),
1000 inet_ntoa(np->server));
1001 if (debug >= 2) printdhcp(bp->dhcp);
1005 put_buf(&bp);
1007 /* When to continue querying a DHCP server? */
1008 if (np->flags & NF_BOUND) {
1009 /* Still bound, keep halving time till next event. */
1010 time_t e, d;
1012 e= now < np->rebind ? np->rebind : np->lease;
1013 d= (e - now) / 2;
1014 if (d < DELTA_SLOW) d= DELTA_SLOW;
1015 np->renew= now + d;
1016 if (np->renew > e) np->renew= e;
1017 } else {
1018 /* Not bound, be desparate. */
1019 np->renew= now + np->delta;
1020 if ((np->delta *= 2) > DELTA_FAST) np->delta= DELTA_FAST;
1023 if (np->renew < event) event= np->renew;
1026 /* Read DHCP responses. */
1027 for (i= 0; i < n_nets; i++) {
1028 np= network[i];
1029 if (!(np->flags & NF_NEGOTIATING)) continue;
1031 if (!(np->flags & NF_BOUND)) {
1032 if (!opendev(np, FT_ETHERNET, 0)) continue;
1033 get_buf(&np->fdp->bp);
1034 r= asyn_read(&asyn, np->fdp->fd,
1035 lwip ? np->fdp->bp->ip : (void *) np->fdp->bp->eth,
1036 lwip ? BUF_IP_SIZE : BUF_ETH_SIZE);
1037 } else {
1038 if (!opendev(np, FT_BOOTPC, 0)) continue;
1039 get_buf(&np->fdp->bp);
1040 r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->udpio,
1041 BUF_UDP_SIZE);
1043 if (r != -1) break;
1044 if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) {
1045 report(np->fdp->device);
1046 sleep(10);
1050 /* Is there a response? */
1051 if (i < n_nets) {
1052 give_buf(&bp, &np->fdp->bp);
1053 if (((!(np->flags & NF_BOUND)
1054 && r >= (lwip ? 0 : (sizeof(eth_hdr_t)) + sizeof(ip_hdr_t)
1055 + sizeof(udp_hdr_t) + offsetof(dhcp_t, options))
1056 && ether2udp(bp)
1057 && bp->udpio->uih_dst_port == port_client)
1059 ((np->flags & NF_BOUND)
1060 && r >= sizeof(udp_io_hdr_t) + offsetof(dhcp_t, options)))
1061 && bp->dhcp->op == DHCP_BOOTREPLY
1062 && bp->dhcp->htype == DHCP_HTYPE_ETH
1063 && bp->dhcp->hlen == DHCP_HLEN_ETH
1064 && bp->dhcp->xid == XID(np)
1065 && memcmp(bp->dhcp->chaddr, &np->eth, sizeof(np->eth)) == 0
1067 /* Pfew! We got a DHCP reply! */
1068 u8_t *pdata;
1069 size_t len;
1070 int type;
1071 ipaddr_t mask, gateway, relay, server;
1072 u16_t mtu;
1073 u32_t lease, renew, rebind, t;
1075 relay= bp->udpio->uih_src_addr;
1076 if (gettag(bp->dhcp, DHCP_TAG_SERVERID, &pdata, nil)) {
1077 memcpy(&server, pdata, sizeof(server));
1078 } else {
1079 server= relay;
1082 if (gettag(bp->dhcp, DHCP_TAG_TYPE, &pdata, nil)) {
1083 type= pdata[0];
1084 } else {
1085 type= DHCP_ACK; /* BOOTP? */
1088 if (debug >= 1) {
1089 printf("%s: Got a DHCP %s from %s",
1090 np->fdp->device, dhcptypename(type), inet_ntoa(server));
1091 printf(relay != server ? " through %s\n" : "\n",
1092 inet_ntoa(relay));
1093 if (debug >= 2) printdhcp(bp->dhcp);
1096 if (gettag(bp->dhcp, DHCP_TAG_NETMASK, &pdata, nil)) {
1097 memcpy(&mask, pdata, sizeof(mask));
1098 } else {
1099 mask= defaultmask(bp->dhcp->ciaddr);
1102 if (gettag(bp->dhcp, DHCP_TAG_IPMTU, &pdata, nil)) {
1103 memcpy(&mtu, pdata, sizeof(mtu));
1104 mtu= ntohs(mtu);
1105 } else {
1106 mtu= 0;
1109 if (gettag(bp->dhcp, DHCP_TAG_GATEWAY, &pdata, nil)) {
1110 memcpy(&gateway, pdata, sizeof(gateway));
1111 } else {
1112 gateway= 0;
1115 lease= NEVER;
1116 if (gettag(bp->dhcp, DHCP_TAG_LEASE, &pdata, nil)) {
1117 memcpy(&lease, pdata, sizeof(lease));
1118 lease= ntohl(lease);
1121 rebind= lease - lease / 8;
1122 if (gettag(bp->dhcp, DHCP_TAG_REBINDING, &pdata, nil)) {
1123 memcpy(&t, pdata, sizeof(t));
1124 t= ntohl(t);
1125 if (t < rebind) rebind= t;
1128 renew= lease / 2;
1129 if (gettag(bp->dhcp, DHCP_TAG_RENEWAL, &pdata, nil)) {
1130 memcpy(&t, pdata, sizeof(t));
1131 t= ntohl(t);
1132 if (t < renew) renew= t;
1135 if (type == DHCP_OFFER && np->rebind <= np->renew) {
1136 /* It's an offer for an address and we haven't taken one
1137 * yet. It's all the same to us, so take this one.
1139 np->ip= bp->dhcp->yiaddr;
1140 np->mask= mask;
1141 np->server= server;
1142 np->gateway= gateway;
1143 np->delta= DELTA_FIRST;
1144 np->renew= now;
1145 np->rebind= np->lease= now + DELTA_FAST;
1147 /* Send out an ARP request to see if the offered address
1148 * is in use already.
1150 make_arp(bp, np);
1151 if (sendpacket(np, bp->eth, sizeof(arp46_t))) {
1152 if (debug >= 2) {
1153 printf("Sent ARP for %s\n", inet_ntoa(np->ip));
1156 np->flags &= ~NF_CONFLICT;
1159 if (type == DHCP_ACK && !(np->flags & NF_CONFLICT)) {
1160 /* An acknowledgment. The address is all mine. */
1161 cachedhcp(np->n, bp->dhcp);
1162 np->ip= bp->dhcp->yiaddr;
1163 np->mask= mask;
1164 np->server= server;
1165 set_ipconf(ipdev(np->n), np->ip, np->mask, mtu);
1166 if (debug >= 1) {
1167 printf("%s: Address set to %s\n",
1168 ipdev(np->n), cidr_ntoa(np->ip, np->mask));
1170 if (lease >= NEVER - now) {
1171 /* The lease is infinite! */
1172 np->renew= np->rebind= np->lease= NEVER;
1173 } else {
1174 np->lease= now + lease;
1175 np->renew= now + renew;
1176 np->rebind= now + rebind;
1178 if (test >= 3) {
1179 np->renew= now + 60;
1180 np->rebind= test >= 4 ? np->renew : np->renew + 60;
1181 np->lease= test >= 5 ? np->rebind : np->rebind + 60;
1183 if (!(np->flags & NF_IRDP)) {
1184 np->sol_ct= (np->flags & NF_BOUND) ? 1 : N_SOLICITS;
1185 np->solicit= 0;
1187 np->flags &= ~NF_NEGOTIATING;
1188 np->flags |= NF_BOUND;
1189 closedev(np, FT_ETHERNET);
1190 closedev(np, FT_BOOTPC);
1193 if (type == DHCP_ACK && (np->flags & NF_CONFLICT)) {
1194 /* Alas there is a conflict. Decline to use the address. */
1195 u8_t ethclid[1+DHCP_HLEN_ETH];
1196 static char USED[]= "Address in use by 00:00:00:00:00:00";
1198 type= DHCP_DECLINE;
1199 dhcp_init(bp->dhcp);
1200 bp->dhcp->op= DHCP_BOOTREQUEST;
1201 bp->dhcp->htype= DHCP_HTYPE_ETH;
1202 bp->dhcp->hlen= DHCP_HLEN_ETH;
1203 bp->dhcp->xid= XID(np);
1204 bp->dhcp->secs= 0;
1205 memcpy(bp->dhcp->chaddr, &np->eth, sizeof(np->eth));
1206 settag(bp->dhcp, DHCP_TAG_REQIP, &np->ip, sizeof(np->ip));
1207 settag(bp->dhcp, DHCP_TAG_TYPE, &type, 1);
1208 ether2clid(ethclid, &np->eth);
1209 settag(bp->dhcp, DHCP_TAG_CLIENTID,ethclid,sizeof(ethclid));
1210 strcpy(USED+18, ether_ntoa(&np->conflict));
1211 settag(bp->dhcp, DHCP_TAG_MESSAGE, USED, strlen(USED));
1213 bp->udpio->uih_src_port= port_client;
1214 bp->udpio->uih_dst_port= port_server;
1215 bp->udpio->uih_ip_opt_len= 0;
1216 bp->udpio->uih_data_len= sizeof(dhcp_t);
1217 udp2ether(bp, np);
1219 if (sendpacket(np, bp->eth,
1220 sizeof(eth_hdr_t) + sizeof(ip_hdr_t)
1221 + sizeof(udp_hdr_t) + sizeof(dhcp_t))) {
1222 if (debug >= 1) {
1223 printf("%s: Broadcast DHCP %s\n",
1224 np->fdp->device, dhcptypename(type));
1225 if (debug >= 2) printdhcp(bp->dhcp);
1229 np->renew= np->rebind= np->lease= now + DELTA_FAST;
1230 np->delta= DELTA_FIRST;
1233 if (type == DHCP_NAK) {
1234 /* Oops, a DHCP server doesn't like me, start over! */
1235 np->renew= np->rebind= np->lease= now + DELTA_FAST;
1236 np->delta= DELTA_FIRST;
1238 fprintf(stderr, "%s: Got a NAK from %s",
1239 program, inet_ntoa(server));
1240 if (relay != server) {
1241 fprintf(stderr, " through %s", inet_ntoa(relay));
1243 if (gettag(bp->dhcp, DHCP_TAG_MESSAGE, &pdata, &len)) {
1244 fprintf(stderr, " saying: \"%.*s\"", (int)len, pdata);
1246 fputc('\n', stderr);
1248 } else
1249 if (!(np->flags & NF_BOUND)
1250 && np->rebind > now
1251 && r >= sizeof(arp46_t)
1252 && is_arp_me(bp, np)
1254 /* Oh no, someone else is using the address offered to me! */
1255 np->flags |= NF_CONFLICT;
1257 fprintf(stderr, "%s: %s: %s offered by ",
1258 program,
1259 np->fdp->device,
1260 inet_ntoa(np->ip));
1261 fprintf(stderr, "%s is already in use by %s\n",
1262 inet_ntoa(np->server),
1263 ether_ntoa(&np->conflict));
1265 put_buf(&bp);
1266 if (np->renew < event) event= np->renew;
1269 /* Perform router solicitations. */
1270 for (i= 0; i < n_nets; i++) {
1271 np= network[i];
1272 if (!(np->flags & NF_BOUND)) continue;
1274 if (np->solicit <= now) {
1275 if (!opendev(np, FT_ICMP, 1)) continue;
1276 np->solicit= NEVER;
1278 get_buf(&bp);
1279 if (np->gateway != 0) {
1280 /* No IRDP response seen yet, advertise the router given
1281 * by DHCP to my own interface.
1283 icmp_advert(bp, np);
1284 if (sendpacket(np, bp->ip, sizeof(ip_hdr_t) + 16)) {
1285 if (debug >= 2) {
1286 printf("%s: Sent advert for %s to self\n",
1287 np->fdp->device, inet_ntoa(np->gateway));
1290 np->solicit= now + DELTA_ADV/2;
1293 if (np->sol_ct >= 0 && --np->sol_ct >= 0) {
1294 /* Send a router solicitation. */
1295 icmp_solicit(bp);
1296 if (sendpacket(np, bp->ip, sizeof(*bp->ip) + 8)) {
1297 if (debug >= 2) {
1298 printf("%s: Broadcast router solicitation\n",
1299 np->fdp->device);
1302 np->solicit= now + DELTA_SOL;
1303 } else {
1304 /* No response, or not soliciting right now. */
1305 closedev(np, FT_ICMP);
1308 put_buf(&bp);
1310 if (np->solicit < event) event= np->solicit;
1313 /* Read router adverts. */
1314 for (i= 0; i < n_nets; i++) {
1315 np= network[i];
1316 if (!(np->flags & NF_BOUND)) continue;
1317 if (np->sol_ct < 0) continue;
1319 if (!opendev(np, FT_ICMP, 0)) continue;
1320 get_buf(&np->fdp->bp);
1321 r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->ip, BUF_IP_SIZE);
1322 if (r != -1) break;
1323 if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) {
1324 report(np->fdp->device);
1325 sleep(10);
1329 /* Is there an advert? */
1330 if (i < n_nets && r >= sizeof(ip_hdr_t) + 8) {
1331 ipaddr_t router;
1333 give_buf(&bp, &np->fdp->bp);
1334 if ((router= icmp_is_advert(bp)) != 0) {
1335 if (debug >= 2) {
1336 printf("%s: Router advert received from %s\n",
1337 np->fdp->device, inet_ntoa(router));
1339 np->solicit= NEVER;
1340 np->sol_ct= -1;
1341 np->flags |= NF_IRDP;
1342 closedev(np, FT_ICMP);
1344 put_buf(&bp);
1347 /* We start serving if all the interfaces so marked are configured. */
1348 for (i= 0; i < n_nets; i++) {
1349 np= network[i];
1350 if ((np->flags & NF_RELAYING) && (np->flags & NF_BOUND)) {
1351 if (((np->ip ^ np->server) & np->mask) == 0) {
1352 /* Don't relay to a server that is on this same net. */
1353 np->flags &= ~NF_RELAYING;
1356 if (!(np->flags & (NF_SERVING|NF_RELAYING))) continue;
1357 if (!(np->flags & NF_BOUND)) { serving= 0; break; }
1358 serving= 1;
1361 /* Read DHCP requests. */
1362 for (i= 0; i < n_nets; i++) {
1363 np= network[i];
1364 if (!(np->flags & NF_BOUND)) continue;
1365 if (!(np->flags & (NF_SERVING|NF_RELAYING)) || !serving) continue;
1367 if (!opendev(np, FT_BOOTPS, 0)) continue;
1368 get_buf(&np->fdp->bp);
1369 r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->udpio, BUF_UDP_SIZE);
1371 if (r != -1) break;
1372 if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) {
1373 report(np->fdp->device);
1374 sleep(10);
1378 /* Is there a request? */
1379 if (i < n_nets
1380 && r >= sizeof(udp_io_hdr_t) + offsetof(dhcp_t, options)
1382 give_buf(&bp, &np->fdp->bp);
1384 if (debug >= 1) {
1385 printf("%s: Got DHCP packet from %s to ",
1386 np->fdp->device, inet_ntoa(bp->udpio->uih_src_addr));
1387 printf("%s\n", inet_ntoa(bp->udpio->uih_dst_addr));
1388 if (debug >= 2) printdhcp(bp->dhcp);
1391 /* Can we do something with this DHCP packet? */
1392 if ((r= servdhcp(np, bp, r)) > 0) {
1393 /* Yes, we have something to send somewhere. */
1394 if (sendpacket(np, bp->udpio, r)) {
1395 if (debug >= 1) {
1396 printf("%s: Sent DHCP packet to %s\n",
1397 np->fdp->device,
1398 inet_ntoa(bp->udpio->uih_dst_addr));
1399 if (debug >= 2) printdhcp(bp->dhcp);
1403 put_buf(&bp);
1406 if (debug >= 1) {
1407 static char *lastbrk;
1408 extern char _end;
1410 if (sbrk(0) != lastbrk) {
1411 lastbrk= sbrk(0);
1412 printf("Memory use = %lu\n",
1413 (unsigned long) (lastbrk - &_end));
1415 fflush(stdout);
1418 /* Bail out if not a server, and there is nothing else to do ever. */
1419 if (!serving && event == NEVER) break;
1421 /* Wait for something to do. */
1422 eventtv.tv_sec= event;
1423 if (asyn_wait(&asyn, 0, event == NEVER ? nil : &eventtv) < 0) {
1424 if (errno != EINTR) {
1425 report("asyn_wait()");
1426 sleep(10);
1430 if (debug >= 1) printf("Nothing more to do! Starting over...\n");
1431 sleep(2);
1432 goto main;
1434 return 0;