db.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <ip.h>
  4. #include <bio.h>
  5. #include <ndb.h>
  6. #include <ctype.h>
  7. #include "dat.h"
  8. /*
  9. * format of a binding entry:
  10. * char ipaddr[32];
  11. * char id[32];
  12. * char hwa[32];
  13. * char otime[10];
  14. */
  15. Binding *bcache;
  16. uchar bfirst[IPaddrlen];
  17. char *binddir = "/lib/ndb/dhcp";
  18. /*
  19. * convert a byte array to hex
  20. */
  21. static char
  22. hex(int x)
  23. {
  24. if(x < 10)
  25. return x + '0';
  26. return x - 10 + 'a';
  27. }
  28. extern char*
  29. tohex(char *hdr, uchar *p, int len)
  30. {
  31. char *s, *sp;
  32. int hlen;
  33. hlen = strlen(hdr);
  34. s = malloc(hlen + 2*len + 1);
  35. sp = s;
  36. strcpy(sp, hdr);
  37. sp += hlen;
  38. for(; len > 0; len--){
  39. *sp++ = hex(*p>>4);
  40. *sp++ = hex(*p & 0xf);
  41. p++;
  42. }
  43. *sp = 0;
  44. return s;
  45. }
  46. /*
  47. * convert a client id to a string. If it's already
  48. * ascii, leave it be. Otherwise, convert it to hex.
  49. */
  50. extern char*
  51. toid(uchar *p, int n)
  52. {
  53. int i;
  54. char *s;
  55. for(i = 0; i < n; i++)
  56. if(!isprint(p[i]))
  57. return tohex("id", p, n);
  58. s = malloc(n + 1);
  59. memmove(s, p, n);
  60. s[n] = 0;
  61. return s;
  62. }
  63. /*
  64. * increment an ip address
  65. */
  66. static void
  67. incip(uchar *ip)
  68. {
  69. int i, x;
  70. for(i = IPaddrlen-1; i >= 0; i--){
  71. x = ip[i];
  72. x++;
  73. ip[i] = x;
  74. if((x & 0x100) == 0)
  75. break;
  76. }
  77. }
  78. /*
  79. * find a binding for an id or hardware address
  80. */
  81. static int
  82. lockopen(char *file)
  83. {
  84. char err[ERRMAX];
  85. int fd, tries;
  86. for(tries = 0; tries < 5; tries++){
  87. fd = open(file, ORDWR);
  88. if(fd >= 0)
  89. return fd;
  90. errstr(err, sizeof err);
  91. if(strstr(err, "lock")){
  92. /* wait for other process to let go of lock */
  93. sleep(250);
  94. /* try again */
  95. continue;
  96. }
  97. if(strstr(err, "exist")){
  98. /* no file, create an exclusive access file */
  99. fd = create(file, ORDWR, DMEXCL|0664);
  100. if(fd >= 0)
  101. return fd;
  102. }
  103. }
  104. if(tries >= 5)
  105. return -1;
  106. }
  107. void
  108. setbinding(Binding *b, char *id, long t)
  109. {
  110. if(b->boundto)
  111. free(b->boundto);
  112. b->boundto = strdup(id);
  113. b->lease = t;
  114. }
  115. static void
  116. parsebinding(Binding *b, char *buf)
  117. {
  118. long t;
  119. char *id, *p;
  120. /* parse */
  121. t = atoi(buf);
  122. id = strchr(buf, '\n');
  123. if(id){
  124. *id++ = 0;
  125. p = strchr(id, '\n');
  126. if(p)
  127. *p = 0;
  128. } else
  129. id = "";
  130. /* replace any past info */
  131. setbinding(b, id, t);
  132. }
  133. static int
  134. writebinding(int fd, Binding *b)
  135. {
  136. Dir *d;
  137. seek(fd, 0, 0);
  138. if(fprint(fd, "%ld\n%s\n", b->lease, b->boundto) < 0)
  139. return -1;
  140. d = dirfstat(fd);
  141. if(d == nil)
  142. return -1;
  143. b->q.type = d->qid.type;
  144. b->q.path = d->qid.path;
  145. b->q.vers = d->qid.vers;
  146. free(d);
  147. return 0;
  148. }
  149. /*
  150. * synchronize cached binding with file. the file always wins.
  151. */
  152. int
  153. syncbinding(Binding *b, int returnfd)
  154. {
  155. char buf[512];
  156. int i, fd;
  157. Dir *d;
  158. snprint(buf, sizeof(buf), "%s/%I", binddir, b->ip);
  159. fd = lockopen(buf);
  160. if(fd < 0){
  161. /* assume someone else is using it */
  162. b->lease = time(0) + OfferTimeout;
  163. return -1;
  164. }
  165. /* reread if changed */
  166. d = dirfstat(fd);
  167. if(d != nil) /* BUG? */
  168. if(d->qid.type != b->q.type || d->qid.path != b->q.path || d->qid.vers != b->q.vers){
  169. i = read(fd, buf, sizeof(buf)-1);
  170. if(i < 0)
  171. i = 0;
  172. buf[i] = 0;
  173. parsebinding(b, buf);
  174. b->lasttouched = d->mtime;
  175. b->q.path = d->qid.path;
  176. b->q.vers = d->qid.vers;
  177. }
  178. free(d);
  179. if(returnfd)
  180. return fd;
  181. close(fd);
  182. return 0;
  183. }
  184. extern int
  185. samenet(uchar *ip, Info *iip)
  186. {
  187. uchar x[IPaddrlen];
  188. maskip(iip->ipmask, ip, x);
  189. return ipcmp(x, iip->ipnet) == 0;
  190. }
  191. /*
  192. * create a record for each binding
  193. */
  194. extern void
  195. initbinding(uchar *first, int n)
  196. {
  197. while(n-- > 0){
  198. iptobinding(first, 1);
  199. incip(first);
  200. }
  201. }
  202. /*
  203. * find a binding for a specific ip address
  204. */
  205. extern Binding*
  206. iptobinding(uchar *ip, int mk)
  207. {
  208. Binding *b;
  209. for(b = bcache; b; b = b->next){
  210. if(ipcmp(b->ip, ip) == 0){
  211. syncbinding(b, 0);
  212. return b;
  213. }
  214. }
  215. if(mk == 0)
  216. return 0;
  217. b = malloc(sizeof(*b));
  218. memset(b, 0, sizeof(*b));
  219. ipmove(b->ip, ip);
  220. b->next = bcache;
  221. bcache = b;
  222. syncbinding(b, 0);
  223. return b;
  224. }
  225. static void
  226. lognolease(Binding *b)
  227. {
  228. /* renew the old binding, and hope it eventually goes away */
  229. b->offer = 5*60;
  230. commitbinding(b);
  231. /* complain if we haven't in the last 5 minutes */
  232. if(now - b->lastcomplained < 5*60)
  233. return;
  234. syslog(0, blog, "dhcp: lease for %I to %s ended at %ld but still in use\n",
  235. b->ip, b->boundto != nil ? b->boundto : "?", b->lease);
  236. b->lastcomplained = now;
  237. }
  238. /*
  239. * find a free binding for a hw addr or id on the same network as iip
  240. */
  241. extern Binding*
  242. idtobinding(char *id, Info *iip, int ping)
  243. {
  244. Binding *b, *oldest;
  245. int oldesttime;
  246. /*
  247. * first look for an old binding that matches. that way
  248. * clients will tend to keep the same ip addresses.
  249. */
  250. for(b = bcache; b; b = b->next){
  251. if(b->boundto && strcmp(b->boundto, id) == 0){
  252. if(!samenet(b->ip, iip))
  253. continue;
  254. /* check with the other servers */
  255. syncbinding(b, 0);
  256. if(strcmp(b->boundto, id) == 0)
  257. return b;
  258. }
  259. }
  260. /*
  261. * look for oldest binding that we think is unused
  262. */
  263. for(;;){
  264. oldest = nil;
  265. oldesttime = 0;
  266. for(b = bcache; b; b = b->next){
  267. if(b->tried != now)
  268. if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
  269. if(oldest == nil || b->lasttouched < oldesttime){
  270. /* sync and check again */
  271. syncbinding(b, 0);
  272. if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
  273. if(oldest == nil || b->lasttouched < oldesttime){
  274. oldest = b;
  275. oldesttime = b->lasttouched;
  276. }
  277. }
  278. }
  279. if(oldest == nil)
  280. break;
  281. /* make sure noone is still using it */
  282. oldest->tried = now;
  283. if(ping == 0 || icmpecho(oldest->ip) == 0)
  284. return oldest;
  285. lognolease(oldest); /* sets lastcomplained */
  286. }
  287. /* try all bindings */
  288. for(b = bcache; b; b = b->next){
  289. syncbinding(b, 0);
  290. if(b->tried != now)
  291. if(b->lease < now && b->expoffer < now && samenet(b->ip, iip)){
  292. b->tried = now;
  293. if(ping == 0 || icmpecho(b->ip) == 0)
  294. return b;
  295. lognolease(b);
  296. }
  297. }
  298. /* nothing worked, give up */
  299. return 0;
  300. }
  301. /*
  302. * create an offer
  303. */
  304. extern void
  305. mkoffer(Binding *b, char *id, long leasetime)
  306. {
  307. if(leasetime <= 0){
  308. if(b->lease > now + minlease)
  309. leasetime = b->lease - now;
  310. else
  311. leasetime = minlease;
  312. }
  313. if(b->offeredto)
  314. free(b->offeredto);
  315. b->offeredto = strdup(id);
  316. b->offer = leasetime;
  317. b->expoffer = now + OfferTimeout;
  318. }
  319. /*
  320. * find an offer for this id
  321. */
  322. extern Binding*
  323. idtooffer(char *id, Info *iip)
  324. {
  325. Binding *b;
  326. /* look for an offer to this id */
  327. for(b = bcache; b; b = b->next){
  328. if(b->offeredto && strcmp(b->offeredto, id) == 0 && samenet(b->ip, iip)){
  329. /* make sure some other system hasn't stolen it */
  330. syncbinding(b, 0);
  331. if(b->lease < now
  332. || (b->boundto && strcmp(b->boundto, b->offeredto) == 0))
  333. return b;
  334. }
  335. }
  336. return 0;
  337. }
  338. /*
  339. * commit a lease, this could fail
  340. */
  341. extern int
  342. commitbinding(Binding *b)
  343. {
  344. int fd;
  345. long now;
  346. now = time(0);
  347. if(b->offeredto == 0)
  348. return -1;
  349. fd = syncbinding(b, 1);
  350. if(fd < 0)
  351. return -1;
  352. if(b->lease > now && b->boundto && strcmp(b->boundto, b->offeredto) != 0){
  353. close(fd);
  354. return -1;
  355. }
  356. setbinding(b, b->offeredto, now + b->offer);
  357. b->lasttouched = now;
  358. if(writebinding(fd, b) < 0){
  359. close(fd);
  360. return -1;
  361. }
  362. close(fd);
  363. return 0;
  364. }
  365. /*
  366. * commit a lease, this could fail
  367. */
  368. extern int
  369. releasebinding(Binding *b, char *id)
  370. {
  371. int fd;
  372. long now;
  373. now = time(0);
  374. fd = syncbinding(b, 1);
  375. if(fd < 0)
  376. return -1;
  377. if(b->lease > now && b->boundto && strcmp(b->boundto, id) != 0){
  378. close(fd);
  379. return -1;
  380. }
  381. b->lease = 0;
  382. b->expoffer = 0;
  383. if(writebinding(fd, b) < 0){
  384. close(fd);
  385. return -1;
  386. }
  387. close(fd);
  388. return 0;
  389. }