shithub: drawterm

Download patch

ref: 23a48c7cfe2e38265eeaa1a8b8adfc57c887be76
parent: 2ca76dc7dec691eba587b2cd89dc1e9b2d05c141
author: David du Colombier <0intro@gmail.com>
date: Fri Aug 3 17:30:17 EDT 2012

devip: add support for IPv6

Addresses are now stored as uchar[16] instead
of ulong, with enough room for IPv6.

Generic IP functions have been removed from
devip.c and replaced by libip, imported from
Plan 9.

Names and addresses are resolved using either
gethostbyname() or getaddrinfo() functions.

On Windows, IPv6 name resolution is not enabled,
because mingw32 doesn't provide inet_ntop().

R=rsc
http://codereview.appspot.com/6408044

diff: cannot open b/libip//null: file does not exist: 'b/libip//null'
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -5,5 +5,6 @@
 
 Andrey Mirtchovski
 Erik Quanstrom <quanstro@quanstro.net>
+David du Colombier <0intro@gmail.com>
 Russ Cox <rsc@swtch.com>
 Yaroslav <yarikos@gmail.com>
--- a/Makefile
+++ b/Makefile
@@ -23,6 +23,7 @@
 	libdraw/libdraw.a\
 	gui-$(GUI)/libgui.a\
 	libc/libc.a\
+	libip/libip.a\
 
 # stupid gcc
 LIBS=$(LIBS1) $(LIBS1) $(LIBS1) libmachdep.a
@@ -66,6 +67,9 @@
 
 libc/libc.a:
 	(cd libc; $(MAKE))
+
+libip/libip.a:
+	(cd libip; $(MAKE))
 
 gui-$(GUI)/libgui.a:
 	(cd gui-$(GUI); $(MAKE))
--- /dev/null
+++ b/include/ip.h
@@ -1,0 +1,38 @@
+enum 
+{
+	IPaddrlen=	16,
+	IPv4addrlen=	4,
+	IPv4off=	12,
+};
+
+uchar*	defmask(uchar*);
+void	maskip(uchar*, uchar*, uchar*);
+int	eipfmt(Fmt*);
+int	isv4(uchar*);
+vlong	parseip(uchar*, char*);
+vlong	parseipmask(uchar*, char*);
+char*	v4parseip(uchar*, char*);
+char*	v4parsecidr(uchar*, uchar*, char*);
+
+void	hnputv(void*, uvlong);
+void	hnputl(void*, uint);
+void	hnputs(void*, ushort);
+uvlong	nhgetv(void*);
+uint	nhgetl(void*);
+ushort	nhgets(void*);
+
+int	v6tov4(uchar*, uchar*);
+void	v4tov6(uchar*, uchar*);
+
+#define	ipcmp(x, y) memcmp(x, y, IPaddrlen)
+#define	ipmove(x, y) memmove(x, y, IPaddrlen)
+
+extern uchar IPv4bcast[IPaddrlen];
+extern uchar IPv4bcastobs[IPaddrlen];
+extern uchar IPv4allsys[IPaddrlen];
+extern uchar IPv4allrouter[IPaddrlen];
+extern uchar IPnoaddr[IPaddrlen];
+extern uchar v4prefix[IPaddrlen];
+extern uchar IPallbits[IPaddrlen];
+
+#define CLASS(p) ((*(uchar*)(p))>>6)
--- a/kern/devip-posix.c
+++ b/kern/devip-posix.c
@@ -3,6 +3,7 @@
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <netdb.h>
+#include <arpa/inet.h>
 
 #include "u.h"
 #include "lib.h"
@@ -9,6 +10,7 @@
 #include "dat.h"
 #include "fns.h"
 #include "error.h"
+#include "ip.h"
 
 #include "devip.h"
 
@@ -16,6 +18,14 @@
 #undef accept
 #undef bind
 
+static int
+family(unsigned char *addr)
+{
+	if(isv4(addr))
+		return AF_INET;
+	return AF_INET6;
+}
+
 void
 osipinit(void)
 {
@@ -26,7 +36,7 @@
 }
 
 int
-so_socket(int type)
+so_socket(int type, unsigned char *addr)
 {
 	int fd, one;
 
@@ -41,7 +51,7 @@
 		break;
 	}
 
-	fd = socket(AF_INET, type, 0);
+	fd = socket(family(addr), type, 0);
 	if(fd < 0)
 		oserror();
 
@@ -54,36 +64,52 @@
 	return fd;
 }
 
-
 void
-so_connect(int fd, unsigned long raddr, unsigned short rport)
+so_connect(int fd, unsigned char *raddr, unsigned short rport)
 {
-	struct sockaddr_in sin;
+	struct sockaddr_storage ss;
 
-	memset(&sin, 0, sizeof(sin));
-	sin.sin_family = AF_INET;
-	hnputs(&sin.sin_port, rport);
-	hnputl(&sin.sin_addr.s_addr, raddr);
+	memset(&ss, 0, sizeof(ss));
 
-	if(connect(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
+	ss.ss_family = family(raddr);
+
+	switch(ss.ss_family){
+	case AF_INET:
+		hnputs(&((struct sockaddr_in*)&ss)->sin_port, rport);
+		v6tov4((unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr, raddr);
+		break;
+	case AF_INET6:
+		hnputs(&((struct sockaddr_in6*)&ss)->sin6_port, rport);
+		memcpy(&((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr, raddr, sizeof(struct in6_addr));
+		break;
+	}
+
+	if(connect(fd, (struct sockaddr*)&ss, sizeof(ss)) < 0)
 		oserror();
 }
 
 void
-so_getsockname(int fd, unsigned long *laddr, unsigned short *lport)
+so_getsockname(int fd, unsigned char *laddr, unsigned short *lport)
 {
 	socklen_t len;
-	struct sockaddr_in sin;
+	struct sockaddr_storage ss;
 
-	len = sizeof(sin);
-	if(getsockname(fd, (struct sockaddr*)&sin, &len) < 0)
+	len = sizeof(ss);
+	if(getsockname(fd, (struct sockaddr*)&ss, &len) < 0)
 		oserror();
 
-	if(sin.sin_family != AF_INET || len != sizeof(sin))
-		error("not AF_INET");
-
-	*laddr = nhgetl(&sin.sin_addr.s_addr);
-	*lport = nhgets(&sin.sin_port);
+	switch(ss.ss_family){
+	case AF_INET:
+		v4tov6(laddr, (unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr);
+		*lport = nhgets(&((struct sockaddr_in*)&ss)->sin_port);
+		break;
+	case AF_INET6:
+		memcpy(laddr, &((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr, sizeof(struct in6_addr));
+		*lport = nhgets(&((struct sockaddr_in6*)&ss)->sin6_port);
+		break;
+	default:
+		error("not AF_INET or AF_INET6");
+	}
 }
 
 void
@@ -94,30 +120,37 @@
 }
 
 int
-so_accept(int fd, unsigned long *raddr, unsigned short *rport)
+so_accept(int fd, unsigned char *raddr, unsigned short *rport)
 {
 	int nfd;
 	socklen_t len;
-	struct sockaddr_in sin;
+	struct sockaddr_storage ss;
 
-	len = sizeof(sin);
-	nfd = accept(fd, (struct sockaddr*)&sin, &len);
+	len = sizeof(ss);
+	nfd = accept(fd, (struct sockaddr*)&ss, &len);
 	if(nfd < 0)
 		oserror();
 
-	if(sin.sin_family != AF_INET || len != sizeof(sin))
-		error("not AF_INET");
-
-	*raddr = nhgetl(&sin.sin_addr.s_addr);
-	*rport = nhgets(&sin.sin_port);
+	switch(ss.ss_family){
+	case AF_INET:
+		v4tov6(raddr, (unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr);
+		*rport = nhgets(&((struct sockaddr_in*)&ss)->sin_port);
+		break;
+	case AF_INET6:
+		memcpy(raddr, &((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr, sizeof(struct in6_addr));
+		*rport = nhgets(&((struct sockaddr_in6*)&ss)->sin6_port);
+		break;
+	default:
+		error("not AF_INET or AF_INET6");
+	}
 	return nfd;
 }
 
 void
-so_bind(int fd, int su, unsigned short port)
+so_bind(int fd, int su, unsigned short port, unsigned char *addr)
 {
 	int i, one;
-	struct sockaddr_in sin;
+	struct sockaddr_storage ss;
 
 	one = 1;
 	if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0){
@@ -127,21 +160,37 @@
 
 	if(su) {
 		for(i = 600; i < 1024; i++) {
-			memset(&sin, 0, sizeof(sin));
-			sin.sin_family = AF_INET;
-			sin.sin_port = i;
+			memset(&ss, 0, sizeof(ss));
+			ss.ss_family = family(addr);
 
-			if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) >= 0)	
+			switch(ss.ss_family){
+			case AF_INET:
+				((struct sockaddr_in*)&ss)->sin_port = i;
+				break;
+			case AF_INET6:
+				((struct sockaddr_in6*)&ss)->sin6_port = i;
+				break;
+			}
+
+			if(bind(fd, (struct sockaddr*)&ss, sizeof(ss)) >= 0)	
 				return;
 		}
 		oserror();
 	}
 
-	memset(&sin, 0, sizeof(sin));
-	sin.sin_family = AF_INET;
-	hnputs(&sin.sin_port, port);
+	memset(&ss, 0, sizeof(ss));
+	ss.ss_family = family(addr);
 
-	if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
+	switch(ss.ss_family){
+	case AF_INET:
+		hnputs(&((struct sockaddr_in*)&ss)->sin_port, port);
+		break;
+	case AF_INET6:
+		hnputs(&((struct sockaddr_in6*)&ss)->sin6_port, port);
+		break;
+	}
+
+	if(bind(fd, (struct sockaddr*)&ss, sizeof(ss)) < 0)
 		oserror();
 }
 
@@ -170,16 +219,28 @@
 char*
 hostlookup(char *host)
 {
-	char buf[100];
+	char buf[INET6_ADDRSTRLEN];
 	uchar *p;
 	struct hostent *he;
+	struct addrinfo *result;
 
 	he = gethostbyname(host);
 	if(he != 0 && he->h_addr_list[0]) {
 		p = (uchar*)he->h_addr_list[0];
 		sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]);
+	} else if(getaddrinfo(host, NULL, NULL, &result) == 0) {
+		switch (result->ai_family) {
+		case AF_INET:
+			inet_ntop(AF_INET, &((struct sockaddr_in*)result->ai_addr)->sin_addr, buf, sizeof(buf));
+			break;
+		case AF_INET6:
+			inet_ntop(AF_INET6, &((struct sockaddr_in6*)result->ai_addr)->sin6_addr, buf, sizeof(buf));
+			break;
+		default:
+			return nil;
+		}
 	} else
-		strcpy(buf, host);
+		return nil;
 
 	return strdup(buf);
 }
--- a/kern/devip-win32.c
+++ b/kern/devip-win32.c
@@ -1,9 +1,11 @@
 #include <windows.h>
+#include <ws2tcpip.h>
 #include "u.h"
 #include "lib.h"
 #include "dat.h"
 #include "fns.h"
 #include "error.h"
+#include "ip.h"
 
 #include "devip.h"
 
@@ -15,6 +17,14 @@
 #undef accept
 #undef bind
 
+static int
+family(unsigned char *addr)
+{
+	if(isv4(addr))
+		return AF_INET;
+	return AF_INET6;
+}
+
 void
 osipinit(void)
 {
@@ -29,7 +39,7 @@
 }
 
 int
-so_socket(int type)
+so_socket(int type, unsigned char *addr)
 {
 	int fd, one;
 
@@ -44,7 +54,7 @@
 		break;
 	}
 
-	fd = socket(AF_INET, type, 0);
+	fd = socket(family(addr), type, 0);
 	if(fd < 0)
 		oserror();
 
@@ -59,34 +69,51 @@
 
 
 void
-so_connect(int fd, unsigned long raddr, unsigned short rport)
+so_connect(int fd, unsigned char *raddr, unsigned short rport)
 {
-	struct sockaddr_in sin;
+	struct sockaddr_storage ss;
 
-	memset(&sin, 0, sizeof(sin));
-	sin.sin_family = AF_INET;
-	hnputs(&sin.sin_port, rport);
-	hnputl(&sin.sin_addr.s_addr, raddr);
+	memset(&ss, 0, sizeof(ss));
 
-	if(connect(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
+	ss.ss_family = family(raddr);
+
+	switch(ss.ss_family){
+	case AF_INET:
+		hnputs(&((struct sockaddr_in*)&ss)->sin_port, rport);
+		v6tov4((unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr, raddr);
+		break;
+	case AF_INET6:
+		hnputs(&((struct sockaddr_in6*)&ss)->sin6_port, rport);
+		memcpy(&((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr, raddr, sizeof(struct in6_addr));
+		break;
+	}
+
+	if(connect(fd, (struct sockaddr*)&ss, sizeof(ss)) < 0)
 		oserror();
 }
 
 void
-so_getsockname(int fd, unsigned long *laddr, unsigned short *lport)
+so_getsockname(int fd, unsigned char *laddr, unsigned short *lport)
 {
 	int len;
-	struct sockaddr_in sin;
+	struct sockaddr_storage ss;
 
-	len = sizeof(sin);
-	if(getsockname(fd, (struct sockaddr*)&sin, &len) < 0)
+	len = sizeof(ss);
+	if(getsockname(fd, (struct sockaddr*)&ss, &len) < 0)
 		oserror();
 
-	if(sin.sin_family != AF_INET || len != sizeof(sin))
-		error("not AF_INET");
-
-	*laddr = nhgetl(&sin.sin_addr.s_addr);
-	*lport = nhgets(&sin.sin_port);
+	switch(ss.ss_family){
+	case AF_INET:
+		v4tov6(laddr, (unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr);
+		*lport = nhgets(&((struct sockaddr_in*)&ss)->sin_port);
+		break;
+	case AF_INET6:
+		memcpy(laddr, &((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr, sizeof(struct in6_addr));
+		*lport = nhgets(&((struct sockaddr_in6*)&ss)->sin6_port);
+		break;
+	default:
+		error("not AF_INET or AF_INET6");
+	}
 }
 
 void
@@ -97,53 +124,77 @@
 }
 
 int
-so_accept(int fd, unsigned long *raddr, unsigned short *rport)
+so_accept(int fd, unsigned char *raddr, unsigned short *rport)
 {
-	int nfd, len;
-	struct sockaddr_in sin;
+	int nfd;
+	int len;
+	struct sockaddr_storage ss;
 
-	len = sizeof(sin);
-	nfd = accept(fd, (struct sockaddr*)&sin, &len);
+	len = sizeof(ss);
+	nfd = accept(fd, (struct sockaddr*)&ss, &len);
 	if(nfd < 0)
 		oserror();
 
-	if(sin.sin_family != AF_INET || len != sizeof(sin))
-		error("not AF_INET");
-
-	*raddr = nhgetl(&sin.sin_addr.s_addr);
-	*rport = nhgets(&sin.sin_port);
+	switch(ss.ss_family){
+	case AF_INET:
+		v4tov6(raddr, (unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr);
+		*rport = nhgets(&((struct sockaddr_in*)&ss)->sin_port);
+		break;
+	case AF_INET6:
+		memcpy(raddr, &((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr, sizeof(struct in6_addr));
+		*rport = nhgets(&((struct sockaddr_in6*)&ss)->sin6_port);
+		break;
+	default:
+		error("not AF_INET or AF_INET6");
+	}
 	return nfd;
 }
 
 void
-so_bind(int fd, int su, unsigned short port)
+so_bind(int fd, int su, unsigned short port, unsigned char *addr)
 {
 	int i, one;
-	struct sockaddr_in sin;
+	struct sockaddr_storage ss;
 
 	one = 1;
 	if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0){
 		oserrstr();
-		print("setsockopt: %s", up->errstr);
+		print("setsockopt: %r");
 	}
 
 	if(su) {
 		for(i = 600; i < 1024; i++) {
-			memset(&sin, 0, sizeof(sin));
-			sin.sin_family = AF_INET;
-			sin.sin_port = i;
+			memset(&ss, 0, sizeof(ss));
+			ss.ss_family = family(addr);
 
-			if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) >= 0)	
+			switch(ss.ss_family){
+			case AF_INET:
+				((struct sockaddr_in*)&ss)->sin_port = i;
+				break;
+			case AF_INET6:
+				((struct sockaddr_in6*)&ss)->sin6_port = i;
+				break;
+			}
+
+			if(bind(fd, (struct sockaddr*)&ss, sizeof(ss)) >= 0)	
 				return;
 		}
 		oserror();
 	}
 
-	memset(&sin, 0, sizeof(sin));
-	sin.sin_family = AF_INET;
-	hnputs(&sin.sin_port, port);
+	memset(&ss, 0, sizeof(ss));
+	ss.ss_family = family(addr);
 
-	if(bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
+	switch(ss.ss_family){
+	case AF_INET:
+		hnputs(&((struct sockaddr_in*)&ss)->sin_port, port);
+		break;
+	case AF_INET6:
+		hnputs(&((struct sockaddr_in6*)&ss)->sin6_port, port);
+		break;
+	}
+
+	if(bind(fd, (struct sockaddr*)&ss, sizeof(ss)) < 0)
 		oserror();
 }
 
@@ -174,7 +225,7 @@
 {
 	char buf[100];
 	uchar *p;
-	HOSTENT *he;
+	struct hostent *he;
 
 	he = gethostbyname(host);
 	if(he != 0 && he->h_addr_list[0]) {
--- a/kern/devip.c
+++ b/kern/devip.c
@@ -3,14 +3,10 @@
 #include "dat.h"
 #include "fns.h"
 #include "error.h"
+#include "ip.h"
 
 #include "devip.h"
 
-void		hnputl(void *p, unsigned long v);
-void		hnputs(void *p, unsigned short v);
-unsigned long	nhgetl(void *p);
-unsigned short	nhgets(void *p);
-unsigned long	parseip(char *to, char *from);
 void	csclose(Chan*);
 long	csread(Chan*, void*, long, vlong);
 long	cswrite(Chan*, void*, long, vlong);
@@ -37,6 +33,7 @@
 #define CONV(x) 	((int)(((x).path >> 4)&0xfff))
 #define PROTO(x) 	((int)(((x).path >> 16)&0xff))
 #define QID(p, c, y) 	(((p)<<16) | ((c)<<4) | (y))
+#define ipzero(x)	memset(x, 0, IPaddrlen)
 
 typedef struct Proto	Proto;
 typedef struct Conv	Conv;
@@ -48,9 +45,9 @@
 	int	perm;
 	char	owner[KNAMELEN];
 	char*	state;
-	ulong	laddr;
+	uchar	laddr[IPaddrlen];
 	ushort	lport;
-	ulong	raddr;
+	uchar	raddr[IPaddrlen];
 	ushort	rport;
 	int	restricted;
 	char	cerr[KNAMELEN];
@@ -71,7 +68,6 @@
 
 static	int	np;
 static	Proto	proto[MAXPROTO];
-int	eipfmt(Fmt*);
 
 static	Conv*	protoclone(Proto*, char*, int);
 static	void	setladdr(Conv*);
@@ -222,7 +218,7 @@
 ipopen(Chan *c, int omode)
 {
 	Proto *p;
-	ulong raddr;
+	uchar raddr[IPaddrlen];
 	ushort rport;
 	int perm, sfd;
 	Conv *cv, *lcv;
@@ -286,13 +282,13 @@
 	case Qlisten:
 		p = &proto[PROTO(c->qid)];
 		lcv = p->conv[CONV(c->qid)];
-		sfd = so_accept(lcv->sfd, &raddr, &rport);
+		sfd = so_accept(lcv->sfd, raddr, &rport);
 		cv = protoclone(p, up->user, sfd);
 		if(cv == 0) {
 			close(sfd);
 			error(Enodev);
 		}
-		cv->raddr = raddr;
+		ipmove(cv->raddr, raddr);
 		cv->rport = rport;
 		setladdr(cv);
 		cv->state = "Established";
@@ -324,8 +320,8 @@
 		strcpy(cc->owner, "network");
 		cc->perm = 0666;
 		cc->state = "Closed";
-		cc->laddr = 0;
-		cc->raddr = 0;
+		ipzero(cc->laddr);
+		ipzero(cc->raddr);
 		cc->lport = 0;
 		cc->rport = 0;
 		close(cc->sfd);
@@ -339,7 +335,7 @@
 	int r;
 	Conv *c;
 	Proto *x;
-	uchar ip[4];
+	uchar ip[IPaddrlen];
 	char buf[128], *p;
 
 /*print("ipread %s %lux\n", c2name(ch), (long)ch->qid.path);*/
@@ -358,12 +354,12 @@
 		return readstr(offset, p, n, buf);
 	case Qremote:
 		c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)];
-		hnputl(ip, c->raddr);
+		ipmove(ip, c->raddr);
 		sprint(buf, "%I!%d\n", ip, c->rport);
 		return readstr(offset, p, n, buf);
 	case Qlocal:
 		c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)];
-		hnputl(ip, c->laddr);
+		ipmove(ip, c->laddr);
 		sprint(buf, "%I!%d\n", ip, c->lport);
 		return readstr(offset, p, n, buf);
 	case Qstatus:
@@ -386,7 +382,7 @@
 static void
 setladdr(Conv *c)
 {
-	so_getsockname(c->sfd, &c->laddr, &c->lport);
+	so_getsockname(c->sfd, c->laddr, &c->lport);
 }
 
 static void
@@ -395,23 +391,27 @@
 	if(c->restricted == 0 && c->lport == 0)
 		return;
 
-	so_bind(c->sfd, c->restricted, c->lport);
+	if(c->sfd == -1)
+		c->sfd = so_socket(c->p->stype, c->laddr);
+
+	so_bind(c->sfd, c->restricted, c->lport, c->laddr);
 }
 
 static void
 setladdrport(Conv *c, char *str)
 {
-	char *p, addr[4];
+	char *p;
+	uchar addr[IPaddrlen];
 
 	p = strchr(str, '!');
 	if(p == 0) {
 		p = str;
-		c->laddr = 0;
+		ipzero(c->laddr);
 	}
 	else {
 		*p++ = 0;
 		parseip(addr, str);
-		c->laddr = nhgetl((uchar*)addr);
+		ipmove(c->laddr, addr);
 	}
 	if(*p == '*')
 		c->lport = 0;
@@ -424,7 +424,8 @@
 static char*
 setraddrport(Conv *c, char *str)
 {
-	char *p, addr[4];
+	char *p;
+	uchar addr[IPaddrlen];
 
 	p = strchr(str, '!');
 	if(p == 0)
@@ -431,7 +432,7 @@
 		return "malformed address";
 	*p++ = 0;
 	parseip(addr, str);
-	c->raddr = nhgetl((uchar*)addr);
+	ipmove(c->raddr, addr);
 	c->rport = atoi(p);
 	p = strchr(p, '!');
 	if(p) {
@@ -480,6 +481,8 @@
 				setlport(c);
 				break;
 			}
+			if(c->sfd == -1)
+				c->sfd = so_socket(c->p->stype, c->raddr);
 			so_connect(c->sfd, c->raddr, c->rport);
 			setladdr(c);
 			c->state = "Established";
@@ -565,13 +568,11 @@
 	c->perm = 0660;
 	c->state = "Closed";
 	c->restricted = 0;
-	c->laddr = 0;
-	c->raddr = 0;
+	ipzero(c->laddr);
+	ipzero(c->raddr);
 	c->lport = 0;
 	c->rport = 0;
 	c->sfd = nfd;
-	if(nfd == -1)
-		c->sfd = so_socket(p->stype);
 
 	unlock(&c->r.lk);
 	unlock(&p->l);
@@ -579,146 +580,7 @@
 	return c;
 }
 
-enum
-{
-	Isprefix= 16,
-};
-
-uchar prefixvals[256] =
-{
-/*0x00*/ 0 | Isprefix,
-		   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0x10*/	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0x20*/	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0x30*/	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0x40*/	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0x50*/	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0x60*/	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0x70*/	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0x80*/ 1 | Isprefix,
-		   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0x90*/	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0xA0*/	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0xB0*/	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0xC0*/ 2 | Isprefix,
-		   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0xD0*/	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0xE0*/ 3 | Isprefix,
-		   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-/*0xF0*/ 4 | Isprefix,
-		   0, 0, 0, 0, 0, 0, 0, 
-/*0xF8*/ 5 | Isprefix,
-		   0, 0, 0, 
-/*0xFC*/ 6 | Isprefix,
-		   0,
-/*0xFE*/ 7 | Isprefix,
-/*0xFF*/ 8 | Isprefix,
-};
-
-int
-eipfmt(Fmt *f)
-{
-	char buf[5*8];
-	static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux";
-	static char *ifmt = "%d.%d.%d.%d";
-	uchar *p, ip[16];
-	ulong ul;
-
-	switch(f->r) {
-	case 'E':		/* Ethernet address */
-		p = va_arg(f->args, uchar*);
-		snprint(buf, sizeof buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
-		return fmtstrcpy(f, buf);
-
-	case 'I':
-		ul = va_arg(f->args, ulong);
-		hnputl(ip, ul);
-		snprint(buf, sizeof buf, ifmt, ip[0], ip[1], ip[2], ip[3]);
-		return fmtstrcpy(f, buf);
-	}
-	return fmtstrcpy(f, "(eipfmt)");
-}
-
 void
-hnputl(void *p, unsigned long v)
-{
-	unsigned char *a;
-
-	a = p;
-	a[0] = v>>24;
-	a[1] = v>>16;
-	a[2] = v>>8;
-	a[3] = v;
-}
-
-void
-hnputs(void *p, unsigned short v)
-{
-	unsigned char *a;
-
-	a = p;
-	a[0] = v>>8;
-	a[1] = v;
-}
-
-unsigned long
-nhgetl(void *p)
-{
-	unsigned char *a;
-	a = p;
-	return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
-}
-
-unsigned short
-nhgets(void *p)
-{
-	unsigned char *a;
-	a = p;
-	return (a[0]<<8)|(a[1]<<0);
-}
-
-#define CLASS(p) ((*(unsigned char*)(p))>>6)
-
-unsigned long
-parseip(char *to, char *from)
-{
-	int i;
-	char *p;
-
-	p = from;
-	memset(to, 0, 4);
-	for(i = 0; i < 4 && *p; i++){
-		to[i] = strtoul(p, &p, 10);
-		if(*p != '.' && *p != 0){
-			memset(to, 0, 4);
-			return 0;
-		}
-		if(*p == '.')
-			p++;
-	}
-	switch(CLASS(to)){
-	case 0:	/* class A - 1 byte net */
-	case 1:
-		if(i == 3){
-			to[3] = to[2];
-			to[2] = to[1];
-			to[1] = 0;
-		} else if (i == 2){
-			to[3] = to[1];
-			to[1] = 0;
-		}
-		break;
-	case 2:	/* class B - 2 byte net */
-		if(i == 3){
-			to[3] = to[2];
-			to[2] = 0;
-		}
-		break;
-	}
-	return nhgetl(to);
-}
-
-void
 csclose(Chan *c)
 {
 	free(c->aux);
@@ -856,23 +718,17 @@
 	return 0;
 }
 
-static ulong
-lookuphost(char *s)
+static int
+lookuphost(char *s, uchar *to)
 {
-	char to[4];
-	ulong ip;
-
-	memset(to, 0, sizeof to);
-	parseip(to, s);
-	ip = nhgetl(to);
-	if(ip != 0)
-		return ip;
-	if((s = hostlookup(s)) == nil)
+	ipzero(to);
+	if(parseip(to, s) != -1)
 		return 0;
+	if((s = hostlookup(s)) == nil)
+		return -1;
 	parseip(to, s);
-	ip = nhgetl(to);
 	free(s);
-	return ip;
+	return 0;
 }
 
 long
@@ -880,7 +736,7 @@
 {
 	char *f[4];
 	char *s, *ns;
-	ulong ip;
+	uchar ip[IPaddrlen];
 	int nf, port;
 
 	s = malloc(n+1);
@@ -900,8 +756,7 @@
 	if(port <= 0)
 		error("no translation for port found");
 
-	ip = lookuphost(f[1]);
-	if(ip == 0)
+	if(lookuphost(f[1], ip) < 0)
 		error("no translation for host found");
 
 	ns = smprint("/net/%s/clone %I!%d", f[0], ip, port);
--- a/kern/devip.h
+++ b/kern/devip.h
@@ -4,14 +4,14 @@
 	S_UDP
 };
 
-int		so_socket(int type);
-void		so_connect(int, unsigned long, unsigned short);
-void		so_getsockname(int, unsigned long*, unsigned short*);
-void		so_bind(int, int, unsigned short);
+int		so_socket(int, unsigned char*);
+void		so_connect(int, unsigned char*, unsigned short);
+void		so_getsockname(int, unsigned char*, unsigned short*);
+void		so_bind(int, int, unsigned short, unsigned char*);
 void		so_listen(int);
 int		so_send(int, void*, int, int);
 int		so_recv(int, void*, int, int);
-int		so_accept(int, unsigned long*, unsigned short*);
+int		so_accept(int, unsigned char*, unsigned short*);
 int		so_getservbyname(char*, char*, char*);
 int		so_gethostbyname(char*, char**, int);
 
--- a/kern/fns.h
+++ b/kern/fns.h
@@ -375,12 +375,6 @@
 Segment*	dupseg(Segment**, int, int);
 Segment*	newseg(int, ulong, ulong);
 Segment*	seg(Proc*, ulong, int);
-void		hnputv(void*, vlong);
-void		hnputl(void*, ulong);
-void		hnputs(void*, ushort);
-vlong		nhgetv(void*);
-ulong		nhgetl(void*);
-ushort		nhgets(void*);
 ulong	ticks(void);
 void	osproc(Proc*);
 void	osnewproc(Proc*);
--- /dev/null
+++ b/libip/Makefile
@@ -1,0 +1,19 @@
+ROOT=..
+include ../Make.config
+LIB=libip.a
+
+OFILES=\
+	eipfmt.$O\
+	parseip.$O\
+	classmask.$O\
+	bo.$O\
+	ipaux.$O\
+
+default: $(LIB)
+$(LIB): $(OFILES)
+	$(AR) r $(LIB) $(OFILES)
+	$(RANLIB) $(LIB)
+
+%.$O: %.c
+	$(CC) $(CFLAGS) $*.c
+
--- /dev/null
+++ b/libip/bo.c
@@ -1,0 +1,77 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+
+void
+hnputv(void *p, uvlong v)
+{
+	uchar *a;
+
+	a = p;
+	a[0] = v>>56;
+	a[1] = v>>48;
+	a[2] = v>>40;
+	a[3] = v>>32;
+	a[4] = v>>24;
+	a[5] = v>>16;
+	a[6] = v>>8;
+	a[7] = v;
+}
+
+void
+hnputl(void *p, uint v)
+{
+	uchar *a;
+
+	a = p;
+	a[0] = v>>24;
+	a[1] = v>>16;
+	a[2] = v>>8;
+	a[3] = v;
+}
+
+void
+hnputs(void *p, ushort v)
+{
+	uchar *a;
+
+	a = p;
+	a[0] = v>>8;
+	a[1] = v;
+}
+
+uvlong
+nhgetv(void *p)
+{
+	uchar *a;
+	uvlong v;
+
+	a = p;
+	v = (uvlong)a[0]<<56;
+	v |= (uvlong)a[1]<<48;
+	v |= (uvlong)a[2]<<40;
+	v |= (uvlong)a[3]<<32;
+	v |= a[4]<<24;
+	v |= a[5]<<16;
+	v |= a[6]<<8;
+	v |= a[7]<<0;
+	return v;
+}
+
+uint
+nhgetl(void *p)
+{
+	uchar *a;
+
+	a = p;
+	return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
+}
+
+ushort
+nhgets(void *p)
+{
+	uchar *a;
+
+	a = p;
+	return (a[0]<<8)|(a[1]<<0);
+}
--- /dev/null
+++ b/libip/classmask.c
@@ -1,0 +1,86 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+
+static uchar classmask[4][16] = {
+	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0x00,0x00,0x00,
+	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0x00,0x00,0x00,
+	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0x00,0x00,
+	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0x00,
+};
+
+static uchar v6loopback[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x01
+};
+
+static uchar v6linklocal[IPaddrlen] = {
+	0xfe, 0x80, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+static uchar v6linklocalmask[IPaddrlen] = {
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+static int v6llpreflen = 8;	/* link-local prefix length in bytes */
+
+static uchar v6multicast[IPaddrlen] = {
+	0xff, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+static uchar v6multicastmask[IPaddrlen] = {
+	0xff, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0
+};
+static int v6mcpreflen = 1;	/* multicast prefix length */
+
+static uchar v6solicitednode[IPaddrlen] = {
+	0xff, 0x02, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0x01,
+	0xff, 0, 0, 0
+};
+static uchar v6solicitednodemask[IPaddrlen] = {
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0x0, 0x0, 0x0
+};
+static int v6snpreflen = 13;
+
+uchar*
+defmask(uchar *ip)
+{
+	if(isv4(ip))
+		return classmask[ip[IPv4off]>>6];
+	else {
+		if(ipcmp(ip, v6loopback) == 0)
+			return IPallbits;
+		else if(memcmp(ip, v6linklocal, v6llpreflen) == 0)
+			return v6linklocalmask;
+		else if(memcmp(ip, v6solicitednode, v6snpreflen) == 0)
+			return v6solicitednodemask;
+		else if(memcmp(ip, v6multicast, v6mcpreflen) == 0)
+			return v6multicastmask;
+		return IPallbits;
+	}
+}
+
+void
+maskip(uchar *from, uchar *mask, uchar *to)
+{
+	int i;
+
+	for(i = 0; i < IPaddrlen; i++)
+		to[i] = from[i] & mask[i];
+}
--- /dev/null
+++ b/libip/eipfmt.c
@@ -1,0 +1,109 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+
+enum
+{
+	Isprefix= 16,
+};
+
+uchar prefixvals[256] =
+{
+[0x00] 0 | Isprefix,
+[0x80] 1 | Isprefix,
+[0xC0] 2 | Isprefix,
+[0xE0] 3 | Isprefix,
+[0xF0] 4 | Isprefix,
+[0xF8] 5 | Isprefix,
+[0xFC] 6 | Isprefix,
+[0xFE] 7 | Isprefix,
+[0xFF] 8 | Isprefix,
+};
+
+int
+eipfmt(Fmt *f)
+{
+	char buf[5*8];
+	static char *efmt = "%.2ux%.2ux%.2ux%.2ux%.2ux%.2ux";
+	static char *ifmt = "%d.%d.%d.%d";
+	uchar *p, ip[16];
+	ulong *lp;
+	ushort s;
+	int i, j, n, eln, eli;
+
+	switch(f->r) {
+	case 'E':		/* Ethernet address */
+		p = va_arg(f->args, uchar*);
+		snprint(buf, sizeof buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
+		return fmtstrcpy(f, buf);
+
+	case 'I':		/* Ip address */
+		p = va_arg(f->args, uchar*);
+common:
+		if(memcmp(p, v4prefix, 12) == 0){
+			snprint(buf, sizeof buf, ifmt, p[12], p[13], p[14], p[15]);
+			return fmtstrcpy(f, buf);
+		}
+
+		/* find longest elision */
+		eln = eli = -1;
+		for(i = 0; i < 16; i += 2){
+			for(j = i; j < 16; j += 2)
+				if(p[j] != 0 || p[j+1] != 0)
+					break;
+			if(j > i && j - i > eln){
+				eli = i;
+				eln = j - i;
+			}
+		}
+
+		/* print with possible elision */
+		n = 0;
+		for(i = 0; i < 16; i += 2){
+			if(i == eli){
+				n += sprint(buf+n, "::");
+				i += eln;
+				if(i >= 16)
+					break;
+			} else if(i != 0)
+				n += sprint(buf+n, ":");
+			s = (p[i]<<8) + p[i+1];
+			n += sprint(buf+n, "%ux", s);
+		}
+		return fmtstrcpy(f, buf);
+
+	case 'i':		/* v6 address as 4 longs */
+		lp = va_arg(f->args, ulong*);
+		for(i = 0; i < 4; i++)
+			hnputl(ip+4*i, *lp++);
+		p = ip;
+		goto common;
+
+	case 'V':		/* v4 ip address */
+		p = va_arg(f->args, uchar*);
+		snprint(buf, sizeof buf, ifmt, p[0], p[1], p[2], p[3]);
+		return fmtstrcpy(f, buf);
+
+	case 'M':		/* ip mask */
+		p = va_arg(f->args, uchar*);
+
+		/* look for a prefix mask */
+		for(i = 0; i < 16; i++)
+			if(p[i] != 0xff)
+				break;
+		if(i < 16){
+			if((prefixvals[p[i]] & Isprefix) == 0)
+				goto common;
+			for(j = i+1; j < 16; j++)
+				if(p[j] != 0)
+					goto common;
+			n = 8*i + (prefixvals[p[i]] & ~Isprefix);
+		} else
+			n = 8*16;
+
+		/* got one, use /xx format */
+		snprint(buf, sizeof buf, "/%d", n);
+		return fmtstrcpy(f, buf);
+	}
+	return fmtstrcpy(f, "(eipfmt)");
+}
--- /dev/null
+++ b/libip/ipaux.c
@@ -1,0 +1,102 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+
+/*
+ *  well known IP addresses
+ */
+uchar IPv4bcast[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff
+};
+uchar IPv4allsys[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0xe0, 0, 0, 0x01
+};
+uchar IPv4allrouter[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0xe0, 0, 0, 0x02
+};
+uchar IPallbits[IPaddrlen] = {
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff
+};
+uchar IPnoaddr[IPaddrlen];
+
+/*
+ *  prefix of all v4 addresses
+ */
+uchar v4prefix[IPaddrlen] = {
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0xff, 0xff,
+	0, 0, 0, 0
+};
+
+int
+isv4(uchar *ip)
+{
+	return memcmp(ip, v4prefix, IPv4off) == 0;
+}
+
+/*
+ *  the following routines are unrolled with no memset's to speed
+ *  up the usual case
+ */
+void
+v4tov6(uchar *v6, uchar *v4)
+{
+	v6[0] = 0;
+	v6[1] = 0;
+	v6[2] = 0;
+	v6[3] = 0;
+	v6[4] = 0;
+	v6[5] = 0;
+	v6[6] = 0;
+	v6[7] = 0;
+	v6[8] = 0;
+	v6[9] = 0;
+	v6[10] = 0xff;
+	v6[11] = 0xff;
+	v6[12] = v4[0];
+	v6[13] = v4[1];
+	v6[14] = v4[2];
+	v6[15] = v4[3];
+}
+
+int
+v6tov4(uchar *v4, uchar *v6)
+{
+	if(v6[0] == 0
+	&& v6[1] == 0
+	&& v6[2] == 0
+	&& v6[3] == 0
+	&& v6[4] == 0
+	&& v6[5] == 0
+	&& v6[6] == 0
+	&& v6[7] == 0
+	&& v6[8] == 0
+	&& v6[9] == 0
+	&& v6[10] == 0xff
+	&& v6[11] == 0xff)
+	{
+		v4[0] = v6[12];
+		v4[1] = v6[13];
+		v4[2] = v6[14];
+		v4[3] = v6[15];
+		return 0;
+	} else {
+		memset(v4, 0, 4);
+		if(memcmp(v6, IPnoaddr, IPaddrlen) == 0)
+			return 0;
+		return -1;
+	}
+}
--- /dev/null
+++ b/libip/parseip.c
@@ -1,0 +1,184 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <ip.h>
+
+char*
+v4parseip(uchar *to, char *from)
+{
+	int i;
+	char *p;
+
+	p = from;
+	for(i = 0; i < 4 && *p; i++){
+		to[i] = strtoul(p, &p, 0);
+		if(*p == '.')
+			p++;
+	}
+	switch(CLASS(to)){
+	case 0:	/* class A - 1 uchar net */
+	case 1:
+		if(i == 3){
+			to[3] = to[2];
+			to[2] = to[1];
+			to[1] = 0;
+		} else if (i == 2){
+			to[3] = to[1];
+			to[1] = 0;
+		}
+		break;
+	case 2:	/* class B - 2 uchar net */
+		if(i == 3){
+			to[3] = to[2];
+			to[2] = 0;
+		}
+		break;
+	}
+	return p;
+}
+
+static int
+ipcharok(int c)
+{
+	return c == '.' || c == ':' || (isascii(c) && isxdigit(c));
+}
+
+static int
+delimchar(int c)
+{
+	if(c == '\0')
+		return 1;
+	if(c == '.' || c == ':' || (isascii(c) && isalnum(c)))
+		return 0;
+	return 1;
+}
+
+/*
+ * `from' may contain an address followed by other characters,
+ * at least in /boot, so we permit whitespace (and more) after the address.
+ * we do ensure that "delete" cannot be parsed as "de::".
+ *
+ * some callers don't check the return value for errors, so
+ * set `to' to something distinctive in the case of a parse error.
+ */
+vlong
+parseip(uchar *to, char *from)
+{
+	int i, elipsis = 0, v4 = 1;
+	ulong x;
+	char *p, *op;
+
+	memset(to, 0, IPaddrlen);
+	p = from;
+	for(i = 0; i < IPaddrlen && ipcharok(*p); i+=2){
+		op = p;
+		x = strtoul(p, &p, 16);
+		if(*p == '.' || (*p == 0 && i == 0)){	/* ends with v4? */
+			p = v4parseip(to+i, op);
+			i += 4;
+			break;
+		}
+		/* v6: at most 4 hex digits, followed by colon or delim */
+		if(x != (ushort)x || (*p != ':' && !delimchar(*p))) {
+			memset(to, 0, IPaddrlen);
+			return -1;			/* parse error */
+		}
+		to[i] = x>>8;
+		to[i+1] = x;
+		if(*p == ':'){
+			v4 = 0;
+			if(*++p == ':'){	/* :: is elided zero short(s) */
+				if (elipsis) {
+					memset(to, 0, IPaddrlen);
+					return -1;	/* second :: */
+				}
+				elipsis = i+2;
+				p++;
+			}
+		} else if (p == op)		/* strtoul made no progress? */
+			break;
+	}
+	if (p == from || !delimchar(*p)) {
+		memset(to, 0, IPaddrlen);
+		return -1;				/* parse error */
+	}
+	if(i < IPaddrlen){
+		memmove(&to[elipsis+IPaddrlen-i], &to[elipsis], i-elipsis);
+		memset(&to[elipsis], 0, IPaddrlen-i);
+	}
+	if(v4){
+		to[10] = to[11] = 0xff;
+		return nhgetl(to + IPv4off);
+	} else
+		return 6;
+}
+
+/*
+ *  hack to allow ip v4 masks to be entered in the old
+ *  style
+ */
+vlong
+parseipmask(uchar *to, char *from)
+{
+	int i, w;
+	vlong x;
+	uchar *p;
+
+	if(*from == '/'){
+		/* as a number of prefix bits */
+		i = atoi(from+1);
+		if(i < 0)
+			i = 0;
+		if(i > 128)
+			i = 128;
+		w = i;
+		memset(to, 0, IPaddrlen);
+		for(p = to; i >= 8; i -= 8)
+			*p++ = 0xff;
+		if(i > 0)
+			*p = ~((1<<(8-i))-1);
+		x = nhgetl(to+IPv4off);
+		/*
+		 * identify as ipv6 if the mask is inexpressible as a v4 mask
+		 * (because it has too few mask bits).  Arguably, we could
+		 * always return 6 here.
+		 */
+		if (w < 8*(IPaddrlen-IPv4addrlen))
+			return 6;
+	} else {
+		/* as a straight v4 bit mask */
+		x = parseip(to, from);
+		if (x != -1)
+			x = (ulong)nhgetl(to + IPv4off);
+		if(memcmp(to, v4prefix, IPv4off) == 0)
+			memset(to, 0xff, IPv4off);
+	}
+	return x;
+}
+
+/*
+ *  parse a v4 ip address/mask in cidr format
+ */
+char*
+v4parsecidr(uchar *addr, uchar *mask, char *from)
+{
+	int i;
+	char *p;
+	uchar *a;
+
+	p = v4parseip(addr, from);
+
+	if(*p == '/'){
+		/* as a number of prefix bits */
+		i = strtoul(p+1, &p, 0);
+		if(i > 32)
+			i = 32;
+		memset(mask, 0, IPv4addrlen);
+		for(a = mask; i >= 8; i -= 8)
+			*a++ = 0xff;
+		if(i > 0)
+			*a = ~((1<<(8-i))-1);
+	} else 
+		memcpy(mask, defmask(addr), IPv4addrlen);
+	return p;
+}