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
This commit is contained in:
David du Colombier 2012-08-03 21:30:17 +02:00
parent 2ca76dc7de
commit 23a48c7cfe
14 changed files with 854 additions and 273 deletions

View File

@ -5,5 +5,6 @@
Andrey Mirtchovski Andrey Mirtchovski
Erik Quanstrom <quanstro@quanstro.net> Erik Quanstrom <quanstro@quanstro.net>
David du Colombier <0intro@gmail.com>
Russ Cox <rsc@swtch.com> Russ Cox <rsc@swtch.com>
Yaroslav <yarikos@gmail.com> Yaroslav <yarikos@gmail.com>

View File

@ -23,6 +23,7 @@ LIBS1=\
libdraw/libdraw.a\ libdraw/libdraw.a\
gui-$(GUI)/libgui.a\ gui-$(GUI)/libgui.a\
libc/libc.a\ libc/libc.a\
libip/libip.a\
# stupid gcc # stupid gcc
LIBS=$(LIBS1) $(LIBS1) $(LIBS1) libmachdep.a LIBS=$(LIBS1) $(LIBS1) $(LIBS1) libmachdep.a
@ -67,5 +68,8 @@ libdraw/libdraw.a:
libc/libc.a: libc/libc.a:
(cd libc; $(MAKE)) (cd libc; $(MAKE))
libip/libip.a:
(cd libip; $(MAKE))
gui-$(GUI)/libgui.a: gui-$(GUI)/libgui.a:
(cd gui-$(GUI); $(MAKE)) (cd gui-$(GUI); $(MAKE))

38
include/ip.h Normal file
View File

@ -0,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)

View File

@ -3,12 +3,14 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <netdb.h> #include <netdb.h>
#include <arpa/inet.h>
#include "u.h" #include "u.h"
#include "lib.h" #include "lib.h"
#include "dat.h" #include "dat.h"
#include "fns.h" #include "fns.h"
#include "error.h" #include "error.h"
#include "ip.h"
#include "devip.h" #include "devip.h"
@ -16,6 +18,14 @@
#undef accept #undef accept
#undef bind #undef bind
static int
family(unsigned char *addr)
{
if(isv4(addr))
return AF_INET;
return AF_INET6;
}
void void
osipinit(void) osipinit(void)
{ {
@ -26,7 +36,7 @@ osipinit(void)
} }
int int
so_socket(int type) so_socket(int type, unsigned char *addr)
{ {
int fd, one; int fd, one;
@ -41,7 +51,7 @@ so_socket(int type)
break; break;
} }
fd = socket(AF_INET, type, 0); fd = socket(family(addr), type, 0);
if(fd < 0) if(fd < 0)
oserror(); oserror();
@ -54,36 +64,52 @@ so_socket(int type)
return fd; return fd;
} }
void 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)); memset(&ss, 0, sizeof(ss));
sin.sin_family = AF_INET;
hnputs(&sin.sin_port, rport);
hnputl(&sin.sin_addr.s_addr, raddr);
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(); oserror();
} }
void void
so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) so_getsockname(int fd, unsigned char *laddr, unsigned short *lport)
{ {
socklen_t len; socklen_t len;
struct sockaddr_in sin; struct sockaddr_storage ss;
len = sizeof(sin); len = sizeof(ss);
if(getsockname(fd, (struct sockaddr*)&sin, &len) < 0) if(getsockname(fd, (struct sockaddr*)&ss, &len) < 0)
oserror(); oserror();
if(sin.sin_family != AF_INET || len != sizeof(sin)) switch(ss.ss_family){
error("not AF_INET"); case AF_INET:
v4tov6(laddr, (unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr);
*laddr = nhgetl(&sin.sin_addr.s_addr); *lport = nhgets(&((struct sockaddr_in*)&ss)->sin_port);
*lport = nhgets(&sin.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 void
@ -94,30 +120,37 @@ so_listen(int fd)
} }
int int
so_accept(int fd, unsigned long *raddr, unsigned short *rport) so_accept(int fd, unsigned char *raddr, unsigned short *rport)
{ {
int nfd; int nfd;
socklen_t len; socklen_t len;
struct sockaddr_in sin; struct sockaddr_storage ss;
len = sizeof(sin); len = sizeof(ss);
nfd = accept(fd, (struct sockaddr*)&sin, &len); nfd = accept(fd, (struct sockaddr*)&ss, &len);
if(nfd < 0) if(nfd < 0)
oserror(); oserror();
if(sin.sin_family != AF_INET || len != sizeof(sin)) switch(ss.ss_family){
error("not AF_INET"); case AF_INET:
v4tov6(raddr, (unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr);
*raddr = nhgetl(&sin.sin_addr.s_addr); *rport = nhgets(&((struct sockaddr_in*)&ss)->sin_port);
*rport = nhgets(&sin.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; return nfd;
} }
void 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; int i, one;
struct sockaddr_in sin; struct sockaddr_storage ss;
one = 1; one = 1;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0){ if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0){
@ -127,21 +160,37 @@ so_bind(int fd, int su, unsigned short port)
if(su) { if(su) {
for(i = 600; i < 1024; i++) { for(i = 600; i < 1024; i++) {
memset(&sin, 0, sizeof(sin)); memset(&ss, 0, sizeof(ss));
sin.sin_family = AF_INET; ss.ss_family = family(addr);
sin.sin_port = i;
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; return;
} }
oserror(); oserror();
} }
memset(&sin, 0, sizeof(sin)); memset(&ss, 0, sizeof(ss));
sin.sin_family = AF_INET; ss.ss_family = family(addr);
hnputs(&sin.sin_port, port);
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(); oserror();
} }
@ -170,16 +219,28 @@ so_gethostbyname(char *host, char**hostv, int n)
char* char*
hostlookup(char *host) hostlookup(char *host)
{ {
char buf[100]; char buf[INET6_ADDRSTRLEN];
uchar *p; uchar *p;
struct hostent *he; struct hostent *he;
struct addrinfo *result;
he = gethostbyname(host); he = gethostbyname(host);
if(he != 0 && he->h_addr_list[0]) { if(he != 0 && he->h_addr_list[0]) {
p = (uchar*)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]); 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 } else
strcpy(buf, host); return nil;
return strdup(buf); return strdup(buf);
} }

View File

@ -1,9 +1,11 @@
#include <windows.h> #include <windows.h>
#include <ws2tcpip.h>
#include "u.h" #include "u.h"
#include "lib.h" #include "lib.h"
#include "dat.h" #include "dat.h"
#include "fns.h" #include "fns.h"
#include "error.h" #include "error.h"
#include "ip.h"
#include "devip.h" #include "devip.h"
@ -15,6 +17,14 @@
#undef accept #undef accept
#undef bind #undef bind
static int
family(unsigned char *addr)
{
if(isv4(addr))
return AF_INET;
return AF_INET6;
}
void void
osipinit(void) osipinit(void)
{ {
@ -29,7 +39,7 @@ osipinit(void)
} }
int int
so_socket(int type) so_socket(int type, unsigned char *addr)
{ {
int fd, one; int fd, one;
@ -44,7 +54,7 @@ so_socket(int type)
break; break;
} }
fd = socket(AF_INET, type, 0); fd = socket(family(addr), type, 0);
if(fd < 0) if(fd < 0)
oserror(); oserror();
@ -59,34 +69,51 @@ so_socket(int type)
void 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)); memset(&ss, 0, sizeof(ss));
sin.sin_family = AF_INET;
hnputs(&sin.sin_port, rport);
hnputl(&sin.sin_addr.s_addr, raddr);
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(); oserror();
} }
void void
so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) so_getsockname(int fd, unsigned char *laddr, unsigned short *lport)
{ {
int len; int len;
struct sockaddr_in sin; struct sockaddr_storage ss;
len = sizeof(sin); len = sizeof(ss);
if(getsockname(fd, (struct sockaddr*)&sin, &len) < 0) if(getsockname(fd, (struct sockaddr*)&ss, &len) < 0)
oserror(); oserror();
if(sin.sin_family != AF_INET || len != sizeof(sin)) switch(ss.ss_family){
error("not AF_INET"); case AF_INET:
v4tov6(laddr, (unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr);
*laddr = nhgetl(&sin.sin_addr.s_addr); *lport = nhgets(&((struct sockaddr_in*)&ss)->sin_port);
*lport = nhgets(&sin.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 void
@ -97,53 +124,77 @@ so_listen(int fd)
} }
int int
so_accept(int fd, unsigned long *raddr, unsigned short *rport) so_accept(int fd, unsigned char *raddr, unsigned short *rport)
{ {
int nfd, len; int nfd;
struct sockaddr_in sin; int len;
struct sockaddr_storage ss;
len = sizeof(sin); len = sizeof(ss);
nfd = accept(fd, (struct sockaddr*)&sin, &len); nfd = accept(fd, (struct sockaddr*)&ss, &len);
if(nfd < 0) if(nfd < 0)
oserror(); oserror();
if(sin.sin_family != AF_INET || len != sizeof(sin)) switch(ss.ss_family){
error("not AF_INET"); case AF_INET:
v4tov6(raddr, (unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr);
*raddr = nhgetl(&sin.sin_addr.s_addr); *rport = nhgets(&((struct sockaddr_in*)&ss)->sin_port);
*rport = nhgets(&sin.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; return nfd;
} }
void 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; int i, one;
struct sockaddr_in sin; struct sockaddr_storage ss;
one = 1; one = 1;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0){ if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0){
oserrstr(); oserrstr();
print("setsockopt: %s", up->errstr); print("setsockopt: %r");
} }
if(su) { if(su) {
for(i = 600; i < 1024; i++) { for(i = 600; i < 1024; i++) {
memset(&sin, 0, sizeof(sin)); memset(&ss, 0, sizeof(ss));
sin.sin_family = AF_INET; ss.ss_family = family(addr);
sin.sin_port = i;
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; return;
} }
oserror(); oserror();
} }
memset(&sin, 0, sizeof(sin)); memset(&ss, 0, sizeof(ss));
sin.sin_family = AF_INET; ss.ss_family = family(addr);
hnputs(&sin.sin_port, port);
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(); oserror();
} }
@ -174,7 +225,7 @@ hostlookup(char *host)
{ {
char buf[100]; char buf[100];
uchar *p; uchar *p;
HOSTENT *he; struct hostent *he;
he = gethostbyname(host); he = gethostbyname(host);
if(he != 0 && he->h_addr_list[0]) { if(he != 0 && he->h_addr_list[0]) {

View File

@ -3,14 +3,10 @@
#include "dat.h" #include "dat.h"
#include "fns.h" #include "fns.h"
#include "error.h" #include "error.h"
#include "ip.h"
#include "devip.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*); void csclose(Chan*);
long csread(Chan*, void*, long, vlong); long csread(Chan*, void*, long, vlong);
long cswrite(Chan*, void*, long, vlong); long cswrite(Chan*, void*, long, vlong);
@ -37,6 +33,7 @@ enum
#define CONV(x) ((int)(((x).path >> 4)&0xfff)) #define CONV(x) ((int)(((x).path >> 4)&0xfff))
#define PROTO(x) ((int)(((x).path >> 16)&0xff)) #define PROTO(x) ((int)(((x).path >> 16)&0xff))
#define QID(p, c, y) (((p)<<16) | ((c)<<4) | (y)) #define QID(p, c, y) (((p)<<16) | ((c)<<4) | (y))
#define ipzero(x) memset(x, 0, IPaddrlen)
typedef struct Proto Proto; typedef struct Proto Proto;
typedef struct Conv Conv; typedef struct Conv Conv;
@ -48,9 +45,9 @@ struct Conv
int perm; int perm;
char owner[KNAMELEN]; char owner[KNAMELEN];
char* state; char* state;
ulong laddr; uchar laddr[IPaddrlen];
ushort lport; ushort lport;
ulong raddr; uchar raddr[IPaddrlen];
ushort rport; ushort rport;
int restricted; int restricted;
char cerr[KNAMELEN]; char cerr[KNAMELEN];
@ -71,7 +68,6 @@ struct Proto
static int np; static int np;
static Proto proto[MAXPROTO]; static Proto proto[MAXPROTO];
int eipfmt(Fmt*);
static Conv* protoclone(Proto*, char*, int); static Conv* protoclone(Proto*, char*, int);
static void setladdr(Conv*); static void setladdr(Conv*);
@ -222,7 +218,7 @@ Chan *
ipopen(Chan *c, int omode) ipopen(Chan *c, int omode)
{ {
Proto *p; Proto *p;
ulong raddr; uchar raddr[IPaddrlen];
ushort rport; ushort rport;
int perm, sfd; int perm, sfd;
Conv *cv, *lcv; Conv *cv, *lcv;
@ -286,13 +282,13 @@ ipopen(Chan *c, int omode)
case Qlisten: case Qlisten:
p = &proto[PROTO(c->qid)]; p = &proto[PROTO(c->qid)];
lcv = p->conv[CONV(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); cv = protoclone(p, up->user, sfd);
if(cv == 0) { if(cv == 0) {
close(sfd); close(sfd);
error(Enodev); error(Enodev);
} }
cv->raddr = raddr; ipmove(cv->raddr, raddr);
cv->rport = rport; cv->rport = rport;
setladdr(cv); setladdr(cv);
cv->state = "Established"; cv->state = "Established";
@ -324,8 +320,8 @@ ipclose(Chan *c)
strcpy(cc->owner, "network"); strcpy(cc->owner, "network");
cc->perm = 0666; cc->perm = 0666;
cc->state = "Closed"; cc->state = "Closed";
cc->laddr = 0; ipzero(cc->laddr);
cc->raddr = 0; ipzero(cc->raddr);
cc->lport = 0; cc->lport = 0;
cc->rport = 0; cc->rport = 0;
close(cc->sfd); close(cc->sfd);
@ -339,7 +335,7 @@ ipread(Chan *ch, void *a, long n, vlong offset)
int r; int r;
Conv *c; Conv *c;
Proto *x; Proto *x;
uchar ip[4]; uchar ip[IPaddrlen];
char buf[128], *p; char buf[128], *p;
/*print("ipread %s %lux\n", c2name(ch), (long)ch->qid.path);*/ /*print("ipread %s %lux\n", c2name(ch), (long)ch->qid.path);*/
@ -358,12 +354,12 @@ ipread(Chan *ch, void *a, long n, vlong offset)
return readstr(offset, p, n, buf); return readstr(offset, p, n, buf);
case Qremote: case Qremote:
c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; 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); sprint(buf, "%I!%d\n", ip, c->rport);
return readstr(offset, p, n, buf); return readstr(offset, p, n, buf);
case Qlocal: case Qlocal:
c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)]; 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); sprint(buf, "%I!%d\n", ip, c->lport);
return readstr(offset, p, n, buf); return readstr(offset, p, n, buf);
case Qstatus: case Qstatus:
@ -386,7 +382,7 @@ ipread(Chan *ch, void *a, long n, vlong offset)
static void static void
setladdr(Conv *c) setladdr(Conv *c)
{ {
so_getsockname(c->sfd, &c->laddr, &c->lport); so_getsockname(c->sfd, c->laddr, &c->lport);
} }
static void static void
@ -395,23 +391,27 @@ setlport(Conv *c)
if(c->restricted == 0 && c->lport == 0) if(c->restricted == 0 && c->lport == 0)
return; 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 static void
setladdrport(Conv *c, char *str) setladdrport(Conv *c, char *str)
{ {
char *p, addr[4]; char *p;
uchar addr[IPaddrlen];
p = strchr(str, '!'); p = strchr(str, '!');
if(p == 0) { if(p == 0) {
p = str; p = str;
c->laddr = 0; ipzero(c->laddr);
} }
else { else {
*p++ = 0; *p++ = 0;
parseip(addr, str); parseip(addr, str);
c->laddr = nhgetl((uchar*)addr); ipmove(c->laddr, addr);
} }
if(*p == '*') if(*p == '*')
c->lport = 0; c->lport = 0;
@ -424,14 +424,15 @@ setladdrport(Conv *c, char *str)
static char* static char*
setraddrport(Conv *c, char *str) setraddrport(Conv *c, char *str)
{ {
char *p, addr[4]; char *p;
uchar addr[IPaddrlen];
p = strchr(str, '!'); p = strchr(str, '!');
if(p == 0) if(p == 0)
return "malformed address"; return "malformed address";
*p++ = 0; *p++ = 0;
parseip(addr, str); parseip(addr, str);
c->raddr = nhgetl((uchar*)addr); ipmove(c->raddr, addr);
c->rport = atoi(p); c->rport = atoi(p);
p = strchr(p, '!'); p = strchr(p, '!');
if(p) { if(p) {
@ -480,6 +481,8 @@ ipwrite(Chan *ch, void *a, long n, vlong offset)
setlport(c); setlport(c);
break; break;
} }
if(c->sfd == -1)
c->sfd = so_socket(c->p->stype, c->raddr);
so_connect(c->sfd, c->raddr, c->rport); so_connect(c->sfd, c->raddr, c->rport);
setladdr(c); setladdr(c);
c->state = "Established"; c->state = "Established";
@ -565,13 +568,11 @@ protoclone(Proto *p, char *user, int nfd)
c->perm = 0660; c->perm = 0660;
c->state = "Closed"; c->state = "Closed";
c->restricted = 0; c->restricted = 0;
c->laddr = 0; ipzero(c->laddr);
c->raddr = 0; ipzero(c->raddr);
c->lport = 0; c->lport = 0;
c->rport = 0; c->rport = 0;
c->sfd = nfd; c->sfd = nfd;
if(nfd == -1)
c->sfd = so_socket(p->stype);
unlock(&c->r.lk); unlock(&c->r.lk);
unlock(&p->l); unlock(&p->l);
@ -579,145 +580,6 @@ protoclone(Proto *p, char *user, int nfd)
return c; 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 void
csclose(Chan *c) csclose(Chan *c)
{ {
@ -856,23 +718,17 @@ lookupport(char *s)
return 0; return 0;
} }
static ulong static int
lookuphost(char *s) lookuphost(char *s, uchar *to)
{ {
char to[4]; ipzero(to);
ulong ip; if(parseip(to, s) != -1)
memset(to, 0, sizeof to);
parseip(to, s);
ip = nhgetl(to);
if(ip != 0)
return ip;
if((s = hostlookup(s)) == nil)
return 0; return 0;
if((s = hostlookup(s)) == nil)
return -1;
parseip(to, s); parseip(to, s);
ip = nhgetl(to);
free(s); free(s);
return ip; return 0;
} }
long long
@ -880,7 +736,7 @@ cswrite(Chan *c, void *a, long n, vlong offset)
{ {
char *f[4]; char *f[4];
char *s, *ns; char *s, *ns;
ulong ip; uchar ip[IPaddrlen];
int nf, port; int nf, port;
s = malloc(n+1); s = malloc(n+1);
@ -900,8 +756,7 @@ cswrite(Chan *c, void *a, long n, vlong offset)
if(port <= 0) if(port <= 0)
error("no translation for port found"); error("no translation for port found");
ip = lookuphost(f[1]); if(lookuphost(f[1], ip) < 0)
if(ip == 0)
error("no translation for host found"); error("no translation for host found");
ns = smprint("/net/%s/clone %I!%d", f[0], ip, port); ns = smprint("/net/%s/clone %I!%d", f[0], ip, port);

View File

@ -4,14 +4,14 @@ enum
S_UDP S_UDP
}; };
int so_socket(int type); int so_socket(int, unsigned char*);
void so_connect(int, unsigned long, unsigned short); void so_connect(int, unsigned char*, unsigned short);
void so_getsockname(int, unsigned long*, unsigned short*); void so_getsockname(int, unsigned char*, unsigned short*);
void so_bind(int, int, unsigned short); void so_bind(int, int, unsigned short, unsigned char*);
void so_listen(int); void so_listen(int);
int so_send(int, void*, int, int); int so_send(int, void*, int, int);
int so_recv(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_getservbyname(char*, char*, char*);
int so_gethostbyname(char*, char**, int); int so_gethostbyname(char*, char**, int);

View File

@ -375,12 +375,6 @@ Segment* data2txt(Segment*);
Segment* dupseg(Segment**, int, int); Segment* dupseg(Segment**, int, int);
Segment* newseg(int, ulong, ulong); Segment* newseg(int, ulong, ulong);
Segment* seg(Proc*, ulong, int); 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); ulong ticks(void);
void osproc(Proc*); void osproc(Proc*);
void osnewproc(Proc*); void osnewproc(Proc*);

19
libip/Makefile Normal file
View File

@ -0,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

77
libip/bo.c Normal file
View File

@ -0,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);
}

86
libip/classmask.c Normal file
View File

@ -0,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];
}

109
libip/eipfmt.c Normal file
View File

@ -0,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)");
}

102
libip/ipaux.c Normal file
View File

@ -0,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;
}
}

184
libip/parseip.c Normal file
View File

@ -0,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;
}