devip: implement ipv6 support in ipmux packet filter

Added a ver= field to the filter to distinguish the ip version.
By default, a filter is parsed as ipv6, and after parsing
proto, src and dst fields are converted to ipv4. When no
ver= field is specified, a ip version filter is implicitely
added and both protocols are parsed.

This change also gets rid of the fast compare types as the
filed might not be aligned correctly in the packet.

This also fixes the ifc= filter, as we have to check any
local address.
This commit is contained in:
cinap_lenrek 2020-06-07 16:56:01 +02:00
parent 8f087e019f
commit 5474646164
2 changed files with 183 additions and 174 deletions

View file

@ -1126,6 +1126,10 @@ of a packet to match. The possible relations are:
.TF "\fLdata[\fIn\fL:\fIm\fL]=\fIexpr\fR " .TF "\fLdata[\fIn\fL:\fIm\fL]=\fIexpr\fR "
.PD .PD
.TP .TP
.BI ver= n
the IP version must be
.IR n .
.TP
.BI proto= n .BI proto= n
the IP protocol number must be the IP protocol number must be
.IR n . .IR n .
@ -1135,7 +1139,7 @@ bytes
.I n .I n
through through
.I m .I m
following the IP packet must match following the IP header must match
.IR expr . .IR expr .
.TP .TP
.BI iph[ n : m ]= expr .BI iph[ n : m ]= expr

View file

@ -14,52 +14,15 @@
typedef struct Ipmuxrock Ipmuxrock; typedef struct Ipmuxrock Ipmuxrock;
typedef struct Ipmux Ipmux; typedef struct Ipmux Ipmux;
typedef struct Myip4hdr Myip4hdr;
struct Myip4hdr
{
uchar vihl; /* Version and header length */
uchar tos; /* Type of service */
uchar length[2]; /* packet length */
uchar id[2]; /* ip->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 dst[4]; /* IP destination */
uchar data[1]; /* start of data */
};
Myip4hdr *ipoff = 0;
enum enum
{ {
Tver,
Tproto, Tproto,
Tdata, Tdata,
Tiph, Tiph,
Tdst, Tdst,
Tsrc, Tsrc,
Tifc, Tifc,
Cother = 0,
Cbyte, /* single byte */
Cmbyte, /* single byte with mask */
Cshort, /* single short */
Cmshort, /* single short with mask */
Clong, /* single long */
Cmlong, /* single long with mask */
Cifc,
Cmifc,
};
char *ftname[] =
{
[Tproto] "proto",
[Tdata] "data",
[Tiph] "iph",
[Tdst] "dst",
[Tsrc] "src",
[Tifc] "ifc",
}; };
/* /*
@ -70,16 +33,12 @@ struct Ipmux
Ipmux *yes; Ipmux *yes;
Ipmux *no; Ipmux *no;
uchar type; /* type of field(Txxxx) */ uchar type; /* type of field(Txxxx) */
uchar ctype; /* tupe of comparison(Cxxxx) */
uchar len; /* length in bytes of item to compare */ uchar len; /* length in bytes of item to compare */
uchar n; /* number of items val points to */ uchar n; /* number of items val points to */
short off; /* offset of comparison */ int off; /* offset of comparison */
short eoff; /* end offset of comparison */
uchar skiphdr; /* should offset start after ipheader */
uchar *val; uchar *val;
uchar *mask; uchar *mask;
uchar *e; /* val+n*len*/ uchar *e; /* val+n*len*/
int ref; /* so we can garbage collect */ int ref; /* so we can garbage collect */
Conv *conv; Conv *conv;
}; };
@ -94,6 +53,7 @@ struct Ipmuxrock
static int ipmuxsprint(Ipmux*, int, char*, int); static int ipmuxsprint(Ipmux*, int, char*, int);
static void ipmuxkick(void *x); static void ipmuxkick(void *x);
static void ipmuxfree(Ipmux *f);
static char* static char*
skipwhite(char *p) skipwhite(char *p)
@ -126,27 +86,33 @@ parseop(char **pp)
Ipmux *f; Ipmux *f;
p = skipwhite(p); p = skipwhite(p);
if(strncmp(p, "dst", 3) == 0){ if(strncmp(p, "ver", 3) == 0){
type = Tver;
off = 0;
len = 1;
p += 3;
}
else if(strncmp(p, "dst", 3) == 0){
type = Tdst; type = Tdst;
off = (int)(uintptr)(ipoff->dst); off = offsetof(Ip6hdr, dst[0]);
len = IPv4addrlen; len = IPaddrlen;
p += 3; p += 3;
} }
else if(strncmp(p, "src", 3) == 0){ else if(strncmp(p, "src", 3) == 0){
type = Tsrc; type = Tsrc;
off = (int)(uintptr)(ipoff->src); off = offsetof(Ip6hdr, src[0]);
len = IPv4addrlen; len = IPaddrlen;
p += 3; p += 3;
} }
else if(strncmp(p, "ifc", 3) == 0){ else if(strncmp(p, "ifc", 3) == 0){
type = Tifc; type = Tifc;
off = -IPv4addrlen; off = -IPaddrlen;
len = IPv4addrlen; len = IPaddrlen;
p += 3; p += 3;
} }
else if(strncmp(p, "proto", 5) == 0){ else if(strncmp(p, "proto", 5) == 0){
type = Tproto; type = Tproto;
off = (int)(uintptr)&(ipoff->proto); off = offsetof(Ip6hdr, proto);
len = 1; len = 1;
p += 5; p += 5;
} }
@ -164,7 +130,7 @@ parseop(char **pp)
return nil; return nil;
p++; p++;
off = strtoul(p, &p, 0); off = strtoul(p, &p, 0);
if(off < 0 || off > (64-IP4HDR)) if(off < 0)
return nil; return nil;
p = skipwhite(p); p = skipwhite(p);
if(*p != ':') if(*p != ':')
@ -193,11 +159,6 @@ parseop(char **pp)
f->mask = nil; f->mask = nil;
f->n = 1; f->n = 1;
f->ref = 1; f->ref = 1;
if(type == Tdata)
f->skiphdr = 1;
else
f->skiphdr = 0;
return f; return f;
} }
@ -233,7 +194,7 @@ parseval(uchar *v, char *p, int len)
static Ipmux* static Ipmux*
parsemux(char *p) parsemux(char *p)
{ {
int n, nomask; int n;
Ipmux *f; Ipmux *f;
char *val; char *val;
char *mask; char *mask;
@ -258,7 +219,7 @@ parsemux(char *p)
case Tdst: case Tdst:
case Tifc: case Tifc:
f->mask = smalloc(f->len); f->mask = smalloc(f->len);
v4parseip(f->mask, mask); parseipmask(f->mask, mask, 0);
break; break;
case Tdata: case Tdata:
case Tiph: case Tiph:
@ -268,25 +229,34 @@ parsemux(char *p)
default: default:
goto parseerror; goto parseerror;
} }
nomask = 0; } else if(f->type == Tver){
} else {
nomask = 1;
f->mask = smalloc(f->len); f->mask = smalloc(f->len);
memset(f->mask, 0xff, f->len); f->mask[0] = 0xF0;
} }
/* parse vals */ /* parse vals */
f->n = getfields(val, vals, sizeof(vals)/sizeof(char*), 1, "|"); f->n = getfields(val, vals, nelem(vals), 1, "|");
if(f->n == 0) if(f->n == 0)
goto parseerror; goto parseerror;
f->val = smalloc(f->n*f->len); f->val = smalloc(f->n*f->len);
v = f->val; v = f->val;
for(n = 0; n < f->n; n++){ for(n = 0; n < f->n; n++){
switch(f->type){ switch(f->type){
case Tver:
if(f->n != 1)
goto parseerror;
if(strcmp(vals[n], "6") == 0)
*v = IP_VER6;
else if(strcmp(vals[n], "4") == 0)
*v = IP_VER4;
else
goto parseerror;
break;
case Tsrc: case Tsrc:
case Tdst: case Tdst:
case Tifc: case Tifc:
v4parseip(v, vals[n]); if(parseip(v, vals[n]) == -1)
goto parseerror;
break; break;
case Tproto: case Tproto:
case Tdata: case Tdata:
@ -296,34 +266,11 @@ parsemux(char *p)
} }
v += f->len; v += f->len;
} }
f->eoff = f->off + f->len;
f->e = f->val + f->n*f->len; f->e = f->val + f->n*f->len;
f->ctype = Cother;
if(f->n == 1){
switch(f->len){
case 1:
f->ctype = nomask ? Cbyte : Cmbyte;
break;
case 2:
f->ctype = nomask ? Cshort : Cmshort;
break;
case 4:
if(f->type == Tifc)
f->ctype = nomask ? Cifc : Cmifc;
else
f->ctype = nomask ? Clong : Cmlong;
break;
}
}
return f; return f;
parseerror: parseerror:
if(f->mask) ipmuxfree(f);
free(f->mask);
if(f->val)
free(f->val);
free(f);
return nil; return nil;
} }
@ -346,8 +293,7 @@ ipmuxcmp(Ipmux *a, Ipmux *b)
return n; return n;
/* compare offsets, call earlier ones more specific */ /* compare offsets, call earlier ones more specific */
n = (a->off+((int)a->skiphdr)*(int)(uintptr)ipoff->data) - n = a->off - b->off;
(b->off+((int)b->skiphdr)*(int)(uintptr)ipoff->data);
if(n != 0) if(n != 0)
return n; return n;
@ -417,6 +363,10 @@ ipmuxcopy(Ipmux *f)
*nf = *f; *nf = *f;
nf->no = ipmuxcopy(f->no); nf->no = ipmuxcopy(f->no);
nf->yes = ipmuxcopy(f->yes); nf->yes = ipmuxcopy(f->yes);
if(f->mask != nil){
nf->mask = smalloc(f->len);
memmove(nf->mask, f->mask, f->len);
}
nf->val = smalloc(f->n*f->len); nf->val = smalloc(f->n*f->len);
nf->e = nf->val + f->len*f->n; nf->e = nf->val + f->len*f->n;
memmove(nf->val, f->val, f->n*f->len); memmove(nf->val, f->val, f->n*f->len);
@ -426,8 +376,10 @@ ipmuxcopy(Ipmux *f)
static void static void
ipmuxfree(Ipmux *f) ipmuxfree(Ipmux *f)
{ {
if(f->val != nil) if(f == nil)
free(f->val); return;
free(f->val);
free(f->mask);
free(f); free(f);
} }
@ -436,10 +388,8 @@ ipmuxtreefree(Ipmux *f)
{ {
if(f == nil) if(f == nil)
return; return;
if(f->no != nil) ipmuxfree(f->no);
ipmuxfree(f->no); ipmuxfree(f->yes);
if(f->yes != nil)
ipmuxfree(f->yes);
ipmuxfree(f); ipmuxfree(f);
} }
@ -514,6 +464,8 @@ ipmuxremove(Ipmux **l, Ipmux *f)
return ipmuxremove(&ft->no, f); return ipmuxremove(&ft->no, f);
} }
ipmuxremove(&ft->no, f->no);
/* we found a match */ /* we found a match */
if(--(ft->ref) == 0){ if(--(ft->ref) == 0){
/* /*
@ -534,9 +486,56 @@ ipmuxremove(Ipmux **l, Ipmux *f)
return ipmuxremove(&ft->yes, f->yes); return ipmuxremove(&ft->yes, f->yes);
} }
/*
* convert to ipv4 filter
*/
static Ipmux*
ipmuxconv4(Ipmux *f)
{
int i, n;
if(f == nil)
return nil;
switch(f->type){
case Tproto:
f->off = offsetof(Ip4hdr, proto);
break;
case Tdst:
f->off = offsetof(Ip4hdr, dst[0]);
if(0){
case Tsrc:
f->off = offsetof(Ip4hdr, src[0]);
}
if(f->len != IPaddrlen)
break;
n = 0;
for(i = 0; i < f->n; i++){
if(isv4(f->val + i*IPaddrlen)){
memmove(f->val + n*IPv4addrlen, f->val + i*IPaddrlen + IPv4off, IPv4addrlen);
n++;
}
}
if(n == 0){
ipmuxtreefree(f);
return nil;
}
f->n = n;
f->len = IPv4addrlen;
if(f->mask != nil)
memmove(f->mask, f->mask+IPv4off, IPv4addrlen);
}
f->e = f->val + f->n*f->len;
f->yes = ipmuxconv4(f->yes);
f->no = ipmuxconv4(f->no);
return f;
}
/* /*
* connection request is a semi separated list of filters * connection request is a semi separated list of filters
* e.g. proto=17;data[0:4]=11aa22bb;ifc=135.104.9.2&255.255.255.0 * e.g. ver=4;proto=17;data[0:4]=11aa22bb;ifc=135.104.9.2&255.255.255.0
* *
* there's no protection against overlapping specs. * there's no protection against overlapping specs.
*/ */
@ -572,6 +571,18 @@ ipmuxconnect(Conv *c, char **argv, int argc)
return Ebadarg; return Ebadarg;
mux->conv = c; mux->conv = c;
if(chain->type != Tver) {
char ver6[] = "ver=6";
mux = parsemux(ver6);
mux->yes = chain;
mux->no = ipmuxcopy(chain);
chain = mux;
}
if(*chain->val == IP_VER4)
chain->yes = ipmuxconv4(chain->yes);
else
chain->no = ipmuxconv4(chain->no);
/* save a copy of the chain so we can later remove it */ /* save a copy of the chain so we can later remove it */
mux = ipmuxcopy(chain); mux = ipmuxcopy(chain);
r = (Ipmuxrock*)(c->ptcl); r = (Ipmuxrock*)(c->ptcl);
@ -647,7 +658,7 @@ ipmuxkick(void *x)
bp = qget(c->wq); bp = qget(c->wq);
if(bp != nil) { if(bp != nil) {
Myip4hdr *ih4 = (Myip4hdr*)(bp->rp); Ip4hdr *ih4 = (Ip4hdr*)(bp->rp);
if((ih4->vihl & 0xF0) != IP_VER6) if((ih4->vihl & 0xF0) != IP_VER6)
ipoput4(c->p->f, bp, 0, ih4->ttl, ih4->tos, nil); ipoput4(c->p->f, bp, 0, ih4->ttl, ih4->tos, nil);
@ -656,82 +667,74 @@ ipmuxkick(void *x)
} }
} }
static int
maskmemcmp(uchar *m, uchar *v, uchar *c, int n)
{
int i;
if(m == nil)
return memcmp(v, c, n) != 0;
for(i = 0; i < n; i++)
if((v[i] & m[i]) != c[i])
return 1;
return 0;
}
static void static void
ipmuxiput(Proto *p, Ipifc *ifc, Block *bp) ipmuxiput(Proto *p, Ipifc *ifc, Block *bp)
{ {
int len, hl;
Fs *f = p->f; Fs *f = p->f;
uchar *m, *h, *v, *e, *ve, *hp;
Conv *c; Conv *c;
Iplifc *lifc;
Ipmux *mux; Ipmux *mux;
Myip4hdr *ip; uchar *v;
Ip4hdr *ip4;
Ip6hdr *ip6; Ip6hdr *ip6;
int off, hl;
ip = (Myip4hdr*)bp->rp; ip4 = (Ip4hdr*)bp->rp;
hl = (ip->vihl&0x0F)<<2; if((ip4->vihl & 0xF0) == IP_VER4) {
hl = (ip4->vihl&0x0F)<<2;
ip6 = nil;
} else {
hl = IP6HDR;
ip6 = (Ip6hdr*)ip4;
}
if(p->priv == nil) if(p->priv == nil)
goto nomatch; goto nomatch;
h = bp->rp;
len = BLEN(bp);
/* run the v4 filter */
rlock(f);
c = nil; c = nil;
lifc = nil;
/* run the filter */
rlock(f);
mux = f->ipmux->priv; mux = f->ipmux->priv;
while(mux != nil){ while(mux != nil){
if(mux->eoff > len){ switch(mux->type){
mux = mux->no; case Tifc:
continue; if(mux->len != IPaddrlen)
} goto no;
hp = h + mux->off + ((int)mux->skiphdr)*hl; for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
switch(mux->ctype){ for(v = mux->val; v < mux->e; v += IPaddrlen)
case Cbyte: if(maskmemcmp(mux->mask, lifc->local, v, IPaddrlen) == 0)
if(*mux->val == *hp) goto yes;
goto yes; goto no;
break; case Tdata:
case Cmbyte: off = hl;
if((*hp & *mux->mask) == *mux->val)
goto yes;
break;
case Cshort:
if(*((ushort*)mux->val) == *(ushort*)hp)
goto yes;
break;
case Cmshort:
if((*(ushort*)hp & (*((ushort*)mux->mask))) == *((ushort*)mux->val))
goto yes;
break;
case Clong:
if(*((ulong*)mux->val) == *(ulong*)hp)
goto yes;
break;
case Cmlong:
if((*(ulong*)hp & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
goto yes;
break;
case Cifc:
if(*((ulong*)mux->val) == *(ulong*)(ifc->lifc->local + IPv4off))
goto yes;
break;
case Cmifc:
if((*(ulong*)(ifc->lifc->local + IPv4off) & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
goto yes;
break; break;
default: default:
v = mux->val; off = 0;
for(e = mux->e; v < e; v = ve){ break;
m = mux->mask;
hp = h + mux->off;
for(ve = v + mux->len; v < ve; v++){
if((*hp++ & *m++) != *v)
break;
}
if(v == ve)
goto yes;
}
} }
off += mux->off;
if(off < 0 || off + mux->len > BLEN(bp))
goto no;
for(v = mux->val; v < mux->e; v += mux->len)
if(maskmemcmp(mux->mask, bp->rp + off, v, mux->len) == 0)
goto yes;
no:
mux = mux->no; mux = mux->no;
continue; continue;
yes: yes:
@ -744,25 +747,24 @@ yes:
if(c != nil){ if(c != nil){
/* tack on interface address */ /* tack on interface address */
bp = padblock(bp, IPaddrlen); bp = padblock(bp, IPaddrlen);
ipmove(bp->rp, ifc->lifc->local); if(lifc == nil)
lifc = ifc->lifc;
ipmove(bp->rp, lifc != nil ? lifc->local : IPnoaddr);
qpass(c->rq, concatblock(bp)); qpass(c->rq, concatblock(bp));
return; return;
} }
nomatch: nomatch:
/* doesn't match any filter, hand it to the specific protocol handler */ /* doesn't match any filter, hand it to the specific protocol handler */
ip = (Myip4hdr*)bp->rp; if(ip6 != nil)
if((ip->vihl & 0xF0) == IP_VER4) {
p = f->t2p[ip->proto];
} else {
ip6 = (Ip6hdr*)bp->rp;
p = f->t2p[ip6->proto]; p = f->t2p[ip6->proto];
}
if(p != nil && p->rcv != nil)
(*p->rcv)(p, ifc, bp);
else else
freeblist(bp); p = f->t2p[ip4->proto];
return; if(p != nil && p->rcv != nil){
(*p->rcv)(p, ifc, bp);
return;
}
freeblist(bp);
} }
static int static int
@ -778,11 +780,14 @@ ipmuxsprint(Ipmux *mux, int level, char *buf, int len)
n += snprint(buf+n, len-n, "\n"); n += snprint(buf+n, len-n, "\n");
return n; return n;
} }
n += snprint(buf+n, len-n, "h[%d:%d]&", n += snprint(buf+n, len-n, "%s[%d:%d]",
mux->off+((int)mux->skiphdr)*((int)(uintptr)ipoff->data), mux->type == Tdata ? "data": "iph",
mux->off+(((int)mux->skiphdr)*((int)(uintptr)ipoff->data))+mux->len-1); mux->off, mux->off+mux->len-1);
for(i = 0; i < mux->len; i++) if(mux->mask != nil){
n += snprint(buf+n, len - n, "%2.2ux", mux->mask[i]); n += snprint(buf+n, len-n, "&");
for(i = 0; i < mux->len; i++)
n += snprint(buf+n, len - n, "%2.2ux", mux->mask[i]);
}
n += snprint(buf+n, len-n, "="); n += snprint(buf+n, len-n, "=");
v = mux->val; v = mux->val;
for(j = 0; j < mux->n; j++){ for(j = 0; j < mux->n; j++){