shithub: riscv

Download patch

ref: cbf5d3dc340da5b96cd1282e622a3f862c5b103e
parent: 169aa63ec5a670d0c72b7e9fb747e16d76b718c3
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun May 21 13:48:42 EDT 2023

ip/ipconfig: initial dhcpv6 support, clean default-routes and /net/ndb on remove

This adds a very basic (probably wrong) DHCPv6 client,
to handle the "managed"-flag in IPv6 router solicitations.

We add -U option to pass the DHCPv6 client id as well as
an -s flag to manually add a dns server (because ppp
is going to call ipconfig to handle all the configuration
and write-back to /net/ndb in the future).

Have the remove command also remove default routes and
/net/ndb entries. (needed by ppp).

--- a/sys/man/8/ipconfig
+++ b/sys/man/8/ipconfig
@@ -6,6 +6,8 @@
 .ti -0.25i
 .B ip/ipconfig
 .RB [ -6DGNOPdnprtuX ]
+.RB [ -U
+.IR duid ]
 .RB [ -b
 .IR baud ]
 .RB [ -c
@@ -18,6 +20,8 @@
 .IR mtu ]
 .RB [ -o
 .IR dhcp-opt ]
+.RB [ -s
+.IR dnsserver ]
 .RB [ -f
 .IR dbfile ]
 .RB [ -x
@@ -160,6 +164,11 @@
 .TP
 .B d
 use DHCP to determine any unspecified configuration parameters.
+For the
+.B ra6
+verb with non-zero
+.BR recvra ,
+this enables DHCPv6 address configuration.
 .TP
 .B D
 turn on debugging.
@@ -197,7 +206,7 @@
 .I local
 IP address or if
 .I local
-is omited and the device is an ethernet then all IP parameters
+is omitted and the device is an ethernet then all IP parameters
 associated with the MAC address. IPv6 addresses are added only
 if a IPv6 link-local address exists on the interface or the
 .B 6
@@ -223,10 +232,25 @@
 .I ipconfig
 instead to fork a background process that keeps trying forever.
 .TP
+.B s
+add
+.I dnsserver
+to the 
+.B /net/ndb
+configuration.
+.TP
 .B t
 enable source address translation on the interface and default route.
-(only usefull for IPv4).
+(only useful for IPv4).
 .TP
+.B U
+specify the DHCPv6 client identifier.
+When omitted, the MAC address is used to generate a link-layer
+based duid (DUID-LL).
+Note that the
+.B -d
+flag must also be given to enable DHCPv6.
+.TP
 .B u
 disable IPv6 duplicate discovery detection,
 which removes any existing ARP table entry for one of our IPv6 addresses
@@ -247,7 +271,7 @@
 .TP
 .B o
 adds
-.I dhcpoption
+.I dhcp-opt
 to the list of paramters requested of the DHCP server.  The
 result will appear in
 .B /net/ndb
--- a/sys/src/cmd/ip/ipconfig/dhcp.c
+++ b/sys/src/cmd/ip/ipconfig/dhcp.c
@@ -287,7 +287,7 @@
 			 * leave everything we've learned somewhere that
 			 * other procs can find it.
 			 */
-			putndb();
+			putndb(1);
 			refresh();
 		}
 	}
--- /dev/null
+++ b/sys/src/cmd/ip/ipconfig/dhcpv6.c
@@ -1,0 +1,233 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ip.h>
+#include <ndb.h>
+#include "ipconfig.h"
+
+enum {
+	SOLICIT	= 1,
+	ADVERTISE,
+	REQUEST,
+	CONFIRM,
+	RENEW,
+	REBIND,
+	REPLY,
+	RELEASE,
+	DECLINE,
+	RECONFIGURE,
+	INFOREQ,
+	RELAYFORW,
+	RELAYREPL,
+};
+
+static uchar v6dhcpservers[IPaddrlen] = {
+	0xff, 0x02, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 1, 0, 2,
+};
+
+static uchar sid[256];
+static int sidlen;
+
+static int
+openlisten(void)
+{
+	int n, fd, cfd;
+	char data[128], devdir[40];
+
+	sprint(data, "%s/udp!%I!546", conf.mpoint, conf.lladdr);
+	for (n = 0; (cfd = announce(data, devdir)) < 0; n++) {
+		if(!noconfig)
+			sysfatal("can't announce for dhcp: %r");
+
+		/* might be another client - wait and try again */
+		warning("can't announce %s: %r", data);
+		sleep(jitter());
+		if(n > 10)
+			return -1;
+	}
+
+	if(fprint(cfd, "headers") < 0)
+		sysfatal("can't set header mode: %r");
+
+	fprint(cfd, "ignoreadvice");
+
+	sprint(data, "%s/data", devdir);
+	fd = open(data, ORDWR);
+	if(fd < 0)
+		sysfatal("open %s: %r", data);
+	close(cfd);
+	return fd;
+}
+
+static int
+transaction(int fd, int type, int timeout)
+{
+	union {
+		Udphdr;
+		uchar	buf[4096];
+	} ipkt, opkt;
+
+	uchar *p, *e, *x;
+	int tra, opt, len, sleepfor;
+
+	tra = lrand() & 0xFFFFFF;
+
+	ipmove(opkt.laddr, conf.lladdr);
+	ipmove(opkt.raddr, v6dhcpservers);
+	ipmove(opkt.ifcaddr, conf.lladdr);
+	hnputs(opkt.lport, 546);
+	hnputs(opkt.rport, 547);
+
+	p = opkt.buf + Udphdrsize;
+
+	*p++ = type;
+	*p++ = tra >> 16;
+	*p++ = tra >> 8;
+	*p++ = tra >> 0;
+
+	/* client identifier */
+	*p++ = 0x00; *p++ = 0x01;
+	/* len */
+	*p++ = conf.duidlen >> 8;
+	*p++ = conf.duidlen;
+	memmove(p, conf.duid, conf.duidlen);
+	p += conf.duidlen;
+
+	/* IA for non-temporary address */
+	len = 12;
+	if(validip(conf.laddr))
+		len += 4 + IPaddrlen + 2*4;
+	*p++ = 0x00; *p++ = 0x03;
+	*p++ = len >> 8;
+	*p++ = len;
+	/* IAID */
+	*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x01;
+	/* T1, T2 */
+	*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
+	*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
+	if(len > 12){
+		*p++ = 0x00; *p++ = 0x05;
+		*p++ = 0x00; *p++ = IPaddrlen + 2*4;
+		memmove(p, conf.laddr, IPaddrlen);
+		p += IPaddrlen;
+		memset(p, 0xFF, 2*4);
+		p += 2*4;
+	}
+
+	/* Option Request */
+	*p++ = 0x00; *p++ = 0x06;
+	*p++ = 0x00; *p++ = 0x02;
+	*p++ = 0x00; *p++ = 0x17;	/* DNS servers */
+
+	if(sidlen > 0){
+		*p++ = 0x00; *p++ = 0x02;
+		/* len */
+		*p++ = sidlen >> 8;
+		*p++ = sidlen;;
+		memmove(p, sid, sidlen);
+		p += sidlen;
+	}
+
+	len = -1;
+	for(sleepfor = 500; timeout > 0; sleepfor <<= 1){
+		DEBUG("sending dhcpv6 request %x", opkt.buf[Udphdrsize]);
+
+		alarm(sleepfor);
+		if(len < 0)
+			write(fd, opkt.buf, p - opkt.buf);
+
+		len = read(fd, ipkt.buf, sizeof(ipkt.buf));
+		timeout += alarm(0);
+		timeout -= sleepfor;
+		if(len == 0)
+			break;
+
+		if(len < Udphdrsize+4)
+			continue;
+		if(ipkt.buf[Udphdrsize+1] != ((tra>>16)&0xFF)
+		|| ipkt.buf[Udphdrsize+2] != ((tra>>8)&0xFF)
+		|| ipkt.buf[Udphdrsize+3] != ((tra>>0)&0xFF))
+			continue;
+
+		DEBUG("got dhcpv6 reply %x from %I on %I", ipkt.buf[Udphdrsize+0], ipkt.raddr, ipkt.ifcaddr);
+
+		type |= (int)ipkt.buf[Udphdrsize+0]<<8;
+		switch(type){
+		case ADVERTISE << 8 | SOLICIT:
+		case REPLY << 8 | REQUEST:
+			goto Response;
+		default:
+			return -1;
+		}
+	}
+	return -1;
+
+Response:
+	for(p = ipkt.buf + Udphdrsize + 4, e = ipkt.buf + len; p < e; p = x) {
+		if (p+4 > e)
+			return -1;
+
+		opt = (int)p[0] << 8 | p[1];
+		len = (int)p[2] << 8 | p[3];
+		p += 4;
+		x = p+len;
+		if (x > e)
+			return -1;
+
+		DEBUG("got dhcpv6 option %x: [%d] %.*H", opt, len, len, p);
+
+		switch(opt){
+		case 0x01:		/* client identifier */
+			continue;
+		case 0x02:		/* server identifier */
+			if(len < 1 || len > sizeof(sid))
+				break;
+			sidlen = len;
+			memmove(sid, p, sidlen);
+			continue;
+		case 0x03:		/* IA for non-temporary address */
+			if(p+12+4+IPaddrlen+2*4 > x)
+				break;
+			/* skip IAID, T1, T2 */
+			p += 12;
+			/* IA Addresss */
+			if(p[0] != 0x00 || p[1] != 0x05
+			|| p[2] != 0x00 || p[3] != IPaddrlen+2*4)
+				break;
+			p += 4;
+			memset(conf.mask, 0xFF, IPaddrlen);
+			memmove(conf.laddr, p, IPaddrlen);
+			continue;
+		case 0x17:	/* dns servers */
+			if(len % IPaddrlen)
+				break;
+			addaddrs(conf.dns, sizeof(conf.dns), p, len);
+			continue;
+		default:
+			DEBUG("unknown dhcpv6 option %x", opt);
+			continue;
+		}
+		warning("dhcpv6: malformed option %x: [%d] %.*H", opt, len, len, x-len);
+	}
+
+	return 0;
+}
+
+void
+dhcpv6query(void)
+{
+	int fd;
+
+	fd = openlisten();
+	if(transaction(fd, SOLICIT, 5000) < 0)
+		goto out;
+	if(!validip(conf.laddr))
+		goto out;
+	if(transaction(fd, REQUEST, 10000) < 0)
+		goto out;
+out:
+	close(fd);
+}
--- a/sys/src/cmd/ip/ipconfig/ipconfig.h
+++ b/sys/src/cmd/ip/ipconfig/ipconfig.h
@@ -35,6 +35,8 @@
 	int	hwalen;
 	uchar	cid[32];
 	int	cidlen;
+	uchar	duid[2+128];
+	int	duidlen;
 	char	*baud;
 
 	/* learned info */
@@ -99,6 +101,7 @@
 extern Conf	conf;
 extern int	myifc;
 extern int	noconfig;
+extern int	dodhcp;
 
 extern int	debug;
 extern int	dolog;
@@ -129,7 +132,7 @@
 int	gnames(char*, int, uchar*, int);
 Ndb*	opendatabase(void);
 void	ndb2conf(Ndb *db, uchar *ip);
-void	putndb(void);
+void	putndb(int);
 void	refresh(void);
 ulong	randint(ulong low, ulong hi);
 int	validip(uchar*);
@@ -155,3 +158,9 @@
 void	ea2lla(uchar *lla, uchar *ea);
 int	findllip(uchar *ip, Ipifc *ifc);
 int	ip6cfg(void);
+
+/*
+ * DHCPv6
+ */
+void	dhcpv6init(void);
+void	dhcpv6query(void);
--- a/sys/src/cmd/ip/ipconfig/ipv6.c
+++ b/sys/src/cmd/ip/ipconfig/ipv6.c
@@ -448,25 +448,16 @@
 	}
 }
 
-/*
- * a router receiving a router adv from another
- * router calls this; it is basically supposed to
- * log the information in the ra and raise a flag
- * if any parameter value is different from its configured values.
- *
- * doing nothing for now since I don't know where to log this yet.
- */
 static void
-recvrarouter(uchar buf[], int pktlen)
-{
-	USED(buf, pktlen);
-}
-
-static void
 ewrite(int fd, char *str)
 {
 	int n;
 
+	if(noconfig){
+		DEBUG("ewrite: %s\n", str);
+		return;
+	}
+
 	if(fd < 0)
 		return;
 
@@ -561,7 +552,7 @@
 /*
  * host receiving a router advertisement calls this
  */
-static void
+static int
 recvrahost(uchar buf[], int pktlen)
 {
 	char dnsdomain[sizeof(conf.dnsdomain)];
@@ -578,19 +569,24 @@
 	m = sizeof *ra;
 	ra = (Routeradv*)buf;
 	if(pktlen < m)
-		return;
+		return -1;
 
 	if(!ISIPV6LINKLOCAL(ra->src))
-		return;
+		return -1;
 
 	conf.ttl = ra->cttl;
-	conf.mflag = (MFMASK & ra->mor);
-	conf.oflag = (OCMASK & ra->mor);
+	conf.mflag = (MFMASK & ra->mor) != 0;
+	conf.oflag = (OCMASK & ra->mor) != 0;
 	conf.routerlt = nhgets(ra->routerlt);
 	conf.reachtime = nhgetl(ra->rchbltime);
 	conf.rxmitra = nhgetl(ra->rxmtimer);
 	conf.linkmtu = DEFMTU;
 
+	if(conf.routerlt == 0)
+		ipmove(conf.gaddr, IPnoaddr);
+	else
+		ipmove(conf.gaddr, ra->src);
+
 	memset(conf.dns, 0, sizeof(conf.dns));
 	memset(conf.fs, 0, sizeof(conf.fs));
 	memset(conf.auth, 0, sizeof(conf.auth));
@@ -602,7 +598,7 @@
 		optype = buf[n];
 		m += 8 * buf[n+1];
 		if(m <= n || pktlen < m)
-			return;
+			return -1;
 
 		switch (optype) {
 		case V6nd_srclladdr:
@@ -660,7 +656,7 @@
 		|| ipcmp(r->src, ra->src) == 0 && r->routerlt != 0 && conf.routerlt == 0){
 			DEBUG("purging RA from %I on %s; pfx %I %M",
 				r->src, conf.dev, r->laddr, r->mask);
-			if(validip(r->gaddr))
+			if(!noconfig && validip(r->gaddr))
 				removedefroute(r->gaddr, conf.lladdr, r->laddr, r->mask);
 			*rr = r->next;
 			free(r);
@@ -678,7 +674,7 @@
 		optype = buf[n];
 		m += 8 * buf[n+1];
 		if(m <= n || pktlen < m)
-			return;
+			return -1;
 
 		if(optype != V6nd_pfxinfo)
 			continue;
@@ -760,17 +756,19 @@
 		DEBUG("got RA from %I on %s; pfx %I %M",
 			ra->src, conf.dev, conf.v6pref, conf.mask);
 
+		if(noconfig)
+			continue;
+
 		if(validip(conf.gaddr)
 		&& ipcmp(conf.gaddr, conf.laddr) != 0
 		&& ipcmp(conf.gaddr, conf.lladdr) != 0)
 			adddefroute(conf.gaddr, conf.lladdr, conf.laddr, conf.mask);
 
-		if(noconfig)
-			continue;
-
-		putndb();
+		putndb(1);
 		refresh();
 	}
+
+	return 0;
 }
 
 /*
@@ -838,7 +836,10 @@
 			exits(nil);
 		}
 
-		if(recvra6on(ifc) == IsHostNoRecv || noconfig && sendrscnt < 0){
+		switch(recvra6on(ifc)){
+		case IsHostRecv:
+			break;
+		default:
 			warning("recvra6: recvra off, quitting on %s", conf.dev);
 			if(recvracnt == 0)
 				rendezvous(recvra6, (void*)-1);
@@ -859,18 +860,27 @@
 			continue;
 		}
 
-		switch (recvra6on(ifc)) {
-		case IsRouter:
-			recvrarouter(buf, n);
-			break;
-		case IsHostRecv:
-			recvrahost(buf, n);
-			break;
-		}
+		if(recvrahost(buf, n) < 0)
+			continue;
 
 		/* got at least initial ra; no whining */
-		if(recvracnt == 0)
+		if(recvracnt == 0){
+			if(dodhcp && conf.mflag){
+				dhcpv6query();
+				if(noconfig || !validip(conf.laddr))
+					continue;
+				fprint(conf.rfd, "tag dhcp");
+				if(ip6cfg() < 0){
+					fprint(conf.rfd, "tag ra6");
+					continue;
+				}
+				putndb(1);
+				refresh();
+				rendezvous(recvra6, (void*)1);
+				exits(nil);
+			}
 			rendezvous(recvra6, (void*)1);
+		}
 
 		if(recvracnt < Maxv6initras)
 			recvracnt++;
--- a/sys/src/cmd/ip/ipconfig/main.c
+++ b/sys/src/cmd/ip/ipconfig/main.c
@@ -39,6 +39,7 @@
 
 static char logfile[] = "ipconfig";
 
+static void	openiproute(void);
 static void	binddevice(void);
 static void	controldevice(void);
 extern void	pppbinddev(void);
@@ -54,8 +55,8 @@
 void
 usage(void)
 {
-	fprint(2, "usage: %s [-6dDGnNOpPrtuX][-b baud][-c ctl]* [-g gw]"
-		"[-h host][-m mtu]\n"
+	fprint(2, "usage: %s [-6dDGnNOpPrtuX][-b baud][-c ctl]* [-U duid] [-g gw]"
+		"[-h host][-m mtu][-s dns]...\n"
 		"\t[-f dbfile][-x mtpt][-o dhcpopt] type dev [verb] [laddr [mask "
 		"[raddr [fs [auth]]]]]\n", argv0);
 	exits("usage");
@@ -277,10 +278,10 @@
 	Ipifc *nifc;
 
 	ifc = readipifc(net, ifc, -1);
-	for(nifc = ifc; nifc != nil; nifc = nifc->next)
+	for(nifc = ifc; nifc != nil; nifc = nifc->next){
 		if(strcmp(nifc->dev, dev) == 0)
 			return nifc->index;
-
+	}
 	return -1;
 }
 
@@ -305,6 +306,15 @@
 		conf.cid[0] = conf.hwatype;
 		memmove(&conf.cid[1], conf.hwa, conf.hwalen);
 		conf.cidlen = conf.hwalen+1;
+		if(conf.duidlen == 0){
+			/* DUID-LL */
+			conf.duid[0] = 0x00;
+			conf.duid[1] = 0x03;
+			conf.duid[2] = conf.hwatype >> 8;
+			conf.duid[3] = conf.hwatype;
+			memmove(conf.duid+4, conf.hwa, conf.hwalen);
+			conf.duidlen = conf.hwalen+4;
+		}
 	} else {
 		conf.hwatype = -1;
 		snprint((char*)conf.cid, sizeof conf.cid,
@@ -318,8 +328,10 @@
 void
 main(int argc, char **argv)
 {
+	uchar ip[IPaddrlen];
 	int action;
 	Ctl *cp;
+	char *s;
 
 	init();
 	ARGBEGIN {
@@ -384,6 +396,11 @@
 	case 'r':
 		rflag = 1;
 		break;
+	case 's':
+		if(parseip(ip, EARGF(usage())) == -1)
+			usage();
+		addaddrs(conf.dns, sizeof(conf.dns), ip, sizeof(ip));
+		break;
 	case 't':
 		tflag = 1;
 		break;
@@ -390,6 +407,12 @@
 	case 'u':		/* IPv6: duplicate neighbour disc. off */
 		dupl_disc = 0;
 		break;
+	case 'U':		/* device unique id used for dhcpv6 */
+		s = EARGF(usage());
+		conf.duidlen = dec16(conf.duid, sizeof(conf.duid), s, strlen(s));
+		if(conf.duidlen <= 0)
+			usage();
+		break;
 	case 'x':
 		setnetmtpt(conf.mpoint, sizeof conf.mpoint, EARGF(usage()));
 		break;
@@ -399,13 +422,13 @@
 	default:
 		usage();
 	} ARGEND;
-	argv0 = "ipconfig";		/* boot invokes us as tcp? */
 
 	action = parseargs(argc, argv);
 
-	if(beprimary == -1 && (ipv6auto || parseverb(conf.type) == Vloopback))
+	if(beprimary == -1 && (ipv6auto || ISIPV6LINKLOCAL(conf.laddr) || parseverb(conf.type) == Vloopback))
 		beprimary = 0;
 
+	openiproute();
 	myifc = findifc(conf.mpoint, conf.dev);
 	if(myifc < 0) {
 		switch(action){
@@ -416,9 +439,15 @@
 			controldevice();
 			binddevice();
 			myifc = findifc(conf.mpoint, conf.dev);
-		case Vremove:
 		case Vunbind:
 			break;
+		case Vremove:
+			/*
+			 * interface gone, just remove
+			 * default route and ndb entries.
+			 */
+			doremove();
+			exits(nil);
 		}
 		if(myifc < 0)
 			sysfatal("interface not found for: %s", conf.dev);
@@ -478,6 +507,7 @@
 		} else
 			sysfatal("no success with DHCP");
 	}
+
 	DEBUG("adding address %I %M on %s", conf.laddr, conf.mask, conf.dev);
 	if(noconfig)
 		return;
@@ -493,7 +523,7 @@
 	}
 
 	/* leave everything we've learned somewhere other procs can find it */
-	putndb();
+	putndb(1);
 	refresh();
 }
 
@@ -504,11 +534,18 @@
 		sysfatal("remove requires an address");
 
 	DEBUG("removing address %I %M on %s", conf.laddr, conf.mask, conf.dev);
-	if(conf.cfd < 0)
+	if(noconfig)
 		return;
 
-	if(fprint(conf.cfd, "remove %I %M", conf.laddr, conf.mask) < 0)
+	if(validip(conf.gaddr))
+		removedefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
+
+	if(conf.cfd >= 0 && fprint(conf.cfd, "remove %I %M", conf.laddr, conf.mask) < 0)
 		warning("can't remove %I %M: %r", conf.laddr, conf.mask);
+
+	/* remove ndb entries matching our ip address */
+	putndb(0);
+	refresh();
 }
 
 static void
@@ -521,6 +558,15 @@
 		warning("can't unbind %s: %r", conf.dev);
 }
 
+static void
+openiproute(void)
+{
+	char buf[127];
+
+	snprint(buf, sizeof buf, "%s/iproute", conf.mpoint);
+	conf.rfd = open(buf, OWRITE);
+}
+
 /* send some ctls to a device */
 static void
 controldevice(void)
@@ -570,8 +616,6 @@
 		if(fprint(conf.cfd, "bind %s %s", conf.type, conf.dev) < 0)
 			sysfatal("%s: bind %s %s: %r", buf, conf.type, conf.dev);
 	}
-	snprint(buf, sizeof buf, "%s/iproute", conf.mpoint);
-	conf.rfd = open(buf, OWRITE);
 }
 
 /* add a logical interface to the ip stack */
@@ -624,9 +668,6 @@
 	if(!validip(conf.mask))
 		ipmove(conf.mask, defmask(conf.laddr));
 
-	if(validip(conf.gaddr))
-		removedefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
-
 	doremove();
 
 	ipmove(conf.laddr, IPnoaddr);
@@ -671,7 +712,7 @@
 
 /* make an ndb entry and put it into /net/ndb for the servers to see */
 void
-putndb(void)
+putndb(int doadd)
 {
 	static char buf[16*1024];
 	char file[64], *p, *e, *np;
@@ -684,32 +725,35 @@
 
 	p = buf;
 	e = buf + sizeof buf;
-	p = seprint(p, e, "ip=%I ipmask=%M ipgw=%I\n",
-		conf.laddr, conf.mask, conf.gaddr);
-	if(np = strchr(conf.hostname, '.')){
-		if(*conf.domainname == 0)
-			strcpy(conf.domainname, np+1);
-		*np = 0;
+
+	if(doadd){
+		p = seprint(p, e, "ip=%I ipmask=%M ipgw=%I\n",
+			conf.laddr, conf.mask, conf.gaddr);
+		if(np = strchr(conf.hostname, '.')){
+			if(*conf.domainname == 0)
+				strcpy(conf.domainname, np+1);
+			*np = 0;
+		}
+		if(*conf.hostname)
+			p = seprint(p, e, "\tsys=%U\n", conf.hostname);
+		if(*conf.domainname)
+			p = seprint(p, e, "\tdom=%U.%U\n",
+				conf.hostname, conf.domainname);
+		if(*conf.dnsdomain)
+			p = putnames(p, e, "\tdnsdomain", conf.dnsdomain);
+		if(validip(conf.dns))
+			p = putaddrs(p, e, "\tdns", conf.dns, sizeof conf.dns);
+		if(validip(conf.fs))
+			p = putaddrs(p, e, "\tfs", conf.fs, sizeof conf.fs);
+		if(validip(conf.auth))
+			p = putaddrs(p, e, "\tauth", conf.auth, sizeof conf.auth);
+		if(validip(conf.ntp))
+			p = putaddrs(p, e, "\tntp", conf.ntp, sizeof conf.ntp);
+		if(ndboptions)
+			p = seprint(p, e, "%s\n", ndboptions);
 	}
-	if(*conf.hostname)
-		p = seprint(p, e, "\tsys=%U\n", conf.hostname);
-	if(*conf.domainname)
-		p = seprint(p, e, "\tdom=%U.%U\n",
-			conf.hostname, conf.domainname);
-	if(*conf.dnsdomain)
-		p = putnames(p, e, "\tdnsdomain", conf.dnsdomain);
-	if(validip(conf.dns))
-		p = putaddrs(p, e, "\tdns", conf.dns, sizeof conf.dns);
-	if(validip(conf.fs))
-		p = putaddrs(p, e, "\tfs", conf.fs, sizeof conf.fs);
-	if(validip(conf.auth))
-		p = putaddrs(p, e, "\tauth", conf.auth, sizeof conf.auth);
-	if(validip(conf.ntp))
-		p = putaddrs(p, e, "\tntp", conf.ntp, sizeof conf.ntp);
-	if(ndboptions)
-		p = seprint(p, e, "%s\n", ndboptions);
 
-	/* append preexisting entries not matching our ip */
+	/* write preexisting entries not matching our ip */
 	snprint(file, sizeof file, "%s/ndb", conf.mpoint);
 	db = ndbopen(file);
 	if(db != nil ){
@@ -728,7 +772,6 @@
 		}
 		ndbclose(db);
 	}
-
 	if((fd = open(file, OWRITE|OTRUNC)) < 0)
 		return;
 	write(fd, buf, p-buf);
--- a/sys/src/cmd/ip/ipconfig/mkfile
+++ b/sys/src/cmd/ip/ipconfig/mkfile
@@ -5,6 +5,7 @@
 OFILES=\
 	main.$O\
 	dhcp.$O\
+	dhcpv6.$O\
 	ipv6.$O\
 	ppp.$O\