dhcp6d: add minimal stateless DHCPv6 server for network boot and DNS configuration
This commit is contained in:
parent
eed90aa0ad
commit
a3f3e31b20
3 changed files with 576 additions and 2 deletions
|
@ -1,6 +1,6 @@
|
|||
.TH DHCPD 8
|
||||
.SH NAME
|
||||
dhcpd, dhcpleases, rarpd, tftpd \- Internet booting
|
||||
dhcpd, dhcp6d, dhcpleases, rarpd, tftpd \- Internet booting
|
||||
.SH SYNOPSIS
|
||||
.PP
|
||||
.B ip/dhcpd
|
||||
|
@ -20,6 +20,13 @@ dhcpd, dhcpleases, rarpd, tftpd \- Internet booting
|
|||
.PP
|
||||
.B ip/dhcpleases
|
||||
.PP
|
||||
.B ip/dhcp6d
|
||||
.RB [ -d ]
|
||||
.RB [ -f
|
||||
.IR ndb-file ]
|
||||
.RB [ -x
|
||||
.IR netmtpt ]
|
||||
.PP
|
||||
.B ip/rarpd
|
||||
.RB [ -d ]
|
||||
.RB [ -e
|
||||
|
@ -37,7 +44,8 @@ dhcpd, dhcpleases, rarpd, tftpd \- Internet booting
|
|||
These programs support booting over the Internet.
|
||||
They should all be run on the same server to
|
||||
allow other systems to be booted.
|
||||
.I Dhcpd
|
||||
.IR Dhcpd ,
|
||||
.I dhcp6d
|
||||
and
|
||||
.I tftpd
|
||||
are used to boot everything;
|
||||
|
@ -221,6 +229,10 @@ Use
|
|||
as the minimum lease time for static addresses.
|
||||
.PD
|
||||
.PP
|
||||
.I Dhcp6d
|
||||
provides DHCPv6 service for IPv6 clients. Only network boot and
|
||||
DNS parameters are supported.
|
||||
.PP
|
||||
.I Dhcpleases
|
||||
prints out the currently valid DHCP leases found in the
|
||||
.B /lib/ndb/dhcp
|
||||
|
|
561
sys/src/cmd/ip/dhcp6d.c
Normal file
561
sys/src/cmd/ip/dhcp6d.c
Normal file
|
@ -0,0 +1,561 @@
|
|||
/* minimal stateless DHCPv6 server for network boot */
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ip.h>
|
||||
#include <bio.h>
|
||||
#include <ndb.h>
|
||||
|
||||
enum {
|
||||
Eaddrlen = 6,
|
||||
};
|
||||
|
||||
typedef struct Req Req;
|
||||
struct Req
|
||||
{
|
||||
int tra;
|
||||
|
||||
Udphdr *udp;
|
||||
Ipifc *ifc;
|
||||
|
||||
uchar mac[Eaddrlen];
|
||||
uchar ips[IPaddrlen*8];
|
||||
int nips;
|
||||
|
||||
Ndb *db;
|
||||
Ndbtuple *t;
|
||||
|
||||
struct {
|
||||
int t;
|
||||
uchar *p;
|
||||
uchar *e;
|
||||
} req;
|
||||
|
||||
struct {
|
||||
int t;
|
||||
uchar *p;
|
||||
uchar *e;
|
||||
} resp;
|
||||
};
|
||||
|
||||
typedef struct Otab Otab;
|
||||
struct Otab
|
||||
{
|
||||
int t;
|
||||
int (*f)(uchar *, int, Otab*, Req*);
|
||||
char *q[3];
|
||||
int done;
|
||||
};
|
||||
|
||||
static Otab otab[];
|
||||
static Ipifc *ipifcs;
|
||||
static ulong starttime;
|
||||
static int debug;
|
||||
|
||||
static uchar v6loopback[IPaddrlen] = {
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 1
|
||||
};
|
||||
|
||||
/*
|
||||
* open ndbfile as db if not already open. also check for stale data
|
||||
* and reload as needed.
|
||||
*/
|
||||
static Ndb *
|
||||
opendb(char *ndbfile)
|
||||
{
|
||||
static Ndb *db;
|
||||
/* check no more often than once every minute */
|
||||
if(db == nil)
|
||||
db = ndbopen(ndbfile);
|
||||
else if (ndbchanged(db))
|
||||
ndbreopen(db);
|
||||
return db;
|
||||
}
|
||||
|
||||
static Ipifc*
|
||||
findifc(char *net, uchar ip[IPaddrlen])
|
||||
{
|
||||
Ipifc *ifc;
|
||||
Iplifc *lifc;
|
||||
|
||||
ipifcs = readipifc(net, ipifcs, -1);
|
||||
for(ifc = ipifcs; ifc != nil; ifc = ifc->next)
|
||||
for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
|
||||
if(ipcmp(lifc->ip, ip) == 0)
|
||||
return ifc;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static int
|
||||
openlisten(char *net)
|
||||
{
|
||||
int fd, cfd;
|
||||
char data[128], devdir[40];
|
||||
Ipifc *ifc;
|
||||
|
||||
sprint(data, "%s/udp!*!dhcp6s", net);
|
||||
cfd = announce(data, devdir);
|
||||
if(cfd < 0)
|
||||
sysfatal("can't announce: %r");
|
||||
if(fprint(cfd, "headers") < 0)
|
||||
sysfatal("can't set header mode: %r");
|
||||
|
||||
ipifcs = readipifc(net, ipifcs, -1);
|
||||
for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
|
||||
if(ifc->lifc == nil)
|
||||
continue;
|
||||
if(strcmp(ifc->dev, "/dev/null") == 0)
|
||||
continue;
|
||||
if(fprint(cfd, "addmulti %I ff02::1:2", ifc->lifc->ip) < 0)
|
||||
fprint(2, "can't add interface %s: %r", ifc->dev);
|
||||
}
|
||||
|
||||
sprint(data, "%s/data", devdir);
|
||||
fd = open(data, ORDWR);
|
||||
if(fd < 0)
|
||||
sysfatal("open udp data: %r");
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static uchar*
|
||||
gettlv(int x, int *plen, uchar *p, uchar *e)
|
||||
{
|
||||
int t;
|
||||
int l;
|
||||
|
||||
if(plen != nil)
|
||||
*plen = 0;
|
||||
while(p+4 <= e){
|
||||
t = p[0]<<8 | p[1];
|
||||
l = p[2]<<8 | p[3];
|
||||
if(p+4+l > e)
|
||||
break;
|
||||
if(t == x){
|
||||
if(plen != nil)
|
||||
*plen = l;
|
||||
return p+4;
|
||||
}
|
||||
p += l+4;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
static int
|
||||
getv6ips(uchar *ip, int n, Ndbtuple *t, char *attr)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
if(n < IPaddrlen)
|
||||
return 0;
|
||||
if(*attr == '@')
|
||||
attr++;
|
||||
for(; t != nil; t = t->entry){
|
||||
if(strcmp(t->attr, attr) != 0)
|
||||
continue;
|
||||
if(parseip(ip, t->val) == -1)
|
||||
continue;
|
||||
if(isv4(ip))
|
||||
continue;
|
||||
ip += IPaddrlen;
|
||||
r += IPaddrlen;
|
||||
if(r >= n)
|
||||
break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
lookupips(uchar *ip, int n, Ndb *db, uchar mac[Eaddrlen])
|
||||
{
|
||||
Ndbtuple *t;
|
||||
Ndbs s;
|
||||
char val[256], *attr;
|
||||
int r;
|
||||
|
||||
/*
|
||||
* use hardware address to find an ip address
|
||||
*/
|
||||
attr = "ether";
|
||||
snprint(val, sizeof val, "%E", mac);
|
||||
|
||||
t = ndbsearch(db, &s, attr, val);
|
||||
r = 0;
|
||||
while(t != nil){
|
||||
r += getv6ips(ip + r, n - r, t, "ip");
|
||||
ndbfree(t);
|
||||
if(r >= n)
|
||||
break;
|
||||
t = ndbsnext(&s, attr, val);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
clearotab(void)
|
||||
{
|
||||
Otab *o;
|
||||
|
||||
for(o = otab; o->t != 0; o++)
|
||||
o->done = 0;
|
||||
}
|
||||
|
||||
static Otab*
|
||||
findotab(int t)
|
||||
{
|
||||
Otab *o;
|
||||
|
||||
for(o = otab; o->t != 0; o++)
|
||||
if(o->t == t)
|
||||
return o;
|
||||
return nil;
|
||||
}
|
||||
|
||||
static int
|
||||
addoption(Req *r, int t)
|
||||
{
|
||||
Otab *o;
|
||||
int n;
|
||||
|
||||
if(r->resp.p+4 > r->resp.e)
|
||||
return -1;
|
||||
o = findotab(t);
|
||||
if(o == nil || o->f == nil || o->done)
|
||||
return -1;
|
||||
o->done = 1;
|
||||
n = (*o->f)(r->resp.p+4, r->resp.e - (r->resp.p+4), o, r);
|
||||
if(n < 0 || r->resp.p+4+n > r->resp.e)
|
||||
return -1;
|
||||
r->resp.p[0] = t>>8, r->resp.p[1] = t;
|
||||
r->resp.p[2] = n>>8, r->resp.p[3] = n;
|
||||
if(debug) fprint(2, "%d(%.*H)\n", t, n, r->resp.p+4);
|
||||
r->resp.p += 4+n;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "%s [-d] [-f ndbfile] [-x netmtpt]\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char *ndbfile = nil;
|
||||
char *net = "/net";
|
||||
uchar ibuf[4096], obuf[4096];
|
||||
Req r[1];
|
||||
int fd, n, i;
|
||||
|
||||
fmtinstall('H', encodefmt);
|
||||
fmtinstall('I', eipfmt);
|
||||
fmtinstall('E', eipfmt);
|
||||
|
||||
ARGBEGIN {
|
||||
case 'd':
|
||||
debug++;
|
||||
break;
|
||||
case 'f':
|
||||
ndbfile = EARGF(usage());
|
||||
break;
|
||||
case 'x':
|
||||
net = EARGF(usage());
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
} ARGEND;
|
||||
|
||||
starttime = time(nil) - 946681200UL;
|
||||
|
||||
if(opendb(ndbfile) == nil)
|
||||
sysfatal("opendb: %r");
|
||||
|
||||
fd = openlisten(net);
|
||||
|
||||
/* put process in background */
|
||||
if(!debug)
|
||||
switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
|
||||
default:
|
||||
exits(nil);
|
||||
case -1:
|
||||
sysfatal("fork: %r");
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
|
||||
while((n = read(fd, ibuf, sizeof(ibuf))) > Udphdrsize+4){
|
||||
r->req.p = ibuf+Udphdrsize;
|
||||
r->req.e = ibuf+n;
|
||||
|
||||
memmove(obuf, ibuf, Udphdrsize);
|
||||
r->udp = (Udphdr*)obuf;
|
||||
r->resp.p = obuf+Udphdrsize;
|
||||
r->resp.e = &obuf[sizeof(obuf)];
|
||||
|
||||
r->tra = r->req.p[1]<<16 | r->req.p[2]<<8 | r->req.p[3];
|
||||
r->req.t = r->req.p[0];
|
||||
|
||||
if((r->ifc = findifc(net, r->udp->ifcaddr)) == nil)
|
||||
continue;
|
||||
|
||||
if(debug)
|
||||
fprint(2, "%I->%I(%s) typ=%d tra=%x\n",
|
||||
r->udp->raddr, r->udp->laddr, r->ifc->dev,
|
||||
r->req.t, r->tra);
|
||||
|
||||
switch(r->req.t){
|
||||
default:
|
||||
continue;
|
||||
case 1: /* solicit */
|
||||
r->resp.t = 2; /* advertise */
|
||||
break;
|
||||
case 3: /* request */
|
||||
case 11: /* information request */
|
||||
r->resp.t = 7; /* reply */
|
||||
break;
|
||||
}
|
||||
r->resp.p[0] = r->resp.t;
|
||||
r->resp.p[1] = r->tra>>16;
|
||||
r->resp.p[2] = r->tra>>8;
|
||||
r->resp.p[3] = r->tra;
|
||||
|
||||
r->req.p += 4;
|
||||
r->resp.p += 4;
|
||||
|
||||
r->t = nil;
|
||||
|
||||
clearotab();
|
||||
|
||||
/* Server Identifier */
|
||||
if(addoption(r, 2) < 0)
|
||||
continue;
|
||||
|
||||
/* Client Identifier */
|
||||
if(addoption(r, 1) < 0)
|
||||
continue;
|
||||
|
||||
/* Lookup taret ip addresses */
|
||||
if((r->db = opendb(ndbfile)) == nil)
|
||||
continue;
|
||||
r->nips = lookupips(r->ips, sizeof(r->ips), r->db, r->mac)/IPaddrlen;
|
||||
if(debug){
|
||||
for(i=0; i<r->nips; i++)
|
||||
fprint(2, "ip=%I\n", r->ips+i*IPaddrlen);
|
||||
}
|
||||
|
||||
addoption(r, 3);
|
||||
addoption(r, 6);
|
||||
|
||||
write(fd, obuf, r->resp.p-obuf);
|
||||
if(debug) fprint(2, "\n");
|
||||
}
|
||||
|
||||
exits(nil);
|
||||
}
|
||||
|
||||
static int
|
||||
oclientid(uchar *w, int n, Otab*, Req *r)
|
||||
{
|
||||
int len;
|
||||
uchar *p;
|
||||
|
||||
if((p = gettlv(1, &len, r->req.p, r->req.e)) == nil)
|
||||
return -1;
|
||||
if(len < 4+4+Eaddrlen || n < len)
|
||||
return -1;
|
||||
memmove(r->mac, p+len-Eaddrlen, Eaddrlen);
|
||||
memmove(w, p, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
oserverid(uchar *w, int n, Otab*, Req *r)
|
||||
{
|
||||
if(n < 4+4+Eaddrlen)
|
||||
return -1;
|
||||
w[0] = 0, w[1] = 1; /* duid type: link layer address + time*/
|
||||
w[2] = 0, w[3] = 1; /* hw type: ethernet */
|
||||
w += 4;
|
||||
w[0] = starttime>>24;
|
||||
w[1] = starttime>>16;
|
||||
w[2] = starttime>>8;
|
||||
w[3] = starttime;
|
||||
w += 4;
|
||||
myetheraddr(w, r->ifc->dev);
|
||||
return 4+4+Eaddrlen;
|
||||
}
|
||||
|
||||
static int
|
||||
oiana(uchar *w, int n, Otab*, Req *r)
|
||||
{
|
||||
int i, len;
|
||||
uchar *p;
|
||||
|
||||
p = gettlv(3, &len, r->req.p, r->req.e);
|
||||
if(p == nil || len < 3*4)
|
||||
return -1;
|
||||
|
||||
len = 3*4 + (4+IPaddrlen+2*4)*r->nips;
|
||||
if(n < len)
|
||||
return -1;
|
||||
|
||||
memmove(w, p, 3*4);
|
||||
w += 3*4;
|
||||
|
||||
for(i = 0; i < r->nips; i++){
|
||||
w[0] = 0, w[1] = 5;
|
||||
w[2] = 0, w[3] = IPaddrlen+2*4;
|
||||
w += 4;
|
||||
|
||||
memmove(w, r->ips + i*IPaddrlen, IPaddrlen);
|
||||
w += IPaddrlen;
|
||||
|
||||
memset(w, 255, 2*4);
|
||||
w += 2*4;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static Ndbtuple*
|
||||
lookup(Req *r, char *av[], int ac)
|
||||
{
|
||||
Ndbtuple *t;
|
||||
char *s;
|
||||
|
||||
if(ac <= 0)
|
||||
return nil;
|
||||
|
||||
t = nil;
|
||||
if(r->nips > 0){
|
||||
int i;
|
||||
|
||||
/* use the target ip's to lookup info if any */
|
||||
for(i=0; i<r->nips; i++){
|
||||
s = smprint("%I", &r->ips[i*IPaddrlen]);
|
||||
t = ndbconcatenate(t, ndbipinfo(r->db, "ip", s, av, ac));
|
||||
free(s);
|
||||
}
|
||||
} else {
|
||||
Iplifc *lifc;
|
||||
|
||||
/* use the ipv6 networks on the interface */
|
||||
for(lifc=r->ifc->lifc; lifc!=nil; lifc=lifc->next){
|
||||
if(isv4(lifc->ip)
|
||||
|| ipcmp(lifc->ip, v6loopback) == 0
|
||||
|| ISIPV6LINKLOCAL(lifc->ip))
|
||||
continue;
|
||||
s = smprint("%I", lifc->net);
|
||||
t = ndbconcatenate(t, ndbipinfo(r->db, "ip", s, av, ac));
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static int
|
||||
oro(uchar*, int, Otab *o, Req *r)
|
||||
{
|
||||
uchar *p;
|
||||
char *av[100];
|
||||
int i, j, l, ac;
|
||||
Ndbtuple *t;
|
||||
|
||||
p = gettlv(6, &l, r->req.p, r->req.e);
|
||||
if(p == nil || l < 2)
|
||||
return -1;
|
||||
|
||||
ac = 0;
|
||||
for(i=0; i<l; i+=2){
|
||||
if((o = findotab(p[i]>>8 | p[i+1])) == nil || o->done)
|
||||
continue;
|
||||
for(j=0; j<3 && o->q[j]!=nil && ac<nelem(av); j++)
|
||||
av[ac++] = o->q[j];
|
||||
}
|
||||
|
||||
r->t = lookup(r, av, ac);
|
||||
|
||||
if(debug){
|
||||
fprint(2, "ndb(");
|
||||
for(t = r->t; t != nil; t = t->entry){
|
||||
fprint(2, "%s=%s ", t->attr, t->val);
|
||||
if(t->entry != nil && t->entry != t->line)
|
||||
fprint(2, "\n");
|
||||
}
|
||||
fprint(2, ")\n");
|
||||
}
|
||||
|
||||
/* process the options */
|
||||
for(i=0; i<l; i+=2)
|
||||
addoption(r, p[i]>>8 | p[i+1]);
|
||||
|
||||
ndbfree(r->t);
|
||||
r->t = nil;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
oservers(uchar *w, int n, Otab *o, Req *r)
|
||||
{
|
||||
return getv6ips(w, n, r->t, o->q[0]);
|
||||
}
|
||||
|
||||
static int
|
||||
odomainlist(uchar *w, int n, Otab *o, Req *q)
|
||||
{
|
||||
Ndbtuple *t;
|
||||
int l, r;
|
||||
char *s;
|
||||
|
||||
r = 0;
|
||||
for(t = q->t; t != nil; t = t->entry){
|
||||
if(strcmp(t->attr, o->q[0]) != 0)
|
||||
continue;
|
||||
for(s = t->val; *s != 0; s++){
|
||||
for(l = 0; *s != 0 && *s != '.'; l++)
|
||||
s++;
|
||||
if(r+1+l > n)
|
||||
return -1;
|
||||
w[r++] = l;
|
||||
memmove(w+r, s-l, l);
|
||||
r += l;
|
||||
}
|
||||
if(r >= n)
|
||||
return -1;
|
||||
w[r++] = 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
obootfileurl(uchar *w, int n, Otab *, Req *q)
|
||||
{
|
||||
uchar ip[IPaddrlen];
|
||||
Ndbtuple *bootf;
|
||||
|
||||
if((bootf = ndbfindattr(q->t, q->t, "bootf")) == nil)
|
||||
return -1;
|
||||
if(strstr(bootf->val, "://") != nil)
|
||||
return snprint((char*)w, n, "%s", bootf->val);
|
||||
else if(getv6ips(ip, sizeof(ip), q->t, "tftp"))
|
||||
return snprint((char*)w, n, "tftp://[%I]/%s", ip, bootf->val);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static Otab otab[] = {
|
||||
{ 1, oclientid, },
|
||||
{ 2, oserverid, },
|
||||
{ 3, oiana, },
|
||||
{ 6, oro, },
|
||||
{ 23, oservers, "@dns" },
|
||||
{ 24, odomainlist, "dnsdomain" },
|
||||
{ 59, obootfileurl, "bootf", "@tftp", },
|
||||
{ 0 },
|
||||
};
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
TARG = 6in4\
|
||||
ayiya\
|
||||
dhcp6d\
|
||||
dhcpclient\
|
||||
ftpd\
|
||||
gping\
|
||||
|
|
Loading…
Reference in a new issue