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:
19
libip/Makefile
Normal file
19
libip/Makefile
Normal 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
77
libip/bo.c
Normal 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
86
libip/classmask.c
Normal 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
109
libip/eipfmt.c
Normal 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
102
libip/ipaux.c
Normal 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
184
libip/parseip.c
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user