10 * format of a binding entry:
17 uchar bfirst[IPaddrlen];
18 char *binddir = "/lib/ndb/dhcp";
21 * convert a byte array to hex
31 tohex(char *hdr, uchar *p, int len)
37 s = malloc(hlen + 2*len + 1);
41 for(; len > 0; len--){
43 *sp++ = hex(*p & 0xf);
51 * convert a client id to a string. If it's already
52 * ascii, leave it be. Otherwise, convert it to hex.
60 for(i = 0; i < n; i++)
62 return tohex("id", p, n);
70 * increment an ip address
77 for(i = IPaddrlen-1; i >= 0; i--){
87 * find a binding for an id or hardware address
95 for(tries = 0; tries < 5; tries++){
96 fd = open(file, ORDWR);
99 errstr(err, sizeof err);
100 if(strstr(err, "lock")){
101 /* wait for other process to let go of lock */
107 if(strstr(err, "exist")){
108 /* no file, create an exclusive access file */
109 fd = create(file, ORDWR, DMEXCL|0664);
118 setbinding(Binding *b, char *id, long t)
123 b->boundto = strdup(id);
128 parsebinding(Binding *b, char *buf)
135 id = strchr(buf, '\n');
138 p = strchr(id, '\n');
144 /* replace any past info */
145 setbinding(b, id, t);
149 writebinding(int fd, Binding *b)
154 if(fprint(fd, "%ld\n%s\n", b->lease, b->boundto) < 0)
159 b->q.type = d->qid.type;
160 b->q.path = d->qid.path;
161 b->q.vers = d->qid.vers;
167 * synchronize cached binding with file. the file always wins.
170 syncbinding(Binding *b, int returnfd)
176 snprint(buf, sizeof(buf), "%s/%I", binddir, b->ip);
179 /* assume someone else is using it */
180 b->lease = time(0) + OfferTimeout;
184 /* reread if changed */
186 if(d != nil) /* BUG? */
187 if(d->qid.type != b->q.type || d->qid.path != b->q.path || d->qid.vers != b->q.vers){
188 i = read(fd, buf, sizeof(buf)-1);
192 parsebinding(b, buf);
193 b->lasttouched = d->mtime;
194 b->q.path = d->qid.path;
195 b->q.vers = d->qid.vers;
208 samenet(uchar *ip, Info *iip)
212 maskip(iip->ipmask, ip, x);
213 return ipcmp(x, iip->ipnet) == 0;
217 * create a record for each binding
220 initbinding(uchar *first, int n)
223 iptobinding(first, 1);
229 * find a binding for a specific ip address
232 iptobinding(uchar *ip, int mk)
236 for(b = bcache; b; b = b->next){
237 if(ipcmp(b->ip, ip) == 0){
245 b = malloc(sizeof(*b));
246 memset(b, 0, sizeof(*b));
255 lognolease(Binding *b)
257 /* renew the old binding, and hope it eventually goes away */
261 /* complain if we haven't in the last 5 minutes */
262 if(now - b->lastcomplained < 5*60)
264 syslog(0, blog, "dhcp: lease for %I to %s ended at %ld but still in use\n",
265 b->ip, b->boundto != nil ? b->boundto : "?", b->lease);
266 b->lastcomplained = now;
270 * find a free binding for a hw addr or id on the same network as iip
273 idtobinding(char *id, Info *iip, int ping)
279 * first look for an old binding that matches. that way
280 * clients will tend to keep the same ip addresses.
282 for(b = bcache; b; b = b->next){
283 if(b->boundto && strcmp(b->boundto, id) == 0){
284 if(!samenet(b->ip, iip))
287 /* check with the other servers */
289 if(strcmp(b->boundto, id) == 0)
295 * look for oldest binding that we think is unused
300 for(b = bcache; b; b = b->next){
302 if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
303 if(oldest == nil || b->lasttouched < oldesttime){
304 /* sync and check again */
306 if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
307 if(oldest == nil || b->lasttouched < oldesttime){
309 oldesttime = b->lasttouched;
316 /* make sure noone is still using it */
318 if(ping == 0 || icmpecho(oldest->ip) == 0)
321 lognolease(oldest); /* sets lastcomplained */
324 /* try all bindings */
325 for(b = bcache; b; b = b->next){
328 if(b->lease < now && b->expoffer < now && samenet(b->ip, iip)){
330 if(ping == 0 || icmpecho(b->ip) == 0)
337 /* nothing worked, give up */
345 mkoffer(Binding *b, char *id, long leasetime)
348 if(b->lease > now + minlease)
349 leasetime = b->lease - now;
351 leasetime = minlease;
355 b->offeredto = strdup(id);
356 b->offer = leasetime;
357 b->expoffer = now + OfferTimeout;
361 * find an offer for this id
364 idtooffer(char *id, Info *iip)
368 /* look for an offer to this id */
369 for(b = bcache; b; b = b->next){
370 if(b->offeredto && strcmp(b->offeredto, id) == 0 && samenet(b->ip, iip)){
371 /* make sure some other system hasn't stolen it */
374 || (b->boundto && strcmp(b->boundto, b->offeredto) == 0))
382 * commit a lease, this could fail
385 commitbinding(Binding *b)
392 if(b->offeredto == 0)
394 fd = syncbinding(b, 1);
397 if(b->lease > now && b->boundto && strcmp(b->boundto, b->offeredto) != 0){
401 setbinding(b, b->offeredto, now + b->offer);
402 b->lasttouched = now;
404 if(writebinding(fd, b) < 0){
413 * commit a lease, this could fail
416 releasebinding(Binding *b, char *id)
423 fd = syncbinding(b, 1);
426 if(b->lease > now && b->boundto && strcmp(b->boundto, id) != 0){
433 if(writebinding(fd, b) < 0){