db.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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. return -1;
  105. }
  106. void
  107. setbinding(Binding *b, char *id, long t)
  108. {
  109. if(b->boundto)
  110. free(b->boundto);
  111. b->boundto = strdup(id);
  112. b->lease = t;
  113. }
  114. static void
  115. parsebinding(Binding *b, char *buf)
  116. {
  117. long t;
  118. char *id, *p;
  119. /* parse */
  120. t = atoi(buf);
  121. id = strchr(buf, '\n');
  122. if(id){
  123. *id++ = 0;
  124. p = strchr(id, '\n');
  125. if(p)
  126. *p = 0;
  127. } else
  128. id = "";
  129. /* replace any past info */
  130. setbinding(b, id, t);
  131. }
  132. static int
  133. writebinding(int fd, Binding *b)
  134. {
  135. Dir *d;
  136. seek(fd, 0, 0);
  137. if(fprint(fd, "%ld\n%s\n", b->lease, b->boundto) < 0)
  138. return -1;
  139. d = dirfstat(fd);
  140. if(d == nil)
  141. return -1;
  142. b->q.type = d->qid.type;
  143. b->q.path = d->qid.path;
  144. b->q.vers = d->qid.vers;
  145. free(d);
  146. return 0;
  147. }
  148. /*
  149. * synchronize cached binding with file. the file always wins.
  150. */
  151. int
  152. syncbinding(Binding *b, int returnfd)
  153. {
  154. char buf[512];
  155. int i, fd;
  156. Dir *d;
  157. snprint(buf, sizeof(buf), "%s/%I", binddir, b->ip);
  158. fd = lockopen(buf);
  159. if(fd < 0){
  160. /* assume someone else is using it */
  161. b->lease = time(0) + OfferTimeout;
  162. return -1;
  163. }
  164. /* reread if changed */
  165. d = dirfstat(fd);
  166. if(d != nil) /* BUG? */
  167. if(d->qid.type != b->q.type || d->qid.path != b->q.path || d->qid.vers != b->q.vers){
  168. i = read(fd, buf, sizeof(buf)-1);
  169. if(i < 0)
  170. i = 0;
  171. buf[i] = 0;
  172. parsebinding(b, buf);
  173. b->lasttouched = d->mtime;
  174. b->q.path = d->qid.path;
  175. b->q.vers = d->qid.vers;
  176. }
  177. free(d);
  178. if(returnfd)
  179. return fd;
  180. close(fd);
  181. return 0;
  182. }
  183. extern int
  184. samenet(uchar *ip, Info *iip)
  185. {
  186. uchar x[IPaddrlen];
  187. maskip(iip->ipmask, ip, x);
  188. return ipcmp(x, iip->ipnet) == 0;
  189. }
  190. /*
  191. * create a record for each binding
  192. */
  193. extern void
  194. initbinding(uchar *first, int n)
  195. {
  196. while(n-- > 0){
  197. iptobinding(first, 1);
  198. incip(first);
  199. }
  200. }
  201. /*
  202. * find a binding for a specific ip address
  203. */
  204. extern Binding*
  205. iptobinding(uchar *ip, int mk)
  206. {
  207. Binding *b;
  208. for(b = bcache; b; b = b->next){
  209. if(ipcmp(b->ip, ip) == 0){
  210. syncbinding(b, 0);
  211. return b;
  212. }
  213. }
  214. if(mk == 0)
  215. return 0;
  216. b = malloc(sizeof(*b));
  217. memset(b, 0, sizeof(*b));
  218. ipmove(b->ip, ip);
  219. b->next = bcache;
  220. bcache = b;
  221. syncbinding(b, 0);
  222. return b;
  223. }
  224. static void
  225. lognolease(Binding *b)
  226. {
  227. /* renew the old binding, and hope it eventually goes away */
  228. b->offer = 5*60;
  229. commitbinding(b);
  230. /* complain if we haven't in the last 5 minutes */
  231. if(now - b->lastcomplained < 5*60)
  232. return;
  233. syslog(0, blog, "dhcp: lease for %I to %s ended at %ld but still in use\n",
  234. b->ip, b->boundto != nil ? b->boundto : "?", b->lease);
  235. b->lastcomplained = now;
  236. }
  237. /*
  238. * find a free binding for a hw addr or id on the same network as iip
  239. */
  240. extern Binding*
  241. idtobinding(char *id, Info *iip, int ping)
  242. {
  243. Binding *b, *oldest;
  244. int oldesttime;
  245. /*
  246. * first look for an old binding that matches. that way
  247. * clients will tend to keep the same ip addresses.
  248. */
  249. for(b = bcache; b; b = b->next){
  250. if(b->boundto && strcmp(b->boundto, id) == 0){
  251. if(!samenet(b->ip, iip))
  252. continue;
  253. /* check with the other servers */
  254. syncbinding(b, 0);
  255. if(strcmp(b->boundto, id) == 0)
  256. return b;
  257. }
  258. }
  259. /*
  260. * look for oldest binding that we think is unused
  261. */
  262. for(;;){
  263. oldest = nil;
  264. oldesttime = 0;
  265. for(b = bcache; b; b = b->next){
  266. if(b->tried != now)
  267. if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
  268. if(oldest == nil || b->lasttouched < oldesttime){
  269. /* sync and check again */
  270. syncbinding(b, 0);
  271. if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
  272. if(oldest == nil || b->lasttouched < oldesttime){
  273. oldest = b;
  274. oldesttime = b->lasttouched;
  275. }
  276. }
  277. }
  278. if(oldest == nil)
  279. break;
  280. /* make sure noone is still using it */
  281. oldest->tried = now;
  282. if(ping == 0 || icmpecho(oldest->ip) == 0)
  283. return oldest;
  284. lognolease(oldest); /* sets lastcomplained */
  285. }
  286. /* try all bindings */
  287. for(b = bcache; b; b = b->next){
  288. syncbinding(b, 0);
  289. if(b->tried != now)
  290. if(b->lease < now && b->expoffer < now && samenet(b->ip, iip)){
  291. b->tried = now;
  292. if(ping == 0 || icmpecho(b->ip) == 0)
  293. return b;
  294. lognolease(b);
  295. }
  296. }
  297. /* nothing worked, give up */
  298. return 0;
  299. }
  300. /*
  301. * create an offer
  302. */
  303. extern void
  304. mkoffer(Binding *b, char *id, long leasetime)
  305. {
  306. if(leasetime <= 0){
  307. if(b->lease > now + minlease)
  308. leasetime = b->lease - now;
  309. else
  310. leasetime = minlease;
  311. }
  312. if(b->offeredto)
  313. free(b->offeredto);
  314. b->offeredto = strdup(id);
  315. b->offer = leasetime;
  316. b->expoffer = now + OfferTimeout;
  317. }
  318. /*
  319. * find an offer for this id
  320. */
  321. extern Binding*
  322. idtooffer(char *id, Info *iip)
  323. {
  324. Binding *b;
  325. /* look for an offer to this id */
  326. for(b = bcache; b; b = b->next){
  327. if(b->offeredto && strcmp(b->offeredto, id) == 0 && samenet(b->ip, iip)){
  328. /* make sure some other system hasn't stolen it */
  329. syncbinding(b, 0);
  330. if(b->lease < now
  331. || (b->boundto && strcmp(b->boundto, b->offeredto) == 0))
  332. return b;
  333. }
  334. }
  335. return 0;
  336. }
  337. /*
  338. * commit a lease, this could fail
  339. */
  340. extern int
  341. commitbinding(Binding *b)
  342. {
  343. int fd;
  344. long now;
  345. now = time(0);
  346. if(b->offeredto == 0)
  347. return -1;
  348. fd = syncbinding(b, 1);
  349. if(fd < 0)
  350. return -1;
  351. if(b->lease > now && b->boundto && strcmp(b->boundto, b->offeredto) != 0){
  352. close(fd);
  353. return -1;
  354. }
  355. setbinding(b, b->offeredto, now + b->offer);
  356. b->lasttouched = now;
  357. if(writebinding(fd, b) < 0){
  358. close(fd);
  359. return -1;
  360. }
  361. close(fd);
  362. return 0;
  363. }
  364. /*
  365. * commit a lease, this could fail
  366. */
  367. extern int
  368. releasebinding(Binding *b, char *id)
  369. {
  370. int fd;
  371. long now;
  372. now = time(0);
  373. fd = syncbinding(b, 1);
  374. if(fd < 0)
  375. return -1;
  376. if(b->lease > now && b->boundto && strcmp(b->boundto, id) != 0){
  377. close(fd);
  378. return -1;
  379. }
  380. b->lease = 0;
  381. b->expoffer = 0;
  382. if(writebinding(fd, b) < 0){
  383. close(fd);
  384. return -1;
  385. }
  386. close(fd);
  387. return 0;
  388. }