dhcpclient.b 24 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033
  1. implement Dhcpclient;
  2. #
  3. # DHCP and BOOTP clients
  4. # Copyright © 2004-2006 Vita Nuova Holdings Limited
  5. #
  6. include "sys.m";
  7. sys: Sys;
  8. include "ip.m";
  9. ip: IP;
  10. IPv4off, IPaddrlen, Udphdrlen, Udpraddr, Udpladdr, Udprport, Udplport: import IP;
  11. IPaddr: import ip;
  12. get2, get4, put2, put4: import ip;
  13. include "keyring.m";
  14. include "security.m"; # for Random
  15. include "dial.m";
  16. dial: Dial;
  17. include "dhcp.m";
  18. debug := 0;
  19. xidgen: int;
  20. init()
  21. {
  22. sys = load Sys Sys->PATH;
  23. random := load Random Random->PATH;
  24. if(random != nil)
  25. xidgen = random->randomint(Random->NotQuiteRandom);
  26. else
  27. xidgen = sys->pctl(0, nil)*sys->millisec();
  28. random = nil;
  29. dial = load Dial Dial->PATH;
  30. ip = load IP IP->PATH;
  31. ip->init();
  32. }
  33. tracing(d: int)
  34. {
  35. debug = d;
  36. }
  37. Bootconf.new(): ref Bootconf
  38. {
  39. bc := ref Bootconf;
  40. bc.lease = 0;
  41. bc.options = array[256] of array of byte;
  42. return bc;
  43. }
  44. Bootconf.get(c: self ref Bootconf, n: int): array of byte
  45. {
  46. a := c.options;
  47. if(n & Ovendor){
  48. a = c.vendor;
  49. n &= ~Ovendor;
  50. }
  51. if(n < 0 || n >= len a)
  52. return nil;
  53. return a[n];
  54. }
  55. Bootconf.getint(c: self ref Bootconf, n: int): int
  56. {
  57. a := c.get(n);
  58. v := 0;
  59. for(i := 0; i < len a; i++)
  60. v = (v<<8) | int a[i];
  61. return v;
  62. }
  63. Bootconf.getip(c: self ref Bootconf, n: int): string
  64. {
  65. l := c.getips(n);
  66. if(l == nil)
  67. return nil;
  68. return hd l;
  69. }
  70. Bootconf.getips(c: self ref Bootconf, n: int): list of string
  71. {
  72. a := c.get(n);
  73. rl: list of string;
  74. while(len a >= 4){
  75. rl = v4text(a) :: rl;
  76. a = a[4:];
  77. }
  78. l: list of string;
  79. for(; rl != nil; rl = tl rl)
  80. l = hd rl :: l;
  81. return l;
  82. }
  83. Bootconf.gets(c: self ref Bootconf, n: int): string
  84. {
  85. a := c.get(n);
  86. if(a == nil)
  87. return nil;
  88. for(i:=0; i<len a; i++)
  89. if(a[i] == byte 0)
  90. break;
  91. return string a[0:i];
  92. }
  93. Bootconf.put(c: self ref Bootconf, n: int, a: array of byte)
  94. {
  95. if(n < 0 || n >= len c.options)
  96. return;
  97. ca := array[len a] of byte;
  98. ca[0:] = a;
  99. c.options[n] = ca;
  100. }
  101. Bootconf.putint(c: self ref Bootconf, n: int, v: int)
  102. {
  103. if(n < 0 || n >= len c.options)
  104. return;
  105. a := array[4] of byte;
  106. put4(a, 0, v);
  107. c.options[n] = a;
  108. }
  109. Bootconf.putips(c: self ref Bootconf, n: int, ips: list of string)
  110. {
  111. if(n < 0 || n >= len c.options)
  112. return;
  113. na := len ips;
  114. a := array[na*4] of byte;
  115. na = 0;
  116. for(; ips != nil; ips = tl ips){
  117. (nil, ipa) := IPaddr.parse(hd ips);
  118. a[na++:] = ipa.v4();
  119. }
  120. c.options[n] = a;
  121. }
  122. Bootconf.puts(c: self ref Bootconf, n: int, s: string)
  123. {
  124. if(n < 0 || n >= len c.options)
  125. return;
  126. c.options[n] = array of byte s;
  127. }
  128. #
  129. #
  130. # DHCP
  131. #
  132. #
  133. # BOOTP operations
  134. Bootprequest, Bootpreply: con 1+iota;
  135. # DHCP operations
  136. NotDHCP, Discover, Offer, Request, Decline, Ack, Nak, Release, Inform: con iota;
  137. Dhcp: adt {
  138. udphdr: array of byte;
  139. op: int;
  140. htype: int;
  141. hops: int;
  142. xid: int;
  143. secs: int;
  144. flags: int;
  145. ciaddr: IPaddr;
  146. yiaddr: IPaddr;
  147. siaddr: IPaddr;
  148. giaddr: IPaddr;
  149. chaddr: array of byte;
  150. sname: string;
  151. file: string;
  152. options: list of (int, array of byte);
  153. dhcpop: int;
  154. };
  155. opnames := array[] of {
  156. Discover => "Discover",
  157. Offer => "Offer",
  158. Request => "Request",
  159. Decline => "Decline",
  160. Ack => "Ack",
  161. Nak => "Nak",
  162. Release => "Release",
  163. Inform => "Inform"
  164. };
  165. opname(op: int): string
  166. {
  167. if(op >= 0 && op < len opnames)
  168. return opnames[op];
  169. return sys->sprint("OP%d", op);
  170. }
  171. stringget(buf: array of byte): string
  172. {
  173. for(x := 0; x < len buf; x++)
  174. if(buf[x] == byte 0)
  175. break;
  176. if(x == 0)
  177. return nil;
  178. return string buf[0 : x];
  179. }
  180. eqbytes(b1: array of byte, b2: array of byte): int
  181. {
  182. l := len b1;
  183. if(l != len b2)
  184. return 0;
  185. for(i := 0; i < l; i++)
  186. if(b1[i] != b2[i])
  187. return 0;
  188. return 1;
  189. }
  190. magic := array[] of {byte 99, byte 130, byte 83, byte 99}; # RFC2132 (replacing RFC1048)
  191. dhcpsend(fd: ref Sys->FD, xid: int, dhcp: ref Dhcp)
  192. {
  193. dhcp.xid = xid;
  194. abuf := array[576+Udphdrlen] of {* => byte 0};
  195. abuf[0:] = dhcp.udphdr;
  196. buf := abuf[Udphdrlen:];
  197. buf[0] = byte dhcp.op;
  198. buf[1] = byte dhcp.htype;
  199. buf[2] = byte len dhcp.chaddr;
  200. buf[3] = byte dhcp.hops;
  201. put4(buf, 4, xid);
  202. put2(buf, 8, dhcp.secs);
  203. put2(buf, 10, dhcp.flags);
  204. buf[12:] = dhcp.ciaddr.v4();
  205. buf[16:] = dhcp.yiaddr.v4();
  206. buf[20:] = dhcp.siaddr.v4();
  207. buf[24:] = dhcp.giaddr.v4();
  208. buf[28:] = dhcp.chaddr;
  209. buf[44:] = array of byte dhcp.sname; # [64]
  210. buf[108:] = array of byte dhcp.file; # [128]
  211. o := 236;
  212. # RFC1542 suggests including magic and Oend as a minimum, even in BOOTP
  213. buf[o:] = magic;
  214. o += 4;
  215. if(dhcp.dhcpop != NotDHCP){
  216. buf[o++] = byte Otype;
  217. buf[o++] = byte 1;
  218. buf[o++] = byte dhcp.dhcpop;
  219. }
  220. for(ol := dhcp.options; ol != nil; ol = tl ol){
  221. (opt, val) := hd ol;
  222. buf[o++] = byte opt;
  223. buf[o++] = byte len val;
  224. if(len val > 0){
  225. buf[o:] = val;
  226. o += len val;
  227. }
  228. }
  229. buf[o++] = byte Oend;
  230. if(debug)
  231. dumpdhcp(dhcp, "->");
  232. sys->write(fd, abuf, len abuf);
  233. }
  234. kill(pid: int, grp: string)
  235. {
  236. fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);
  237. if(fd != nil)
  238. sys->fprint(fd, "kill%s", grp);
  239. }
  240. v4text(a: array of byte): string
  241. {
  242. return sys->sprint("%ud.%ud.%ud.%ud", int a[0], int a[1], int a[2], int a[3]);
  243. }
  244. parseopt(a: array of byte, isdhcp: int): (int, list of (int, array of byte))
  245. {
  246. opts: list of (int, array of byte);
  247. xop := NotDHCP;
  248. for(i := 0; i < len a;){
  249. op := int a[i++];
  250. if(op == Opad)
  251. continue;
  252. if(op == Oend || i >= len a)
  253. break;
  254. l := int a[i++];
  255. if(i+l > len a)
  256. break;
  257. if(isdhcp && op == Otype)
  258. xop = int a[i];
  259. else
  260. opts = (op, a[i:i+l]) :: opts;
  261. i += l;
  262. }
  263. rl := opts;
  264. opts = nil;
  265. for(; rl != nil; rl = tl rl)
  266. opts = hd rl :: opts;
  267. return (xop, opts);
  268. }
  269. dhcpreader(pidc: chan of int, srv: ref DhcpIO)
  270. {
  271. pidc <-= sys->pctl(0, nil);
  272. for(;;){
  273. abuf := array [576+Udphdrlen] of byte;
  274. n := sys->read(srv.fd, abuf, len abuf);
  275. if(n < 0){
  276. if(debug)
  277. sys->print("read error: %r\n");
  278. sys->sleep(1000);
  279. continue;
  280. }
  281. if(n < Udphdrlen+236){
  282. if(debug)
  283. sys->print("short read: %d\n", n);
  284. continue;
  285. }
  286. buf := abuf[Udphdrlen:n];
  287. n -= Udphdrlen;
  288. dhcp := ref Dhcp;
  289. dhcp.op = int buf[0];
  290. if(dhcp.op != Bootpreply){
  291. if(debug)
  292. sys->print("bootp: not reply, discarded\n");
  293. continue;
  294. }
  295. dhcp.dhcpop = NotDHCP;
  296. if(n >= 240 && eqbytes(buf[236:240], magic)) # otherwise it's something we won't understand
  297. (dhcp.dhcpop, dhcp.options) = parseopt(buf[240:n], 1);
  298. case dhcp.dhcpop {
  299. NotDHCP or Ack or Nak or Offer =>
  300. ;
  301. * =>
  302. if(debug)
  303. sys->print("dhcp: ignore dhcp op %d\n", dhcp.dhcpop);
  304. continue;
  305. }
  306. dhcp.udphdr = abuf[0:Udphdrlen];
  307. dhcp.htype = int buf[1];
  308. hlen := int buf[2];
  309. dhcp.hops = int buf[3];
  310. dhcp.xid = get4(buf, 4);
  311. dhcp.secs = get2(buf, 8);
  312. dhcp.flags = get2(buf, 10);
  313. dhcp.ciaddr = IPaddr.newv4(buf[12:]);
  314. dhcp.yiaddr = IPaddr.newv4(buf[16:]);
  315. dhcp.siaddr = IPaddr.newv4(buf[20:]);
  316. dhcp.giaddr = IPaddr.newv4(buf[24:]);
  317. dhcp.chaddr = buf[28 : 28 + hlen];
  318. dhcp.sname = stringget(buf[44 : 108]);
  319. dhcp.file = stringget(buf[108 : 236]);
  320. srv.dc <-= dhcp;
  321. }
  322. }
  323. timeoutstart(msecs: int): (int, chan of int)
  324. {
  325. tc := chan of int;
  326. spawn timeoutproc(tc, msecs);
  327. return (<-tc, tc);
  328. }
  329. timeoutproc(c: chan of int, msecs: int)
  330. {
  331. c <-= sys->pctl(0, nil);
  332. sys->sleep(msecs);
  333. c <-= 1;
  334. }
  335. hex(b: int): int
  336. {
  337. if(b >= '0' && b <= '9')
  338. return b-'0';
  339. if(b >= 'A' && b <= 'F')
  340. return b-'A' + 10;
  341. if(b >= 'a' && b <= 'f')
  342. return b-'a' + 10;
  343. return -1;
  344. }
  345. gethaddr(device: string): (int, string, array of byte)
  346. {
  347. fd := sys->open(device, Sys->OREAD);
  348. if(fd == nil)
  349. return (-1, sys->sprint("%r"), nil);
  350. buf := array [100] of byte;
  351. n := sys->read(fd, buf, len buf);
  352. if(n < 0)
  353. return (-1, sys->sprint("%r"), nil);
  354. if(n == 0)
  355. return (-1, "empty address file", nil);
  356. addr := array [n/2] of byte;
  357. for(i := 0; i < len addr; i++){
  358. u := hex(int buf[2*i]);
  359. l := hex(int buf[2*i+1]);
  360. if(u < 0 || l < 0)
  361. return (-1, "bad address syntax", nil);
  362. addr[i] = byte ((u<<4)|l);
  363. }
  364. return (1, nil, addr);
  365. }
  366. newrequest(dest: IPaddr, bootfile: string, htype: int, haddr: array of byte, ipaddr: IPaddr, options: array of array of byte): ref Dhcp
  367. {
  368. dhcp := ref Dhcp;
  369. dhcp.op = Bootprequest;
  370. hdr := array[Udphdrlen] of {* => byte 0};
  371. hdr[Udpraddr:] = dest.v6();
  372. put2(hdr, Udprport, 67);
  373. dhcp.udphdr = hdr;
  374. dhcp.htype = htype;
  375. dhcp.chaddr = haddr;
  376. dhcp.hops = 0;
  377. dhcp.secs = 0;
  378. dhcp.flags = 0;
  379. dhcp.xid = 0;
  380. dhcp.ciaddr = ipaddr;
  381. dhcp.yiaddr = ip->v4noaddr;
  382. dhcp.siaddr = ip->v4noaddr;
  383. dhcp.giaddr = ip->v4noaddr;
  384. dhcp.file = bootfile;
  385. dhcp.dhcpop = NotDHCP;
  386. if(options != nil){
  387. for(i := 0; i < len options; i++)
  388. if(options[i] != nil)
  389. dhcp.options = (i, options[i]) :: dhcp.options;
  390. }
  391. clientid := array[len haddr + 1] of byte;
  392. clientid[0] = byte htype;
  393. clientid[1:] = haddr;
  394. dhcp.options = (Oclientid, clientid) :: dhcp.options;
  395. dhcp.options = (Ovendorclass, array of byte "plan9_386") :: dhcp.options; # 386 will do because type doesn't matter
  396. return dhcp;
  397. }
  398. udpannounce(net: string): (ref Sys->FD, string)
  399. {
  400. if(net == nil)
  401. net = "/net";
  402. conn := dial->announce(net+"/udp!*!68");
  403. if(conn == nil)
  404. return (nil, sys->sprint("can't announce dhcp port: %r"));
  405. if(sys->fprint(conn.cfd, "headers") < 0)
  406. return (nil, sys->sprint("can't set headers mode on dhcp port: %r"));
  407. conn.dfd = sys->open(conn.dir+"/data", Sys->ORDWR);
  408. if(conn.dfd == nil)
  409. return (nil, sys->sprint("can't open %s: %r", conn.dir+"/data"));
  410. return (conn.dfd, nil);
  411. }
  412. ifcnoaddr(fd: ref Sys->FD, s: string)
  413. {
  414. if(fd != nil && sys->fprint(fd, "%s %s %s", s, (ip->noaddr).text(), (ip->noaddr).text()) < 0){
  415. if(debug)
  416. sys->print("dhcp: ctl %s: %r\n", s);
  417. }
  418. }
  419. setup(net: string, device: string, init: ref Bootconf): (ref Dhcp, ref DhcpIO, string)
  420. {
  421. (htype, err, mac) := gethaddr(device);
  422. if(htype < 0)
  423. return (nil, nil, sys->sprint("can't get hardware MAC address: %s", err));
  424. ciaddr := ip->v4noaddr;
  425. if(init != nil && init.ip != nil){
  426. valid: int;
  427. (valid, ciaddr) = IPaddr.parse(init.ip);
  428. if(valid < 0)
  429. return (nil, nil, sys->sprint("invalid ip address: %s", init.ip));
  430. }
  431. (dfd, err2) := udpannounce(net);
  432. if(err2 != nil)
  433. return (nil, nil, err);
  434. bootfile: string;
  435. options: array of array of byte;
  436. if(init != nil){
  437. bootfile = init.bootf;
  438. options = init.options;
  439. }
  440. return (newrequest(ip->v4bcast, bootfile, htype, mac, ciaddr, options), DhcpIO.new(dfd), nil);
  441. }
  442. #
  443. # BOOTP (RFC951) is used by Inferno only during net boots, to get initial IP address and TFTP address and parameters
  444. #
  445. bootp(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf): (ref Bootconf, string)
  446. {
  447. (req, srv, err) := setup(net, device, init);
  448. if(err != nil)
  449. return (nil, err);
  450. ifcnoaddr(ctlifc, "add");
  451. rdhcp := exchange(srv, ++xidgen, req, 1<<NotDHCP);
  452. srv.rstop();
  453. ifcnoaddr(ctlifc, "remove");
  454. if(rdhcp == nil)
  455. return (nil, "no response to BOOTP request");
  456. return (fillbootconf(init, rdhcp), nil);
  457. }
  458. defparams := array[] of {
  459. byte Omask, byte Orouter, byte Odnsserver, byte Ohostname, byte Odomainname, byte Ontpserver,
  460. };
  461. #
  462. # DHCP (RFC2131)
  463. #
  464. dhcp(net: string, ctlifc: ref Sys->FD, device: string, init: ref Bootconf, needparam: array of int): (ref Bootconf, ref Lease, string)
  465. {
  466. (req, srv, err) := setup(net, device, init);
  467. if(err != nil)
  468. return (nil, nil, err);
  469. params := defparams;
  470. if(needparam != nil){
  471. n := len defparams;
  472. params = array[n+len needparam] of byte;
  473. params[0:] = defparams;
  474. for(i := 0; i < len needparam; i++)
  475. params[n+i] = byte needparam[i];
  476. }
  477. initopt := (Oparams, params) :: req.options; # RFC2131 requires parameters to be repeated each time
  478. lease := ref Lease(0, chan[1] of (ref Bootconf, string));
  479. spawn dhcp1(srv, lease, net, ctlifc, req, init, initopt);
  480. bc: ref Bootconf;
  481. (bc, err) = <-lease.configs;
  482. return (bc, lease, err);
  483. }
  484. dhcp1(srv: ref DhcpIO, lease: ref Lease, net: string, ctlifc: ref Sys->FD, req: ref Dhcp, init: ref Bootconf, initopt: list of (int, array of byte))
  485. {
  486. cfd := -1;
  487. if(ctlifc != nil)
  488. cfd = ctlifc.fd;
  489. lease.pid = sys->pctl(Sys->NEWPGRP|Sys->NEWFD, 1 :: srv.fd.fd :: cfd :: nil);
  490. if(ctlifc != nil)
  491. ctlifc = sys->fildes(ctlifc.fd);
  492. srv.fd = sys->fildes(srv.fd.fd);
  493. rep: ref Dhcp;
  494. ifcnoaddr(ctlifc, "add");
  495. if(req.ciaddr.isvalid())
  496. rep = reacquire(srv, req, initopt, req.ciaddr);
  497. if(rep == nil)
  498. rep = askround(srv, req, initopt);
  499. srv.rstop();
  500. ifcnoaddr(ctlifc, "remove");
  501. if(rep == nil){
  502. lease.pid = 0;
  503. lease.configs <-= (nil, "no response");
  504. exit;
  505. }
  506. for(;;){
  507. conf := fillbootconf(init, rep);
  508. applycfg(net, ctlifc, conf);
  509. if(conf.lease == 0){
  510. srv.rstop();
  511. lease.pid = 0;
  512. flush(lease.configs);
  513. lease.configs <-= (conf, nil);
  514. exit;
  515. }
  516. flush(lease.configs);
  517. lease.configs <-= (conf, nil);
  518. req.ciaddr = rep.yiaddr;
  519. while((rep = tenancy(srv, req, conf.lease)) != nil){
  520. if(rep.dhcpop == Nak || !rep.ciaddr.eq(req.ciaddr))
  521. break;
  522. req.udphdr[Udpraddr:] = rep.udphdr[Udpraddr:Udpraddr+IPaddrlen];
  523. conf = fillbootconf(init, rep);
  524. }
  525. removecfg(net, ctlifc, conf);
  526. ifcnoaddr(ctlifc, "add");
  527. while((rep = askround(srv, req, initopt)) == nil){
  528. flush(lease.configs);
  529. lease.configs <-= (nil, "no response");
  530. srv.rstop();
  531. sys->sleep(60*1000);
  532. }
  533. ifcnoaddr(ctlifc, "remove");
  534. }
  535. }
  536. reacquire(srv: ref DhcpIO, req: ref Dhcp, initopt: list of (int, array of byte), addr: IPaddr): ref Dhcp
  537. {
  538. # INIT-REBOOT: know an address; try requesting it (once)
  539. # TO DO: could use Inform when our address is static but we need a few service parameters
  540. req.ciaddr = ip->v4noaddr;
  541. rep := request(srv, ++xidgen, req, (Oipaddr, addr.v4()) :: initopt);
  542. if(rep != nil && rep.dhcpop == Ack && addr.eq(rep.yiaddr)){
  543. if(debug)
  544. sys->print("req: server accepted\n");
  545. req.udphdr[Udpraddr:] = rep.udphdr[Udpraddr:Udpraddr+IPaddrlen];
  546. return rep;
  547. }
  548. if(debug)
  549. sys->print("req: cannot reclaim\n");
  550. return nil;
  551. }
  552. askround(srv: ref DhcpIO, req: ref Dhcp, initopt: list of (int, array of byte)): ref Dhcp
  553. {
  554. # INIT
  555. req.ciaddr = ip->v4noaddr;
  556. req.udphdr[Udpraddr:] = (ip->v4bcast).v6();
  557. for(retries := 0; retries < 5; retries++){
  558. # SELECTING
  559. req.dhcpop = Discover;
  560. req.options = initopt;
  561. rep := exchange(srv, ++xidgen, req, 1<<Offer);
  562. if(rep == nil)
  563. break;
  564. #
  565. # could wait a little while and accumulate offers, but is it sensible?
  566. # we do sometimes see arguments between DHCP servers that could
  567. # only be resolved by user choice
  568. #
  569. if(!rep.yiaddr.isvalid())
  570. continue; # server has no idea either
  571. serverid := getopt(rep.options, Oserverid, 4);
  572. if(serverid == nil)
  573. continue; # broken server
  574. # REQUESTING
  575. options := (Oserverid, serverid) :: (Oipaddr, rep.yiaddr.v4()) :: initopt;
  576. lease := getlease(rep);
  577. if(lease != nil)
  578. options = (Olease, lease) :: options;
  579. rep = request(srv, rep.xid, req, options);
  580. if(rep != nil){
  581. # could probe with ARP here, and if found, Decline
  582. if(debug)
  583. sys->print("req: server accepted\n");
  584. req.udphdr[Udpraddr:] = rep.udphdr[Udpraddr:Udpraddr+IPaddrlen];
  585. return rep;
  586. }
  587. }
  588. return nil;
  589. }
  590. request(srv: ref DhcpIO, xid: int, req: ref Dhcp, options: list of (int, array of byte)): ref Dhcp
  591. {
  592. req.dhcpop = Request; # Selecting
  593. req.options = options;
  594. rep := exchange(srv, xid, req, (1<<Ack)|(1<<Nak));
  595. if(rep == nil || rep.dhcpop == Nak)
  596. return nil;
  597. return rep;
  598. }
  599. # renew
  600. # direct to server from T1 to T2 [RENEW]
  601. # Request must not include
  602. # requested IP address, server identifier
  603. # Request must include
  604. # ciaddr set to client's address
  605. # Request might include
  606. # lease time
  607. # similar, but broadcast, from T2 to T3 [REBIND]
  608. # at T3, unbind, restart Discover
  609. tenancy(srv: ref DhcpIO, req: ref Dhcp, leasesec: int): ref Dhcp
  610. {
  611. # configure address...
  612. t3 := big leasesec * big 1000; # lease expires; restart
  613. t2 := (big 3 * t3)/big 4; # broadcast renewal request at ¾time
  614. t1 := t2/big 2; # renew lease with original server at ½time
  615. srv.rstop();
  616. thebigsleep(t1);
  617. # RENEW
  618. rep := renewing(srv, req, t1, t2);
  619. if(rep != nil)
  620. return rep;
  621. # REBIND
  622. req.udphdr[Udpraddr:] = (ip->v4bcast).v6(); # now try broadcast
  623. return renewing(srv, req, t2, t3);
  624. }
  625. renewing(srv: ref DhcpIO, req: ref Dhcp, a: big, b: big): ref Dhcp
  626. {
  627. Minute: con big(60*1000);
  628. while(a < b){
  629. rep := exchange(srv, req.xid, req, (1<<Ack)|(1<<Nak));
  630. if(rep != nil)
  631. return rep;
  632. delta := (b-a)/big 2;
  633. if(delta < Minute)
  634. delta = Minute;
  635. thebigsleep(delta);
  636. a += delta;
  637. }
  638. return nil;
  639. }
  640. thebigsleep(msec: big)
  641. {
  642. Day: con big (24*3600*1000); # 1 day in msec
  643. while(msec > big 0){
  644. n := msec;
  645. if(n > Day)
  646. n = Day;
  647. sys->sleep(int n);
  648. msec -= n;
  649. }
  650. }
  651. getlease(m: ref Dhcp): array of byte
  652. {
  653. lease := getopt(m.options, Olease, 4);
  654. if(lease == nil)
  655. return nil;
  656. if(get4(lease, 0) == 0){
  657. lease = array[4] of byte;
  658. put4(lease, 0, 15*60);
  659. }
  660. return lease;
  661. }
  662. fillbootconf(init: ref Bootconf, pkt: ref Dhcp): ref Bootconf
  663. {
  664. bc := ref Bootconf;
  665. if(init != nil)
  666. *bc = *init;
  667. if(bc.options == nil)
  668. bc.options = array[256] of array of byte;
  669. for(l := pkt.options; l != nil; l = tl l){
  670. (c, v) := hd l;
  671. if(bc.options[c] == nil)
  672. bc.options[c] = v; # give priority to first occurring
  673. }
  674. if((a := bc.get(Ovendorinfo)) != nil){
  675. if(bc.vendor == nil)
  676. bc.vendor = array[256] of array of byte;
  677. for(l = parseopt(a, 0).t1; l != nil; l = tl l){
  678. (c, v) := hd l;
  679. if(bc.vendor[c] == nil)
  680. bc.vendor[c] = v;
  681. }
  682. }
  683. if(pkt.yiaddr.isvalid()){
  684. bc.ip = pkt.yiaddr.text();
  685. bc.ipmask = bc.getip(Omask);
  686. if(bc.ipmask == nil)
  687. bc.ipmask = pkt.yiaddr.classmask().masktext();
  688. }
  689. bc.bootf = pkt.file;
  690. bc.dhcpip = IPaddr.newv6(pkt.udphdr[Udpraddr:]).text();
  691. bc.siaddr = pkt.siaddr.text();
  692. bc.lease = bc.getint(Olease);
  693. if(bc.lease == Infinite)
  694. bc.lease = 0;
  695. else if(debug > 1)
  696. bc.lease = 2*60; # shorten time, for testing
  697. bc.dom = bc.gets(Odomainname);
  698. s := bc.gets(Ohostname);
  699. for(i:=0; i<len s; i++)
  700. if(s[i] == '.'){
  701. if(bc.dom == nil)
  702. bc.dom = s[i+1:];
  703. s = s[0:i];
  704. break;
  705. }
  706. bc.sys = s;
  707. bc.ipgw = bc.getip(Orouter);
  708. bc.bootip = bc.getip(Otftpserver);
  709. bc.serverid = bc.getip(Oserverid);
  710. return bc;
  711. }
  712. Lease.release(l: self ref Lease)
  713. {
  714. # could send a Release message
  715. # should unconfigure
  716. if(l.pid){
  717. kill(l.pid, "grp");
  718. l.pid = 0;
  719. }
  720. }
  721. flush(c: chan of (ref Bootconf, string))
  722. {
  723. alt{
  724. <-c => ;
  725. * => ;
  726. }
  727. }
  728. DhcpIO: adt {
  729. fd: ref Sys->FD;
  730. pid: int;
  731. dc: chan of ref Dhcp;
  732. new: fn(fd: ref Sys->FD): ref DhcpIO;
  733. rstart: fn(io: self ref DhcpIO);
  734. rstop: fn(io: self ref DhcpIO);
  735. };
  736. DhcpIO.new(fd: ref Sys->FD): ref DhcpIO
  737. {
  738. return ref DhcpIO(fd, 0, chan of ref Dhcp);
  739. }
  740. DhcpIO.rstart(io: self ref DhcpIO)
  741. {
  742. if(io.pid == 0){
  743. pids := chan of int;
  744. spawn dhcpreader(pids, io);
  745. io.pid = <-pids;
  746. }
  747. }
  748. DhcpIO.rstop(io: self ref DhcpIO)
  749. {
  750. if(io.pid != 0){
  751. kill(io.pid, "");
  752. io.pid = 0;
  753. }
  754. }
  755. getopt(options: list of (int, array of byte), op: int, minlen: int): array of byte
  756. {
  757. for(; options != nil; options = tl options){
  758. (opt, val) := hd options;
  759. if(opt == op && len val >= minlen)
  760. return val;
  761. }
  762. return nil;
  763. }
  764. exchange(srv: ref DhcpIO, xid: int, req: ref Dhcp, accept: int): ref Dhcp
  765. {
  766. srv.rstart();
  767. nsec := 3;
  768. for(count := 0; count < 5; count++) {
  769. (tpid, tc) := timeoutstart(nsec*1000);
  770. dhcpsend(srv.fd, xid, req);
  771. Wait:
  772. for(;;){
  773. alt {
  774. <-tc=>
  775. break Wait;
  776. rep := <-srv.dc=>
  777. if(debug)
  778. dumpdhcp(rep, "<-");
  779. if(rep.op == Bootpreply &&
  780. rep.xid == req.xid &&
  781. rep.ciaddr.eq(req.ciaddr) &&
  782. eqbytes(rep.chaddr, req.chaddr)){
  783. if((accept & (1<<rep.dhcpop)) == 0){
  784. if(debug)
  785. sys->print("req: unexpected reply %s to %s\n", opname(rep.dhcpop), opname(req.dhcpop));
  786. continue;
  787. }
  788. kill(tpid, "");
  789. return rep;
  790. }
  791. if(debug)
  792. sys->print("req: mismatch\n");
  793. }
  794. }
  795. req.secs += nsec;
  796. nsec++;
  797. }
  798. return nil;
  799. }
  800. applycfg(net: string, ctlfd: ref Sys->FD, bc: ref Bootconf): string
  801. {
  802. # write addresses to /net/...
  803. # local address, mask[or default], remote address [mtu]
  804. if(net == nil)
  805. net = "/net";
  806. if(bc.ip == nil)
  807. return "invalid address";
  808. if(ctlfd != nil){
  809. if(sys->fprint(ctlfd, "add %s %s", bc.ip, bc.ipmask) < 0) # TO DO: [raddr [mtu]]
  810. return sys->sprint("add interface: %r");
  811. # could use "mtu n" request to set/change mtu
  812. }
  813. # if primary:
  814. # add default route if gateway valid
  815. # put ndb entries ip=, ipmask=, ipgw=; sys= dom=; fs=; auth=; dns=; ntp=; other options from bc.options
  816. if(bc.ipgw != nil){
  817. fd := sys->open(net+"/iproute", Sys->OWRITE);
  818. if(fd != nil)
  819. sys->fprint(fd, "add 0 0 %s", bc.ipgw);
  820. }
  821. s := sys->sprint("ip=%s ipmask=%s", bc.ip, bc.ipmask);
  822. if(bc.ipgw != nil)
  823. s += sys->sprint(" ipgw=%s", bc.ipgw);
  824. s += "\n";
  825. if(bc.sys != nil)
  826. s += sys->sprint(" sys=%s\n", bc.sys);
  827. if(bc.dom != nil)
  828. s += sys->sprint(" dom=%s.%s\n", bc.sys, bc.dom);
  829. if((addr := bc.getip(OP9auth)) != nil)
  830. s += sys->sprint(" auth=%s\n", addr); # TO DO: several addresses
  831. if((addr = bc.getip(OP9fs)) != nil)
  832. s += sys->sprint(" fs=%s\n", addr);
  833. if((addr = bc.getip(Odnsserver)) != nil)
  834. s += sys->sprint(" dns=%s\n", addr);
  835. fd := sys->open(net+"/ndb", Sys->OWRITE | Sys->OTRUNC);
  836. if(fd != nil){
  837. a := array of byte s;
  838. sys->write(fd, a, len a);
  839. }
  840. return nil;
  841. }
  842. removecfg(nil: string, ctlfd: ref Sys->FD, bc: ref Bootconf): string
  843. {
  844. # remove localaddr, localmask[or default]
  845. if(ctlfd != nil){
  846. if(sys->fprint(ctlfd, "remove %s %s", bc.ip, bc.ipmask) < 0)
  847. return sys->sprint("remove address: %r");
  848. }
  849. bc.ip = nil;
  850. bc.ipgw = nil;
  851. bc.ipmask = nil;
  852. # remote address?
  853. # clear net+"/ndb"?
  854. return nil;
  855. }
  856. #
  857. # the following is just for debugging
  858. #
  859. dumpdhcp(m: ref Dhcp, dir: string)
  860. {
  861. s := "";
  862. sys->print("%s %s/%ud: ", dir, IPaddr.newv6(m.udphdr[Udpraddr:]).text(), get2(m.udphdr, Udprport));
  863. if(m.dhcpop != NotDHCP)
  864. s = " "+opname(m.dhcpop);
  865. sys->print("op %d%s htype %d hops %d xid %ud\n", m.op, s, m.htype, m.hops, m.xid);
  866. sys->print("\tsecs %d flags 0x%.4ux\n", m.secs, m.flags);
  867. sys->print("\tciaddr %s\n", m.ciaddr.text());
  868. sys->print("\tyiaddr %s\n", m.yiaddr.text());
  869. sys->print("\tsiaddr %s\n", m.siaddr.text());
  870. sys->print("\tgiaddr %s\n", m.giaddr.text());
  871. sys->print("\tchaddr ");
  872. for(x := 0; x < len m.chaddr; x++)
  873. sys->print("%2.2ux", int m.chaddr[x]);
  874. sys->print("\n");
  875. if(m.sname != nil)
  876. sys->print("\tsname %s\n", m.sname);
  877. if(m.file != nil)
  878. sys->print("\tfile %s\n", m.file);
  879. if(m.options != nil){
  880. sys->print("\t");
  881. printopts(m.options, opts);
  882. sys->print("\n");
  883. }
  884. }
  885. Optbytes, Optaddr, Optmask, Optint, Optstr, Optopts, Opthex: con iota;
  886. Opt: adt
  887. {
  888. code: int;
  889. name: string;
  890. otype: int;
  891. };
  892. opts: array of Opt = array[] of {
  893. (Omask, "ipmask", Optmask),
  894. (Orouter, "ipgw", Optaddr),
  895. (Odnsserver, "dns", Optaddr),
  896. (Ohostname, "hostname", Optstr),
  897. (Odomainname, "domain", Optstr),
  898. (Ontpserver, "ntp", Optaddr),
  899. (Oipaddr, "requestedip", Optaddr),
  900. (Olease, "lease", Optint),
  901. (Oserverid, "serverid", Optaddr),
  902. (Otype, "dhcpop", Optint),
  903. (Ovendorclass, "vendorclass", Optstr),
  904. (Ovendorinfo, "vendorinfo", Optopts),
  905. (Onetbiosns, "wins", Optaddr),
  906. (Opop3server, "pop3", Optaddr),
  907. (Osmtpserver, "smtp", Optaddr),
  908. (Owwwserver, "www", Optaddr),
  909. (Oparams, "params", Optbytes),
  910. (Otftpserver, "tftp", Optaddr),
  911. (Oclientid, "clientid", Opthex),
  912. };
  913. p9opts: array of Opt = array[] of {
  914. (OP9fs, "fs", Optaddr),
  915. (OP9auth, "auth", Optaddr),
  916. };
  917. lookopt(optab: array of Opt, code: int): (int, string, int)
  918. {
  919. for(i:=0; i<len optab; i++)
  920. if(opts[i].code == code)
  921. return opts[i];
  922. return (-1, nil, 0);
  923. }
  924. printopts(options: list of (int, array of byte), opts: array of Opt)
  925. {
  926. for(; options != nil; options = tl options){
  927. (code, val) := hd options;
  928. sys->print("(%d %d", code, len val);
  929. (nil, name, otype) := lookopt(opts, code);
  930. if(name == nil){
  931. for(v := 0; v < len val; v++)
  932. sys->print(" %d", int val[v]);
  933. }else{
  934. sys->print(" %s", name);
  935. case otype {
  936. Optbytes =>
  937. for(v := 0; v < len val; v++)
  938. sys->print(" %d", int val[v]);
  939. Opthex =>
  940. for(v := 0; v < len val; v++)
  941. sys->print(" %#.2ux", int val[v]);
  942. Optaddr or Optmask =>
  943. while(len val >= 4){
  944. sys->print(" %s", v4text(val));
  945. val = val[4:];
  946. }
  947. Optstr =>
  948. sys->print(" \"%s\"", string val);
  949. Optint =>
  950. n := 0;
  951. for(v := 0; v < len val; v++)
  952. n = (n<<8) | int val[v];
  953. sys->print(" %d", n);
  954. Optopts =>
  955. printopts(parseopt(val, 0).t1, p9opts);
  956. }
  957. }
  958. sys->print(")");
  959. }
  960. }