1 /* minimal stateless DHCPv6 server for network boot */
26 typedef struct Req Req;
35 uchar ips[IPaddrlen*8];
54 typedef struct Otab Otab;
58 int (*f)(uchar *, int, Otab*, Req*);
65 static ulong starttime;
67 static char *netmtpt = "/net";
70 static uchar v6loopback[IPaddrlen] = {
78 * open ndbfile as db if not already open. also check for stale data
79 * and reload as needed.
84 static ulong lastcheck;
86 ulong now = time(nil);
88 /* check no more often than once every minute */
90 db = ndbopen(ndbfile);
93 } else if(now >= lastcheck + 60) {
102 findifc(char *net, uchar ip[IPaddrlen])
107 ipifcs = readipifc(net, ipifcs, -1);
108 for(ifc = ipifcs; ifc != nil; ifc = ifc->next)
109 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
110 if(ipcmp(lifc->ip, ip) == 0)
117 localonifc(Ipifc *ifc, uchar ip[IPaddrlen])
120 uchar net[IPaddrlen];
122 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
123 maskip(ip, lifc->mask, net);
124 if(ipcmp(net, lifc->net) == 0)
132 openlisten(char *net)
135 char data[128], devdir[40];
139 sprint(data, "%s/udp!*!dhcp6s", net);
140 cfd = announce(data, devdir);
142 sysfatal("can't announce: %r");
143 if(fprint(cfd, "headers") < 0)
144 sysfatal("can't set header mode: %r");
146 ipifcs = readipifc(net, ipifcs, -1);
147 for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
148 if(strcmp(ifc->dev, "/dev/null") == 0)
150 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
151 if(!ISIPV6LINKLOCAL(lifc->ip))
153 if(fprint(cfd, "addmulti %I ff02::1:2", lifc->ip) < 0)
154 fprint(2, "addmulti: %I: %r\n", lifc->ip);
158 sprint(data, "%s/data", devdir);
159 fd = open(data, ORDWR);
161 sysfatal("open udp data: %r");
167 gettlv(int x, int *plen, uchar *p, uchar *e)
190 getv6ips(uchar *ip, int n, Ndbtuple *t, char *attr)
198 for(; t != nil; t = t->entry){
199 if(strcmp(t->attr, attr) != 0)
201 if(parseip(ip, t->val) == -1)
214 lookupips(uchar *ip, int n, Ndb *db, uchar mac[Eaddrlen])
218 char val[256], *attr;
222 * use hardware address to find an ip address
225 snprint(val, sizeof val, "%E", mac);
227 t = ndbsearch(db, &s, attr, val);
230 r += getv6ips(ip + r, n - r, t, "ip");
234 t = ndbsnext(&s, attr, val);
244 for(o = otab; o->t != 0; o++)
253 for(o = otab; o->t != 0; o++)
260 addoption(Req *r, int t)
265 if(r->resp.p+4 > r->resp.e)
268 if(o == nil || o->f == nil || o->done)
271 n = (*o->f)(r->resp.p+4, r->resp.e - (r->resp.p+4), o, r);
272 if(n < 0 || r->resp.p+4+n > r->resp.e)
274 r->resp.p[0] = t>>8, r->resp.p[1] = t;
275 r->resp.p[2] = n>>8, r->resp.p[3] = n;
276 if(debug) fprint(2, "%d(%.*H)\n", t, n, r->resp.p+4);
284 fprint(2, "%s [-d] [-f ndbfile] [-x netmtpt]\n", argv0);
289 main(int argc, char *argv[])
291 uchar ibuf[4096], obuf[4096];
295 fmtinstall('H', encodefmt);
296 fmtinstall('I', eipfmt);
297 fmtinstall('E', eipfmt);
304 ndbfile = EARGF(usage());
307 netmtpt = EARGF(usage());
313 starttime = time(nil) - 946681200UL;
316 sysfatal("opendb: %r");
318 fd = openlisten(netmtpt);
320 /* put process in background */
322 switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
326 sysfatal("fork: %r");
331 while((n = read(fd, ibuf, sizeof(ibuf))) > 0){
335 r->udp = (Udphdr*)ibuf;
336 if(isv4(r->udp->raddr))
338 if((r->ifc = findifc(netmtpt, r->udp->ifcaddr)) == nil)
340 if(localonifc(r->ifc, r->udp->raddr) == nil)
343 memmove(obuf, ibuf, Udphdrsize);
344 r->req.p = ibuf+Udphdrsize;
346 r->resp.p = obuf+Udphdrsize;
347 r->resp.e = &obuf[sizeof(obuf)];
349 r->tra = r->req.p[1]<<16 | r->req.p[2]<<8 | r->req.p[3];
350 r->req.t = r->req.p[0];
353 fprint(2, "%I->%I(%s) typ=%d tra=%x\n",
354 r->udp->raddr, r->udp->laddr, r->ifc->dev,
361 r->resp.t = ADVERTISE;
368 r->resp.p[0] = r->resp.t;
369 r->resp.p[1] = r->tra>>16;
370 r->resp.p[2] = r->tra>>8;
371 r->resp.p[3] = r->tra;
380 /* Server Identifier */
381 if(addoption(r, 2) < 0)
384 /* Client Identifier */
385 if(addoption(r, 1) < 0)
388 if((r->db = opendb()) == nil)
390 r->nips = lookupips(r->ips, sizeof(r->ips), r->db, r->mac)/IPaddrlen;
392 for(i=0; i<r->nips; i++)
393 fprint(2, "ip=%I\n", r->ips+i*IPaddrlen);
399 write(fd, obuf, r->resp.p-obuf);
400 if(debug) fprint(2, "\n");
407 oclientid(uchar *w, int n, Otab*, Req *r)
412 if((p = gettlv(1, &len, r->req.p, r->req.e)) == nil)
414 if(len < 4+4+Eaddrlen || n < len)
416 memmove(r->mac, p+len-Eaddrlen, Eaddrlen);
423 oserverid(uchar *w, int n, Otab*, Req *r)
430 w[0] = 0, w[1] = 1; /* duid type: link layer address + time*/
431 w[2] = 0, w[3] = 1; /* hw type: ethernet */
432 w[4] = starttime>>24;
433 w[5] = starttime>>16;
436 myetheraddr(w+8, r->ifc->dev);
438 /* check if server id matches from the request */
439 p = gettlv(2, &len, r->req.p, r->req.e);
440 if(p != nil && (len != 4+4+Eaddrlen || memcmp(w, p, 4+4+Eaddrlen) != 0))
447 oiana(uchar *w, int n, Otab*, Req *r)
452 p = gettlv(3, &len, r->req.p, r->req.e);
453 if(p == nil || len < 3*4)
456 len = 3*4 + (4+IPaddrlen+2*4)*r->nips;
463 for(i = 0; i < r->nips; i++){
465 w[2] = 0, w[3] = IPaddrlen+2*4;
468 memmove(w, r->ips + i*IPaddrlen, IPaddrlen);
479 lookup(Req *r, char *av[], int ac)
491 /* use the target ip's to lookup info if any */
492 for(i=0; i<r->nips; i++){
493 s = smprint("%I", &r->ips[i*IPaddrlen]);
494 t = ndbconcatenate(t, ndbipinfo(r->db, "ip", s, av, ac));
500 /* use the ipv6 networks on the interface */
501 for(lifc=r->ifc->lifc; lifc!=nil; lifc=lifc->next){
503 || ipcmp(lifc->ip, v6loopback) == 0
504 || ISIPV6LINKLOCAL(lifc->ip))
506 s = smprint("%I", lifc->net);
507 t = ndbconcatenate(t, ndbipinfo(r->db, "ip", s, av, ac));
515 oro(uchar*, int, Otab *o, Req *r)
522 p = gettlv(6, &l, r->req.p, r->req.e);
523 if(p == nil || l < 2)
528 if((o = findotab(p[i]>>8 | p[i+1])) == nil || o->done)
530 for(j=0; j<3 && o->q[j]!=nil && ac<nelem(av); j++)
534 r->t = lookup(r, av, ac);
538 for(t = r->t; t != nil; t = t->entry){
539 fprint(2, "%s=%s ", t->attr, t->val);
540 if(t->entry != nil && t->entry != t->line)
546 /* process the options */
548 addoption(r, p[i]>>8 | p[i+1]);
557 oservers(uchar *w, int n, Otab *o, Req *r)
559 return getv6ips(w, n, r->t, o->q[0]);
563 odomainlist(uchar *w, int n, Otab *o, Req *q)
571 for(t = q->t; t != nil; t = t->entry){
572 if(strcmp(t->attr, o->q[0]) != 0)
574 if(utf2idn(t->val, val, sizeof(val)) <= 0)
576 for(s = val; *s != 0; s++){
577 for(l = 0; *s != 0 && *s != '.'; l++)
582 memmove(w+r, s-l, l);
595 obootfileurl(uchar *w, int n, Otab *, Req *q)
600 if((bootf = ndbfindattr(q->t, q->t, "bootf")) == nil)
602 if(strstr(bootf->val, "://") != nil)
603 return snprint((char*)w, n, "%s", bootf->val);
604 else if(getv6ips(ip, sizeof(ip), q->t, "tftp"))
605 return snprint((char*)w, n, "tftp://[%I]/%s", ip, bootf->val);
609 static Otab otab[] = {
614 { 23, oservers, "@dns" },
615 { 24, odomainlist, "dnsdomain" },
616 { 59, obootfileurl, "bootf", "@tftp", },