code: plan9front

Download patch

ref: 16656468211681edad2c8489b7d5fc9a8e219545
parent: cbe4b116989c0bfec49b9ae7a1d9a85ead6648ae
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun May 14 17:24:47 EDT 2023

ip/ppp: ipv6 support, cleanup routes, remove source filter, and more

establish ipv6 link-local addresses if supported.
to get a full prefix, one would need to do dhcpv6
or using slaac configuration. this will come later.

remove ipv4 default route on exit.

maintain the entries in /net/ndb on ip change and
also remove them on exit.

dont filter source address, this should be done
differently.

fix various bugs in the option iteration loops.

honour primary flag (it used to just ignore that
and set it unasked in the code... what the hell...)

--- a/sys/man/8/ppp
+++ b/sys/man/8/ppp
@@ -104,8 +104,7 @@
 can run either as a client or, with the
 .I \-S
 option, as a server.  The only differences between a client and a server is
-that the server will not believe any local address the client tries to
-supply it and that the server always initiates the authentication of the
+that the server always initiates the authentication of the
 client.
 .PP
 With no option,
--- a/sys/src/cmd/ip/ppp/ppp.c
+++ b/sys/src/cmd/ip/ppp/ppp.c
@@ -18,7 +18,6 @@
 static	int	noipcompress;
 static	int	server;
 static	int noauth;
-static	int	nip;		/* number of ip interfaces */
 static	int	dying;		/* flag to signal to all threads its time to go */
 static	int	primary;	/* this is the primary IP interface */
 static	char	*chatfile;
@@ -27,11 +26,6 @@
 char*	LOG = "ppp";
 char*	keyspec = "";
 
-enum
-{
-	Rmagic=	0x12345
-};
-
 /*
  * Calculate FCS - rfc 1331
  */
@@ -84,6 +78,7 @@
 static	void		authtimer(PPP*);
 static	void		chapinit(PPP*);
 static	void		config(PPP*, Pstate*, int);
+static	int		euitov6(Ipaddr, uchar*, int);
 static	uchar*		escapeuchar(PPP*, ulong, uchar*, ushort*);
 static	void		getchap(PPP*, Block*);
 static	Block*		getframe(PPP*, int*);
@@ -96,7 +91,6 @@
 static	char*		ipopen(PPP*);
 static	void		mediainproc(PPP*);
 static	void		newstate(PPP*, Pstate*, int);
-static	int		nipifcs(char*);
 static	void		papinit(PPP*);
 static	void		pinit(PPP*, Pstate*);
 static	void		ppptimer(PPP*);
@@ -104,7 +98,8 @@
 static	void		ptimer(PPP*, Pstate*);
 static	int		putframe(PPP*, int, Block*);
 static	void		putlqm(PPP*);
-static	void		putndb(PPP*, char*);
+static	void		putndb(PPP*);
+static	void		refresh(char*);
 static	void		putpaprequest(PPP*);
 static	void		rcv(PPP*, Pstate*, Block*);
 static	void		rejopts(PPP*, Pstate*, Block*, int);
@@ -111,8 +106,9 @@
 static	void		sendechoreq(PPP*, Pstate*);
 static	void		sendtermreq(PPP*, Pstate*);
 static	void		setphase(PPP*, int);
-static	void		terminate(PPP*, char *);
+static	void		terminate(PPP*, char*, int);
 static	int		validv4(Ipaddr);
+static	int		validv6(Ipaddr);
 static  void		dmppkt(char *s, uchar *a, int na);
 
 void
@@ -131,15 +127,26 @@
 	invalidate(ppp->wins[0]);
 	invalidate(ppp->wins[1]);
 
+	invalidate(ppp->local6);
+	invalidate(ppp->remote6);
+	invalidate(ppp->curremote6);
+	invalidate(ppp->curlocal6);
+
 	ppp->mediain = mediain;
 	ppp->mediaout = mediaout;
 	if(validv4(remip)){
 		ipmove(ppp->remote, remip);
 		ppp->remotefrozen = 1;
+	} else if(validv6(remip)){
+		ipmove(ppp->remote6, remip);
+		ppp->remote6frozen = 1;
 	}
 	if(validv4(ipaddr)){
 		ipmove(ppp->local, ipaddr);
 		ppp->localfrozen = 1;
+	} else if(validv6(ipaddr)){
+		ipmove(ppp->local6, ipaddr);
+		ppp->local6frozen = 1;
 	}
 	ppp->mtu = Defmtu;
 	ppp->mru = mtu;
@@ -149,10 +156,10 @@
 	init(ppp);
 	switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
 	case -1:
-		sysfatal("forking mediainproc");
+		terminate(ppp, "forking mediainproc", 1);
 	case 0:
 		mediainproc(ppp);
-		terminate(ppp, "mediainproc");
+		terminate(ppp, "mediainproc", 0);
 		exits(nil);
 	}
 }
@@ -187,6 +194,12 @@
 		ppp->ipcp->proto = Pipcp;
 		ppp->ipcp->state = Sclosed;
 
+		ppp->ipv6cp = mallocz(sizeof(*ppp->ipv6cp), 1);
+		if(ppp->ipv6cp == nil)
+			abort();
+		ppp->ipv6cp->proto = Pipv6cp;
+		ppp->ipv6cp->state = Sclosed;
+
 		ppp->chap = mallocz(sizeof(*ppp->chap), 1);
 		if(ppp->chap == nil)
 			abort();
@@ -197,7 +210,7 @@
 
 		switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
 		case -1:
-			sysfatal("forking ppptimer");
+			terminate(ppp, "forking ppptimer", 1);
 		case 0:
 			ppptimer(ppp);
 			exits(nil);
@@ -219,9 +232,9 @@
 	ppp->phase = phase;
 	switch(phase){
 	default:
-		sysfatal("ppp: unknown phase %d", phase);
+		terminate(ppp, "phase error", 1);
 	case Pdead:
-		terminate(ppp, "protocol");
+		terminate(ppp, "protocol", 0);
 		break;
 	case Plink:
 		/* link down */
@@ -237,6 +250,7 @@
 			ppp->chap->state = Cunauth;
 			newstate(ppp, ppp->ccp, Sclosed);
 			newstate(ppp, ppp->ipcp, Sclosed);
+			newstate(ppp, ppp->ipv6cp, Sclosed);
 		}
 		break;
 	case Pauth:
@@ -260,6 +274,7 @@
 	case Pnet:
 		pinit(ppp, ppp->ccp);
 		pinit(ppp, ppp->ipcp);
+		pinit(ppp, ppp->ipv6cp);
 		break;
 	}
 }
@@ -288,6 +303,8 @@
 			p->optmask &= ~Fpc;
 			ppp->ipcp->optmask &= ~Fipcompress;
 		}
+		ppp->ipv6cp->state = Sclosed;
+		ppp->ipv6cp->optmask = 0xffffffff;
 		p->echoack = 0;
 		p->echotimeout = 0;
 
@@ -320,6 +337,9 @@
 		p->optmask = 0xffffffff;
 		ppp->ctcp = compress_init(ppp->ctcp);
 		break;
+	case Pipv6cp:
+		p->optmask = 0xffffffff;
+		break;
 	}
 	p->confid = p->rcvdconfid = -1;
 	config(ppp, p, 1);
@@ -367,13 +387,13 @@
 		}
 	}
 
-	if(p->proto == Pipcp && state == Sopened) {
+	if((p->proto == Pipcp || p->proto == Pipv6cp) && state == Sopened) {
 		if(server && !noauth && ppp->chap->state != Cauthok)
 			abort();
 
 		err = ipopen(ppp);
 		if(err != nil)
-			sysfatal("%s", err);
+			terminate(ppp, err, 1);
 	}
 
 	p->state = state;
@@ -653,6 +673,15 @@
 	*b->wptr++ = 2;
 }
 
+static void
+putoeui64(Block *b, int type, uchar data[8])
+{
+	*b->wptr++ = type;
+	*b->wptr++ = 8+2;
+	memmove(b->wptr, data, 8);
+	b->wptr += 8;
+}
+
 /*
  *  send configuration request
  */
@@ -706,17 +735,16 @@
 		break;
 	case Pipcp:
 		if(p->optmask & Fipaddr){
-			syslog(0, LOG, "requesting %I", ppp->local);
+			syslog(0, LOG, "requesting IPv4 %I", ppp->local);
 			putv4o(b, Oipaddr, ppp->local);
 		}
-		primary = 1;
-		if(primary && (p->optmask & Fipdns))
+		if(p->optmask & Fipdns)
 			putv4o(b, Oipdns, ppp->dns[0]);
-		if(primary && (p->optmask & Fipdns2))
+		if(p->optmask & Fipdns2)
 			putv4o(b, Oipdns2, ppp->dns[1]);
-		if(primary && (p->optmask & Fipwins))
+		if(p->optmask & Fipwins)
 			putv4o(b, Oipwins, ppp->wins[0]);
-		if(primary && (p->optmask & Fipwins2))
+		if(p->optmask & Fipwins2)
 			putv4o(b, Oipwins2, ppp->wins[1]);
 		/*
 		 * don't ask for header compression while data compression is still pending.
@@ -731,6 +759,12 @@
 			*b->wptr++ = 1;
 		}
 		break;
+	case Pipv6cp:
+		if(p->optmask & Fipv6eui){
+			syslog(0, LOG, "requesting IPv6 %I", ppp->local6);
+			putoeui64(b, Oipv6eui, &ppp->local6[8]);
+		}
+		break;
 	}
 	hnputs(m->len, BLEN(b));
 	printopts(p, b, 1);
@@ -785,7 +819,7 @@
 	ulong x;
 	Block *repb;
 	Comptype *ctype;
-	Ipaddr ipaddr;
+	Ipaddr ipaddr, ipaddr6;
 
 	rejecting = 0;
 	nacking = 0;
@@ -793,6 +827,7 @@
 
 	/* defaults */
 	invalidate(ipaddr);
+	invalidate(ipaddr6);
 	mtu = ppp->mtu;
 	ctlmap = 0xffffffff;
 	period = 0;
@@ -809,7 +844,7 @@
 	/* look for options we don't recognize or like */
 	for(cp = m->data; cp < b->wptr; cp += o->len){
 		o = (Lcpopt*)cp;
-		if(cp + o->len > b->wptr || o->len==0){
+		if(cp + o->len > b->wptr || o->len < 2){
 			freeb(repb);
 			netlog("ppp: bad option length %ux\n", o->type);
 			return -1;
@@ -965,6 +1000,24 @@
 				continue;
 			}
 			break;
+		case Pipv6cp:
+			switch(o->type){
+			case Oipv6eui:	
+				euitov6(ipaddr6, o->data, o->len-2);
+				if(!validv6(ppp->remote6))
+					continue;
+				if(!validv6(ipaddr6) && !rejecting){
+					/* other side requesting an address */
+					if(!nacking){
+						nacking = 1;
+						repb->wptr = repm->data;
+						repm->code = Lconfnak;
+					}
+					putoeui64(repb, Oipv6eui, &ppp->remote6[8]);
+				}
+				continue;
+			}
+			break;
 		}
 
 		/* come here if option is not recognized */
@@ -1006,6 +1059,10 @@
 			if(validv4(ipaddr) && ppp->remotefrozen == 0)
  				ipmove(ppp->remote, ipaddr);
 			break;
+		case Pipv6cp:
+			if(validv6(ipaddr6) && ppp->remote6frozen == 0)
+ 				ipmove(ppp->remote6, ipaddr6);
+			break;
 		}
 		p->flags = flags;
 	}
@@ -1034,28 +1091,36 @@
 static void
 dropoption(Pstate *p, Lcpopt *o)
 {
-	unsigned n = o->type;
-
-	switch(n){
-	case Oipaddr:
+	switch(p->proto){
+	case Pipcp:
+		switch(o->type){
+		case Oipaddr:
+			/* never drop it */
+			return;
+		case Oipdns:
+			p->optmask &= ~Fipdns;
+			return;
+		case Oipwins:
+			p->optmask &= ~Fipwins;
+			return;
+		case Oipdns2:
+			p->optmask &= ~Fipdns2;
+			return;
+		case Oipwins2:
+			p->optmask &= ~Fipwins2;
+			return;
+		}
 		break;
-	case Oipdns:
-		p->optmask &= ~Fipdns;
+	case Pipv6cp:
+		switch(o->type){
+		case Oipv6eui:
+			/* never drop it */
+			return;
+		}
 		break;
-	case Oipwins:
-		p->optmask &= ~Fipwins;
-		break;
-	case Oipdns2:
-		p->optmask &= ~Fipdns2;
-		break;
-	case Oipwins2:
-		p->optmask &= ~Fipwins2;
-		break;
-	default:
-		if(o->type < 8*sizeof(p->optmask))
-			p->optmask &= ~(1<<o->type);
-		break;
 	}
+	if(o->type < 8*sizeof(p->optmask))
+		p->optmask &= ~(1<<o->type);
 }
 
 /*
@@ -1067,13 +1132,13 @@
 {
 	Lcpmsg *m;
 	Lcpopt *o;
-	uchar newip[IPaddrlen];
+	Ipaddr newip;
 
 	/* just give up trying what the other side doesn't like */
 	m = (Lcpmsg*)b->rptr;
 	for(b->rptr = m->data; b->rptr < b->wptr; b->rptr += o->len){
 		o = (Lcpopt*)b->rptr;
-		if(b->rptr + o->len > b->wptr){
+		if(b->rptr + o->len > b->wptr || o->len < 2){
 			netlog("ppp: bad roption length %ux\n", o->type);
 			return;
 		}
@@ -1094,12 +1159,10 @@
 			case Oauth:
 				/* don't allow client to request no auth */
 				/* could try different auth protocol here */
-				fprint(2, "ppp: can not reject CHAP\n");
-				exits("ppp: CHAP");
-				break;
+				terminate(ppp, "chap rejected", 0);
+				return;
 			default:
-				if(o->type < 8*sizeof(p->optmask))
-					p->optmask &= ~(1<<o->type);
+				dropoption(p, o);
 				break;
 			};
 			break;
@@ -1113,7 +1176,8 @@
 		case Pipcp:
 			switch(o->type){
 			case Oipaddr:
-				syslog(0, LOG, "rejected addr %I with %V", ppp->local, o->data);
+				v4tov6(newip, o->data);
+				syslog(0, LOG, "rejected IPv4 addr %I with %I", ppp->local, newip);
 				/* if we're a server, don't let other end change our addr */
 				if(ppp->localfrozen){
 					dropoption(p, o);
@@ -1122,13 +1186,12 @@
 
 				/* accept whatever server tells us */
 				if(!validv4(ppp->local)){
-					v4tov6(ppp->local, o->data);
+					ipmove(ppp->local, newip);
 					dropoption(p, o);
 					break;
 				}
 
 				/* if he didn't like our addr, ask for a generic one */
-				v4tov6(newip, o->data);
 				if(!validv4(newip)){
 					invalidate(ppp->local);
 					break;
@@ -1135,7 +1198,7 @@
 				}
 
 				/* if he gives us something different, use it anyways */
-				v4tov6(ppp->local, o->data);
+				ipmove(ppp->local, newip);
 				dropoption(p, o);
 				break;
 			case Oipdns:
@@ -1199,6 +1262,36 @@
 				break;
 			}
 			break;
+		case Pipv6cp:
+			switch(o->type){
+			case Oipv6eui:
+				euitov6(newip, o->data, o->len-2);
+				syslog(0, LOG, "rejected IPv6 addr %I with %I", ppp->local6, newip);
+
+				/* if we're a server, don't let other end change our addr */
+				if(ppp->local6frozen){
+					dropoption(p, o);
+					break;
+				}
+				/* accept whatever server tells us */
+				if(!validv6(ppp->local6)){
+					ipmove(ppp->local6, newip);
+					dropoption(p, o);
+					break;
+				}
+				/* if he didn't like our addr, ask for a generic one */
+				if(!validv6(newip)){
+					invalidate(ppp->local6);
+					break;
+				}
+				/* if he gives us something different, use it anyways */
+				ipmove(ppp->local6, newip);
+				dropoption(p, o);
+				break;
+			default:
+				dropoption(p, o);
+				break;
+			}
 		}
 	}
 }
@@ -1316,6 +1409,8 @@
 		case Sacksent:
 			printopts(p, b, 0);
 			rejopts(ppp, p, b, m->code);
+			if(dying)
+				break;
 			config(ppp, p, 1);
 			break;
 		}
@@ -1453,7 +1548,7 @@
 		putpaprequest(ppp);
 	else {
 		netlog("ppp: pap timed out--not authorized\n");
-		terminate(ppp, "pap timeout");
+		terminate(ppp, "pap timeout", 0);
 	}
 }
 
@@ -1464,7 +1559,7 @@
 		ppp->lcp->echotimeout = (3*4*1000+Period-1)/Period;
 	else if(--(ppp->lcp->echotimeout) <= 0){
 		netlog("ppp: echo request timeout\n");
-		terminate(ppp, "echo timeout");
+		terminate(ppp, "echo timeout", 0);
 		return;
 	}
 	ppp->lcp->echoack = 0;
@@ -1489,6 +1584,7 @@
 			case Pnet:
 				ptimer(ppp, ppp->ccp);
 				ptimer(ppp, ppp->ipcp);
+				ptimer(ppp, ppp->ipv6cp);
 
 				pingtimer(ppp);
 				break;
@@ -1511,20 +1607,52 @@
 static void
 defroute(char *net, char *verb, Ipaddr gate, Ipaddr local)
 {
-	int fd;
 	char path[128];
+	int fd;
 
 	snprint(path, sizeof path, "%s/iproute", net);
 	fd = open(path, ORDWR);
 	if(fd < 0)
 		return;
-	fprint(fd, "tag ppp");
-	if(primary)
-		fprint(fd, "%s 0.0.0.0 0.0.0.0 %I", verb, gate);
-	fprint(fd, "%s 0.0.0.0 0.0.0.0 %I %I 255.255.255.255", verb, gate, local);
+	fprint(fd, "tag ppp\n");
+	fprint(fd, "%s 0.0.0.0 0.0.0.0 %I %I 255.255.255.255\n", verb, gate, local);
 	close(fd);
 }
 
+static int
+addip(int cfd, char *net, Ipaddr local, Ipaddr remote, int mtu)
+{
+	if(validv4(local) && validv4(remote)){
+		netlog("ppp: setting up IPv4 interface local %I remote %I\n", local, remote);
+
+		if(fprint(cfd, "add %I 255.255.255.255 %I %d proxy", local, remote, mtu-10) < 0)
+			return -1;
+
+		defroute(net, "add", remote, local);
+	} else if(validv6(local) && validv6(remote)){
+		netlog("ppp: setting up IPv6 interface local %I remote %I\n", local, remote);
+
+		if(fprint(cfd, "add %I /64 %I %d", local, remote, mtu-10) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+static int
+removeip(int cfd, char *net, Ipaddr local, Ipaddr remote)
+{
+	if(validv4(local) && validv4(remote)){
+		defroute(net, "remove", remote, local);
+
+		if(fprint(cfd, "remove %I 255.255.255.255 %I", local, remote) < 0)
+			return -1;
+	} else if(validv6(local) && validv6(remote)){
+		if(fprint(cfd, "remove %I /64 %I", local, remote) < 0)
+			return -1;
+	}
+	return 0;
+}
+
 static char*
 ipopen(PPP *ppp)
 {
@@ -1545,12 +1673,6 @@
 			return "can't open ip interface";
 		}
 		buf[n] = 0;
-
-		netlog("ppp: setting up IP interface local %I remote %I (valid %d)\n",
-			ppp->local, ppp->remote, validv4(ppp->remote));
-		if(!validv4(ppp->remote))
-			ipmove(ppp->remote, ppp->local);
-
 		snprint(path, sizeof path, "%s/ipifc/%s/data", ppp->net, buf);
 		fd = open(path, ORDWR);
 		if(fd < 0){
@@ -1557,51 +1679,71 @@
 			close(cfd);
 			return "can't open ip interface";
 		}
-
-		if(fprint(cfd, "bind pkt") < 0)
-			return "binding pkt to ip interface";
-		if(fprint(cfd, "add %I 255.255.255.255 %I %lud proxy", ppp->local,
-			ppp->remote, ppp->mtu-10) < 0){
+		if(fprint(cfd, "bind pkt") < 0){
+			close(fd);
 			close(cfd);
-			return "can't set addresses";
+			return "binding pkt to ip interface";
 		}
+		if(validv4(ppp->local)){
+			if(!validv4(ppp->remote))
+				ipmove(ppp->remote, ppp->local);
+			if(addip(cfd, ppp->net, ppp->local, ppp->remote, ppp->mtu) < 0){
+				close(fd);
+				close(cfd);
+				return "can't set addresses";
+			}
+			syslog(0, LOG, "%I/%I", ppp->local, ppp->remote);
+		}
+		if(validv6(ppp->local6) && validv6(ppp->remote6)){
+			if(addip(cfd, ppp->net, ppp->local6, ppp->remote6, ppp->mtu) < 0){
+				close(fd);
+				close(cfd);
+				return "can't set addresses";
+			}
+			syslog(0, LOG, "%I/%I", ppp->local6, ppp->remote6);
+		}
 		if(baud)
 			fprint(cfd, "speed %d", baud);
-		defroute(ppp->net, "add", ppp->remote, ppp->local);
 		ppp->ipfd = fd;
 		ppp->ipcfd = cfd;
+		putndb(ppp);
+		refresh(ppp->net);
 
 		/* signal main() that ip is configured */
-		rendezvous((void*)Rmagic, 0);
+		rendezvous(ppp, 0);
 
 		switch(ipinprocpid = rfork(RFPROC|RFMEM|RFNOWAIT)){
 		case -1:
-			sysfatal("forking ipinproc");
+			terminate(ppp, "forking ipinproc", 1);
 		case 0:
 			ipinproc(ppp);
-			terminate(ppp, "ipinproc");
+			terminate(ppp, "ipinproc", 0);
 			exits(nil);
 		}
 	} else {
 		/* we may have changed addresses */
-		if(ipcmp(ppp->local, ppp->curlocal) != 0 ||
-		   ipcmp(ppp->remote, ppp->curremote) != 0){
-			defroute(ppp->net, "remove", ppp->curremote, ppp->curlocal);
-			snprint(buf, sizeof buf, "remove %I 255.255.255.255 %I",
-			    ppp->curlocal, ppp->curremote);
-			if(fprint(ppp->ipcfd, "%s", buf) < 0)
-				syslog(0, LOG, "can't %s: %r", buf);
-			snprint(buf, sizeof buf, "add %I 255.255.255.255 %I %lud proxy",
-			    ppp->local, ppp->remote, ppp->mtu-10);
-			if(fprint(ppp->ipcfd, "%s", buf) < 0)
-				syslog(0, LOG, "can't %s: %r", buf);
-			defroute(ppp->net, "add", ppp->remote, ppp->local);
+		if(ipcmp(ppp->local, ppp->curlocal) != 0 || ipcmp(ppp->remote, ppp->curremote) != 0){
+			removeip(ppp->ipcfd, ppp->net, ppp->curlocal, ppp->curremote);
+			addip(ppp->ipcfd, ppp->net, ppp->local, ppp->remote, ppp->mtu);
+
+			syslog(0, LOG, "%I/%I -> %I/%I", ppp->curlocal, ppp->curremote,
+				ppp->local, ppp->remote);
 		}
-		syslog(0, LOG, "%I/%I -> %I/%I", ppp->curlocal, ppp->curremote,
-		   ppp->local, ppp->remote);
+		if(ipcmp(ppp->local6, ppp->curlocal6) != 0 || ipcmp(ppp->remote6, ppp->curremote6) != 0){
+			removeip(ppp->ipcfd, ppp->net, ppp->curlocal6, ppp->curremote6);
+			addip(ppp->ipcfd, ppp->net, ppp->local6, ppp->remote6, ppp->mtu);
+
+			syslog(0, LOG, "%I/%I -> %I/%I", ppp->curlocal6, ppp->curremote6,
+				ppp->local6, ppp->remote6);
+		}
+		putndb(ppp);
+		refresh(ppp->net);
 	}
+
 	ipmove(ppp->curlocal, ppp->local);
 	ipmove(ppp->curremote, ppp->remote);
+	ipmove(ppp->curlocal6, ppp->local6);
+	ipmove(ppp->curremote6, ppp->remote6);
 
 	return nil;
 }
@@ -1630,11 +1772,20 @@
 		case Pipcp:
 			rcv(ppp, ppp->ipcp, b);
 			break;
+		case Pipv6cp:
+			rcv(ppp, ppp->ipv6cp, b);
+			break;
 		case Pip:
 			if(ppp->ipcp->state == Sopened)
 				return b;
+			freeb(b);
 			netlog("ppp: IP recved: link not up\n");
+			break;
+		case Pipv6:
+			if(ppp->ipv6cp->state == Sopened)
+				return b;
 			freeb(b);
+			netlog("ppp: IPv6 recved: link not up\n");
 			break;
 		case Plqm:
 			getlqm(ppp, b);
@@ -1703,38 +1854,75 @@
 	return nil;
 }
 
+typedef struct Iphdr Iphdr;
+struct Iphdr
+{
+	uchar	vihl;		/* Version and header length */
+	uchar	tos;		/* Type of service */
+	uchar	length[2];	/* packet length */
+	uchar	id[2];		/* Identification */
+	uchar	frag[2];	/* Fragment information */
+	uchar	ttl;		/* Time to live */
+	uchar	proto;		/* Protocol */
+	uchar	cksum[2];	/* Header checksum */
+	uchar	src[4];		/* Ip source (uchar ordering unimportant) */
+	uchar	dst[4];		/* Ip destination (uchar ordering unimportant) */
+};
+
 /* transmit an IP packet */
 int
 pppwrite(PPP *ppp, Block *b)
 {
 	int proto;
-	int len;
+	int len, tot;
+	Iphdr *ip;
+	Ip6hdr *ip6;
 
-	qlock(ppp);
-	/* can't send ip packets till we're established */
-	if(ppp->ipcp->state != Sopened) {
-		qunlock(ppp);
-		syslog(0, LOG, "IP write: link not up");
-		len = blen(b);
+	len = blen(b);
+	ip = (Iphdr*)b->rptr;
+	switch(ip->vihl & 0xF0){
+	default:
+	Badhdr:
 		freeb(b);
 		return len;
-	}
+	case IP_VER4:
+		if(len < IPV4HDR_LEN || (tot = nhgets(ip->length)) < IPV4HDR_LEN)
+			goto Badhdr;
+		b = btrim(b, 0, tot);
+		if(b == nil)
+			return len;
 
-	proto = Pip;
-	ppp->stat.ipsend++;
-
-	if(ppp->ipcp->flags & Fipcompress){
-		b = compress(ppp->ctcp, b, &proto);
-		if(b == nil){
-			qunlock(ppp);
-			return 0;
+		qlock(ppp);
+		if(ppp->ipcp->state != Sopened)
+			goto Drop;
+		proto = Pip;
+		if(ppp->ipcp->flags & Fipcompress){
+			b = compress(ppp->ctcp, b, &proto);
+			if(b == nil){
+				qunlock(ppp);
+				return len;
+			}
+			if(proto != Pip)
+				ppp->stat.vjout++;
 		}
-		if(proto != Pip)
-			ppp->stat.vjout++;
+		break;
+	case IP_VER6:
+		if(len < IPV6HDR_LEN)
+			goto Badhdr;
+		ip6 = (Ip6hdr*)ip;
+		b = btrim(b, 0, IPV6HDR_LEN + nhgets(ip6->ploadlen));
+		if(b == nil)
+			return len;
+
+		qlock(ppp);
+		if(ppp->ipv6cp->state != Sopened)
+			goto Drop;
+		proto = Pipv6;
+		break;
 	}
+	ppp->stat.ipsend++;
 
 	if(ppp->ctype != nil) {
-		len = blen(b);
 		b = (*ppp->ctype->compress)(ppp, proto, b, &proto);
 		if(proto == Pcdata) {
 			ppp->stat.comp++;
@@ -1748,15 +1936,14 @@
 		freeb(b);
 		return -1;
 	}
+Drop:
 	qunlock(ppp);
-
-	len = blen(b);
 	freeb(b);
 	return len;
 }
 
 static void
-terminate(PPP *ppp, char *why)
+terminate(PPP *ppp, char *why, int fatal)
 {
 	if(dying++)
 		return;
@@ -1763,8 +1950,12 @@
 
 	syslog(0, LOG, "ppp: terminated: %s", why);
 
-	if(ppp->net != nil && validv4(ppp->curremote) && validv4(ppp->curlocal))
+	if(validv4(ppp->curremote) && validv4(ppp->curlocal)){
 		defroute(ppp->net, "remove", ppp->curremote, ppp->curlocal);
+		invalidate(ppp->remote);
+		invalidate(ppp->local);
+		putndb(ppp);
+	}
 
 	close(ppp->ipfd);
 	ppp->ipfd = -1;
@@ -1775,44 +1966,28 @@
 	ppp->mediain = -1;
 	ppp->mediaout = -1;
 	postnote(PNGROUP, getpid(), "die");
-}
 
-typedef struct Iphdr Iphdr;
-struct Iphdr
-{
-	uchar	vihl;		/* Version and header length */
-	uchar	tos;		/* Type of service */
-	uchar	length[2];	/* packet length */
-	uchar	id[2];		/* Identification */
-	uchar	frag[2];	/* Fragment information */
-	uchar	ttl;		/* Time to live */
-	uchar	proto;		/* Protocol */
-	uchar	cksum[2];	/* Header checksum */
-	uchar	src[4];		/* Ip source (uchar ordering unimportant) */
-	uchar	dst[4];		/* Ip destination (uchar ordering unimportant) */
-};
+	/* interface gone, notify cs */
+	refresh(ppp->net);
 
+	if(fatal)
+		sysfatal("%s", why);
+}
+
 static void
 ipinproc(PPP *ppp)
 {
 	Block *b;
-	int m, n;
-	Iphdr *ip;
+	int n;
 
 	while(!dying){
-
 		b = allocb(Buflen);
 		n = read(ppp->ipfd, b->wptr, b->lim-b->wptr);
-		if(n < 0)
+		if(n < 1){
+			freeb(b);
 			break;
-
-		/* trim packet if there's padding (e.g. from ether) */
-		ip = (Iphdr*)b->rptr;
-		m = nhgets(ip->length);
-		if(m < n && m > 0)
-			n = m;
+		}
 		b->wptr += n;
-
 		if(pppwrite(ppp, b) < 0)
 			break;
 	}
@@ -1855,7 +2030,6 @@
 mediainproc(PPP *ppp)
 {
 	Block *b;
-	Ipaddr remote;
 
 	notify(catchdie);
 	while(!dying){
@@ -1865,20 +2039,6 @@
 			break;
 		}
 		ppp->stat.iprecv++;
-		if(ppp->ipcp->state != Sopened) {
-			ppp->stat.iprecvnotup++;
-			freeb(b);
-			continue;
-		}
-
-		if(server) {
-			v4tov6(remote, b->rptr+12);
-			if(ipcmp(remote, ppp->remote) != 0) {
-				ppp->stat.iprecvbadsrc++;
-				freeb(b);
-				continue;
-			}
-		}
 		if(debug > 1){
 			netlog("ip write pkt %p %d\n", b->rptr, blen(b));
 			hexdump(b->rptr, blen(b));
@@ -1888,12 +2048,11 @@
 			freeb(b);
 			break;
 		}
-
 		freeb(b);
 	}
 
-	netlog(": remote=%I: ppp shutting down\n", ppp->remote);
-	syslog(0, LOG, ": remote=%I: ppp shutting down", ppp->remote);
+	netlog(": remote=%I/%I: ppp shutting down\n", ppp->remote, ppp->remote6);
+	syslog(0, LOG, ": remote=%I/%I: ppp shutting down", ppp->remote, ppp->remote6);
 	syslog(0, LOG, "\t\tppp send = %lud/%lud recv= %lud/%lud",
 		ppp->out.packets, ppp->out.uchars,
 		ppp->in.packets, ppp->in.uchars);
@@ -2003,8 +2162,11 @@
 		auth_freeAI(c->ai);
 		c->ai = nil;
 	}
-	if((c->cs = auth_challenge("proto=%q role=server", getaproto(c->proto))) == nil)
-		sysfatal("auth_challenge: %r");
+	if((c->cs = auth_challenge("proto=%q role=server", getaproto(c->proto))) == nil){
+		char err[ERRMAX];
+		snprint(err, sizeof(err), "auth_challenge: %r");
+		terminate(ppp, err, 1);
+	}
 	syslog(0, LOG, ": remote=%I: sending %d byte challenge", ppp->remote, c->cs->nchal);
 	len = 4 + 1 + c->cs->nchal + strlen(ppp->chapname);
 	b = alloclcp(Cchallenge, c->id, len, &m);
@@ -2032,13 +2194,13 @@
 	switch(c->proto){
 	case APmschap:
 		if(c->ai == nil || c->ai->nsecret != 16)
-			sysfatal("could not get the encryption key");
+			terminate(ppp, "could not get the encryption key", 1);
 		memmove(ppp->sendkey, c->ai->secret, 16);
 		memmove(ppp->recvkey, c->ai->secret, 16);
 		break;
 	case APmschapv2:
 		if(c->ai == nil || c->ai->nsecret != 16+20)
-			sysfatal("could not get the encryption key + authenticator");
+			terminate(ppp, "could not get the encryption key + authenticator", 1);
 		getasymkey(ppp->sendkey, c->ai->secret, 1, isserver);
 		getasymkey(ppp->recvkey, c->ai->secret, 0, isserver);
 		break;
@@ -2078,9 +2240,13 @@
 			ppp->chapname, sizeof(ppp->chapname), 
 			resp, sizeof(resp), &c->ai,
 			auth_getkey,
-			"proto=%s role=client service=ppp %s", getaproto(c->proto), keyspec);
-		if(nresp < 0)
-			sysfatal("auth_respond: %r");
+			"proto=%s role=client service=ppp %s",
+			getaproto(c->proto), keyspec);
+		if(nresp < 0){
+			char err[ERRMAX];
+			snprint(err, sizeof(err), "auth_respond: %r");
+			terminate(ppp, err, 1);
+		}
 		if(c->proto == APmschap || c->proto == APmschapv2)
 			while(nresp < 49) resp[nresp++] = 0;
 		freeb(b);
@@ -2102,6 +2268,7 @@
 		}
 		switch(c->proto) {
 		default:
+			terminate(ppp, "unknown chap protocol", 0);
 			sysfatal("unknown chap protocol: %d", c->proto);
 		case APmd5:
 			if(vlen > len - 5 || vlen != 16) {
@@ -2189,7 +2356,7 @@
 			n = snprint((char*)resp, sizeof(resp), "S=%.20H", c->ai->secret+16);
 			if(len - 4 < n || tsmemcmp(m->data, resp, n) != 0){
 				netlog("ppp: chap: bad authenticator\n");
-				terminate(ppp, "chap: bad authenticator");
+				terminate(ppp, "chap: bad authenticator", 0);
 				break;
 			}
 		}
@@ -2199,7 +2366,7 @@
 		break;
 	case Cfailure:
 		netlog("ppp: chap failed\n");
-		terminate(ppp, "chap failed");
+		terminate(ppp, "chap failed", 0);
 		break;
 	default:
 		syslog(0, LOG, "chap code %d?", m->code);
@@ -2219,9 +2386,11 @@
 	int len, nlen, slen;
 
 	up = auth_getuserpasswd(auth_getkey, "proto=pass service=ppp %s", keyspec);
-	if(up == nil)
-		sysfatal("auth_getuserpasswd: %r");
-
+	if(up == nil){
+		char err[ERRMAX];
+		snprint(err, sizeof(err), "auth_getuserpasswd: %r");
+		terminate(ppp, err, 1);
+	}
 	c = ppp->chap;
 	c->id++;
 	netlog("ppp: pap: send authreq %d %s %s\n", c->id, up->user, "****");
@@ -2239,9 +2408,13 @@
 	b->wptr += slen;
 	hnputs(m->len, len);
 
+	memset(up->user, 0, nlen);	/* no leaks */
+	memset(up->passwd, 0, slen);	/* no leaks */
 	free(up);
 
 	putframe(ppp, Ppasswd, b);
+
+	memset(b->rptr, 0, BLEN(b));	/* no leaks */
 	freeb(b);
 }
 
@@ -2287,7 +2460,7 @@
 		&& m->id <= ppp-> chap->id){
 			netlog("ppp: pap failed (%d:%.*s)\n",
 				m->data[0], utfnlen((char*)m->data+1, m->data[0]), (char*)m->data+1);
-			terminate(ppp, "pap failed");
+			terminate(ppp, "pap failed", 0);
 		}
 		break;
 	default:
@@ -2324,7 +2497,7 @@
 
 	for(cp = m->data; cp < b->wptr; cp += o->len){
 		o = (Lcpopt*)cp;
-		if(cp + o->len > b->wptr){
+		if(cp + o->len > b->wptr || o->len < 2){
 			netlog("\tbad option length %ux\n", o->type);
 			return;
 		}
@@ -2439,6 +2612,16 @@
 				break;
 			}
 			break;
+		case Pipv6cp:
+			switch(o->type){
+			default:
+				netlog("\tunknown %d len=%d\n", o->type, o->len);
+				break;
+			case Oipv6eui:	
+				netlog("\tipv6eui %.*H\n", o->len, o->data);
+				break;
+			}
+			break;
 		}
 	}
 }
@@ -2757,10 +2940,6 @@
 		usage();
 	}
 
-	nip = nipifcs(net);
-	if(nip == 0 && !server)
-		primary = 1;
-
 	if(dev != nil){
 		mediain = open(dev, ORDWR);
 		if(mediain < 0){
@@ -2814,12 +2993,8 @@
 	pppopen(ppp, mediain, mediaout, net, ipaddr, remip, mtu, framing);
 
 	/* wait until ip is configured */
-	rendezvous((void*)Rmagic, 0);
+	rendezvous(ppp, 0);
 
-	/* create a /net/ndb entry */
-	if(primary)
-		putndb(ppp, net);
-
 	exits(nil);
 }
 
@@ -2860,52 +3035,75 @@
 	ipmove(addr, IPnoaddr);
 }
 
+static uchar v6llprefix[16] = {
+	0xfe, 0x80, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+};
+
+enum {
+	v6llprefixlen = 8,
+};
+
 /*
- *  return number of networks
+ *  return non-zero when addr is a valid link-local ipv6 address
  */
 static int
-nipifcs(char *net)
+validv6(Ipaddr addr)
 {
-	static Ipifc *ifc;
-	Ipifc *nifc;
-	Iplifc *lifc;
-	int n;
+	return memcmp(addr, v6llprefix, v6llprefixlen) == 0 && memcmp(addr, v6llprefix, IPaddrlen) != 0;
+}
 
-	n = 0;
-	ifc = readipifc(net, ifc, -1);
-	for(nifc = ifc; nifc != nil; nifc = nifc->next)
-		for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
-			n++;
-	return n;
+/*
+ *  convert EUI-64 or EUI-48 to link-local ipv6 address,
+ *  return non-zero when successfull or zero on failure.
+ */
+static int
+euitov6(Ipaddr addr, uchar *data, int len)
+{
+	if(len < 1 || len > IPaddrlen-v6llprefixlen){
+		invalidate(addr);
+		return 0;
+	}
+	ipmove(addr, v6llprefix);
+	memmove(&addr[IPaddrlen-len], data, len);
+	return memcmp(addr, v6llprefix, IPaddrlen) != 0;
 }
 
 /*
- *   make an ndb entry and put it into /net/ndb for the servers to see
+ *  make an ndb entry and put it into /net/ndb for the servers to see
  */
 static void
-putndb(PPP *ppp, char *net)
+putndb(PPP *ppp)
 {
 	static char buf[16*1024];
-	char file[64], *p, *e;
+	char file[128], *p, *e;
 	Ndbtuple *t, *nt;
 	Ndb *db;
 	int fd;
 
+	if(!primary)
+		return;
+
 	e = buf + sizeof(buf);
 	p = buf;
-	p = seprint(p, e, "ip=%I ipmask=255.255.255.255 ipgw=%I\n",
-		ppp->local, ppp->remote);
-	if(validv4(ppp->dns[0]))
-		p = seprint(p, e, "\tdns=%I\n", ppp->dns[0]);
-	if(validv4(ppp->dns[1]))
-		p = seprint(p, e, "\tdns=%I\n", ppp->dns[1]);
-	if(validv4(ppp->wins[0]))
-		p = seprint(p, e, "\twins=%I\n", ppp->wins[0]);
-	if(validv4(ppp->wins[1]))
-		p = seprint(p, e, "\twins=%I\n", ppp->wins[1]);
 
-	/* append preexisting entries not matching our ip */
-	snprint(file, sizeof file, "%s/ndb", net);
+	if(validv4(ppp->local) && validv4(ppp->remote)){
+		p = seprint(p, e, "ip=%I ipmask=255.255.255.255 ipgw=%I\n",
+			ppp->local, ppp->remote);
+		if(validv4(ppp->dns[0]))
+			p = seprint(p, e, "\tdns=%I\n", ppp->dns[0]);
+		if(validv4(ppp->dns[1]))
+			p = seprint(p, e, "\tdns=%I\n", ppp->dns[1]);
+		if(validv4(ppp->wins[0]))
+			p = seprint(p, e, "\twins=%I\n", ppp->wins[0]);
+		if(validv4(ppp->wins[1]))
+			p = seprint(p, e, "\twins=%I\n", ppp->wins[1]);
+	}
+
+	/* append preexisting entries not matching our old or new ip */
+	snprint(file, sizeof file, "%s/ndb", ppp->net);
 	db = ndbopen(file);
 	if(db != nil ){
 		while((t = ndbparse(db)) != nil){
@@ -2913,7 +3111,7 @@
 
 			if((nt = ndbfindattr(t, t, "ip")) == nil
 			|| parseip(ip, nt->val) == -1
-			|| ipcmp(ip, ppp->local) != 0){
+			|| (ipcmp(ip, ppp->local) != 0 && ipcmp(ip, ppp->curlocal) != 0)){
 				p = seprint(p, e, "\n");
 				for(nt = t; nt != nil; nt = nt->entry)
 					p = seprint(p, e, "%s=%s%s", nt->attr, nt->val,
@@ -2928,6 +3126,13 @@
 		return;
 	write(fd, buf, p-buf);
 	close(fd);
+}
+
+static void
+refresh(char *net)
+{
+	char file[128];
+	int fd;
 
 	snprint(file, sizeof file, "%s/cs", net);
 	if((fd = open(file, OWRITE)) >= 0){
--- a/sys/src/cmd/ip/ppp/ppp.h
+++ b/sys/src/cmd/ip/ppp/ppp.h
@@ -78,6 +78,7 @@
 	Pvjutcp=	0x2f,		/* uncompressing van jacobson tcp */
 	Pcdata=		0xfd,		/* compressed datagram */
 	Pipcp=		0x8021,		/* ip control */
+	Pipv6cp=	0x8057,		/* ipv6 control */
 	Pecp=		0x8053,		/* encryption control */
 	Pccp=		0x80fd,		/* compressed datagram control */
 	Plcp=		0xc021,		/* link control */
@@ -178,6 +179,9 @@
 	Oipdns2=	131,
 	Oipwins2=	132,
 
+	/* ipv6cp configure options */
+	Oipv6eui=	1,
+
 	/* ipcp flags */
 	Fipaddrs=	1<<Oipaddrs,
 	Fipcompress=	1<<Oipcompress,
@@ -187,6 +191,9 @@
 	Fipdns2=	1<<10,	// Oipdns2,
 	Fipwins2=	1<<11,	// Oipwins2,
 
+	/* ipv6cp flags */
+	Fipv6eui=	1<<Oipv6eui,
+
 	Period=		5*1000,	/* period of retransmit process (in ms) */
 	Timeout=	20,	/* xmit timeout (in Periods) */
 	Buflen=		4096,
@@ -262,11 +269,18 @@
 	int		framing;	/* non-zero to use framing characters */
 	Ipaddr		local;
 	Ipaddr		curlocal;
-	int		localfrozen;
 	Ipaddr		remote;
 	Ipaddr		curremote;
-	int		remotefrozen;
+	Ipaddr		local6;
+	Ipaddr		curlocal6;
+	Ipaddr		remote6;
+	Ipaddr		curremote6;
 
+	char		localfrozen;
+	char		remotefrozen;
+	char		local6frozen;
+	char		remote6frozen;
+
 	Ipaddr		dns[2];		/* dns servers */
 	Ipaddr		wins[2];	/* wins servers */
 
@@ -280,6 +294,7 @@
 	Pstate*		lcp;		/* lcp state */
 	Pstate*		ccp;		/* ccp state */
 	Pstate*		ipcp;		/* ipcp state */
+	Pstate*		ipv6cp;		/* ipv6cp state */
 	Chap*		chap;		/* chap state */
 	Tcpc*		ctcp;		/* tcp compression state */
 	ulong		mtu;		/* maximum xmit size */
--- a/sys/src/cmd/ip/ppp/thw.c
+++ b/sys/src/cmd/ip/ppp/thw.c
@@ -121,8 +121,12 @@
 
 	/* put ack and protocol into b */
 	n = BLEN(b);
-	if(b->rptr - (2+4) < b->base)
-		sysfatal("thwack: not enough header in block");
+	if(b->rptr - (2+4) < b->base){
+		fprint(2, "thwack: not enough header in block\n");
+		freeb(b);
+		return nil;
+	}
+
 	acked = 0;
 	if(ppp->unctype == &uncthwack){
 		uncs = ppp->uncstate;