1 /* dhcpd 1.15 - Dynamic Host Configuration Protocol daemon.
16 #include <configfile.h>
17 #include <sys/ioctl.h>
18 #include <sys/asynchio.h>
20 #include <net/gen/socket.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>
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 #define BCAST_IP htonl(0xFFFFFFFFUL)
44 /* We try to play with up to this many networks. */
46 static unsigned n_nets
; /* Actual number of networks. */
50 void report(const char *label
)
54 logfp
= fopen("/usr/log/dhcp.log", "w");
56 fprintf(logfp
, "%s: %s: %s\n", program
, label
, strerror(errno
));
59 void fatal(const char *label
)
65 void *allocate(size_t size
)
69 if ((mem
= malloc(size
)) == nil
) fatal("Can't allocate memory");
73 /* Choose a DHCP xid based on the start time and network number. Not really
74 * random, but we don't have anything more random than the clock anyway.
76 #define XID(np) htonl(((u32_t) (np)->start << 8) | (np)->n)
78 static network_t
*network
[N_NETS
];
80 int ifname2if(const char *name
)
82 /* Translate an interface name to a number, -1 if bad. */
86 if (*name
++ != 'i' || *name
++ != 'p') return -1;
87 n
= strtoul(name
, &end
, 10);
88 if (end
== name
|| *end
!= 0) return -1;
89 if (n
>= N_NETS
) return -1;
93 network_t
*if2net(int n
)
95 /* Translate an interface number to a network struct. */
98 for (i
= 0; i
< n_nets
; i
++) {
99 if (network
[i
]->n
== n
) return network
[i
];
104 static ipaddr_t
defaultmask(ipaddr_t ip
)
106 /* Compute netmask by the oldfashioned Class rules. */
107 if (B(&ip
)[0] < 0x80) return htonl(0xFF000000UL
); /* Class A. */
108 if (B(&ip
)[0] < 0xC0) return htonl(0xFFFF0000UL
); /* Class B. */
109 if (B(&ip
)[0] < 0xE0) return htonl(0xFFFFFF00UL
); /* Class C. */
110 return htonl(0xFFFFFFFFUL
); /* Multicast? Shouldn't happen... */
113 #define POOL_MAGIC htonl(0x81F85D00UL)
115 typedef struct pool
{ /* Dynamic pool entry. */
116 u32_t magic
; /* Pool file magic number. */
117 ipaddr_t ip
; /* IP address. */
118 u32_t expire
; /* When does/did the lease expire? */
119 u8_t len
; /* Client ID length. */
120 u8_t unused
[19]; /* Space for extensions. */
121 u8_t clid
[CLID_MAX
]; /* Client ID of current/last user. */
124 static int openpool(int mode
)
126 /* Open the dynamic pool and lock it, return fd on success or -1. */
130 if ((fd
= open(poolfile
, mode
, 0644)) < 0) {
131 if (errno
!= ENOENT
) fatal(poolfile
);
134 if (mode
!= O_RDONLY
) {
136 lck
.l_whence
= SEEK_SET
;
139 if (fcntl(fd
, F_SETLKW
, &lck
) < 0) fatal(poolfile
);
144 static int readpool(int fd
, pool_t
*entry
)
146 /* Read one pool table entry, return true unless EOF. */
149 if ((r
= read(fd
, entry
, sizeof(*entry
))) < 0) fatal(poolfile
);
150 if (r
== 0) return 0;
152 if (r
!= sizeof(*entry
) || entry
->magic
!= POOL_MAGIC
) {
153 fprintf(stderr
, "%s: %s: Pool table is corrupt\n",
161 static void writepool(int fd
, pool_t
*entry
)
163 /* (Over)write a pool table entry. */
164 if (write(fd
, entry
, sizeof(*entry
)) < 0
165 || (entry
->expire
> now
&& fsync(fd
) < 0)
171 static ipaddr_t
findpool(u8_t
*client
, size_t len
, ipaddr_t ifip
)
173 /* Look for a client ID in the dynamic address pool within the same network
174 * as 'ifip'. Select an unused one for a new client if necessary. Return
175 * 0 if nothing is available, otherwise the IP address we can offer.
178 pool_t entry
, oldest
;
183 /* Any information available on the network the client is at? */
184 if (!makedhcp(&dhcp
, nil
, 0, nil
, 0, ifip
, ifip
, nil
)) return 0;
186 if ((fd
= openpool(O_RDWR
)) < 0) return 0;
188 (void) gettag(&dhcp
, DHCP_TAG_NETMASK
, &pmask
, nil
);
189 memcpy(&mask
, pmask
, sizeof(mask
));
191 oldest
.expire
= NEVER
;
192 while ((found
= readpool(fd
, &entry
))) {
194 if (entry
.ip
== 0) continue;
196 /* Correct network? */
197 if (((entry
.ip
^ ifip
) & mask
) != 0) continue;
199 /* Client present? */
200 if (entry
.len
== len
&& memcmp(entry
.clid
, client
, len
) == 0) break;
202 /* Oldest candidate for a new lease? */
203 entry
.expire
= ntohl(entry
.expire
);
204 if (entry
.expire
< oldest
.expire
) oldest
= entry
;
208 if (found
) return entry
.ip
;
209 if (oldest
.expire
<= now
) return oldest
.ip
;
213 static int commitpool(ipaddr_t ip
, u8_t
*client
, size_t len
, time_t expire
)
215 /* Commit a new binding to stable storage, return true on success. */
219 if ((fd
= openpool(O_RDWR
)) < 0) return 0;
222 if (!readpool(fd
, &entry
)) {
226 } while (entry
.ip
!= ip
);
228 entry
.expire
= htonl(expire
);
230 memcpy(entry
.clid
, client
, len
);
231 if (lseek(fd
, -(off_t
)sizeof(entry
), SEEK_CUR
) == -1) fatal(poolfile
);
232 writepool(fd
, &entry
);
237 static void updatepool(int add
, const char *name
)
239 /* Add a new IP address to the dynamic pool. */
246 if ((he
= gethostbyname(name
)) == nil
|| he
->h_addrtype
!= AF_INET
) {
247 fprintf(stderr
, "%s: %s: Unknown host\n", program
, name
);
250 for (i
= 0; he
->h_addr_list
[i
] != nil
; i
++) {}
252 fprintf(stderr
, "%s: %s has %d addresses\n", program
, name
, i
);
255 memcpy(&ip
, he
->h_addr_list
[0], sizeof(ip
));
257 if ((fd
= openpool(O_RDWR
|O_CREAT
)) < 0) fatal(poolfile
);
261 while (readpool(fd
, &entry
)) {
263 if (entry
.ip
== ip
) {
264 fprintf(stderr
, "%s: %s: %s is already present\n",
265 program
, poolfile
, name
);
268 if (entry
.ip
== 0 && off0
== -1) off0
= off
;
270 if (entry
.ip
== ip
) {
271 memset(&entry
, 0, sizeof(entry
));
272 entry
.magic
= POOL_MAGIC
;
274 if (lseek(fd
, off
, SEEK_SET
) == -1) fatal(poolfile
);
275 writepool(fd
, &entry
);
282 if (off0
!= -1 && lseek(fd
, off0
, SEEK_SET
) == -1) fatal(poolfile
);
283 memset(&entry
, 0, sizeof(entry
));
284 entry
.magic
= POOL_MAGIC
;
286 writepool(fd
, &entry
);
291 static void cachedhcp(int n
, dhcp_t
*dp
)
293 /* Store a DHCP packet in a cache where those who care can find it. */
299 if (test
> 0) return;
302 /* First time, clear store and also save my pid. */
303 if ((fp
= fopen(PATH_DHCPPID
, "w")) != nil
) {
304 if (fprintf(fp
, "%d\n", getpid()) == EOF
|| fclose(fp
) == EOF
) {
309 mode
= O_WRONLY
| O_CREAT
| O_TRUNC
;
314 dp
->xid
= htonl(now
); /* To tell how old this data is. */
316 if ((fd
= open(cachefile
, mode
, 0666)) < 0
317 || lseek(fd
, (off_t
) n
* sizeof(*dp
), SEEK_SET
) == -1
318 || write(fd
, dp
, sizeof(*dp
)) < 0
321 if (errno
!= ENOENT
) fatal(cachefile
);
325 static void printdata(void)
327 /* Show the contents of the cache and the dynamic pool. */
333 unsigned long expire
;
334 char delta
[3*sizeof(u32_t
)];
338 if ((fd
= open(cachefile
, O_RDONLY
)) < 0) fatal(cachefile
);
340 while ((r
= read(fd
, &d
, sizeof(d
))) == sizeof(d
)) {
342 printf("DHCP data for network %d:\n", i
);
347 if (r
< 0) fatal(cachefile
);
350 if ((fd
= openpool(O_RDONLY
)) >= 0) {
351 printf("Dynamic address pool since %ld:\n", (long) now
);
352 while (readpool(fd
, &entry
)) {
353 if (entry
.ip
== 0) continue;
354 expire
= ntohl(entry
.expire
);
356 strcpy(delta
, "unused");
358 if (expire
== 0xFFFFFFFFUL
) {
359 strcpy(delta
, "infinite");
362 sprintf(delta
, "-%lu", now
- expire
);
364 sprintf(delta
, "+%lu", expire
- now
);
366 printf("\t%-15s %8s ", inet_ntoa(entry
.ip
), delta
);
367 for (i
= 0; i
< entry
.len
; i
++) {
368 printf("%02X", entry
.clid
[i
]);
376 static udpport_t
portbyname(const char *name
)
380 if ((se
= getservbyname(name
, "udp")) == nil
) {
381 fprintf(stderr
, "%s: Unknown port \"%s\"\n", program
, name
);
387 static int sendpacket(network_t
*np
, void *data
, size_t len
)
389 /* Send out a packet using a filedescriptor that is probably in async mode,
390 * so first dup() a sync version, then write. Return true on success.
395 if ((fd
= dup(np
->fdp
->fd
)) < 0) fatal("Can't dup()");
396 if ((r
= write(fd
, data
, len
)) < 0) {
397 report(np
->fdp
->device
);
404 static size_t servdhcp(network_t
*np
, buf_t
*bp
, size_t dlen
)
408 u8_t defclid
[1+sizeof(bp
->dhcp
->chaddr
)];
409 u8_t
*pdata
, *client
, *class, *server
, *reqip
, *lease
;
411 size_t len
, cilen
, calen
;
414 static char NAKMESS
[] = "IP address requested isn't yours";
416 if (test
> 0) return 0;
418 /* The IP address of the interface close to the client. */
419 ifip
= bp
->dhcp
->giaddr
!= 0 ? bp
->dhcp
->giaddr
: np
->ip
;
421 /* All kinds of DHCP tags. */
422 if (gettag(bp
->dhcp
, DHCP_TAG_TYPE
, &pdata
, nil
)) {
425 type
= -1; /* BOOTP? */
428 if (!gettag(bp
->dhcp
, DHCP_TAG_CLIENTID
, &client
, &cilen
)) {
429 defclid
[0]= bp
->dhcp
->htype
;
430 memcpy(defclid
+1, bp
->dhcp
->chaddr
, bp
->dhcp
->hlen
);
432 cilen
= 1+bp
->dhcp
->hlen
;
435 if (!gettag(bp
->dhcp
, DHCP_TAG_CLASSID
, &class, &calen
)) {
439 if (!gettag(bp
->dhcp
, DHCP_TAG_SERVERID
, &server
, nil
)) {
443 if (!gettag(bp
->dhcp
, DHCP_TAG_REQIP
, &reqip
, nil
)) {
447 /* I'm a server? Then see if I know this client. */
448 if ((np
->flags
& NF_SERVING
)
449 && bp
->dhcp
->op
== DHCP_BOOTREQUEST
450 && between(1, bp
->dhcp
->hlen
, sizeof(bp
->dhcp
->chaddr
))
451 && (server
== nil
|| memcmp(server
, &np
->ip
, sizeof(np
->ip
)) == 0)
455 /* Is the client in my tables? */
456 (void) makedhcp(abp
->dhcp
, class, calen
, client
, cilen
, 0, ifip
, nil
);
457 cip
= abp
->dhcp
->yiaddr
;
460 /* If not, do we have a dynamic address? */
461 if (cip
== 0 && (cip
= findpool(client
, cilen
, ifip
)) != 0) dyn
= 1;
463 if (type
== DHCP_INFORM
) {
464 /* The client already has an address, it just wants information.
465 * We only answer if we could answer a normal request (cip != 0),
466 * unless configured to answer anyone.
468 if (cip
!= 0 || (np
->flags
& NF_INFORM
)) cip
= bp
->dhcp
->ciaddr
;
471 if (cip
== 0 || !makedhcp(abp
->dhcp
, class, calen
,
472 client
, cilen
, cip
, ifip
, nil
)) {
477 if (gettag(abp
->dhcp
, DHCP_TAG_LEASE
, &lease
, nil
)) {
478 memcpy(&expire
, lease
, sizeof(expire
));
479 expire
= now
+ ntohl(expire
);
480 if (expire
< now
) expire
= 0xFFFFFFFFUL
;
483 /* A dynamic address must have a lease. */
484 fprintf(stderr
, "%s: No lease set for address %s\n",
485 program
, inet_ntoa(cip
));
489 expire
= 0xFFFFFFFFUL
;
492 /* What does our client want, and what do we say? */
497 /* Assign this address for a short moment. */
498 if (dyn
&& !commitpool(cip
, client
, cilen
, now
+ DELTA_FAST
)) {
507 /* The address wanted must be the address we offer. */
508 if ((reqip
!= nil
&& memcmp(reqip
, &cip
, sizeof(cip
)) != 0)
509 || (bp
->dhcp
->ciaddr
!= 0 && bp
->dhcp
->ciaddr
!= cip
)
513 if (dyn
&& type
== DHCP_REQUEST
) {
514 /* Assign this address for the duration of the lease. */
515 if (!commitpool(cip
, client
, cilen
, expire
)) put_buf(&abp
);
520 /* Our client doesn't want the offered address! */
523 && memcmp(reqip
, &cip
, sizeof(cip
)) == 0
527 fprintf(stderr
, "%s: ", program
);
528 for (i
= 0; i
< cilen
; i
++) {
529 fprintf(stderr
, "%02X", client
[i
]);
531 fprintf(stderr
, " declines %s", inet_ntoa(cip
));
532 if (gettag(bp
->dhcp
, DHCP_TAG_MESSAGE
, &pdata
, &len
)) {
533 fprintf(stderr
, " saying: \"%.*s\"", (int)len
, pdata
);
537 /* Disable address for the duration of the lease. */
538 (void) commitpool(cip
, nil
, 0, expire
);
544 /* Our client is nice enough to return its address. */
545 if (dyn
) (void) commitpool(cip
, client
, cilen
, now
);
549 default: /* Anything else is ignored. */
555 /* Finish the return packet. */
556 abp
->dhcp
->htype
= bp
->dhcp
->htype
;
557 abp
->dhcp
->hlen
= bp
->dhcp
->hlen
;
559 abp
->dhcp
->xid
= bp
->dhcp
->xid
;
561 abp
->dhcp
->flags
= bp
->dhcp
->flags
;
562 abp
->dhcp
->ciaddr
= 0;
563 abp
->dhcp
->yiaddr
= atype
== DHCP_NAK
? 0 : cip
;
564 if (atype
== DHCP_NAK
) abp
->dhcp
->siaddr
= 0;
565 abp
->dhcp
->giaddr
= bp
->dhcp
->giaddr
;
566 memcpy(abp
->dhcp
->chaddr
,bp
->dhcp
->chaddr
,sizeof(bp
->dhcp
->chaddr
));
568 settag(abp
->dhcp
, DHCP_TAG_SERVERID
, &np
->ip
, sizeof(np
->ip
));
571 /* No lease specified? Then give an infinite lease. */
572 settag(abp
->dhcp
, DHCP_TAG_LEASE
, &expire
, sizeof(expire
));
575 if (type
== DHCP_INFORM
) {
576 /* Oops, this one has a fixed address, so no lease business. */
577 abp
->dhcp
->yiaddr
= 0;
578 settag(abp
->dhcp
, DHCP_TAG_LEASE
, nil
, 0);
579 settag(abp
->dhcp
, DHCP_TAG_RENEWAL
, nil
, 0);
580 settag(abp
->dhcp
, DHCP_TAG_REBINDING
, nil
, 0);
583 if (atype
== DHCP_NAK
) {
584 /* A NAK doesn't need much. */
585 memset(abp
->dhcp
->sname
, 0, sizeof(abp
->dhcp
->sname
));
586 memset(abp
->dhcp
->file
, 0, sizeof(abp
->dhcp
->file
));
587 memset(abp
->dhcp
->options
, 255, sizeof(abp
->dhcp
->options
));
588 settag(abp
->dhcp
, DHCP_TAG_MESSAGE
, NAKMESS
, sizeof(NAKMESS
));
591 settag(abp
->dhcp
, DHCP_TAG_TYPE
, &atype
, sizeof(atype
));
593 /* Figure out where to send this to. */
594 abp
->udpio
->uih_src_addr
= np
->ip
;
595 abp
->udpio
->uih_src_port
= port_server
;
596 if (bp
->dhcp
->giaddr
!= 0) {
597 abp
->udpio
->uih_dst_addr
= bp
->dhcp
->giaddr
;
598 abp
->udpio
->uih_dst_port
= port_server
;
600 if (bp
->dhcp
->flags
& DHCP_FLAGS_BCAST
) {
601 abp
->udpio
->uih_dst_addr
= BCAST_IP
;
602 abp
->udpio
->uih_dst_port
= port_client
;
604 if (bp
->udpio
->uih_src_addr
!= 0
605 && bp
->udpio
->uih_dst_addr
== np
->ip
607 abp
->udpio
->uih_dst_addr
= bp
->udpio
->uih_src_addr
;
608 abp
->udpio
->uih_dst_port
= port_client
;
610 abp
->udpio
->uih_dst_addr
= BCAST_IP
;
611 abp
->udpio
->uih_dst_port
= port_client
;
613 abp
->udpio
->uih_ip_opt_len
= 0;
614 abp
->udpio
->uih_data_len
= sizeof(dhcp_t
);
616 /* Copy the packet to the input buffer, and return the new size. */
617 memcpy(bp
->buf
, abp
->buf
, sizeof(bp
->buf
));
619 return sizeof(udp_io_hdr_t
) + sizeof(dhcp_t
);
623 /* I'm a relay? If it is a not already a relayed request then relay. */
624 if ((np
->flags
& NF_RELAYING
)
625 && bp
->dhcp
->op
== DHCP_BOOTREQUEST
626 && bp
->dhcp
->giaddr
== 0
628 bp
->dhcp
->giaddr
= np
->ip
;
629 bp
->udpio
->uih_src_addr
= np
->ip
;
630 bp
->udpio
->uih_src_port
= port_server
;
631 bp
->udpio
->uih_dst_addr
= np
->server
;
632 bp
->udpio
->uih_dst_port
= port_server
;
636 /* I'm a relay? If the server sends a reply to me then relay back. */
637 if ((np
->flags
& NF_RELAYING
)
638 && bp
->dhcp
->op
== DHCP_BOOTREPLY
639 && bp
->dhcp
->giaddr
== np
->ip
642 bp
->udpio
->uih_src_addr
= np
->ip
;
643 bp
->udpio
->uih_src_port
= port_server
;
644 bp
->udpio
->uih_dst_addr
= BCAST_IP
;
645 bp
->udpio
->uih_dst_port
= port_client
;
649 /* Don't know what to do otherwise, so doing nothing seems wise. */
653 static void onsig(int sig
)
656 case SIGUSR1
: debug
++; break;
657 case SIGUSR2
: debug
= 0; break;
661 static void usage(void)
664 "Usage: %s [-qar] [-t[L]] [-d[L]] [-f config] [-c cache] [-p pool] [host ...]\n",
669 int main(int argc
, char **argv
)
676 static struct timeval eventtv
;
682 start
= now
= time(nil
);
686 while (i
< argc
&& argv
[i
][0] == '-') {
687 char *opt
= argv
[i
++]+1;
689 if (opt
[0] == '-' && opt
[1] == 0) break; /* -- */
691 if (strcmp(opt
, "-lwip") == 0) {
696 while (*opt
!= 0) switch (*opt
++) {
699 if (i
== argc
) usage();
707 if (i
== argc
) usage();
715 if (i
== argc
) usage();
723 if (between('0', *opt
, '9')) test
= strtoul(opt
, &opt
, 10);
727 if (between('0', *opt
, '9')) debug
= strtoul(opt
, &opt
, 10);
742 if (aflag
+ rflag
+ qflag
> 1) usage();
744 if (aflag
|| rflag
) {
745 /* Add or remove addresses from the dynamic pool. */
746 while (i
< argc
) updatepool(aflag
, argv
[i
++]);
750 if (i
!= argc
) usage();
753 /* Only show the contents of the cache and dynamic pool to the user. */
759 port_server
= portbyname("bootps");
760 port_client
= portbyname("bootpc");
762 sa
.sa_handler
= onsig
;
763 sigemptyset(&sa
.sa_mask
);
765 sigaction(SIGUSR1
, &sa
, nil
);
766 sigaction(SIGUSR2
, &sa
, nil
);
768 /* Initial configuration. */
769 for (i
= 0; i
< N_NETS
; i
++) {
773 /* Is there something there? */
774 if ((fd
= open(ipdev(i
), O_RDWR
|O_NONBLOCK
)) < 0) {
775 if (errno
!= ENOENT
&& errno
!= ENODEV
&& errno
!= ENXIO
) {
782 network
[n_nets
++]= np
= newnetwork();
787 np
->type
= NT_ETHERNET
;
788 } else if (opendev(np
, FT_ETHERNET
, 1)) {
789 np
->type
= B(&np
->eth
)[0] != 'Z' ? NT_ETHERNET
: NT_SINK
;
791 printf("%s: Ethernet address is %s%s\n",
792 np
->fdp
->device
, ether_ntoa(&np
->eth
),
793 np
->type
== NT_SINK
? " (sink)" : "");
795 closedev(np
, FT_ETHERNET
);
798 /* Only true Ethernets worry about DHCP. */
799 if (np
->type
!= NT_ETHERNET
) np
->renew
= np
->rebind
= np
->lease
= NEVER
;
802 /* Try to find my interfaces in the DHCP table. */
803 for (i
= 0; i
< n_nets
; i
++) {
805 u8_t clid
[1+DHCP_HLEN_ETH
];
809 if (np
->flags
& NF_BOUND
) continue;
811 if (np
->type
== NT_IP
) {
814 ether2clid(clid
, &np
->eth
);
815 cilen
= 1+DHCP_HLEN_ETH
;
818 /* Try to find an Ethernet address, or the IP address of an already
819 * configured network. If we have data we get an IP address back.
822 (void) makedhcp(bp
->dhcp
, (u8_t
*) "Minix", 5,
823 clid
, cilen
, np
->ip
, 0, np
);
824 cip
= bp
->dhcp
->yiaddr
;
826 /* Gather information on the interface. */
828 && makedhcp(bp
->dhcp
, (u8_t
*) "Minix", 5,
829 clid
, cilen
, cip
, cip
, np
)
835 cachedhcp(np
->n
, bp
->dhcp
);
837 (void) gettag(bp
->dhcp
, DHCP_TAG_NETMASK
, &pdata
, nil
);
838 memcpy(&np
->mask
, pdata
, sizeof(np
->mask
));
839 if (gettag(bp
->dhcp
, DHCP_TAG_GATEWAY
, &pdata
, nil
)) {
840 memcpy(&np
->gateway
, pdata
, sizeof(np
->gateway
));
844 if (gettag(bp
->dhcp
, DHCP_TAG_IPMTU
, &pdata
, nil
)) {
845 memcpy(&mtu
, pdata
, sizeof(mtu
));
850 set_ipconf(ipdev(np
->n
), np
->ip
, np
->mask
, mtu
);
852 printf("%s: IP address is %s\n",
853 ipdev(np
->n
), cidr_ntoa(np
->ip
, np
->mask
));
855 np
->flags
|= NF_BOUND
;
856 np
->renew
= np
->rebind
= np
->lease
= NEVER
;
857 np
->sol_ct
= N_SOLICITS
;
860 /* Other (previous) interfaces may have been defined. */
870 /* Is it time to request/renew a lease? */
871 for (i
= 0; i
< n_nets
; i
++) {
874 if (np
->renew
<= now
) {
876 static u8_t taglist
[] = {
877 DHCP_TAG_NETMASK
, DHCP_TAG_GATEWAY
, DHCP_TAG_DNS
,
880 u8_t ethclid
[1+DHCP_HLEN_ETH
];
882 /* We may have lost our binding or even our lease. */
883 if (np
->rebind
<= now
) np
->server
= BCAST_IP
;
885 if (np
->lease
<= now
) {
886 if (np
->flags
& NF_BOUND
) closedev(np
, FT_ALL
);
888 if ((np
->flags
& (NF_BOUND
| NF_POSSESSIVE
)) == NF_BOUND
) {
889 set_ipconf(ipdev(np
->n
), np
->ip
= 0, np
->mask
= 0, 0);
891 printf("%s: Interface disabled (lease expired)\n",
895 np
->flags
&= ~NF_BOUND
;
898 /* See if we can open the network we need to send on. */
899 if (!(np
->flags
& NF_BOUND
)) {
900 if (!opendev(np
, FT_ETHERNET
, 1)) continue;
902 if (!opendev(np
, FT_BOOTPC
, 1)) continue;
905 if (!(np
->flags
& NF_NEGOTIATING
)) {
906 /* We need to start querying a DHCP server. */
908 np
->delta
= DELTA_FIRST
;
909 np
->flags
|= NF_NEGOTIATING
;
912 /* Fill in a DHCP query packet. */
915 bp
->dhcp
->op
= DHCP_BOOTREQUEST
;
916 bp
->dhcp
->htype
= DHCP_HTYPE_ETH
;
917 bp
->dhcp
->hlen
= DHCP_HLEN_ETH
;
918 bp
->dhcp
->xid
= XID(np
);
919 bp
->dhcp
->secs
= htons(now
- np
->start
> 0xFFFF
920 ? 0xFFFF : now
- np
->start
);
921 memcpy(bp
->dhcp
->chaddr
, &np
->eth
, sizeof(np
->eth
));
923 if (np
->lease
<= now
) {
924 /* First time, or my old server is unresponsive. */
927 /* Request an offered address or renew an address. */
929 if (np
->flags
& NF_BOUND
) {
930 /* A renewal, I claim my current address. */
931 bp
->dhcp
->ciaddr
= np
->ip
;
933 /* Nicely ask for the address just offered. */
934 settag(bp
->dhcp
, DHCP_TAG_REQIP
, &np
->ip
,
936 settag(bp
->dhcp
, DHCP_TAG_SERVERID
, &np
->server
,
940 settag(bp
->dhcp
, DHCP_TAG_TYPE
, &type
, 1);
942 /* My client ID. Simply use the default. */
943 ether2clid(ethclid
, &np
->eth
);
944 settag(bp
->dhcp
, DHCP_TAG_CLIENTID
, ethclid
, sizeof(ethclid
));
946 /* The Class ID may serve to recognize Minix hosts. */
947 settag(bp
->dhcp
, DHCP_TAG_CLASSID
, "Minix", 5);
949 /* The few tags that Minix can make good use of. */
950 settag(bp
->dhcp
, DHCP_TAG_REQPAR
, taglist
, sizeof(taglist
));
952 /* Some weird sites use a hostname, not a client ID. */
953 if (np
->hostname
!= nil
) {
954 settag(bp
->dhcp
, DHCP_TAG_HOSTNAME
,
955 np
->hostname
, strlen(np
->hostname
));
958 bp
->udpio
->uih_src_addr
= np
->ip
;
959 bp
->udpio
->uih_dst_addr
= np
->server
;
960 bp
->udpio
->uih_src_port
= port_client
;
961 bp
->udpio
->uih_dst_port
= port_server
;
962 bp
->udpio
->uih_ip_opt_len
= 0;
963 bp
->udpio
->uih_data_len
= sizeof(dhcp_t
);
965 if (!(np
->flags
& NF_BOUND
)) {
966 /* Rebind over Ethernet. */
968 if (sendpacket(np
, (lwip
? bp
->ip
: (void *) bp
->eth
),
969 (lwip
? 0 : sizeof(eth_hdr_t
)) + sizeof(ip_hdr_t
)
970 + sizeof(udp_hdr_t
) + sizeof(dhcp_t
))) {
972 printf("%s: Broadcast DHCP %s\n",
973 np
->fdp
->device
, dhcptypename(type
));
974 if (debug
>= 2) printdhcp(bp
->dhcp
);
978 /* Renew over UDP. */
979 if (sendpacket(np
, bp
->udpio
, sizeof(udp_io_hdr_t
)
982 printf("%s: Sent DHCP %s to %s\n",
985 inet_ntoa(np
->server
));
986 if (debug
>= 2) printdhcp(bp
->dhcp
);
992 /* When to continue querying a DHCP server? */
993 if (np
->flags
& NF_BOUND
) {
994 /* Still bound, keep halving time till next event. */
997 e
= now
< np
->rebind
? np
->rebind
: np
->lease
;
999 if (d
< DELTA_SLOW
) d
= DELTA_SLOW
;
1001 if (np
->renew
> e
) np
->renew
= e
;
1003 /* Not bound, be desparate. */
1004 np
->renew
= now
+ np
->delta
;
1005 if ((np
->delta
*= 2) > DELTA_FAST
) np
->delta
= DELTA_FAST
;
1008 if (np
->renew
< event
) event
= np
->renew
;
1011 /* Read DHCP responses. */
1012 for (i
= 0; i
< n_nets
; i
++) {
1014 if (!(np
->flags
& NF_NEGOTIATING
)) continue;
1016 if (!(np
->flags
& NF_BOUND
)) {
1017 if (!opendev(np
, FT_ETHERNET
, 0)) continue;
1018 get_buf(&np
->fdp
->bp
);
1019 r
= asyn_read(&asyn
, np
->fdp
->fd
,
1020 lwip
? np
->fdp
->bp
->ip
: (void *) np
->fdp
->bp
->eth
,
1021 lwip
? BUF_IP_SIZE
: BUF_ETH_SIZE
);
1023 if (!opendev(np
, FT_BOOTPC
, 0)) continue;
1024 get_buf(&np
->fdp
->bp
);
1025 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->udpio
,
1029 if (errno
!= ASYN_INPROGRESS
&& errno
!= EPACKSIZE
) {
1030 report(np
->fdp
->device
);
1035 /* Is there a response? */
1037 give_buf(&bp
, &np
->fdp
->bp
);
1038 if (((!(np
->flags
& NF_BOUND
)
1039 && r
>= (lwip
? 0 : (sizeof(eth_hdr_t
)) + sizeof(ip_hdr_t
)
1040 + sizeof(udp_hdr_t
) + offsetof(dhcp_t
, options
))
1042 && bp
->udpio
->uih_dst_port
== port_client
)
1044 ((np
->flags
& NF_BOUND
)
1045 && r
>= sizeof(udp_io_hdr_t
) + offsetof(dhcp_t
, options
)))
1046 && bp
->dhcp
->op
== DHCP_BOOTREPLY
1047 && bp
->dhcp
->htype
== DHCP_HTYPE_ETH
1048 && bp
->dhcp
->hlen
== DHCP_HLEN_ETH
1049 && bp
->dhcp
->xid
== XID(np
)
1050 && memcmp(bp
->dhcp
->chaddr
, &np
->eth
, sizeof(np
->eth
)) == 0
1052 /* Pfew! We got a DHCP reply! */
1056 ipaddr_t mask
, gateway
, relay
, server
;
1058 u32_t lease
, renew
, rebind
, t
;
1060 relay
= bp
->udpio
->uih_src_addr
;
1061 if (gettag(bp
->dhcp
, DHCP_TAG_SERVERID
, &pdata
, nil
)) {
1062 memcpy(&server
, pdata
, sizeof(server
));
1067 if (gettag(bp
->dhcp
, DHCP_TAG_TYPE
, &pdata
, nil
)) {
1070 type
= DHCP_ACK
; /* BOOTP? */
1074 printf("%s: Got a DHCP %s from %s",
1075 np
->fdp
->device
, dhcptypename(type
), inet_ntoa(server
));
1076 printf(relay
!= server
? " through %s\n" : "\n",
1078 if (debug
>= 2) printdhcp(bp
->dhcp
);
1081 if (gettag(bp
->dhcp
, DHCP_TAG_NETMASK
, &pdata
, nil
)) {
1082 memcpy(&mask
, pdata
, sizeof(mask
));
1084 mask
= defaultmask(bp
->dhcp
->ciaddr
);
1087 if (gettag(bp
->dhcp
, DHCP_TAG_IPMTU
, &pdata
, nil
)) {
1088 memcpy(&mtu
, pdata
, sizeof(mtu
));
1094 if (gettag(bp
->dhcp
, DHCP_TAG_GATEWAY
, &pdata
, nil
)) {
1095 memcpy(&gateway
, pdata
, sizeof(gateway
));
1101 if (gettag(bp
->dhcp
, DHCP_TAG_LEASE
, &pdata
, nil
)) {
1102 memcpy(&lease
, pdata
, sizeof(lease
));
1103 lease
= ntohl(lease
);
1106 rebind
= lease
- lease
/ 8;
1107 if (gettag(bp
->dhcp
, DHCP_TAG_REBINDING
, &pdata
, nil
)) {
1108 memcpy(&t
, pdata
, sizeof(t
));
1110 if (t
< rebind
) rebind
= t
;
1114 if (gettag(bp
->dhcp
, DHCP_TAG_RENEWAL
, &pdata
, nil
)) {
1115 memcpy(&t
, pdata
, sizeof(t
));
1117 if (t
< renew
) renew
= t
;
1120 if (type
== DHCP_OFFER
&& np
->rebind
<= np
->renew
) {
1121 /* It's an offer for an address and we haven't taken one
1122 * yet. It's all the same to us, so take this one.
1124 np
->ip
= bp
->dhcp
->yiaddr
;
1127 np
->gateway
= gateway
;
1128 np
->delta
= DELTA_FIRST
;
1130 np
->rebind
= np
->lease
= now
+ DELTA_FAST
;
1132 /* Send out an ARP request to see if the offered address
1133 * is in use already.
1136 if (sendpacket(np
, bp
->eth
, sizeof(arp46_t
))) {
1138 printf("Sent ARP for %s\n", inet_ntoa(np
->ip
));
1141 np
->flags
&= ~NF_CONFLICT
;
1144 if (type
== DHCP_ACK
&& !(np
->flags
& NF_CONFLICT
)) {
1145 /* An acknowledgment. The address is all mine. */
1146 cachedhcp(np
->n
, bp
->dhcp
);
1147 np
->ip
= bp
->dhcp
->yiaddr
;
1150 set_ipconf(ipdev(np
->n
), np
->ip
, np
->mask
, mtu
);
1152 printf("%s: Address set to %s\n",
1153 ipdev(np
->n
), cidr_ntoa(np
->ip
, np
->mask
));
1155 if (lease
>= NEVER
- now
) {
1156 /* The lease is infinite! */
1157 np
->renew
= np
->rebind
= np
->lease
= NEVER
;
1159 np
->lease
= now
+ lease
;
1160 np
->renew
= now
+ renew
;
1161 np
->rebind
= now
+ rebind
;
1164 np
->renew
= now
+ 60;
1165 np
->rebind
= test
>= 4 ? np
->renew
: np
->renew
+ 60;
1166 np
->lease
= test
>= 5 ? np
->rebind
: np
->rebind
+ 60;
1168 if (!(np
->flags
& NF_IRDP
)) {
1169 np
->sol_ct
= (np
->flags
& NF_BOUND
) ? 1 : N_SOLICITS
;
1172 np
->flags
&= ~NF_NEGOTIATING
;
1173 np
->flags
|= NF_BOUND
;
1174 closedev(np
, FT_ETHERNET
);
1175 closedev(np
, FT_BOOTPC
);
1178 if (type
== DHCP_ACK
&& (np
->flags
& NF_CONFLICT
)) {
1179 /* Alas there is a conflict. Decline to use the address. */
1180 u8_t ethclid
[1+DHCP_HLEN_ETH
];
1181 static char USED
[]= "Address in use by 00:00:00:00:00:00";
1184 dhcp_init(bp
->dhcp
);
1185 bp
->dhcp
->op
= DHCP_BOOTREQUEST
;
1186 bp
->dhcp
->htype
= DHCP_HTYPE_ETH
;
1187 bp
->dhcp
->hlen
= DHCP_HLEN_ETH
;
1188 bp
->dhcp
->xid
= XID(np
);
1190 memcpy(bp
->dhcp
->chaddr
, &np
->eth
, sizeof(np
->eth
));
1191 settag(bp
->dhcp
, DHCP_TAG_REQIP
, &np
->ip
, sizeof(np
->ip
));
1192 settag(bp
->dhcp
, DHCP_TAG_TYPE
, &type
, 1);
1193 ether2clid(ethclid
, &np
->eth
);
1194 settag(bp
->dhcp
, DHCP_TAG_CLIENTID
,ethclid
,sizeof(ethclid
));
1195 strcpy(USED
+18, ether_ntoa(&np
->conflict
));
1196 settag(bp
->dhcp
, DHCP_TAG_MESSAGE
, USED
, strlen(USED
));
1198 bp
->udpio
->uih_src_port
= port_client
;
1199 bp
->udpio
->uih_dst_port
= port_server
;
1200 bp
->udpio
->uih_ip_opt_len
= 0;
1201 bp
->udpio
->uih_data_len
= sizeof(dhcp_t
);
1204 if (sendpacket(np
, bp
->eth
,
1205 sizeof(eth_hdr_t
) + sizeof(ip_hdr_t
)
1206 + sizeof(udp_hdr_t
) + sizeof(dhcp_t
))) {
1208 printf("%s: Broadcast DHCP %s\n",
1209 np
->fdp
->device
, dhcptypename(type
));
1210 if (debug
>= 2) printdhcp(bp
->dhcp
);
1214 np
->renew
= np
->rebind
= np
->lease
= now
+ DELTA_FAST
;
1215 np
->delta
= DELTA_FIRST
;
1218 if (type
== DHCP_NAK
) {
1219 /* Oops, a DHCP server doesn't like me, start over! */
1220 np
->renew
= np
->rebind
= np
->lease
= now
+ DELTA_FAST
;
1221 np
->delta
= DELTA_FIRST
;
1223 fprintf(stderr
, "%s: Got a NAK from %s",
1224 program
, inet_ntoa(server
));
1225 if (relay
!= server
) {
1226 fprintf(stderr
, " through %s", inet_ntoa(relay
));
1228 if (gettag(bp
->dhcp
, DHCP_TAG_MESSAGE
, &pdata
, &len
)) {
1229 fprintf(stderr
, " saying: \"%.*s\"", (int)len
, pdata
);
1231 fputc('\n', stderr
);
1234 if (!(np
->flags
& NF_BOUND
)
1236 && r
>= sizeof(arp46_t
)
1237 && is_arp_me(bp
, np
)
1239 /* Oh no, someone else is using the address offered to me! */
1240 np
->flags
|= NF_CONFLICT
;
1242 fprintf(stderr
, "%s: %s: %s offered by ",
1246 fprintf(stderr
, "%s is already in use by %s\n",
1247 inet_ntoa(np
->server
),
1248 ether_ntoa(&np
->conflict
));
1251 if (np
->renew
< event
) event
= np
->renew
;
1254 /* Perform router solicitations. */
1255 for (i
= 0; i
< n_nets
; i
++) {
1257 if (!(np
->flags
& NF_BOUND
)) continue;
1259 if (np
->solicit
<= now
) {
1260 if (!opendev(np
, FT_ICMP
, 1)) continue;
1264 if (np
->gateway
!= 0) {
1265 /* No IRDP response seen yet, advertise the router given
1266 * by DHCP to my own interface.
1268 icmp_advert(bp
, np
);
1269 if (sendpacket(np
, bp
->ip
, sizeof(ip_hdr_t
) + 16)) {
1271 printf("%s: Sent advert for %s to self\n",
1272 np
->fdp
->device
, inet_ntoa(np
->gateway
));
1275 np
->solicit
= now
+ DELTA_ADV
/2;
1278 if (np
->sol_ct
>= 0 && --np
->sol_ct
>= 0) {
1279 /* Send a router solicitation. */
1281 if (sendpacket(np
, bp
->ip
, sizeof(*bp
->ip
) + 8)) {
1283 printf("%s: Broadcast router solicitation\n",
1287 np
->solicit
= now
+ DELTA_SOL
;
1289 /* No response, or not soliciting right now. */
1290 closedev(np
, FT_ICMP
);
1295 if (np
->solicit
< event
) event
= np
->solicit
;
1298 /* Read router adverts. */
1299 for (i
= 0; i
< n_nets
; i
++) {
1301 if (!(np
->flags
& NF_BOUND
)) continue;
1302 if (np
->sol_ct
< 0) continue;
1304 if (!opendev(np
, FT_ICMP
, 0)) continue;
1305 get_buf(&np
->fdp
->bp
);
1306 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->ip
, BUF_IP_SIZE
);
1308 if (errno
!= ASYN_INPROGRESS
&& errno
!= EPACKSIZE
) {
1309 report(np
->fdp
->device
);
1314 /* Is there an advert? */
1315 if (i
< n_nets
&& r
>= sizeof(ip_hdr_t
) + 8) {
1318 give_buf(&bp
, &np
->fdp
->bp
);
1319 if ((router
= icmp_is_advert(bp
)) != 0) {
1321 printf("%s: Router advert received from %s\n",
1322 np
->fdp
->device
, inet_ntoa(router
));
1326 np
->flags
|= NF_IRDP
;
1327 closedev(np
, FT_ICMP
);
1332 /* We start serving if all the interfaces so marked are configured. */
1333 for (i
= 0; i
< n_nets
; i
++) {
1335 if ((np
->flags
& NF_RELAYING
) && (np
->flags
& NF_BOUND
)) {
1336 if (((np
->ip
^ np
->server
) & np
->mask
) == 0) {
1337 /* Don't relay to a server that is on this same net. */
1338 np
->flags
&= ~NF_RELAYING
;
1341 if (!(np
->flags
& (NF_SERVING
|NF_RELAYING
))) continue;
1342 if (!(np
->flags
& NF_BOUND
)) { serving
= 0; break; }
1346 /* Read DHCP requests. */
1347 for (i
= 0; i
< n_nets
; i
++) {
1349 if (!(np
->flags
& NF_BOUND
)) continue;
1350 if (!(np
->flags
& (NF_SERVING
|NF_RELAYING
)) || !serving
) continue;
1352 if (!opendev(np
, FT_BOOTPS
, 0)) continue;
1353 get_buf(&np
->fdp
->bp
);
1354 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->udpio
, BUF_UDP_SIZE
);
1357 if (errno
!= ASYN_INPROGRESS
&& errno
!= EPACKSIZE
) {
1358 report(np
->fdp
->device
);
1363 /* Is there a request? */
1365 && r
>= sizeof(udp_io_hdr_t
) + offsetof(dhcp_t
, options
)
1367 give_buf(&bp
, &np
->fdp
->bp
);
1370 printf("%s: Got DHCP packet from %s to ",
1371 np
->fdp
->device
, inet_ntoa(bp
->udpio
->uih_src_addr
));
1372 printf("%s\n", inet_ntoa(bp
->udpio
->uih_dst_addr
));
1373 if (debug
>= 2) printdhcp(bp
->dhcp
);
1376 /* Can we do something with this DHCP packet? */
1377 if ((r
= servdhcp(np
, bp
, r
)) > 0) {
1378 /* Yes, we have something to send somewhere. */
1379 if (sendpacket(np
, bp
->udpio
, r
)) {
1381 printf("%s: Sent DHCP packet to %s\n",
1383 inet_ntoa(bp
->udpio
->uih_dst_addr
));
1384 if (debug
>= 2) printdhcp(bp
->dhcp
);
1392 static char *lastbrk
;
1395 if (sbrk(0) != lastbrk
) {
1397 printf("Memory use = %lu\n",
1398 (unsigned long) (lastbrk
- &_end
));
1403 /* Bail out if not a server, and there is nothing else to do ever. */
1404 if (!serving
&& event
== NEVER
) break;
1406 /* Wait for something to do. */
1407 eventtv
.tv_sec
= event
;
1408 if (asyn_wait(&asyn
, 0, event
== NEVER
? nil
: &eventtv
) < 0) {
1409 if (errno
!= EINTR
) {
1410 report("asyn_wait()");
1415 if (debug
>= 1) printf("Nothing more to do! Starting over...\n");