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 send(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
;
681 start
= now
= time(nil
);
685 while (i
< argc
&& argv
[i
][0] == '-') {
686 char *opt
= argv
[i
++]+1;
688 if (opt
[0] == '-' && opt
[1] == 0) break; /* -- */
690 while (*opt
!= 0) switch (*opt
++) {
693 if (i
== argc
) usage();
701 if (i
== argc
) usage();
709 if (i
== argc
) usage();
717 if (between('0', *opt
, '9')) test
= strtoul(opt
, &opt
, 10);
721 if (between('0', *opt
, '9')) debug
= strtoul(opt
, &opt
, 10);
736 if (aflag
+ rflag
+ qflag
> 1) usage();
738 if (aflag
|| rflag
) {
739 /* Add or remove addresses from the dynamic pool. */
740 while (i
< argc
) updatepool(aflag
, argv
[i
++]);
744 if (i
!= argc
) usage();
747 /* Only show the contents of the cache and dynamic pool to the user. */
753 port_server
= portbyname("bootps");
754 port_client
= portbyname("bootpc");
756 sa
.sa_handler
= onsig
;
757 sigemptyset(&sa
.sa_mask
);
759 sigaction(SIGUSR1
, &sa
, nil
);
760 sigaction(SIGUSR2
, &sa
, nil
);
762 /* Initial configuration. */
763 for (i
= 0; i
< N_NETS
; i
++) {
767 /* Is there something there? */
768 if ((fd
= open(ipdev(i
), O_RDWR
|O_NONBLOCK
)) < 0) {
769 if (errno
!= ENOENT
&& errno
!= ENODEV
&& errno
!= ENXIO
) {
776 network
[n_nets
++]= np
= newnetwork();
780 if (opendev(np
, FT_ETHERNET
, 1)) {
781 np
->type
= B(&np
->eth
)[0] != 'Z' ? NT_ETHERNET
: NT_SINK
;
783 printf("%s: Ethernet address is %s%s\n",
784 np
->fdp
->device
, ether_ntoa(&np
->eth
),
785 np
->type
== NT_SINK
? " (sink)" : "");
787 closedev(np
, FT_ETHERNET
);
790 /* Only true Ethernets worry about DHCP. */
791 if (np
->type
!= NT_ETHERNET
) np
->renew
= np
->rebind
= np
->lease
= NEVER
;
794 /* Try to find my interfaces in the DHCP table. */
795 for (i
= 0; i
< n_nets
; i
++) {
797 u8_t clid
[1+DHCP_HLEN_ETH
];
801 if (np
->flags
& NF_BOUND
) continue;
803 if (np
->type
== NT_IP
) {
806 ether2clid(clid
, &np
->eth
);
807 cilen
= 1+DHCP_HLEN_ETH
;
810 /* Try to find an Ethernet address, or the IP address of an already
811 * configured network. If we have data we get an IP address back.
814 (void) makedhcp(bp
->dhcp
, (u8_t
*) "Minix", 5,
815 clid
, cilen
, np
->ip
, 0, np
);
816 cip
= bp
->dhcp
->yiaddr
;
818 /* Gather information on the interface. */
820 && makedhcp(bp
->dhcp
, (u8_t
*) "Minix", 5,
821 clid
, cilen
, cip
, cip
, np
)
827 cachedhcp(np
->n
, bp
->dhcp
);
829 (void) gettag(bp
->dhcp
, DHCP_TAG_NETMASK
, &pdata
, nil
);
830 memcpy(&np
->mask
, pdata
, sizeof(np
->mask
));
831 if (gettag(bp
->dhcp
, DHCP_TAG_GATEWAY
, &pdata
, nil
)) {
832 memcpy(&np
->gateway
, pdata
, sizeof(np
->gateway
));
836 if (gettag(bp
->dhcp
, DHCP_TAG_IPMTU
, &pdata
, nil
)) {
837 memcpy(&mtu
, pdata
, sizeof(mtu
));
842 set_ipconf(ipdev(np
->n
), np
->ip
, np
->mask
, mtu
);
844 printf("%s: IP address is %s\n",
845 ipdev(np
->n
), cidr_ntoa(np
->ip
, np
->mask
));
847 np
->flags
|= NF_BOUND
;
848 np
->renew
= np
->rebind
= np
->lease
= NEVER
;
849 np
->sol_ct
= N_SOLICITS
;
852 /* Other (previous) interfaces may have been defined. */
862 /* Is it time to request/renew a lease? */
863 for (i
= 0; i
< n_nets
; i
++) {
866 if (np
->renew
<= now
) {
868 static u8_t taglist
[] = {
869 DHCP_TAG_NETMASK
, DHCP_TAG_GATEWAY
, DHCP_TAG_DNS
,
872 u8_t ethclid
[1+DHCP_HLEN_ETH
];
874 /* We may have lost our binding or even our lease. */
875 if (np
->rebind
<= now
) np
->server
= BCAST_IP
;
877 if (np
->lease
<= now
) {
878 if (np
->flags
& NF_BOUND
) closedev(np
, FT_ALL
);
880 if ((np
->flags
& (NF_BOUND
| NF_POSSESSIVE
)) == NF_BOUND
) {
881 set_ipconf(ipdev(np
->n
), np
->ip
= 0, np
->mask
= 0, 0);
883 printf("%s: Interface disabled (lease expired)\n",
887 np
->flags
&= ~NF_BOUND
;
890 /* See if we can open the network we need to send on. */
891 if (!(np
->flags
& NF_BOUND
)) {
892 if (!opendev(np
, FT_ETHERNET
, 1)) continue;
894 if (!opendev(np
, FT_BOOTPC
, 1)) continue;
897 if (!(np
->flags
& NF_NEGOTIATING
)) {
898 /* We need to start querying a DHCP server. */
900 np
->delta
= DELTA_FIRST
;
901 np
->flags
|= NF_NEGOTIATING
;
904 /* Fill in a DHCP query packet. */
907 bp
->dhcp
->op
= DHCP_BOOTREQUEST
;
908 bp
->dhcp
->htype
= DHCP_HTYPE_ETH
;
909 bp
->dhcp
->hlen
= DHCP_HLEN_ETH
;
910 bp
->dhcp
->xid
= XID(np
);
911 bp
->dhcp
->secs
= htons(now
- np
->start
> 0xFFFF
912 ? 0xFFFF : now
- np
->start
);
913 memcpy(bp
->dhcp
->chaddr
, &np
->eth
, sizeof(np
->eth
));
915 if (np
->lease
<= now
) {
916 /* First time, or my old server is unresponsive. */
919 /* Request an offered address or renew an address. */
921 if (np
->flags
& NF_BOUND
) {
922 /* A renewal, I claim my current address. */
923 bp
->dhcp
->ciaddr
= np
->ip
;
925 /* Nicely ask for the address just offered. */
926 settag(bp
->dhcp
, DHCP_TAG_REQIP
, &np
->ip
,
928 settag(bp
->dhcp
, DHCP_TAG_SERVERID
, &np
->server
,
932 settag(bp
->dhcp
, DHCP_TAG_TYPE
, &type
, 1);
934 /* My client ID. Simply use the default. */
935 ether2clid(ethclid
, &np
->eth
);
936 settag(bp
->dhcp
, DHCP_TAG_CLIENTID
, ethclid
, sizeof(ethclid
));
938 /* The Class ID may serve to recognize Minix hosts. */
939 settag(bp
->dhcp
, DHCP_TAG_CLASSID
, "Minix", 5);
941 /* The few tags that Minix can make good use of. */
942 settag(bp
->dhcp
, DHCP_TAG_REQPAR
, taglist
, sizeof(taglist
));
944 /* Some weird sites use a hostname, not a client ID. */
945 if (np
->hostname
!= nil
) {
946 settag(bp
->dhcp
, DHCP_TAG_HOSTNAME
,
947 np
->hostname
, strlen(np
->hostname
));
950 bp
->udpio
->uih_src_addr
= np
->ip
;
951 bp
->udpio
->uih_dst_addr
= np
->server
;
952 bp
->udpio
->uih_src_port
= port_client
;
953 bp
->udpio
->uih_dst_port
= port_server
;
954 bp
->udpio
->uih_ip_opt_len
= 0;
955 bp
->udpio
->uih_data_len
= sizeof(dhcp_t
);
957 if (!(np
->flags
& NF_BOUND
)) {
958 /* Rebind over Ethernet. */
960 if (send(np
, bp
->eth
, sizeof(eth_hdr_t
) + sizeof(ip_hdr_t
)
961 + sizeof(udp_hdr_t
) + sizeof(dhcp_t
))) {
963 printf("%s: Broadcast DHCP %s\n",
964 np
->fdp
->device
, dhcptypename(type
));
965 if (debug
>= 2) printdhcp(bp
->dhcp
);
969 /* Renew over UDP. */
970 if (send(np
, bp
->udpio
, sizeof(udp_io_hdr_t
)
973 printf("%s: Sent DHCP %s to %s\n",
976 inet_ntoa(np
->server
));
977 if (debug
>= 2) printdhcp(bp
->dhcp
);
983 /* When to continue querying a DHCP server? */
984 if (np
->flags
& NF_BOUND
) {
985 /* Still bound, keep halving time till next event. */
988 e
= now
< np
->rebind
? np
->rebind
: np
->lease
;
990 if (d
< DELTA_SLOW
) d
= DELTA_SLOW
;
992 if (np
->renew
> e
) np
->renew
= e
;
994 /* Not bound, be desparate. */
995 np
->renew
= now
+ np
->delta
;
996 if ((np
->delta
*= 2) > DELTA_FAST
) np
->delta
= DELTA_FAST
;
999 if (np
->renew
< event
) event
= np
->renew
;
1002 /* Read DHCP responses. */
1003 for (i
= 0; i
< n_nets
; i
++) {
1005 if (!(np
->flags
& NF_NEGOTIATING
)) continue;
1007 if (!(np
->flags
& NF_BOUND
)) {
1008 if (!opendev(np
, FT_ETHERNET
, 0)) continue;
1009 get_buf(&np
->fdp
->bp
);
1010 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->eth
,
1013 if (!opendev(np
, FT_BOOTPC
, 0)) continue;
1014 get_buf(&np
->fdp
->bp
);
1015 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->udpio
,
1019 if (errno
!= ASYN_INPROGRESS
&& errno
!= EPACKSIZE
) {
1020 report(np
->fdp
->device
);
1025 /* Is there a response? */
1027 give_buf(&bp
, &np
->fdp
->bp
);
1028 if (((!(np
->flags
& NF_BOUND
)
1029 && r
>= (sizeof(eth_hdr_t
) + sizeof(ip_hdr_t
)
1030 + sizeof(udp_hdr_t
) + offsetof(dhcp_t
, options
))
1032 && bp
->udpio
->uih_dst_port
== port_client
)
1034 ((np
->flags
& NF_BOUND
)
1035 && r
>= sizeof(udp_io_hdr_t
) + offsetof(dhcp_t
, options
)))
1036 && bp
->dhcp
->op
== DHCP_BOOTREPLY
1037 && bp
->dhcp
->htype
== DHCP_HTYPE_ETH
1038 && bp
->dhcp
->hlen
== DHCP_HLEN_ETH
1039 && bp
->dhcp
->xid
== XID(np
)
1040 && memcmp(bp
->dhcp
->chaddr
, &np
->eth
, sizeof(np
->eth
)) == 0
1042 /* Pfew! We got a DHCP reply! */
1046 ipaddr_t mask
, gateway
, relay
, server
;
1048 u32_t lease
, renew
, rebind
, t
;
1050 relay
= bp
->udpio
->uih_src_addr
;
1051 if (gettag(bp
->dhcp
, DHCP_TAG_SERVERID
, &pdata
, nil
)) {
1052 memcpy(&server
, pdata
, sizeof(server
));
1057 if (gettag(bp
->dhcp
, DHCP_TAG_TYPE
, &pdata
, nil
)) {
1060 type
= DHCP_ACK
; /* BOOTP? */
1064 printf("%s: Got a DHCP %s from %s",
1065 np
->fdp
->device
, dhcptypename(type
), inet_ntoa(server
));
1066 printf(relay
!= server
? " through %s\n" : "\n",
1068 if (debug
>= 2) printdhcp(bp
->dhcp
);
1071 if (gettag(bp
->dhcp
, DHCP_TAG_NETMASK
, &pdata
, nil
)) {
1072 memcpy(&mask
, pdata
, sizeof(mask
));
1074 mask
= defaultmask(bp
->dhcp
->ciaddr
);
1077 if (gettag(bp
->dhcp
, DHCP_TAG_IPMTU
, &pdata
, nil
)) {
1078 memcpy(&mtu
, pdata
, sizeof(mtu
));
1084 if (gettag(bp
->dhcp
, DHCP_TAG_GATEWAY
, &pdata
, nil
)) {
1085 memcpy(&gateway
, pdata
, sizeof(gateway
));
1091 if (gettag(bp
->dhcp
, DHCP_TAG_LEASE
, &pdata
, nil
)) {
1092 memcpy(&lease
, pdata
, sizeof(lease
));
1093 lease
= ntohl(lease
);
1096 rebind
= lease
- lease
/ 8;
1097 if (gettag(bp
->dhcp
, DHCP_TAG_REBINDING
, &pdata
, nil
)) {
1098 memcpy(&t
, pdata
, sizeof(t
));
1100 if (t
< rebind
) rebind
= t
;
1104 if (gettag(bp
->dhcp
, DHCP_TAG_RENEWAL
, &pdata
, nil
)) {
1105 memcpy(&t
, pdata
, sizeof(t
));
1107 if (t
< renew
) renew
= t
;
1110 if (type
== DHCP_OFFER
&& np
->rebind
<= np
->renew
) {
1111 /* It's an offer for an address and we haven't taken one
1112 * yet. It's all the same to us, so take this one.
1114 np
->ip
= bp
->dhcp
->yiaddr
;
1117 np
->gateway
= gateway
;
1118 np
->delta
= DELTA_FIRST
;
1120 np
->rebind
= np
->lease
= now
+ DELTA_FAST
;
1122 /* Send out an ARP request to see if the offered address
1123 * is in use already.
1126 if (send(np
, bp
->eth
, sizeof(arp46_t
))) {
1128 printf("Sent ARP for %s\n", inet_ntoa(np
->ip
));
1131 np
->flags
&= ~NF_CONFLICT
;
1134 if (type
== DHCP_ACK
&& !(np
->flags
& NF_CONFLICT
)) {
1135 /* An acknowledgment. The address is all mine. */
1136 cachedhcp(np
->n
, bp
->dhcp
);
1137 np
->ip
= bp
->dhcp
->yiaddr
;
1140 set_ipconf(ipdev(np
->n
), np
->ip
, np
->mask
, mtu
);
1142 printf("%s: Address set to %s\n",
1143 ipdev(np
->n
), cidr_ntoa(np
->ip
, np
->mask
));
1145 if (lease
>= NEVER
- now
) {
1146 /* The lease is infinite! */
1147 np
->renew
= np
->rebind
= np
->lease
= NEVER
;
1149 np
->lease
= now
+ lease
;
1150 np
->renew
= now
+ renew
;
1151 np
->rebind
= now
+ rebind
;
1154 np
->renew
= now
+ 60;
1155 np
->rebind
= test
>= 4 ? np
->renew
: np
->renew
+ 60;
1156 np
->lease
= test
>= 5 ? np
->rebind
: np
->rebind
+ 60;
1158 if (!(np
->flags
& NF_IRDP
)) {
1159 np
->sol_ct
= (np
->flags
& NF_BOUND
) ? 1 : N_SOLICITS
;
1162 np
->flags
&= ~NF_NEGOTIATING
;
1163 np
->flags
|= NF_BOUND
;
1164 closedev(np
, FT_ETHERNET
);
1165 closedev(np
, FT_BOOTPC
);
1168 if (type
== DHCP_ACK
&& (np
->flags
& NF_CONFLICT
)) {
1169 /* Alas there is a conflict. Decline to use the address. */
1170 u8_t ethclid
[1+DHCP_HLEN_ETH
];
1171 static char USED
[]= "Address in use by 00:00:00:00:00:00";
1174 dhcp_init(bp
->dhcp
);
1175 bp
->dhcp
->op
= DHCP_BOOTREQUEST
;
1176 bp
->dhcp
->htype
= DHCP_HTYPE_ETH
;
1177 bp
->dhcp
->hlen
= DHCP_HLEN_ETH
;
1178 bp
->dhcp
->xid
= XID(np
);
1180 memcpy(bp
->dhcp
->chaddr
, &np
->eth
, sizeof(np
->eth
));
1181 settag(bp
->dhcp
, DHCP_TAG_REQIP
, &np
->ip
, sizeof(np
->ip
));
1182 settag(bp
->dhcp
, DHCP_TAG_TYPE
, &type
, 1);
1183 ether2clid(ethclid
, &np
->eth
);
1184 settag(bp
->dhcp
, DHCP_TAG_CLIENTID
,ethclid
,sizeof(ethclid
));
1185 strcpy(USED
+18, ether_ntoa(&np
->conflict
));
1186 settag(bp
->dhcp
, DHCP_TAG_MESSAGE
, USED
, strlen(USED
));
1188 bp
->udpio
->uih_src_port
= port_client
;
1189 bp
->udpio
->uih_dst_port
= port_server
;
1190 bp
->udpio
->uih_ip_opt_len
= 0;
1191 bp
->udpio
->uih_data_len
= sizeof(dhcp_t
);
1194 if (send(np
, bp
->eth
, sizeof(eth_hdr_t
) + sizeof(ip_hdr_t
)
1195 + sizeof(udp_hdr_t
) + sizeof(dhcp_t
))) {
1197 printf("%s: Broadcast DHCP %s\n",
1198 np
->fdp
->device
, dhcptypename(type
));
1199 if (debug
>= 2) printdhcp(bp
->dhcp
);
1203 np
->renew
= np
->rebind
= np
->lease
= now
+ DELTA_FAST
;
1204 np
->delta
= DELTA_FIRST
;
1207 if (type
== DHCP_NAK
) {
1208 /* Oops, a DHCP server doesn't like me, start over! */
1209 np
->renew
= np
->rebind
= np
->lease
= now
+ DELTA_FAST
;
1210 np
->delta
= DELTA_FIRST
;
1212 fprintf(stderr
, "%s: Got a NAK from %s",
1213 program
, inet_ntoa(server
));
1214 if (relay
!= server
) {
1215 fprintf(stderr
, " through %s", inet_ntoa(relay
));
1217 if (gettag(bp
->dhcp
, DHCP_TAG_MESSAGE
, &pdata
, &len
)) {
1218 fprintf(stderr
, " saying: \"%.*s\"", (int)len
, pdata
);
1220 fputc('\n', stderr
);
1223 if (!(np
->flags
& NF_BOUND
)
1225 && r
>= sizeof(arp46_t
)
1226 && is_arp_me(bp
, np
)
1228 /* Oh no, someone else is using the address offered to me! */
1229 np
->flags
|= NF_CONFLICT
;
1231 fprintf(stderr
, "%s: %s: %s offered by ",
1235 fprintf(stderr
, "%s is already in use by %s\n",
1236 inet_ntoa(np
->server
),
1237 ether_ntoa(&np
->conflict
));
1240 if (np
->renew
< event
) event
= np
->renew
;
1243 /* Perform router solicitations. */
1244 for (i
= 0; i
< n_nets
; i
++) {
1246 if (!(np
->flags
& NF_BOUND
)) continue;
1248 if (np
->solicit
<= now
) {
1249 if (!opendev(np
, FT_ICMP
, 1)) continue;
1253 if (np
->gateway
!= 0) {
1254 /* No IRDP response seen yet, advertise the router given
1255 * by DHCP to my own interface.
1257 icmp_advert(bp
, np
);
1258 if (send(np
, bp
->ip
, sizeof(ip_hdr_t
) + 16)) {
1260 printf("%s: Sent advert for %s to self\n",
1261 np
->fdp
->device
, inet_ntoa(np
->gateway
));
1264 np
->solicit
= now
+ DELTA_ADV
/2;
1267 if (np
->sol_ct
>= 0 && --np
->sol_ct
>= 0) {
1268 /* Send a router solicitation. */
1270 if (send(np
, bp
->ip
, sizeof(*bp
->ip
) + 8)) {
1272 printf("%s: Broadcast router solicitation\n",
1276 np
->solicit
= now
+ DELTA_SOL
;
1278 /* No response, or not soliciting right now. */
1279 closedev(np
, FT_ICMP
);
1284 if (np
->solicit
< event
) event
= np
->solicit
;
1287 /* Read router adverts. */
1288 for (i
= 0; i
< n_nets
; i
++) {
1290 if (!(np
->flags
& NF_BOUND
)) continue;
1291 if (np
->sol_ct
< 0) continue;
1293 if (!opendev(np
, FT_ICMP
, 0)) continue;
1294 get_buf(&np
->fdp
->bp
);
1295 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->ip
, BUF_IP_SIZE
);
1297 if (errno
!= ASYN_INPROGRESS
&& errno
!= EPACKSIZE
) {
1298 report(np
->fdp
->device
);
1303 /* Is there an advert? */
1304 if (i
< n_nets
&& r
>= sizeof(ip_hdr_t
) + 8) {
1307 give_buf(&bp
, &np
->fdp
->bp
);
1308 if ((router
= icmp_is_advert(bp
)) != 0) {
1310 printf("%s: Router advert received from %s\n",
1311 np
->fdp
->device
, inet_ntoa(router
));
1315 np
->flags
|= NF_IRDP
;
1316 closedev(np
, FT_ICMP
);
1321 /* We start serving if all the interfaces so marked are configured. */
1322 for (i
= 0; i
< n_nets
; i
++) {
1324 if ((np
->flags
& NF_RELAYING
) && (np
->flags
& NF_BOUND
)) {
1325 if (((np
->ip
^ np
->server
) & np
->mask
) == 0) {
1326 /* Don't relay to a server that is on this same net. */
1327 np
->flags
&= ~NF_RELAYING
;
1330 if (!(np
->flags
& (NF_SERVING
|NF_RELAYING
))) continue;
1331 if (!(np
->flags
& NF_BOUND
)) { serving
= 0; break; }
1335 /* Read DHCP requests. */
1336 for (i
= 0; i
< n_nets
; i
++) {
1338 if (!(np
->flags
& NF_BOUND
)) continue;
1339 if (!(np
->flags
& (NF_SERVING
|NF_RELAYING
)) || !serving
) continue;
1341 if (!opendev(np
, FT_BOOTPS
, 0)) continue;
1342 get_buf(&np
->fdp
->bp
);
1343 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->udpio
, BUF_UDP_SIZE
);
1346 if (errno
!= ASYN_INPROGRESS
&& errno
!= EPACKSIZE
) {
1347 report(np
->fdp
->device
);
1352 /* Is there a request? */
1354 && r
>= sizeof(udp_io_hdr_t
) + offsetof(dhcp_t
, options
)
1356 give_buf(&bp
, &np
->fdp
->bp
);
1359 printf("%s: Got DHCP packet from %s to ",
1360 np
->fdp
->device
, inet_ntoa(bp
->udpio
->uih_src_addr
));
1361 printf("%s\n", inet_ntoa(bp
->udpio
->uih_dst_addr
));
1362 if (debug
>= 2) printdhcp(bp
->dhcp
);
1365 /* Can we do something with this DHCP packet? */
1366 if ((r
= servdhcp(np
, bp
, r
)) > 0) {
1367 /* Yes, we have something to send somewhere. */
1368 if (send(np
, bp
->udpio
, r
)) {
1370 printf("%s: Sent DHCP packet to %s\n",
1372 inet_ntoa(bp
->udpio
->uih_dst_addr
));
1373 if (debug
>= 2) printdhcp(bp
->dhcp
);
1381 static char *lastbrk
;
1384 if (sbrk(0) != lastbrk
) {
1386 printf("Memory use = %lu\n",
1387 (unsigned long) (lastbrk
- &_end
));
1392 /* Bail out if not a server, and there is nothing else to do ever. */
1393 if (!serving
&& event
== NEVER
) break;
1395 /* Wait for something to do. */
1396 eventtv
.tv_sec
= event
;
1397 if (asyn_wait(&asyn
, 0, event
== NEVER
? nil
: &eventtv
) < 0) {
1398 if (errno
!= EINTR
) {
1399 report("asyn_wait()");
1404 if (debug
>= 1) printf("Nothing more to do! Bailing out...\n");