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>
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>
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. */
48 void report(const char *label
)
52 logfp
= fopen("/usr/log/dhcp.log", "w");
54 fprintf(logfp
, "%s: %s: %s\n", program
, label
, strerror(errno
));
57 void fatal(const char *label
)
63 void *allocate(size_t size
)
67 if ((mem
= malloc(size
)) == nil
) fatal("Can't allocate memory");
71 /* Choose a DHCP xid based on the start time and network number. Not really
72 * random, but we don't have anything more random than the clock anyway.
74 #define XID(np) htonl(((u32_t) (np)->start << 8) | (np)->n)
76 static network_t
*network
[N_NETS
];
78 int ifname2if(const char *name
)
80 /* Translate an interface name to a number, -1 if bad. */
84 if (*name
++ != 'i' || *name
++ != 'p') return -1;
85 n
= strtoul(name
, &end
, 10);
86 if (end
== name
|| *end
!= 0) return -1;
87 if (n
>= N_NETS
) return -1;
91 network_t
*if2net(int n
)
93 /* Translate an interface number to a network struct. */
96 for (i
= 0; i
< n_nets
; i
++) {
97 if (network
[i
]->n
== n
) return network
[i
];
102 static ipaddr_t
defaultmask(ipaddr_t ip
)
104 /* Compute netmask by the oldfashioned Class rules. */
105 if (B(&ip
)[0] < 0x80) return HTONL(0xFF000000UL
); /* Class A. */
106 if (B(&ip
)[0] < 0xC0) return HTONL(0xFFFF0000UL
); /* Class B. */
107 if (B(&ip
)[0] < 0xE0) return HTONL(0xFFFFFF00UL
); /* Class C. */
108 return HTONL(0xFFFFFFFFUL
); /* Multicast? Shouldn't happen... */
111 #define POOL_MAGIC HTONL(0x81F85D00UL)
113 typedef struct pool
{ /* Dynamic pool entry. */
114 u32_t magic
; /* Pool file magic number. */
115 ipaddr_t ip
; /* IP address. */
116 u32_t expire
; /* When does/did the lease expire? */
117 u8_t len
; /* Client ID length. */
118 u8_t unused
[19]; /* Space for extensions. */
119 u8_t clid
[CLID_MAX
]; /* Client ID of current/last user. */
122 static int openpool(int mode
)
124 /* Open the dynamic pool and lock it, return fd on success or -1. */
128 if ((fd
= open(poolfile
, mode
, 0644)) < 0) {
129 if (errno
!= ENOENT
) fatal(poolfile
);
132 if (mode
!= O_RDONLY
) {
134 lck
.l_whence
= SEEK_SET
;
137 if (fcntl(fd
, F_SETLKW
, &lck
) < 0) fatal(poolfile
);
142 static int readpool(int fd
, pool_t
*entry
)
144 /* Read one pool table entry, return true unless EOF. */
147 if ((r
= read(fd
, entry
, sizeof(*entry
))) < 0) fatal(poolfile
);
148 if (r
== 0) return 0;
150 if (r
!= sizeof(*entry
) || entry
->magic
!= POOL_MAGIC
) {
151 fprintf(stderr
, "%s: %s: Pool table is corrupt\n",
159 #if !__minix_vmd /* No fsync() for Minix. */
160 #define fsync(fd) sync()
163 static void writepool(int fd
, pool_t
*entry
)
165 /* (Over)write a pool table entry. */
166 if (write(fd
, entry
, sizeof(*entry
)) < 0
167 || (entry
->expire
> now
&& fsync(fd
) < 0)
173 static ipaddr_t
findpool(u8_t
*client
, size_t len
, ipaddr_t ifip
)
175 /* Look for a client ID in the dynamic address pool within the same network
176 * as 'ifip'. Select an unused one for a new client if necessary. Return
177 * 0 if nothing is available, otherwise the IP address we can offer.
180 pool_t entry
, oldest
;
185 /* Any information available on the network the client is at? */
186 if (!makedhcp(&dhcp
, nil
, 0, nil
, 0, ifip
, ifip
, nil
)) return 0;
188 if ((fd
= openpool(O_RDWR
)) < 0) return 0;
190 (void) gettag(&dhcp
, DHCP_TAG_NETMASK
, &pmask
, nil
);
191 memcpy(&mask
, pmask
, sizeof(mask
));
193 oldest
.expire
= NEVER
;
194 while ((found
= readpool(fd
, &entry
))) {
196 if (entry
.ip
== 0) continue;
198 /* Correct network? */
199 if (((entry
.ip
^ ifip
) & mask
) != 0) continue;
201 /* Client present? */
202 if (entry
.len
== len
&& memcmp(entry
.clid
, client
, len
) == 0) break;
204 /* Oldest candidate for a new lease? */
205 entry
.expire
= ntohl(entry
.expire
);
206 if (entry
.expire
< oldest
.expire
) oldest
= entry
;
210 if (found
) return entry
.ip
;
211 if (oldest
.expire
<= now
) return oldest
.ip
;
215 static int commitpool(ipaddr_t ip
, u8_t
*client
, size_t len
, time_t expire
)
217 /* Commit a new binding to stable storage, return true on success. */
221 if ((fd
= openpool(O_RDWR
)) < 0) return 0;
224 if (!readpool(fd
, &entry
)) {
228 } while (entry
.ip
!= ip
);
230 entry
.expire
= htonl(expire
);
232 memcpy(entry
.clid
, client
, len
);
233 if (lseek(fd
, -(off_t
)sizeof(entry
), SEEK_CUR
) == -1) fatal(poolfile
);
234 writepool(fd
, &entry
);
239 static void updatepool(int add
, const char *name
)
241 /* Add a new IP address to the dynamic pool. */
248 if ((he
= gethostbyname(name
)) == nil
|| he
->h_addrtype
!= AF_INET
) {
249 fprintf(stderr
, "%s: %s: Unknown host\n", program
, name
);
252 for (i
= 0; he
->h_addr_list
[i
] != nil
; i
++) {}
254 fprintf(stderr
, "%s: %s has %d addresses\n", program
, name
, i
);
257 memcpy(&ip
, he
->h_addr_list
[0], sizeof(ip
));
259 if ((fd
= openpool(O_RDWR
|O_CREAT
)) < 0) fatal(poolfile
);
263 while (readpool(fd
, &entry
)) {
265 if (entry
.ip
== ip
) {
266 fprintf(stderr
, "%s: %s: %s is already present\n",
267 program
, poolfile
, name
);
270 if (entry
.ip
== 0 && off0
== -1) off0
= off
;
272 if (entry
.ip
== ip
) {
273 memset(&entry
, 0, sizeof(entry
));
274 entry
.magic
= POOL_MAGIC
;
276 if (lseek(fd
, off
, SEEK_SET
) == -1) fatal(poolfile
);
277 writepool(fd
, &entry
);
284 if (off0
!= -1 && lseek(fd
, off0
, SEEK_SET
) == -1) fatal(poolfile
);
285 memset(&entry
, 0, sizeof(entry
));
286 entry
.magic
= POOL_MAGIC
;
288 writepool(fd
, &entry
);
293 static void cachedhcp(int n
, dhcp_t
*dp
)
295 /* Store a DHCP packet in a cache where those who care can find it. */
301 if (test
> 0) return;
304 /* First time, clear store and also save my pid. */
305 if ((fp
= fopen(PATH_DHCPPID
, "w")) != nil
) {
306 if (fprintf(fp
, "%d\n", getpid()) == EOF
|| fclose(fp
) == EOF
) {
311 mode
= O_WRONLY
| O_CREAT
| O_TRUNC
;
316 dp
->xid
= htonl(now
); /* To tell how old this data is. */
318 if ((fd
= open(cachefile
, mode
, 0666)) < 0
319 || lseek(fd
, (off_t
) n
* sizeof(*dp
), SEEK_SET
) == -1
320 || write(fd
, dp
, sizeof(*dp
)) < 0
323 if (errno
!= ENOENT
) fatal(cachefile
);
327 static void printdata(void)
329 /* Show the contents of the cache and the dynamic pool. */
335 unsigned long expire
;
336 char delta
[3*sizeof(u32_t
)];
340 if ((fd
= open(cachefile
, O_RDONLY
)) < 0) fatal(cachefile
);
342 while ((r
= read(fd
, &d
, sizeof(d
))) == sizeof(d
)) {
344 printf("DHCP data for network %d:\n", i
);
349 if (r
< 0) fatal(cachefile
);
352 if ((fd
= openpool(O_RDONLY
)) >= 0) {
353 printf("Dynamic address pool since %ld:\n", (long) now
);
354 while (readpool(fd
, &entry
)) {
355 if (entry
.ip
== 0) continue;
356 expire
= ntohl(entry
.expire
);
358 strcpy(delta
, "unused");
360 if (expire
== 0xFFFFFFFFUL
) {
361 strcpy(delta
, "infinite");
364 sprintf(delta
, "-%lu", now
- expire
);
366 sprintf(delta
, "+%lu", expire
- now
);
368 printf("\t%-15s %8s ", inet_ntoa(entry
.ip
), delta
);
369 for (i
= 0; i
< entry
.len
; i
++) {
370 printf("%02X", entry
.clid
[i
]);
378 static udpport_t
portbyname(const char *name
)
382 if ((se
= getservbyname(name
, "udp")) == nil
) {
383 fprintf(stderr
, "%s: Unknown port \"%s\"\n", program
, name
);
389 static int sendpacket(network_t
*np
, void *data
, size_t len
)
391 /* Send out a packet using a filedescriptor that is probably in async mode,
392 * so first dup() a sync version, then write. Return true on success.
397 if ((fd
= dup(np
->fdp
->fd
)) < 0) fatal("Can't dup()");
398 if ((r
= write(fd
, data
, len
)) < 0) {
399 report(np
->fdp
->device
);
406 static size_t servdhcp(network_t
*np
, buf_t
*bp
, size_t dlen
)
410 u8_t defclid
[1+sizeof(bp
->dhcp
->chaddr
)];
411 u8_t
*pdata
, *client
, *class, *server
, *reqip
, *lease
;
413 size_t len
, cilen
, calen
;
416 static char NAKMESS
[] = "IP address requested isn't yours";
418 if (test
> 0) return 0;
420 /* The IP address of the interface close to the client. */
421 ifip
= bp
->dhcp
->giaddr
!= 0 ? bp
->dhcp
->giaddr
: np
->ip
;
423 /* All kinds of DHCP tags. */
424 if (gettag(bp
->dhcp
, DHCP_TAG_TYPE
, &pdata
, nil
)) {
427 type
= -1; /* BOOTP? */
430 if (!gettag(bp
->dhcp
, DHCP_TAG_CLIENTID
, &client
, &cilen
)) {
431 defclid
[0]= bp
->dhcp
->htype
;
432 memcpy(defclid
+1, bp
->dhcp
->chaddr
, bp
->dhcp
->hlen
);
434 cilen
= 1+bp
->dhcp
->hlen
;
437 if (!gettag(bp
->dhcp
, DHCP_TAG_CLASSID
, &class, &calen
)) {
441 if (!gettag(bp
->dhcp
, DHCP_TAG_SERVERID
, &server
, nil
)) {
445 if (!gettag(bp
->dhcp
, DHCP_TAG_REQIP
, &reqip
, nil
)) {
449 /* I'm a server? Then see if I know this client. */
450 if ((np
->flags
& NF_SERVING
)
451 && bp
->dhcp
->op
== DHCP_BOOTREQUEST
452 && between(1, bp
->dhcp
->hlen
, sizeof(bp
->dhcp
->chaddr
))
453 && (server
== nil
|| memcmp(server
, &np
->ip
, sizeof(np
->ip
)) == 0)
457 /* Is the client in my tables? */
458 (void) makedhcp(abp
->dhcp
, class, calen
, client
, cilen
, 0, ifip
, nil
);
459 cip
= abp
->dhcp
->yiaddr
;
462 /* If not, do we have a dynamic address? */
463 if (cip
== 0 && (cip
= findpool(client
, cilen
, ifip
)) != 0) dyn
= 1;
465 if (type
== DHCP_INFORM
) {
466 /* The client already has an address, it just wants information.
467 * We only answer if we could answer a normal request (cip != 0),
468 * unless configured to answer anyone.
470 if (cip
!= 0 || (np
->flags
& NF_INFORM
)) cip
= bp
->dhcp
->ciaddr
;
473 if (cip
== 0 || !makedhcp(abp
->dhcp
, class, calen
,
474 client
, cilen
, cip
, ifip
, nil
)) {
479 if (gettag(abp
->dhcp
, DHCP_TAG_LEASE
, &lease
, nil
)) {
480 memcpy(&expire
, lease
, sizeof(expire
));
481 expire
= now
+ ntohl(expire
);
482 if (expire
< now
) expire
= 0xFFFFFFFFUL
;
485 /* A dynamic address must have a lease. */
486 fprintf(stderr
, "%s: No lease set for address %s\n",
487 program
, inet_ntoa(cip
));
491 expire
= 0xFFFFFFFFUL
;
494 /* What does our client want, and what do we say? */
499 /* Assign this address for a short moment. */
500 if (dyn
&& !commitpool(cip
, client
, cilen
, now
+ DELTA_FAST
)) {
509 /* The address wanted must be the address we offer. */
510 if ((reqip
!= nil
&& memcmp(reqip
, &cip
, sizeof(cip
)) != 0)
511 || (bp
->dhcp
->ciaddr
!= 0 && bp
->dhcp
->ciaddr
!= cip
)
515 if (dyn
&& type
== DHCP_REQUEST
) {
516 /* Assign this address for the duration of the lease. */
517 if (!commitpool(cip
, client
, cilen
, expire
)) put_buf(&abp
);
522 /* Our client doesn't want the offered address! */
525 && memcmp(reqip
, &cip
, sizeof(cip
)) == 0
529 fprintf(stderr
, "%s: ", program
);
530 for (i
= 0; i
< cilen
; i
++) {
531 fprintf(stderr
, "%02X", client
[i
]);
533 fprintf(stderr
, " declines %s", inet_ntoa(cip
));
534 if (gettag(bp
->dhcp
, DHCP_TAG_MESSAGE
, &pdata
, &len
)) {
535 fprintf(stderr
, " saying: \"%.*s\"", (int)len
, pdata
);
539 /* Disable address for the duration of the lease. */
540 (void) commitpool(cip
, nil
, 0, expire
);
546 /* Our client is nice enough to return its address. */
547 if (dyn
) (void) commitpool(cip
, client
, cilen
, now
);
551 default: /* Anything else is ignored. */
557 /* Finish the return packet. */
558 abp
->dhcp
->htype
= bp
->dhcp
->htype
;
559 abp
->dhcp
->hlen
= bp
->dhcp
->hlen
;
561 abp
->dhcp
->xid
= bp
->dhcp
->xid
;
563 abp
->dhcp
->flags
= bp
->dhcp
->flags
;
564 abp
->dhcp
->ciaddr
= 0;
565 abp
->dhcp
->yiaddr
= atype
== DHCP_NAK
? 0 : cip
;
566 if (atype
== DHCP_NAK
) abp
->dhcp
->siaddr
= 0;
567 abp
->dhcp
->giaddr
= bp
->dhcp
->giaddr
;
568 memcpy(abp
->dhcp
->chaddr
,bp
->dhcp
->chaddr
,sizeof(bp
->dhcp
->chaddr
));
570 settag(abp
->dhcp
, DHCP_TAG_SERVERID
, &np
->ip
, sizeof(np
->ip
));
573 /* No lease specified? Then give an infinite lease. */
574 settag(abp
->dhcp
, DHCP_TAG_LEASE
, &expire
, sizeof(expire
));
577 if (type
== DHCP_INFORM
) {
578 /* Oops, this one has a fixed address, so no lease business. */
579 abp
->dhcp
->yiaddr
= 0;
580 settag(abp
->dhcp
, DHCP_TAG_LEASE
, nil
, 0);
581 settag(abp
->dhcp
, DHCP_TAG_RENEWAL
, nil
, 0);
582 settag(abp
->dhcp
, DHCP_TAG_REBINDING
, nil
, 0);
585 if (atype
== DHCP_NAK
) {
586 /* A NAK doesn't need much. */
587 memset(abp
->dhcp
->sname
, 0, sizeof(abp
->dhcp
->sname
));
588 memset(abp
->dhcp
->file
, 0, sizeof(abp
->dhcp
->file
));
589 memset(abp
->dhcp
->options
, 255, sizeof(abp
->dhcp
->options
));
590 settag(abp
->dhcp
, DHCP_TAG_MESSAGE
, NAKMESS
, sizeof(NAKMESS
));
593 settag(abp
->dhcp
, DHCP_TAG_TYPE
, &atype
, sizeof(atype
));
595 /* Figure out where to send this to. */
596 abp
->udpio
->uih_src_addr
= np
->ip
;
597 abp
->udpio
->uih_src_port
= port_server
;
598 if (bp
->dhcp
->giaddr
!= 0) {
599 abp
->udpio
->uih_dst_addr
= bp
->dhcp
->giaddr
;
600 abp
->udpio
->uih_dst_port
= port_server
;
602 if (bp
->dhcp
->flags
& DHCP_FLAGS_BCAST
) {
603 abp
->udpio
->uih_dst_addr
= BCAST_IP
;
604 abp
->udpio
->uih_dst_port
= port_client
;
606 if (bp
->udpio
->uih_src_addr
!= 0
607 && bp
->udpio
->uih_dst_addr
== np
->ip
609 abp
->udpio
->uih_dst_addr
= bp
->udpio
->uih_src_addr
;
610 abp
->udpio
->uih_dst_port
= port_client
;
612 abp
->udpio
->uih_dst_addr
= BCAST_IP
;
613 abp
->udpio
->uih_dst_port
= port_client
;
615 abp
->udpio
->uih_ip_opt_len
= 0;
616 abp
->udpio
->uih_data_len
= sizeof(dhcp_t
);
618 /* Copy the packet to the input buffer, and return the new size. */
619 memcpy(bp
->buf
, abp
->buf
, sizeof(bp
->buf
));
621 return sizeof(udp_io_hdr_t
) + sizeof(dhcp_t
);
625 /* I'm a relay? If it is a not already a relayed request then relay. */
626 if ((np
->flags
& NF_RELAYING
)
627 && bp
->dhcp
->op
== DHCP_BOOTREQUEST
628 && bp
->dhcp
->giaddr
== 0
630 bp
->dhcp
->giaddr
= np
->ip
;
631 bp
->udpio
->uih_src_addr
= np
->ip
;
632 bp
->udpio
->uih_src_port
= port_server
;
633 bp
->udpio
->uih_dst_addr
= np
->server
;
634 bp
->udpio
->uih_dst_port
= port_server
;
638 /* I'm a relay? If the server sends a reply to me then relay back. */
639 if ((np
->flags
& NF_RELAYING
)
640 && bp
->dhcp
->op
== DHCP_BOOTREPLY
641 && 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
= BCAST_IP
;
647 bp
->udpio
->uih_dst_port
= port_client
;
651 /* Don't know what to do otherwise, so doing nothing seems wise. */
655 static void onsig(int sig
)
658 case SIGUSR1
: debug
++; break;
659 case SIGUSR2
: debug
= 0; break;
663 static void usage(void)
666 "Usage: %s [-qar] [-t[L]] [-d[L]] [-f config] [-c cache] [-p pool] [host ...]\n",
671 int main(int argc
, char **argv
)
678 static struct timeval eventtv
;
684 start
= now
= time(nil
);
688 while (i
< argc
&& argv
[i
][0] == '-') {
689 char *opt
= argv
[i
++]+1;
691 if (opt
[0] == '-' && opt
[1] == 0) break; /* -- */
693 while (*opt
!= 0) switch (*opt
++) {
696 if (i
== argc
) usage();
704 if (i
== argc
) usage();
712 if (i
== argc
) usage();
720 if (between('0', *opt
, '9')) test
= strtoul(opt
, &opt
, 10);
724 if (between('0', *opt
, '9')) debug
= strtoul(opt
, &opt
, 10);
739 if (aflag
+ rflag
+ qflag
> 1) usage();
741 if (aflag
|| rflag
) {
742 /* Add or remove addresses from the dynamic pool. */
743 while (i
< argc
) updatepool(aflag
, argv
[i
++]);
747 if (i
!= argc
) usage();
750 /* Only show the contents of the cache and dynamic pool to the user. */
756 port_server
= portbyname("bootps");
757 port_client
= portbyname("bootpc");
759 sa
.sa_handler
= onsig
;
760 sigemptyset(&sa
.sa_mask
);
762 sigaction(SIGUSR1
, &sa
, nil
);
763 sigaction(SIGUSR2
, &sa
, nil
);
765 /* Initial configuration. */
766 for (i
= 0; i
< N_NETS
; i
++) {
770 /* Is there something there? */
771 if ((fd
= open(ipdev(i
), O_RDWR
|O_NONBLOCK
)) < 0) {
772 if (errno
!= ENOENT
&& errno
!= ENODEV
&& errno
!= ENXIO
) {
779 network
[n_nets
++]= np
= newnetwork();
783 if (opendev(np
, FT_ETHERNET
, 1)) {
784 np
->type
= B(&np
->eth
)[0] != 'Z' ? NT_ETHERNET
: NT_SINK
;
786 printf("%s: Ethernet address is %s%s\n",
787 np
->fdp
->device
, ether_ntoa(&np
->eth
),
788 np
->type
== NT_SINK
? " (sink)" : "");
790 closedev(np
, FT_ETHERNET
);
793 /* Only true Ethernets worry about DHCP. */
794 if (np
->type
!= NT_ETHERNET
) np
->renew
= np
->rebind
= np
->lease
= NEVER
;
797 /* Try to find my interfaces in the DHCP table. */
798 for (i
= 0; i
< n_nets
; i
++) {
800 u8_t clid
[1+DHCP_HLEN_ETH
];
804 if (np
->flags
& NF_BOUND
) continue;
806 if (np
->type
== NT_IP
) {
809 ether2clid(clid
, &np
->eth
);
810 cilen
= 1+DHCP_HLEN_ETH
;
813 /* Try to find an Ethernet address, or the IP address of an already
814 * configured network. If we have data we get an IP address back.
817 (void) makedhcp(bp
->dhcp
, (u8_t
*) "Minix", 5,
818 clid
, cilen
, np
->ip
, 0, np
);
819 cip
= bp
->dhcp
->yiaddr
;
821 /* Gather information on the interface. */
823 && makedhcp(bp
->dhcp
, (u8_t
*) "Minix", 5,
824 clid
, cilen
, cip
, cip
, np
)
830 cachedhcp(np
->n
, bp
->dhcp
);
832 (void) gettag(bp
->dhcp
, DHCP_TAG_NETMASK
, &pdata
, nil
);
833 memcpy(&np
->mask
, pdata
, sizeof(np
->mask
));
834 if (gettag(bp
->dhcp
, DHCP_TAG_GATEWAY
, &pdata
, nil
)) {
835 memcpy(&np
->gateway
, pdata
, sizeof(np
->gateway
));
839 if (gettag(bp
->dhcp
, DHCP_TAG_IPMTU
, &pdata
, nil
)) {
840 memcpy(&mtu
, pdata
, sizeof(mtu
));
845 set_ipconf(ipdev(np
->n
), np
->ip
, np
->mask
, mtu
);
847 printf("%s: IP address is %s\n",
848 ipdev(np
->n
), cidr_ntoa(np
->ip
, np
->mask
));
850 np
->flags
|= NF_BOUND
;
851 np
->renew
= np
->rebind
= np
->lease
= NEVER
;
852 np
->sol_ct
= N_SOLICITS
;
855 /* Other (previous) interfaces may have been defined. */
865 /* Is it time to request/renew a lease? */
866 for (i
= 0; i
< n_nets
; i
++) {
869 if (np
->renew
<= now
) {
871 static u8_t taglist
[] = {
872 DHCP_TAG_NETMASK
, DHCP_TAG_GATEWAY
, DHCP_TAG_DNS
,
875 u8_t ethclid
[1+DHCP_HLEN_ETH
];
877 /* We may have lost our binding or even our lease. */
878 if (np
->rebind
<= now
) np
->server
= BCAST_IP
;
880 if (np
->lease
<= now
) {
881 if (np
->flags
& NF_BOUND
) closedev(np
, FT_ALL
);
883 if ((np
->flags
& (NF_BOUND
| NF_POSSESSIVE
)) == NF_BOUND
) {
884 set_ipconf(ipdev(np
->n
), np
->ip
= 0, np
->mask
= 0, 0);
886 printf("%s: Interface disabled (lease expired)\n",
890 np
->flags
&= ~NF_BOUND
;
893 /* See if we can open the network we need to send on. */
894 if (!(np
->flags
& NF_BOUND
)) {
895 if (!opendev(np
, FT_ETHERNET
, 1)) continue;
897 if (!opendev(np
, FT_BOOTPC
, 1)) continue;
900 if (!(np
->flags
& NF_NEGOTIATING
)) {
901 /* We need to start querying a DHCP server. */
903 np
->delta
= DELTA_FIRST
;
904 np
->flags
|= NF_NEGOTIATING
;
907 /* Fill in a DHCP query packet. */
910 bp
->dhcp
->op
= DHCP_BOOTREQUEST
;
911 bp
->dhcp
->htype
= DHCP_HTYPE_ETH
;
912 bp
->dhcp
->hlen
= DHCP_HLEN_ETH
;
913 bp
->dhcp
->xid
= XID(np
);
914 bp
->dhcp
->secs
= htons(now
- np
->start
> 0xFFFF
915 ? 0xFFFF : now
- np
->start
);
916 memcpy(bp
->dhcp
->chaddr
, &np
->eth
, sizeof(np
->eth
));
918 if (np
->lease
<= now
) {
919 /* First time, or my old server is unresponsive. */
922 /* Request an offered address or renew an address. */
924 if (np
->flags
& NF_BOUND
) {
925 /* A renewal, I claim my current address. */
926 bp
->dhcp
->ciaddr
= np
->ip
;
928 /* Nicely ask for the address just offered. */
929 settag(bp
->dhcp
, DHCP_TAG_REQIP
, &np
->ip
,
931 settag(bp
->dhcp
, DHCP_TAG_SERVERID
, &np
->server
,
935 settag(bp
->dhcp
, DHCP_TAG_TYPE
, &type
, 1);
937 /* My client ID. Simply use the default. */
938 ether2clid(ethclid
, &np
->eth
);
939 settag(bp
->dhcp
, DHCP_TAG_CLIENTID
, ethclid
, sizeof(ethclid
));
941 /* The Class ID may serve to recognize Minix hosts. */
942 settag(bp
->dhcp
, DHCP_TAG_CLASSID
, "Minix", 5);
944 /* The few tags that Minix can make good use of. */
945 settag(bp
->dhcp
, DHCP_TAG_REQPAR
, taglist
, sizeof(taglist
));
947 /* Some weird sites use a hostname, not a client ID. */
948 if (np
->hostname
!= nil
) {
949 settag(bp
->dhcp
, DHCP_TAG_HOSTNAME
,
950 np
->hostname
, strlen(np
->hostname
));
953 bp
->udpio
->uih_src_addr
= np
->ip
;
954 bp
->udpio
->uih_dst_addr
= np
->server
;
955 bp
->udpio
->uih_src_port
= port_client
;
956 bp
->udpio
->uih_dst_port
= port_server
;
957 bp
->udpio
->uih_ip_opt_len
= 0;
958 bp
->udpio
->uih_data_len
= sizeof(dhcp_t
);
960 if (!(np
->flags
& NF_BOUND
)) {
961 /* Rebind over Ethernet. */
963 if (sendpacket(np
, bp
->eth
,
964 sizeof(eth_hdr_t
) + sizeof(ip_hdr_t
)
965 + sizeof(udp_hdr_t
) + sizeof(dhcp_t
))) {
967 printf("%s: Broadcast DHCP %s\n",
968 np
->fdp
->device
, dhcptypename(type
));
969 if (debug
>= 2) printdhcp(bp
->dhcp
);
973 /* Renew over UDP. */
974 if (sendpacket(np
, bp
->udpio
, sizeof(udp_io_hdr_t
)
977 printf("%s: Sent DHCP %s to %s\n",
980 inet_ntoa(np
->server
));
981 if (debug
>= 2) printdhcp(bp
->dhcp
);
987 /* When to continue querying a DHCP server? */
988 if (np
->flags
& NF_BOUND
) {
989 /* Still bound, keep halving time till next event. */
992 e
= now
< np
->rebind
? np
->rebind
: np
->lease
;
994 if (d
< DELTA_SLOW
) d
= DELTA_SLOW
;
996 if (np
->renew
> e
) np
->renew
= e
;
998 /* Not bound, be desparate. */
999 np
->renew
= now
+ np
->delta
;
1000 if ((np
->delta
*= 2) > DELTA_FAST
) np
->delta
= DELTA_FAST
;
1003 if (np
->renew
< event
) event
= np
->renew
;
1006 /* Read DHCP responses. */
1007 for (i
= 0; i
< n_nets
; i
++) {
1009 if (!(np
->flags
& NF_NEGOTIATING
)) continue;
1011 if (!(np
->flags
& NF_BOUND
)) {
1012 if (!opendev(np
, FT_ETHERNET
, 0)) continue;
1013 get_buf(&np
->fdp
->bp
);
1014 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->eth
,
1017 if (!opendev(np
, FT_BOOTPC
, 0)) continue;
1018 get_buf(&np
->fdp
->bp
);
1019 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->udpio
,
1023 if (errno
!= ASYN_INPROGRESS
&& errno
!= EPACKSIZE
) {
1024 report(np
->fdp
->device
);
1029 /* Is there a response? */
1031 give_buf(&bp
, &np
->fdp
->bp
);
1032 if (((!(np
->flags
& NF_BOUND
)
1033 && r
>= (sizeof(eth_hdr_t
) + sizeof(ip_hdr_t
)
1034 + sizeof(udp_hdr_t
) + offsetof(dhcp_t
, options
))
1036 && bp
->udpio
->uih_dst_port
== port_client
)
1038 ((np
->flags
& NF_BOUND
)
1039 && r
>= sizeof(udp_io_hdr_t
) + offsetof(dhcp_t
, options
)))
1040 && bp
->dhcp
->op
== DHCP_BOOTREPLY
1041 && bp
->dhcp
->htype
== DHCP_HTYPE_ETH
1042 && bp
->dhcp
->hlen
== DHCP_HLEN_ETH
1043 && bp
->dhcp
->xid
== XID(np
)
1044 && memcmp(bp
->dhcp
->chaddr
, &np
->eth
, sizeof(np
->eth
)) == 0
1046 /* Pfew! We got a DHCP reply! */
1050 ipaddr_t mask
, gateway
, relay
, server
;
1052 u32_t lease
, renew
, rebind
, t
;
1054 relay
= bp
->udpio
->uih_src_addr
;
1055 if (gettag(bp
->dhcp
, DHCP_TAG_SERVERID
, &pdata
, nil
)) {
1056 memcpy(&server
, pdata
, sizeof(server
));
1061 if (gettag(bp
->dhcp
, DHCP_TAG_TYPE
, &pdata
, nil
)) {
1064 type
= DHCP_ACK
; /* BOOTP? */
1068 printf("%s: Got a DHCP %s from %s",
1069 np
->fdp
->device
, dhcptypename(type
), inet_ntoa(server
));
1070 printf(relay
!= server
? " through %s\n" : "\n",
1072 if (debug
>= 2) printdhcp(bp
->dhcp
);
1075 if (gettag(bp
->dhcp
, DHCP_TAG_NETMASK
, &pdata
, nil
)) {
1076 memcpy(&mask
, pdata
, sizeof(mask
));
1078 mask
= defaultmask(bp
->dhcp
->ciaddr
);
1081 if (gettag(bp
->dhcp
, DHCP_TAG_IPMTU
, &pdata
, nil
)) {
1082 memcpy(&mtu
, pdata
, sizeof(mtu
));
1088 if (gettag(bp
->dhcp
, DHCP_TAG_GATEWAY
, &pdata
, nil
)) {
1089 memcpy(&gateway
, pdata
, sizeof(gateway
));
1095 if (gettag(bp
->dhcp
, DHCP_TAG_LEASE
, &pdata
, nil
)) {
1096 memcpy(&lease
, pdata
, sizeof(lease
));
1097 lease
= ntohl(lease
);
1100 rebind
= lease
- lease
/ 8;
1101 if (gettag(bp
->dhcp
, DHCP_TAG_REBINDING
, &pdata
, nil
)) {
1102 memcpy(&t
, pdata
, sizeof(t
));
1104 if (t
< rebind
) rebind
= t
;
1108 if (gettag(bp
->dhcp
, DHCP_TAG_RENEWAL
, &pdata
, nil
)) {
1109 memcpy(&t
, pdata
, sizeof(t
));
1111 if (t
< renew
) renew
= t
;
1114 if (type
== DHCP_OFFER
&& np
->rebind
<= np
->renew
) {
1115 /* It's an offer for an address and we haven't taken one
1116 * yet. It's all the same to us, so take this one.
1118 np
->ip
= bp
->dhcp
->yiaddr
;
1121 np
->gateway
= gateway
;
1122 np
->delta
= DELTA_FIRST
;
1124 np
->rebind
= np
->lease
= now
+ DELTA_FAST
;
1126 /* Send out an ARP request to see if the offered address
1127 * is in use already.
1130 if (sendpacket(np
, bp
->eth
, sizeof(arp46_t
))) {
1132 printf("Sent ARP for %s\n", inet_ntoa(np
->ip
));
1135 np
->flags
&= ~NF_CONFLICT
;
1138 if (type
== DHCP_ACK
&& !(np
->flags
& NF_CONFLICT
)) {
1139 /* An acknowledgment. The address is all mine. */
1140 cachedhcp(np
->n
, bp
->dhcp
);
1141 np
->ip
= bp
->dhcp
->yiaddr
;
1144 set_ipconf(ipdev(np
->n
), np
->ip
, np
->mask
, mtu
);
1146 printf("%s: Address set to %s\n",
1147 ipdev(np
->n
), cidr_ntoa(np
->ip
, np
->mask
));
1149 if (lease
>= NEVER
- now
) {
1150 /* The lease is infinite! */
1151 np
->renew
= np
->rebind
= np
->lease
= NEVER
;
1153 np
->lease
= now
+ lease
;
1154 np
->renew
= now
+ renew
;
1155 np
->rebind
= now
+ rebind
;
1158 np
->renew
= now
+ 60;
1159 np
->rebind
= test
>= 4 ? np
->renew
: np
->renew
+ 60;
1160 np
->lease
= test
>= 5 ? np
->rebind
: np
->rebind
+ 60;
1162 if (!(np
->flags
& NF_IRDP
)) {
1163 np
->sol_ct
= (np
->flags
& NF_BOUND
) ? 1 : N_SOLICITS
;
1166 np
->flags
&= ~NF_NEGOTIATING
;
1167 np
->flags
|= NF_BOUND
;
1168 closedev(np
, FT_ETHERNET
);
1169 closedev(np
, FT_BOOTPC
);
1172 if (type
== DHCP_ACK
&& (np
->flags
& NF_CONFLICT
)) {
1173 /* Alas there is a conflict. Decline to use the address. */
1174 u8_t ethclid
[1+DHCP_HLEN_ETH
];
1175 static char USED
[]= "Address in use by 00:00:00:00:00:00";
1178 dhcp_init(bp
->dhcp
);
1179 bp
->dhcp
->op
= DHCP_BOOTREQUEST
;
1180 bp
->dhcp
->htype
= DHCP_HTYPE_ETH
;
1181 bp
->dhcp
->hlen
= DHCP_HLEN_ETH
;
1182 bp
->dhcp
->xid
= XID(np
);
1184 memcpy(bp
->dhcp
->chaddr
, &np
->eth
, sizeof(np
->eth
));
1185 settag(bp
->dhcp
, DHCP_TAG_REQIP
, &np
->ip
, sizeof(np
->ip
));
1186 settag(bp
->dhcp
, DHCP_TAG_TYPE
, &type
, 1);
1187 ether2clid(ethclid
, &np
->eth
);
1188 settag(bp
->dhcp
, DHCP_TAG_CLIENTID
,ethclid
,sizeof(ethclid
));
1189 strcpy(USED
+18, ether_ntoa(&np
->conflict
));
1190 settag(bp
->dhcp
, DHCP_TAG_MESSAGE
, USED
, strlen(USED
));
1192 bp
->udpio
->uih_src_port
= port_client
;
1193 bp
->udpio
->uih_dst_port
= port_server
;
1194 bp
->udpio
->uih_ip_opt_len
= 0;
1195 bp
->udpio
->uih_data_len
= sizeof(dhcp_t
);
1198 if (sendpacket(np
, bp
->eth
,
1199 sizeof(eth_hdr_t
) + sizeof(ip_hdr_t
)
1200 + sizeof(udp_hdr_t
) + sizeof(dhcp_t
))) {
1202 printf("%s: Broadcast DHCP %s\n",
1203 np
->fdp
->device
, dhcptypename(type
));
1204 if (debug
>= 2) printdhcp(bp
->dhcp
);
1208 np
->renew
= np
->rebind
= np
->lease
= now
+ DELTA_FAST
;
1209 np
->delta
= DELTA_FIRST
;
1212 if (type
== DHCP_NAK
) {
1213 /* Oops, a DHCP server doesn't like me, start over! */
1214 np
->renew
= np
->rebind
= np
->lease
= now
+ DELTA_FAST
;
1215 np
->delta
= DELTA_FIRST
;
1217 fprintf(stderr
, "%s: Got a NAK from %s",
1218 program
, inet_ntoa(server
));
1219 if (relay
!= server
) {
1220 fprintf(stderr
, " through %s", inet_ntoa(relay
));
1222 if (gettag(bp
->dhcp
, DHCP_TAG_MESSAGE
, &pdata
, &len
)) {
1223 fprintf(stderr
, " saying: \"%.*s\"", (int)len
, pdata
);
1225 fputc('\n', stderr
);
1228 if (!(np
->flags
& NF_BOUND
)
1230 && r
>= sizeof(arp46_t
)
1231 && is_arp_me(bp
, np
)
1233 /* Oh no, someone else is using the address offered to me! */
1234 np
->flags
|= NF_CONFLICT
;
1236 fprintf(stderr
, "%s: %s: %s offered by ",
1240 fprintf(stderr
, "%s is already in use by %s\n",
1241 inet_ntoa(np
->server
),
1242 ether_ntoa(&np
->conflict
));
1245 if (np
->renew
< event
) event
= np
->renew
;
1248 /* Perform router solicitations. */
1249 for (i
= 0; i
< n_nets
; i
++) {
1251 if (!(np
->flags
& NF_BOUND
)) continue;
1253 if (np
->solicit
<= now
) {
1254 if (!opendev(np
, FT_ICMP
, 1)) continue;
1258 if (np
->gateway
!= 0) {
1259 /* No IRDP response seen yet, advertise the router given
1260 * by DHCP to my own interface.
1262 icmp_advert(bp
, np
);
1263 if (sendpacket(np
, bp
->ip
, sizeof(ip_hdr_t
) + 16)) {
1265 printf("%s: Sent advert for %s to self\n",
1266 np
->fdp
->device
, inet_ntoa(np
->gateway
));
1269 np
->solicit
= now
+ DELTA_ADV
/2;
1272 if (np
->sol_ct
>= 0 && --np
->sol_ct
>= 0) {
1273 /* Send a router solicitation. */
1275 if (sendpacket(np
, bp
->ip
, sizeof(*bp
->ip
) + 8)) {
1277 printf("%s: Broadcast router solicitation\n",
1281 np
->solicit
= now
+ DELTA_SOL
;
1283 /* No response, or not soliciting right now. */
1284 closedev(np
, FT_ICMP
);
1289 if (np
->solicit
< event
) event
= np
->solicit
;
1292 /* Read router adverts. */
1293 for (i
= 0; i
< n_nets
; i
++) {
1295 if (!(np
->flags
& NF_BOUND
)) continue;
1296 if (np
->sol_ct
< 0) continue;
1298 if (!opendev(np
, FT_ICMP
, 0)) continue;
1299 get_buf(&np
->fdp
->bp
);
1300 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->ip
, BUF_IP_SIZE
);
1302 if (errno
!= ASYN_INPROGRESS
&& errno
!= EPACKSIZE
) {
1303 report(np
->fdp
->device
);
1308 /* Is there an advert? */
1309 if (i
< n_nets
&& r
>= sizeof(ip_hdr_t
) + 8) {
1312 give_buf(&bp
, &np
->fdp
->bp
);
1313 if ((router
= icmp_is_advert(bp
)) != 0) {
1315 printf("%s: Router advert received from %s\n",
1316 np
->fdp
->device
, inet_ntoa(router
));
1320 np
->flags
|= NF_IRDP
;
1321 closedev(np
, FT_ICMP
);
1326 /* We start serving if all the interfaces so marked are configured. */
1327 for (i
= 0; i
< n_nets
; i
++) {
1329 if ((np
->flags
& NF_RELAYING
) && (np
->flags
& NF_BOUND
)) {
1330 if (((np
->ip
^ np
->server
) & np
->mask
) == 0) {
1331 /* Don't relay to a server that is on this same net. */
1332 np
->flags
&= ~NF_RELAYING
;
1335 if (!(np
->flags
& (NF_SERVING
|NF_RELAYING
))) continue;
1336 if (!(np
->flags
& NF_BOUND
)) { serving
= 0; break; }
1340 /* Read DHCP requests. */
1341 for (i
= 0; i
< n_nets
; i
++) {
1343 if (!(np
->flags
& NF_BOUND
)) continue;
1344 if (!(np
->flags
& (NF_SERVING
|NF_RELAYING
)) || !serving
) continue;
1346 if (!opendev(np
, FT_BOOTPS
, 0)) continue;
1347 get_buf(&np
->fdp
->bp
);
1348 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->udpio
, BUF_UDP_SIZE
);
1351 if (errno
!= ASYN_INPROGRESS
&& errno
!= EPACKSIZE
) {
1352 report(np
->fdp
->device
);
1357 /* Is there a request? */
1359 && r
>= sizeof(udp_io_hdr_t
) + offsetof(dhcp_t
, options
)
1361 give_buf(&bp
, &np
->fdp
->bp
);
1364 printf("%s: Got DHCP packet from %s to ",
1365 np
->fdp
->device
, inet_ntoa(bp
->udpio
->uih_src_addr
));
1366 printf("%s\n", inet_ntoa(bp
->udpio
->uih_dst_addr
));
1367 if (debug
>= 2) printdhcp(bp
->dhcp
);
1370 /* Can we do something with this DHCP packet? */
1371 if ((r
= servdhcp(np
, bp
, r
)) > 0) {
1372 /* Yes, we have something to send somewhere. */
1373 if (sendpacket(np
, bp
->udpio
, r
)) {
1375 printf("%s: Sent DHCP packet to %s\n",
1377 inet_ntoa(bp
->udpio
->uih_dst_addr
));
1378 if (debug
>= 2) printdhcp(bp
->dhcp
);
1386 static char *lastbrk
;
1389 if (sbrk(0) != lastbrk
) {
1391 printf("Memory use = %lu\n",
1392 (unsigned long) (lastbrk
- &_end
));
1397 /* Bail out if not a server, and there is nothing else to do ever. */
1398 if (!serving
&& event
== NEVER
) break;
1400 /* Wait for something to do. */
1401 eventtv
.tv_sec
= event
;
1402 if (asyn_wait(&asyn
, 0, event
== NEVER
? nil
: &eventtv
) < 0) {
1403 if (errno
!= EINTR
) {
1404 report("asyn_wait()");
1409 if (debug
>= 1) printf("Nothing more to do! Starting over...\n");