10 * format of a binding entry:
17 uchar bfirst[IPaddrlen];
19 char *xbinddir = "#9/ndb/dhcp";
22 * convert a byte array to hex
32 tohex(char *hdr, uchar *p, int len)
38 s = malloc(hlen + 2*len + 1);
42 for(; len > 0; len--){
44 *sp++ = hex(*p & 0xf);
52 * convert a client id to a string. If it's already
53 * ascii, leave it be. Otherwise, convert it to hex.
61 for(i = 0; i < n; i++)
63 return tohex("id", p, n);
71 * increment an ip address
78 for(i = IPaddrlen-1; i >= 0; i--){
88 * find a binding for an id or hardware address
96 for(tries = 0; tries < 5; tries++){
97 fd = open(file, OLOCK|ORDWR);
100 errstr(err, sizeof err);
101 if(strstr(err, "lock")){
102 /* wait for other process to let go of lock */
108 if(strstr(err, "exist") || strstr(err, "No such")){
109 /* no file, create an exclusive access file */
110 fd = create(file, ORDWR, DMEXCL|0666);
120 setbinding(Binding *b, char *id, long t)
125 b->boundto = strdup(id);
130 parsebinding(Binding *b, char *buf)
137 id = strchr(buf, '\n');
140 p = strchr(id, '\n');
146 /* replace any past info */
147 setbinding(b, id, t);
151 writebinding(int fd, Binding *b)
156 if(fprint(fd, "%ld\n%s\n", b->lease, b->boundto) < 0)
161 b->q.type = d->qid.type;
162 b->q.path = d->qid.path;
163 b->q.vers = d->qid.vers;
169 * synchronize cached binding with file. the file always wins.
172 syncbinding(Binding *b, int returnfd)
179 binddir = unsharp(xbinddir);
181 snprint(buf, sizeof(buf), "%s/%I", binddir, b->ip);
184 /* assume someone else is using it */
185 b->lease = time(0) + OfferTimeout;
189 /* reread if changed */
191 if(d != nil) /* BUG? */
192 if(d->qid.type != b->q.type || d->qid.path != b->q.path || d->qid.vers != b->q.vers){
193 i = read(fd, buf, sizeof(buf)-1);
197 parsebinding(b, buf);
198 b->lasttouched = d->mtime;
199 b->q.path = d->qid.path;
200 b->q.vers = d->qid.vers;
213 samenet(uchar *ip, Info *iip)
217 maskip(iip->ipmask, ip, x);
218 return ipcmp(x, iip->ipnet) == 0;
222 * create a record for each binding
225 initbinding(uchar *first, int n)
228 iptobinding(first, 1);
234 * find a binding for a specific ip address
237 iptobinding(uchar *ip, int mk)
241 for(b = bcache; b; b = b->next){
242 if(ipcmp(b->ip, ip) == 0){
250 b = malloc(sizeof(*b));
251 memset(b, 0, sizeof(*b));
260 lognolease(Binding *b)
262 /* renew the old binding, and hope it eventually goes away */
266 /* complain if we haven't in the last 5 minutes */
267 if(now - b->lastcomplained < 5*60)
269 syslog(0, blog, "dhcp: lease for %I to %s ended at %ld but still in use\n",
270 b->ip, b->boundto != nil ? b->boundto : "?", b->lease);
271 b->lastcomplained = now;
275 * find a free binding for a hw addr or id on the same network as iip
278 idtobinding(char *id, Info *iip, int ping)
284 * first look for an old binding that matches. that way
285 * clients will tend to keep the same ip addresses.
287 for(b = bcache; b; b = b->next){
288 if(b->boundto && strcmp(b->boundto, id) == 0){
289 if(!samenet(b->ip, iip))
292 /* check with the other servers */
294 if(strcmp(b->boundto, id) == 0)
300 * look for oldest binding that we think is unused
305 for(b = bcache; b; b = b->next){
307 if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
308 if(oldest == nil || b->lasttouched < oldesttime){
309 /* sync and check again */
311 if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
312 if(oldest == nil || b->lasttouched < oldesttime){
314 oldesttime = b->lasttouched;
321 /* make sure noone is still using it */
323 if(ping == 0 || icmpecho(oldest->ip) == 0)
326 lognolease(oldest); /* sets lastcomplained */
329 /* try all bindings */
330 for(b = bcache; b; b = b->next){
333 if(b->lease < now && b->expoffer < now && samenet(b->ip, iip)){
335 if(ping == 0 || icmpecho(b->ip) == 0)
342 /* nothing worked, give up */
350 mkoffer(Binding *b, char *id, long leasetime)
353 if(b->lease > now + minlease)
354 leasetime = b->lease - now;
356 leasetime = minlease;
360 b->offeredto = strdup(id);
361 b->offer = leasetime;
362 b->expoffer = now + OfferTimeout;
366 * find an offer for this id
369 idtooffer(char *id, Info *iip)
373 /* look for an offer to this id */
374 for(b = bcache; b; b = b->next){
375 if(b->offeredto && strcmp(b->offeredto, id) == 0 && samenet(b->ip, iip)){
376 /* make sure some other system hasn't stolen it */
379 || (b->boundto && strcmp(b->boundto, b->offeredto) == 0))
387 * commit a lease, this could fail
390 commitbinding(Binding *b)
397 if(b->offeredto == 0)
399 fd = syncbinding(b, 1);
402 if(b->lease > now && b->boundto && strcmp(b->boundto, b->offeredto) != 0){
406 setbinding(b, b->offeredto, now + b->offer);
407 b->lasttouched = now;
409 if(writebinding(fd, b) < 0){
418 * commit a lease, this could fail
421 releasebinding(Binding *b, char *id)
428 fd = syncbinding(b, 1);
431 if(b->lease > now && b->boundto && strcmp(b->boundto, id) != 0){
438 if(writebinding(fd, b) < 0){