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. */
43 #define BCAST_IP htonl(0xFFFFFFFFUL)
45 #define BCAST_IP HTONL(0xFFFFFFFFUL)
48 /* We try to play with up to this many networks. */
50 static unsigned n_nets
; /* Actual number of networks. */
54 void report(const char *label
)
58 logfp
= fopen("/usr/log/dhcp.log", "w");
60 fprintf(logfp
, "%s: %s: %s\n", program
, label
, strerror(errno
));
63 void fatal(const char *label
)
69 void *allocate(size_t size
)
73 if ((mem
= malloc(size
)) == nil
) fatal("Can't allocate memory");
77 /* Choose a DHCP xid based on the start time and network number. Not really
78 * random, but we don't have anything more random than the clock anyway.
80 #define XID(np) htonl(((u32_t) (np)->start << 8) | (np)->n)
82 static network_t
*network
[N_NETS
];
84 int ifname2if(const char *name
)
86 /* Translate an interface name to a number, -1 if bad. */
90 if (*name
++ != 'i' || *name
++ != 'p') return -1;
91 n
= strtoul(name
, &end
, 10);
92 if (end
== name
|| *end
!= 0) return -1;
93 if (n
>= N_NETS
) return -1;
97 network_t
*if2net(int n
)
99 /* Translate an interface number to a network struct. */
102 for (i
= 0; i
< n_nets
; i
++) {
103 if (network
[i
]->n
== n
) return network
[i
];
108 static ipaddr_t
defaultmask(ipaddr_t ip
)
110 /* Compute netmask by the oldfashioned Class rules. */
112 if (B(&ip
)[0] < 0x80) return htonl(0xFF000000UL
); /* Class A. */
113 if (B(&ip
)[0] < 0xC0) return htonl(0xFFFF0000UL
); /* Class B. */
114 if (B(&ip
)[0] < 0xE0) return htonl(0xFFFFFF00UL
); /* Class C. */
115 return htonl(0xFFFFFFFFUL
); /* Multicast? Shouldn't happen... */
117 if (B(&ip
)[0] < 0x80) return HTONL(0xFF000000UL
); /* Class A. */
118 if (B(&ip
)[0] < 0xC0) return HTONL(0xFFFF0000UL
); /* Class B. */
119 if (B(&ip
)[0] < 0xE0) return HTONL(0xFFFFFF00UL
); /* Class C. */
120 return HTONL(0xFFFFFFFFUL
); /* Multicast? Shouldn't happen... */
125 #define POOL_MAGIC htonl(0x81F85D00UL)
127 #define POOL_MAGIC HTONL(0x81F85D00UL)
130 typedef struct pool
{ /* Dynamic pool entry. */
131 u32_t magic
; /* Pool file magic number. */
132 ipaddr_t ip
; /* IP address. */
133 u32_t expire
; /* When does/did the lease expire? */
134 u8_t len
; /* Client ID length. */
135 u8_t unused
[19]; /* Space for extensions. */
136 u8_t clid
[CLID_MAX
]; /* Client ID of current/last user. */
139 static int openpool(int mode
)
141 /* Open the dynamic pool and lock it, return fd on success or -1. */
145 if ((fd
= open(poolfile
, mode
, 0644)) < 0) {
146 if (errno
!= ENOENT
) fatal(poolfile
);
149 if (mode
!= O_RDONLY
) {
151 lck
.l_whence
= SEEK_SET
;
154 if (fcntl(fd
, F_SETLKW
, &lck
) < 0) fatal(poolfile
);
159 static int readpool(int fd
, pool_t
*entry
)
161 /* Read one pool table entry, return true unless EOF. */
164 if ((r
= read(fd
, entry
, sizeof(*entry
))) < 0) fatal(poolfile
);
165 if (r
== 0) return 0;
167 if (r
!= sizeof(*entry
) || entry
->magic
!= POOL_MAGIC
) {
168 fprintf(stderr
, "%s: %s: Pool table is corrupt\n",
176 static void writepool(int fd
, pool_t
*entry
)
178 /* (Over)write a pool table entry. */
179 if (write(fd
, entry
, sizeof(*entry
)) < 0
180 || (entry
->expire
> now
&& fsync(fd
) < 0)
186 static ipaddr_t
findpool(u8_t
*client
, size_t len
, ipaddr_t ifip
)
188 /* Look for a client ID in the dynamic address pool within the same network
189 * as 'ifip'. Select an unused one for a new client if necessary. Return
190 * 0 if nothing is available, otherwise the IP address we can offer.
193 pool_t entry
, oldest
;
198 /* Any information available on the network the client is at? */
199 if (!makedhcp(&dhcp
, nil
, 0, nil
, 0, ifip
, ifip
, nil
)) return 0;
201 if ((fd
= openpool(O_RDWR
)) < 0) return 0;
203 (void) gettag(&dhcp
, DHCP_TAG_NETMASK
, &pmask
, nil
);
204 memcpy(&mask
, pmask
, sizeof(mask
));
206 oldest
.expire
= NEVER
;
207 while ((found
= readpool(fd
, &entry
))) {
209 if (entry
.ip
== 0) continue;
211 /* Correct network? */
212 if (((entry
.ip
^ ifip
) & mask
) != 0) continue;
214 /* Client present? */
215 if (entry
.len
== len
&& memcmp(entry
.clid
, client
, len
) == 0) break;
217 /* Oldest candidate for a new lease? */
218 entry
.expire
= ntohl(entry
.expire
);
219 if (entry
.expire
< oldest
.expire
) oldest
= entry
;
223 if (found
) return entry
.ip
;
224 if (oldest
.expire
<= now
) return oldest
.ip
;
228 static int commitpool(ipaddr_t ip
, u8_t
*client
, size_t len
, time_t expire
)
230 /* Commit a new binding to stable storage, return true on success. */
234 if ((fd
= openpool(O_RDWR
)) < 0) return 0;
237 if (!readpool(fd
, &entry
)) {
241 } while (entry
.ip
!= ip
);
243 entry
.expire
= htonl(expire
);
245 memcpy(entry
.clid
, client
, len
);
246 if (lseek(fd
, -(off_t
)sizeof(entry
), SEEK_CUR
) == -1) fatal(poolfile
);
247 writepool(fd
, &entry
);
252 static void updatepool(int add
, const char *name
)
254 /* Add a new IP address to the dynamic pool. */
261 if ((he
= gethostbyname(name
)) == nil
|| he
->h_addrtype
!= AF_INET
) {
262 fprintf(stderr
, "%s: %s: Unknown host\n", program
, name
);
265 for (i
= 0; he
->h_addr_list
[i
] != nil
; i
++) {}
267 fprintf(stderr
, "%s: %s has %d addresses\n", program
, name
, i
);
270 memcpy(&ip
, he
->h_addr_list
[0], sizeof(ip
));
272 if ((fd
= openpool(O_RDWR
|O_CREAT
)) < 0) fatal(poolfile
);
276 while (readpool(fd
, &entry
)) {
278 if (entry
.ip
== ip
) {
279 fprintf(stderr
, "%s: %s: %s is already present\n",
280 program
, poolfile
, name
);
283 if (entry
.ip
== 0 && off0
== -1) off0
= off
;
285 if (entry
.ip
== ip
) {
286 memset(&entry
, 0, sizeof(entry
));
287 entry
.magic
= POOL_MAGIC
;
289 if (lseek(fd
, off
, SEEK_SET
) == -1) fatal(poolfile
);
290 writepool(fd
, &entry
);
297 if (off0
!= -1 && lseek(fd
, off0
, SEEK_SET
) == -1) fatal(poolfile
);
298 memset(&entry
, 0, sizeof(entry
));
299 entry
.magic
= POOL_MAGIC
;
301 writepool(fd
, &entry
);
306 static void cachedhcp(int n
, dhcp_t
*dp
)
308 /* Store a DHCP packet in a cache where those who care can find it. */
314 if (test
> 0) return;
317 /* First time, clear store and also save my pid. */
318 if ((fp
= fopen(PATH_DHCPPID
, "w")) != nil
) {
319 if (fprintf(fp
, "%d\n", getpid()) == EOF
|| fclose(fp
) == EOF
) {
324 mode
= O_WRONLY
| O_CREAT
| O_TRUNC
;
329 dp
->xid
= htonl(now
); /* To tell how old this data is. */
331 if ((fd
= open(cachefile
, mode
, 0666)) < 0
332 || lseek(fd
, (off_t
) n
* sizeof(*dp
), SEEK_SET
) == -1
333 || write(fd
, dp
, sizeof(*dp
)) < 0
336 if (errno
!= ENOENT
) fatal(cachefile
);
340 static void printdata(void)
342 /* Show the contents of the cache and the dynamic pool. */
348 unsigned long expire
;
349 char delta
[3*sizeof(u32_t
)];
353 if ((fd
= open(cachefile
, O_RDONLY
)) < 0) fatal(cachefile
);
355 while ((r
= read(fd
, &d
, sizeof(d
))) == sizeof(d
)) {
357 printf("DHCP data for network %d:\n", i
);
362 if (r
< 0) fatal(cachefile
);
365 if ((fd
= openpool(O_RDONLY
)) >= 0) {
366 printf("Dynamic address pool since %ld:\n", (long) now
);
367 while (readpool(fd
, &entry
)) {
368 if (entry
.ip
== 0) continue;
369 expire
= ntohl(entry
.expire
);
371 strcpy(delta
, "unused");
373 if (expire
== 0xFFFFFFFFUL
) {
374 strcpy(delta
, "infinite");
377 sprintf(delta
, "-%lu", now
- expire
);
379 sprintf(delta
, "+%lu", expire
- now
);
381 printf("\t%-15s %8s ", inet_ntoa(entry
.ip
), delta
);
382 for (i
= 0; i
< entry
.len
; i
++) {
383 printf("%02X", entry
.clid
[i
]);
391 static udpport_t
portbyname(const char *name
)
395 if ((se
= getservbyname(name
, "udp")) == nil
) {
396 fprintf(stderr
, "%s: Unknown port \"%s\"\n", program
, name
);
402 static int sendpacket(network_t
*np
, void *data
, size_t len
)
404 /* Send out a packet using a filedescriptor that is probably in async mode,
405 * so first dup() a sync version, then write. Return true on success.
410 if ((fd
= dup(np
->fdp
->fd
)) < 0) fatal("Can't dup()");
411 if ((r
= write(fd
, data
, len
)) < 0) {
412 report(np
->fdp
->device
);
419 static size_t servdhcp(network_t
*np
, buf_t
*bp
, size_t dlen
)
423 u8_t defclid
[1+sizeof(bp
->dhcp
->chaddr
)];
424 u8_t
*pdata
, *client
, *class, *server
, *reqip
, *lease
;
426 size_t len
, cilen
, calen
;
429 static char NAKMESS
[] = "IP address requested isn't yours";
431 if (test
> 0) return 0;
433 /* The IP address of the interface close to the client. */
434 ifip
= bp
->dhcp
->giaddr
!= 0 ? bp
->dhcp
->giaddr
: np
->ip
;
436 /* All kinds of DHCP tags. */
437 if (gettag(bp
->dhcp
, DHCP_TAG_TYPE
, &pdata
, nil
)) {
440 type
= -1; /* BOOTP? */
443 if (!gettag(bp
->dhcp
, DHCP_TAG_CLIENTID
, &client
, &cilen
)) {
444 defclid
[0]= bp
->dhcp
->htype
;
445 memcpy(defclid
+1, bp
->dhcp
->chaddr
, bp
->dhcp
->hlen
);
447 cilen
= 1+bp
->dhcp
->hlen
;
450 if (!gettag(bp
->dhcp
, DHCP_TAG_CLASSID
, &class, &calen
)) {
454 if (!gettag(bp
->dhcp
, DHCP_TAG_SERVERID
, &server
, nil
)) {
458 if (!gettag(bp
->dhcp
, DHCP_TAG_REQIP
, &reqip
, nil
)) {
462 /* I'm a server? Then see if I know this client. */
463 if ((np
->flags
& NF_SERVING
)
464 && bp
->dhcp
->op
== DHCP_BOOTREQUEST
465 && between(1, bp
->dhcp
->hlen
, sizeof(bp
->dhcp
->chaddr
))
466 && (server
== nil
|| memcmp(server
, &np
->ip
, sizeof(np
->ip
)) == 0)
470 /* Is the client in my tables? */
471 (void) makedhcp(abp
->dhcp
, class, calen
, client
, cilen
, 0, ifip
, nil
);
472 cip
= abp
->dhcp
->yiaddr
;
475 /* If not, do we have a dynamic address? */
476 if (cip
== 0 && (cip
= findpool(client
, cilen
, ifip
)) != 0) dyn
= 1;
478 if (type
== DHCP_INFORM
) {
479 /* The client already has an address, it just wants information.
480 * We only answer if we could answer a normal request (cip != 0),
481 * unless configured to answer anyone.
483 if (cip
!= 0 || (np
->flags
& NF_INFORM
)) cip
= bp
->dhcp
->ciaddr
;
486 if (cip
== 0 || !makedhcp(abp
->dhcp
, class, calen
,
487 client
, cilen
, cip
, ifip
, nil
)) {
492 if (gettag(abp
->dhcp
, DHCP_TAG_LEASE
, &lease
, nil
)) {
493 memcpy(&expire
, lease
, sizeof(expire
));
494 expire
= now
+ ntohl(expire
);
495 if (expire
< now
) expire
= 0xFFFFFFFFUL
;
498 /* A dynamic address must have a lease. */
499 fprintf(stderr
, "%s: No lease set for address %s\n",
500 program
, inet_ntoa(cip
));
504 expire
= 0xFFFFFFFFUL
;
507 /* What does our client want, and what do we say? */
512 /* Assign this address for a short moment. */
513 if (dyn
&& !commitpool(cip
, client
, cilen
, now
+ DELTA_FAST
)) {
522 /* The address wanted must be the address we offer. */
523 if ((reqip
!= nil
&& memcmp(reqip
, &cip
, sizeof(cip
)) != 0)
524 || (bp
->dhcp
->ciaddr
!= 0 && bp
->dhcp
->ciaddr
!= cip
)
528 if (dyn
&& type
== DHCP_REQUEST
) {
529 /* Assign this address for the duration of the lease. */
530 if (!commitpool(cip
, client
, cilen
, expire
)) put_buf(&abp
);
535 /* Our client doesn't want the offered address! */
538 && memcmp(reqip
, &cip
, sizeof(cip
)) == 0
542 fprintf(stderr
, "%s: ", program
);
543 for (i
= 0; i
< cilen
; i
++) {
544 fprintf(stderr
, "%02X", client
[i
]);
546 fprintf(stderr
, " declines %s", inet_ntoa(cip
));
547 if (gettag(bp
->dhcp
, DHCP_TAG_MESSAGE
, &pdata
, &len
)) {
548 fprintf(stderr
, " saying: \"%.*s\"", (int)len
, pdata
);
552 /* Disable address for the duration of the lease. */
553 (void) commitpool(cip
, nil
, 0, expire
);
559 /* Our client is nice enough to return its address. */
560 if (dyn
) (void) commitpool(cip
, client
, cilen
, now
);
564 default: /* Anything else is ignored. */
570 /* Finish the return packet. */
571 abp
->dhcp
->htype
= bp
->dhcp
->htype
;
572 abp
->dhcp
->hlen
= bp
->dhcp
->hlen
;
574 abp
->dhcp
->xid
= bp
->dhcp
->xid
;
576 abp
->dhcp
->flags
= bp
->dhcp
->flags
;
577 abp
->dhcp
->ciaddr
= 0;
578 abp
->dhcp
->yiaddr
= atype
== DHCP_NAK
? 0 : cip
;
579 if (atype
== DHCP_NAK
) abp
->dhcp
->siaddr
= 0;
580 abp
->dhcp
->giaddr
= bp
->dhcp
->giaddr
;
581 memcpy(abp
->dhcp
->chaddr
,bp
->dhcp
->chaddr
,sizeof(bp
->dhcp
->chaddr
));
583 settag(abp
->dhcp
, DHCP_TAG_SERVERID
, &np
->ip
, sizeof(np
->ip
));
586 /* No lease specified? Then give an infinite lease. */
587 settag(abp
->dhcp
, DHCP_TAG_LEASE
, &expire
, sizeof(expire
));
590 if (type
== DHCP_INFORM
) {
591 /* Oops, this one has a fixed address, so no lease business. */
592 abp
->dhcp
->yiaddr
= 0;
593 settag(abp
->dhcp
, DHCP_TAG_LEASE
, nil
, 0);
594 settag(abp
->dhcp
, DHCP_TAG_RENEWAL
, nil
, 0);
595 settag(abp
->dhcp
, DHCP_TAG_REBINDING
, nil
, 0);
598 if (atype
== DHCP_NAK
) {
599 /* A NAK doesn't need much. */
600 memset(abp
->dhcp
->sname
, 0, sizeof(abp
->dhcp
->sname
));
601 memset(abp
->dhcp
->file
, 0, sizeof(abp
->dhcp
->file
));
602 memset(abp
->dhcp
->options
, 255, sizeof(abp
->dhcp
->options
));
603 settag(abp
->dhcp
, DHCP_TAG_MESSAGE
, NAKMESS
, sizeof(NAKMESS
));
606 settag(abp
->dhcp
, DHCP_TAG_TYPE
, &atype
, sizeof(atype
));
608 /* Figure out where to send this to. */
609 abp
->udpio
->uih_src_addr
= np
->ip
;
610 abp
->udpio
->uih_src_port
= port_server
;
611 if (bp
->dhcp
->giaddr
!= 0) {
612 abp
->udpio
->uih_dst_addr
= bp
->dhcp
->giaddr
;
613 abp
->udpio
->uih_dst_port
= port_server
;
615 if (bp
->dhcp
->flags
& DHCP_FLAGS_BCAST
) {
616 abp
->udpio
->uih_dst_addr
= BCAST_IP
;
617 abp
->udpio
->uih_dst_port
= port_client
;
619 if (bp
->udpio
->uih_src_addr
!= 0
620 && bp
->udpio
->uih_dst_addr
== np
->ip
622 abp
->udpio
->uih_dst_addr
= bp
->udpio
->uih_src_addr
;
623 abp
->udpio
->uih_dst_port
= port_client
;
625 abp
->udpio
->uih_dst_addr
= BCAST_IP
;
626 abp
->udpio
->uih_dst_port
= port_client
;
628 abp
->udpio
->uih_ip_opt_len
= 0;
629 abp
->udpio
->uih_data_len
= sizeof(dhcp_t
);
631 /* Copy the packet to the input buffer, and return the new size. */
632 memcpy(bp
->buf
, abp
->buf
, sizeof(bp
->buf
));
634 return sizeof(udp_io_hdr_t
) + sizeof(dhcp_t
);
638 /* I'm a relay? If it is a not already a relayed request then relay. */
639 if ((np
->flags
& NF_RELAYING
)
640 && bp
->dhcp
->op
== DHCP_BOOTREQUEST
641 && bp
->dhcp
->giaddr
== 0
643 bp
->dhcp
->giaddr
= np
->ip
;
644 bp
->udpio
->uih_src_addr
= np
->ip
;
645 bp
->udpio
->uih_src_port
= port_server
;
646 bp
->udpio
->uih_dst_addr
= np
->server
;
647 bp
->udpio
->uih_dst_port
= port_server
;
651 /* I'm a relay? If the server sends a reply to me then relay back. */
652 if ((np
->flags
& NF_RELAYING
)
653 && bp
->dhcp
->op
== DHCP_BOOTREPLY
654 && bp
->dhcp
->giaddr
== np
->ip
657 bp
->udpio
->uih_src_addr
= np
->ip
;
658 bp
->udpio
->uih_src_port
= port_server
;
659 bp
->udpio
->uih_dst_addr
= BCAST_IP
;
660 bp
->udpio
->uih_dst_port
= port_client
;
664 /* Don't know what to do otherwise, so doing nothing seems wise. */
668 static void onsig(int sig
)
671 case SIGUSR1
: debug
++; break;
672 case SIGUSR2
: debug
= 0; break;
676 static void usage(void)
679 "Usage: %s [-qar] [-t[L]] [-d[L]] [-f config] [-c cache] [-p pool] [host ...]\n",
684 int main(int argc
, char **argv
)
691 static struct timeval eventtv
;
697 start
= now
= time(nil
);
701 while (i
< argc
&& argv
[i
][0] == '-') {
702 char *opt
= argv
[i
++]+1;
704 if (opt
[0] == '-' && opt
[1] == 0) break; /* -- */
706 if (strcmp(opt
, "-lwip") == 0) {
711 while (*opt
!= 0) switch (*opt
++) {
714 if (i
== argc
) usage();
722 if (i
== argc
) usage();
730 if (i
== argc
) usage();
738 if (between('0', *opt
, '9')) test
= strtoul(opt
, &opt
, 10);
742 if (between('0', *opt
, '9')) debug
= strtoul(opt
, &opt
, 10);
757 if (aflag
+ rflag
+ qflag
> 1) usage();
759 if (aflag
|| rflag
) {
760 /* Add or remove addresses from the dynamic pool. */
761 while (i
< argc
) updatepool(aflag
, argv
[i
++]);
765 if (i
!= argc
) usage();
768 /* Only show the contents of the cache and dynamic pool to the user. */
774 port_server
= portbyname("bootps");
775 port_client
= portbyname("bootpc");
777 sa
.sa_handler
= onsig
;
778 sigemptyset(&sa
.sa_mask
);
780 sigaction(SIGUSR1
, &sa
, nil
);
781 sigaction(SIGUSR2
, &sa
, nil
);
783 /* Initial configuration. */
784 for (i
= 0; i
< N_NETS
; i
++) {
788 /* Is there something there? */
789 if ((fd
= open(ipdev(i
), O_RDWR
|O_NONBLOCK
)) < 0) {
790 if (errno
!= ENOENT
&& errno
!= ENODEV
&& errno
!= ENXIO
) {
797 network
[n_nets
++]= np
= newnetwork();
802 np
->type
= NT_ETHERNET
;
803 } else if (opendev(np
, FT_ETHERNET
, 1)) {
804 np
->type
= B(&np
->eth
)[0] != 'Z' ? NT_ETHERNET
: NT_SINK
;
806 printf("%s: Ethernet address is %s%s\n",
807 np
->fdp
->device
, ether_ntoa(&np
->eth
),
808 np
->type
== NT_SINK
? " (sink)" : "");
810 closedev(np
, FT_ETHERNET
);
813 /* Only true Ethernets worry about DHCP. */
814 if (np
->type
!= NT_ETHERNET
) np
->renew
= np
->rebind
= np
->lease
= NEVER
;
817 /* Try to find my interfaces in the DHCP table. */
818 for (i
= 0; i
< n_nets
; i
++) {
820 u8_t clid
[1+DHCP_HLEN_ETH
];
824 if (np
->flags
& NF_BOUND
) continue;
826 if (np
->type
== NT_IP
) {
829 ether2clid(clid
, &np
->eth
);
830 cilen
= 1+DHCP_HLEN_ETH
;
833 /* Try to find an Ethernet address, or the IP address of an already
834 * configured network. If we have data we get an IP address back.
837 (void) makedhcp(bp
->dhcp
, (u8_t
*) "Minix", 5,
838 clid
, cilen
, np
->ip
, 0, np
);
839 cip
= bp
->dhcp
->yiaddr
;
841 /* Gather information on the interface. */
843 && makedhcp(bp
->dhcp
, (u8_t
*) "Minix", 5,
844 clid
, cilen
, cip
, cip
, np
)
850 cachedhcp(np
->n
, bp
->dhcp
);
852 (void) gettag(bp
->dhcp
, DHCP_TAG_NETMASK
, &pdata
, nil
);
853 memcpy(&np
->mask
, pdata
, sizeof(np
->mask
));
854 if (gettag(bp
->dhcp
, DHCP_TAG_GATEWAY
, &pdata
, nil
)) {
855 memcpy(&np
->gateway
, pdata
, sizeof(np
->gateway
));
859 if (gettag(bp
->dhcp
, DHCP_TAG_IPMTU
, &pdata
, nil
)) {
860 memcpy(&mtu
, pdata
, sizeof(mtu
));
865 set_ipconf(ipdev(np
->n
), np
->ip
, np
->mask
, mtu
);
867 printf("%s: IP address is %s\n",
868 ipdev(np
->n
), cidr_ntoa(np
->ip
, np
->mask
));
870 np
->flags
|= NF_BOUND
;
871 np
->renew
= np
->rebind
= np
->lease
= NEVER
;
872 np
->sol_ct
= N_SOLICITS
;
875 /* Other (previous) interfaces may have been defined. */
885 /* Is it time to request/renew a lease? */
886 for (i
= 0; i
< n_nets
; i
++) {
889 if (np
->renew
<= now
) {
891 static u8_t taglist
[] = {
892 DHCP_TAG_NETMASK
, DHCP_TAG_GATEWAY
, DHCP_TAG_DNS
,
895 u8_t ethclid
[1+DHCP_HLEN_ETH
];
897 /* We may have lost our binding or even our lease. */
898 if (np
->rebind
<= now
) np
->server
= BCAST_IP
;
900 if (np
->lease
<= now
) {
901 if (np
->flags
& NF_BOUND
) closedev(np
, FT_ALL
);
903 if ((np
->flags
& (NF_BOUND
| NF_POSSESSIVE
)) == NF_BOUND
) {
904 set_ipconf(ipdev(np
->n
), np
->ip
= 0, np
->mask
= 0, 0);
906 printf("%s: Interface disabled (lease expired)\n",
910 np
->flags
&= ~NF_BOUND
;
913 /* See if we can open the network we need to send on. */
914 if (!(np
->flags
& NF_BOUND
)) {
915 if (!opendev(np
, FT_ETHERNET
, 1)) continue;
917 if (!opendev(np
, FT_BOOTPC
, 1)) continue;
920 if (!(np
->flags
& NF_NEGOTIATING
)) {
921 /* We need to start querying a DHCP server. */
923 np
->delta
= DELTA_FIRST
;
924 np
->flags
|= NF_NEGOTIATING
;
927 /* Fill in a DHCP query packet. */
930 bp
->dhcp
->op
= DHCP_BOOTREQUEST
;
931 bp
->dhcp
->htype
= DHCP_HTYPE_ETH
;
932 bp
->dhcp
->hlen
= DHCP_HLEN_ETH
;
933 bp
->dhcp
->xid
= XID(np
);
934 bp
->dhcp
->secs
= htons(now
- np
->start
> 0xFFFF
935 ? 0xFFFF : now
- np
->start
);
936 memcpy(bp
->dhcp
->chaddr
, &np
->eth
, sizeof(np
->eth
));
938 if (np
->lease
<= now
) {
939 /* First time, or my old server is unresponsive. */
942 /* Request an offered address or renew an address. */
944 if (np
->flags
& NF_BOUND
) {
945 /* A renewal, I claim my current address. */
946 bp
->dhcp
->ciaddr
= np
->ip
;
948 /* Nicely ask for the address just offered. */
949 settag(bp
->dhcp
, DHCP_TAG_REQIP
, &np
->ip
,
951 settag(bp
->dhcp
, DHCP_TAG_SERVERID
, &np
->server
,
955 settag(bp
->dhcp
, DHCP_TAG_TYPE
, &type
, 1);
957 /* My client ID. Simply use the default. */
958 ether2clid(ethclid
, &np
->eth
);
959 settag(bp
->dhcp
, DHCP_TAG_CLIENTID
, ethclid
, sizeof(ethclid
));
961 /* The Class ID may serve to recognize Minix hosts. */
962 settag(bp
->dhcp
, DHCP_TAG_CLASSID
, "Minix", 5);
964 /* The few tags that Minix can make good use of. */
965 settag(bp
->dhcp
, DHCP_TAG_REQPAR
, taglist
, sizeof(taglist
));
967 /* Some weird sites use a hostname, not a client ID. */
968 if (np
->hostname
!= nil
) {
969 settag(bp
->dhcp
, DHCP_TAG_HOSTNAME
,
970 np
->hostname
, strlen(np
->hostname
));
973 bp
->udpio
->uih_src_addr
= np
->ip
;
974 bp
->udpio
->uih_dst_addr
= np
->server
;
975 bp
->udpio
->uih_src_port
= port_client
;
976 bp
->udpio
->uih_dst_port
= port_server
;
977 bp
->udpio
->uih_ip_opt_len
= 0;
978 bp
->udpio
->uih_data_len
= sizeof(dhcp_t
);
980 if (!(np
->flags
& NF_BOUND
)) {
981 /* Rebind over Ethernet. */
983 if (sendpacket(np
, (lwip
? bp
->ip
: (void *) bp
->eth
),
984 (lwip
? 0 : sizeof(eth_hdr_t
)) + sizeof(ip_hdr_t
)
985 + sizeof(udp_hdr_t
) + sizeof(dhcp_t
))) {
987 printf("%s: Broadcast DHCP %s\n",
988 np
->fdp
->device
, dhcptypename(type
));
989 if (debug
>= 2) printdhcp(bp
->dhcp
);
993 /* Renew over UDP. */
994 if (sendpacket(np
, bp
->udpio
, sizeof(udp_io_hdr_t
)
997 printf("%s: Sent DHCP %s to %s\n",
1000 inet_ntoa(np
->server
));
1001 if (debug
>= 2) printdhcp(bp
->dhcp
);
1007 /* When to continue querying a DHCP server? */
1008 if (np
->flags
& NF_BOUND
) {
1009 /* Still bound, keep halving time till next event. */
1012 e
= now
< np
->rebind
? np
->rebind
: np
->lease
;
1014 if (d
< DELTA_SLOW
) d
= DELTA_SLOW
;
1016 if (np
->renew
> e
) np
->renew
= e
;
1018 /* Not bound, be desparate. */
1019 np
->renew
= now
+ np
->delta
;
1020 if ((np
->delta
*= 2) > DELTA_FAST
) np
->delta
= DELTA_FAST
;
1023 if (np
->renew
< event
) event
= np
->renew
;
1026 /* Read DHCP responses. */
1027 for (i
= 0; i
< n_nets
; i
++) {
1029 if (!(np
->flags
& NF_NEGOTIATING
)) continue;
1031 if (!(np
->flags
& NF_BOUND
)) {
1032 if (!opendev(np
, FT_ETHERNET
, 0)) continue;
1033 get_buf(&np
->fdp
->bp
);
1034 r
= asyn_read(&asyn
, np
->fdp
->fd
,
1035 lwip
? np
->fdp
->bp
->ip
: (void *) np
->fdp
->bp
->eth
,
1036 lwip
? BUF_IP_SIZE
: BUF_ETH_SIZE
);
1038 if (!opendev(np
, FT_BOOTPC
, 0)) continue;
1039 get_buf(&np
->fdp
->bp
);
1040 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->udpio
,
1044 if (errno
!= ASYN_INPROGRESS
&& errno
!= EPACKSIZE
) {
1045 report(np
->fdp
->device
);
1050 /* Is there a response? */
1052 give_buf(&bp
, &np
->fdp
->bp
);
1053 if (((!(np
->flags
& NF_BOUND
)
1054 && r
>= (lwip
? 0 : (sizeof(eth_hdr_t
)) + sizeof(ip_hdr_t
)
1055 + sizeof(udp_hdr_t
) + offsetof(dhcp_t
, options
))
1057 && bp
->udpio
->uih_dst_port
== port_client
)
1059 ((np
->flags
& NF_BOUND
)
1060 && r
>= sizeof(udp_io_hdr_t
) + offsetof(dhcp_t
, options
)))
1061 && bp
->dhcp
->op
== DHCP_BOOTREPLY
1062 && bp
->dhcp
->htype
== DHCP_HTYPE_ETH
1063 && bp
->dhcp
->hlen
== DHCP_HLEN_ETH
1064 && bp
->dhcp
->xid
== XID(np
)
1065 && memcmp(bp
->dhcp
->chaddr
, &np
->eth
, sizeof(np
->eth
)) == 0
1067 /* Pfew! We got a DHCP reply! */
1071 ipaddr_t mask
, gateway
, relay
, server
;
1073 u32_t lease
, renew
, rebind
, t
;
1075 relay
= bp
->udpio
->uih_src_addr
;
1076 if (gettag(bp
->dhcp
, DHCP_TAG_SERVERID
, &pdata
, nil
)) {
1077 memcpy(&server
, pdata
, sizeof(server
));
1082 if (gettag(bp
->dhcp
, DHCP_TAG_TYPE
, &pdata
, nil
)) {
1085 type
= DHCP_ACK
; /* BOOTP? */
1089 printf("%s: Got a DHCP %s from %s",
1090 np
->fdp
->device
, dhcptypename(type
), inet_ntoa(server
));
1091 printf(relay
!= server
? " through %s\n" : "\n",
1093 if (debug
>= 2) printdhcp(bp
->dhcp
);
1096 if (gettag(bp
->dhcp
, DHCP_TAG_NETMASK
, &pdata
, nil
)) {
1097 memcpy(&mask
, pdata
, sizeof(mask
));
1099 mask
= defaultmask(bp
->dhcp
->ciaddr
);
1102 if (gettag(bp
->dhcp
, DHCP_TAG_IPMTU
, &pdata
, nil
)) {
1103 memcpy(&mtu
, pdata
, sizeof(mtu
));
1109 if (gettag(bp
->dhcp
, DHCP_TAG_GATEWAY
, &pdata
, nil
)) {
1110 memcpy(&gateway
, pdata
, sizeof(gateway
));
1116 if (gettag(bp
->dhcp
, DHCP_TAG_LEASE
, &pdata
, nil
)) {
1117 memcpy(&lease
, pdata
, sizeof(lease
));
1118 lease
= ntohl(lease
);
1121 rebind
= lease
- lease
/ 8;
1122 if (gettag(bp
->dhcp
, DHCP_TAG_REBINDING
, &pdata
, nil
)) {
1123 memcpy(&t
, pdata
, sizeof(t
));
1125 if (t
< rebind
) rebind
= t
;
1129 if (gettag(bp
->dhcp
, DHCP_TAG_RENEWAL
, &pdata
, nil
)) {
1130 memcpy(&t
, pdata
, sizeof(t
));
1132 if (t
< renew
) renew
= t
;
1135 if (type
== DHCP_OFFER
&& np
->rebind
<= np
->renew
) {
1136 /* It's an offer for an address and we haven't taken one
1137 * yet. It's all the same to us, so take this one.
1139 np
->ip
= bp
->dhcp
->yiaddr
;
1142 np
->gateway
= gateway
;
1143 np
->delta
= DELTA_FIRST
;
1145 np
->rebind
= np
->lease
= now
+ DELTA_FAST
;
1147 /* Send out an ARP request to see if the offered address
1148 * is in use already.
1151 if (sendpacket(np
, bp
->eth
, sizeof(arp46_t
))) {
1153 printf("Sent ARP for %s\n", inet_ntoa(np
->ip
));
1156 np
->flags
&= ~NF_CONFLICT
;
1159 if (type
== DHCP_ACK
&& !(np
->flags
& NF_CONFLICT
)) {
1160 /* An acknowledgment. The address is all mine. */
1161 cachedhcp(np
->n
, bp
->dhcp
);
1162 np
->ip
= bp
->dhcp
->yiaddr
;
1165 set_ipconf(ipdev(np
->n
), np
->ip
, np
->mask
, mtu
);
1167 printf("%s: Address set to %s\n",
1168 ipdev(np
->n
), cidr_ntoa(np
->ip
, np
->mask
));
1170 if (lease
>= NEVER
- now
) {
1171 /* The lease is infinite! */
1172 np
->renew
= np
->rebind
= np
->lease
= NEVER
;
1174 np
->lease
= now
+ lease
;
1175 np
->renew
= now
+ renew
;
1176 np
->rebind
= now
+ rebind
;
1179 np
->renew
= now
+ 60;
1180 np
->rebind
= test
>= 4 ? np
->renew
: np
->renew
+ 60;
1181 np
->lease
= test
>= 5 ? np
->rebind
: np
->rebind
+ 60;
1183 if (!(np
->flags
& NF_IRDP
)) {
1184 np
->sol_ct
= (np
->flags
& NF_BOUND
) ? 1 : N_SOLICITS
;
1187 np
->flags
&= ~NF_NEGOTIATING
;
1188 np
->flags
|= NF_BOUND
;
1189 closedev(np
, FT_ETHERNET
);
1190 closedev(np
, FT_BOOTPC
);
1193 if (type
== DHCP_ACK
&& (np
->flags
& NF_CONFLICT
)) {
1194 /* Alas there is a conflict. Decline to use the address. */
1195 u8_t ethclid
[1+DHCP_HLEN_ETH
];
1196 static char USED
[]= "Address in use by 00:00:00:00:00:00";
1199 dhcp_init(bp
->dhcp
);
1200 bp
->dhcp
->op
= DHCP_BOOTREQUEST
;
1201 bp
->dhcp
->htype
= DHCP_HTYPE_ETH
;
1202 bp
->dhcp
->hlen
= DHCP_HLEN_ETH
;
1203 bp
->dhcp
->xid
= XID(np
);
1205 memcpy(bp
->dhcp
->chaddr
, &np
->eth
, sizeof(np
->eth
));
1206 settag(bp
->dhcp
, DHCP_TAG_REQIP
, &np
->ip
, sizeof(np
->ip
));
1207 settag(bp
->dhcp
, DHCP_TAG_TYPE
, &type
, 1);
1208 ether2clid(ethclid
, &np
->eth
);
1209 settag(bp
->dhcp
, DHCP_TAG_CLIENTID
,ethclid
,sizeof(ethclid
));
1210 strcpy(USED
+18, ether_ntoa(&np
->conflict
));
1211 settag(bp
->dhcp
, DHCP_TAG_MESSAGE
, USED
, strlen(USED
));
1213 bp
->udpio
->uih_src_port
= port_client
;
1214 bp
->udpio
->uih_dst_port
= port_server
;
1215 bp
->udpio
->uih_ip_opt_len
= 0;
1216 bp
->udpio
->uih_data_len
= sizeof(dhcp_t
);
1219 if (sendpacket(np
, bp
->eth
,
1220 sizeof(eth_hdr_t
) + sizeof(ip_hdr_t
)
1221 + sizeof(udp_hdr_t
) + sizeof(dhcp_t
))) {
1223 printf("%s: Broadcast DHCP %s\n",
1224 np
->fdp
->device
, dhcptypename(type
));
1225 if (debug
>= 2) printdhcp(bp
->dhcp
);
1229 np
->renew
= np
->rebind
= np
->lease
= now
+ DELTA_FAST
;
1230 np
->delta
= DELTA_FIRST
;
1233 if (type
== DHCP_NAK
) {
1234 /* Oops, a DHCP server doesn't like me, start over! */
1235 np
->renew
= np
->rebind
= np
->lease
= now
+ DELTA_FAST
;
1236 np
->delta
= DELTA_FIRST
;
1238 fprintf(stderr
, "%s: Got a NAK from %s",
1239 program
, inet_ntoa(server
));
1240 if (relay
!= server
) {
1241 fprintf(stderr
, " through %s", inet_ntoa(relay
));
1243 if (gettag(bp
->dhcp
, DHCP_TAG_MESSAGE
, &pdata
, &len
)) {
1244 fprintf(stderr
, " saying: \"%.*s\"", (int)len
, pdata
);
1246 fputc('\n', stderr
);
1249 if (!(np
->flags
& NF_BOUND
)
1251 && r
>= sizeof(arp46_t
)
1252 && is_arp_me(bp
, np
)
1254 /* Oh no, someone else is using the address offered to me! */
1255 np
->flags
|= NF_CONFLICT
;
1257 fprintf(stderr
, "%s: %s: %s offered by ",
1261 fprintf(stderr
, "%s is already in use by %s\n",
1262 inet_ntoa(np
->server
),
1263 ether_ntoa(&np
->conflict
));
1266 if (np
->renew
< event
) event
= np
->renew
;
1269 /* Perform router solicitations. */
1270 for (i
= 0; i
< n_nets
; i
++) {
1272 if (!(np
->flags
& NF_BOUND
)) continue;
1274 if (np
->solicit
<= now
) {
1275 if (!opendev(np
, FT_ICMP
, 1)) continue;
1279 if (np
->gateway
!= 0) {
1280 /* No IRDP response seen yet, advertise the router given
1281 * by DHCP to my own interface.
1283 icmp_advert(bp
, np
);
1284 if (sendpacket(np
, bp
->ip
, sizeof(ip_hdr_t
) + 16)) {
1286 printf("%s: Sent advert for %s to self\n",
1287 np
->fdp
->device
, inet_ntoa(np
->gateway
));
1290 np
->solicit
= now
+ DELTA_ADV
/2;
1293 if (np
->sol_ct
>= 0 && --np
->sol_ct
>= 0) {
1294 /* Send a router solicitation. */
1296 if (sendpacket(np
, bp
->ip
, sizeof(*bp
->ip
) + 8)) {
1298 printf("%s: Broadcast router solicitation\n",
1302 np
->solicit
= now
+ DELTA_SOL
;
1304 /* No response, or not soliciting right now. */
1305 closedev(np
, FT_ICMP
);
1310 if (np
->solicit
< event
) event
= np
->solicit
;
1313 /* Read router adverts. */
1314 for (i
= 0; i
< n_nets
; i
++) {
1316 if (!(np
->flags
& NF_BOUND
)) continue;
1317 if (np
->sol_ct
< 0) continue;
1319 if (!opendev(np
, FT_ICMP
, 0)) continue;
1320 get_buf(&np
->fdp
->bp
);
1321 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->ip
, BUF_IP_SIZE
);
1323 if (errno
!= ASYN_INPROGRESS
&& errno
!= EPACKSIZE
) {
1324 report(np
->fdp
->device
);
1329 /* Is there an advert? */
1330 if (i
< n_nets
&& r
>= sizeof(ip_hdr_t
) + 8) {
1333 give_buf(&bp
, &np
->fdp
->bp
);
1334 if ((router
= icmp_is_advert(bp
)) != 0) {
1336 printf("%s: Router advert received from %s\n",
1337 np
->fdp
->device
, inet_ntoa(router
));
1341 np
->flags
|= NF_IRDP
;
1342 closedev(np
, FT_ICMP
);
1347 /* We start serving if all the interfaces so marked are configured. */
1348 for (i
= 0; i
< n_nets
; i
++) {
1350 if ((np
->flags
& NF_RELAYING
) && (np
->flags
& NF_BOUND
)) {
1351 if (((np
->ip
^ np
->server
) & np
->mask
) == 0) {
1352 /* Don't relay to a server that is on this same net. */
1353 np
->flags
&= ~NF_RELAYING
;
1356 if (!(np
->flags
& (NF_SERVING
|NF_RELAYING
))) continue;
1357 if (!(np
->flags
& NF_BOUND
)) { serving
= 0; break; }
1361 /* Read DHCP requests. */
1362 for (i
= 0; i
< n_nets
; i
++) {
1364 if (!(np
->flags
& NF_BOUND
)) continue;
1365 if (!(np
->flags
& (NF_SERVING
|NF_RELAYING
)) || !serving
) continue;
1367 if (!opendev(np
, FT_BOOTPS
, 0)) continue;
1368 get_buf(&np
->fdp
->bp
);
1369 r
= asyn_read(&asyn
, np
->fdp
->fd
, np
->fdp
->bp
->udpio
, BUF_UDP_SIZE
);
1372 if (errno
!= ASYN_INPROGRESS
&& errno
!= EPACKSIZE
) {
1373 report(np
->fdp
->device
);
1378 /* Is there a request? */
1380 && r
>= sizeof(udp_io_hdr_t
) + offsetof(dhcp_t
, options
)
1382 give_buf(&bp
, &np
->fdp
->bp
);
1385 printf("%s: Got DHCP packet from %s to ",
1386 np
->fdp
->device
, inet_ntoa(bp
->udpio
->uih_src_addr
));
1387 printf("%s\n", inet_ntoa(bp
->udpio
->uih_dst_addr
));
1388 if (debug
>= 2) printdhcp(bp
->dhcp
);
1391 /* Can we do something with this DHCP packet? */
1392 if ((r
= servdhcp(np
, bp
, r
)) > 0) {
1393 /* Yes, we have something to send somewhere. */
1394 if (sendpacket(np
, bp
->udpio
, r
)) {
1396 printf("%s: Sent DHCP packet to %s\n",
1398 inet_ntoa(bp
->udpio
->uih_dst_addr
));
1399 if (debug
>= 2) printdhcp(bp
->dhcp
);
1407 static char *lastbrk
;
1410 if (sbrk(0) != lastbrk
) {
1412 printf("Memory use = %lu\n",
1413 (unsigned long) (lastbrk
- &_end
));
1418 /* Bail out if not a server, and there is nothing else to do ever. */
1419 if (!serving
&& event
== NEVER
) break;
1421 /* Wait for something to do. */
1422 eventtv
.tv_sec
= event
;
1423 if (asyn_wait(&asyn
, 0, event
== NEVER
? nil
: &eventtv
) < 0) {
1424 if (errno
!= EINTR
) {
1425 report("asyn_wait()");
1430 if (debug
>= 1) printf("Nothing more to do! Starting over...\n");