diff options
| -rw-r--r-- | sys/man/3/ip | 6 | ||||
| -rw-r--r-- | sys/src/9/ip/ipmux.c | 349 |
2 files changed, 182 insertions, 173 deletions
diff --git a/sys/man/3/ip b/sys/man/3/ip index 421faeac7..6ace97e97 100644 --- a/sys/man/3/ip +++ b/sys/man/3/ip @@ -1126,6 +1126,10 @@ of a packet to match. The possible relations are: .TF "\fLdata[\fIn\fL:\fIm\fL]=\fIexpr\fR " .PD .TP +.BI ver= n +the IP version must be +.IR n . +.TP .BI proto= n the IP protocol number must be .IR n . @@ -1135,7 +1139,7 @@ bytes .I n through .I m -following the IP packet must match +following the IP header must match .IR expr . .TP .BI iph[ n : m ]= expr diff --git a/sys/src/9/ip/ipmux.c b/sys/src/9/ip/ipmux.c index 2fefe7520..84de6fa3a 100644 --- a/sys/src/9/ip/ipmux.c +++ b/sys/src/9/ip/ipmux.c @@ -14,52 +14,15 @@ typedef struct Ipmuxrock Ipmuxrock; 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 { + Tver, Tproto, Tdata, Tiph, Tdst, Tsrc, 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 *no; uchar type; /* type of field(Txxxx) */ - uchar ctype; /* tupe of comparison(Cxxxx) */ uchar len; /* length in bytes of item to compare */ uchar n; /* number of items val points to */ - short off; /* offset of comparison */ - short eoff; /* end offset of comparison */ - uchar skiphdr; /* should offset start after ipheader */ + int off; /* offset of comparison */ uchar *val; uchar *mask; uchar *e; /* val+n*len*/ - int ref; /* so we can garbage collect */ Conv *conv; }; @@ -94,6 +53,7 @@ struct Ipmuxrock static int ipmuxsprint(Ipmux*, int, char*, int); static void ipmuxkick(void *x); +static void ipmuxfree(Ipmux *f); static char* skipwhite(char *p) @@ -126,27 +86,33 @@ parseop(char **pp) Ipmux *f; 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; - off = (int)(uintptr)(ipoff->dst); - len = IPv4addrlen; + off = offsetof(Ip6hdr, dst[0]); + len = IPaddrlen; p += 3; } else if(strncmp(p, "src", 3) == 0){ type = Tsrc; - off = (int)(uintptr)(ipoff->src); - len = IPv4addrlen; + off = offsetof(Ip6hdr, src[0]); + len = IPaddrlen; p += 3; } else if(strncmp(p, "ifc", 3) == 0){ type = Tifc; - off = -IPv4addrlen; - len = IPv4addrlen; + off = -IPaddrlen; + len = IPaddrlen; p += 3; } else if(strncmp(p, "proto", 5) == 0){ type = Tproto; - off = (int)(uintptr)&(ipoff->proto); + off = offsetof(Ip6hdr, proto); len = 1; p += 5; } @@ -164,7 +130,7 @@ parseop(char **pp) return nil; p++; off = strtoul(p, &p, 0); - if(off < 0 || off > (64-IP4HDR)) + if(off < 0) return nil; p = skipwhite(p); if(*p != ':') @@ -193,11 +159,6 @@ parseop(char **pp) f->mask = nil; f->n = 1; f->ref = 1; - if(type == Tdata) - f->skiphdr = 1; - else - f->skiphdr = 0; - return f; } @@ -233,7 +194,7 @@ parseval(uchar *v, char *p, int len) static Ipmux* parsemux(char *p) { - int n, nomask; + int n; Ipmux *f; char *val; char *mask; @@ -258,7 +219,7 @@ parsemux(char *p) case Tdst: case Tifc: f->mask = smalloc(f->len); - v4parseip(f->mask, mask); + parseipmask(f->mask, mask, 0); break; case Tdata: case Tiph: @@ -268,25 +229,34 @@ parsemux(char *p) default: goto parseerror; } - nomask = 0; - } else { - nomask = 1; + } else if(f->type == Tver){ f->mask = smalloc(f->len); - memset(f->mask, 0xff, f->len); + f->mask[0] = 0xF0; } /* parse vals */ - f->n = getfields(val, vals, sizeof(vals)/sizeof(char*), 1, "|"); + f->n = getfields(val, vals, nelem(vals), 1, "|"); if(f->n == 0) goto parseerror; f->val = smalloc(f->n*f->len); v = f->val; for(n = 0; n < f->n; n++){ 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 Tdst: case Tifc: - v4parseip(v, vals[n]); + if(parseip(v, vals[n]) == -1) + goto parseerror; break; case Tproto: case Tdata: @@ -296,34 +266,11 @@ parsemux(char *p) } v += f->len; } - - f->eoff = f->off + 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; parseerror: - if(f->mask) - free(f->mask); - if(f->val) - free(f->val); - free(f); + ipmuxfree(f); return nil; } @@ -346,8 +293,7 @@ ipmuxcmp(Ipmux *a, Ipmux *b) return n; /* compare offsets, call earlier ones more specific */ - n = (a->off+((int)a->skiphdr)*(int)(uintptr)ipoff->data) - - (b->off+((int)b->skiphdr)*(int)(uintptr)ipoff->data); + n = a->off - b->off; if(n != 0) return n; @@ -417,6 +363,10 @@ ipmuxcopy(Ipmux *f) *nf = *f; nf->no = ipmuxcopy(f->no); 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->e = nf->val + f->len*f->n; memmove(nf->val, f->val, f->n*f->len); @@ -426,8 +376,10 @@ ipmuxcopy(Ipmux *f) static void ipmuxfree(Ipmux *f) { - if(f->val != nil) - free(f->val); + if(f == nil) + return; + free(f->val); + free(f->mask); free(f); } @@ -436,10 +388,8 @@ ipmuxtreefree(Ipmux *f) { if(f == nil) return; - if(f->no != nil) - ipmuxfree(f->no); - if(f->yes != nil) - ipmuxfree(f->yes); + ipmuxfree(f->no); + ipmuxfree(f->yes); ipmuxfree(f); } @@ -514,6 +464,8 @@ ipmuxremove(Ipmux **l, Ipmux *f) return ipmuxremove(&ft->no, f); } + ipmuxremove(&ft->no, f->no); + /* we found a match */ if(--(ft->ref) == 0){ /* @@ -535,8 +487,55 @@ ipmuxremove(Ipmux **l, Ipmux *f) } /* + * 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 - * 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. */ @@ -572,6 +571,18 @@ ipmuxconnect(Conv *c, char **argv, int argc) return Ebadarg; 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 */ mux = ipmuxcopy(chain); r = (Ipmuxrock*)(c->ptcl); @@ -647,7 +658,7 @@ ipmuxkick(void *x) bp = qget(c->wq); if(bp != nil) { - Myip4hdr *ih4 = (Myip4hdr*)(bp->rp); + Ip4hdr *ih4 = (Ip4hdr*)(bp->rp); if((ih4->vihl & 0xF0) != IP_VER6) 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 ipmuxiput(Proto *p, Ipifc *ifc, Block *bp) { - int len, hl; Fs *f = p->f; - uchar *m, *h, *v, *e, *ve, *hp; Conv *c; + Iplifc *lifc; Ipmux *mux; - Myip4hdr *ip; + uchar *v; + Ip4hdr *ip4; Ip6hdr *ip6; + int off, hl; - ip = (Myip4hdr*)bp->rp; - hl = (ip->vihl&0x0F)<<2; + ip4 = (Ip4hdr*)bp->rp; + if((ip4->vihl & 0xF0) == IP_VER4) { + hl = (ip4->vihl&0x0F)<<2; + ip6 = nil; + } else { + hl = IP6HDR; + ip6 = (Ip6hdr*)ip4; + } if(p->priv == nil) goto nomatch; - h = bp->rp; - len = BLEN(bp); + c = nil; + lifc = nil; - /* run the v4 filter */ + /* run the filter */ rlock(f); - c = nil; mux = f->ipmux->priv; while(mux != nil){ - if(mux->eoff > len){ - mux = mux->no; - continue; - } - hp = h + mux->off + ((int)mux->skiphdr)*hl; - switch(mux->ctype){ - case Cbyte: - if(*mux->val == *hp) - goto yes; - break; - case Cmbyte: - 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; + switch(mux->type){ + case Tifc: + if(mux->len != IPaddrlen) + goto no; + for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next) + for(v = mux->val; v < mux->e; v += IPaddrlen) + if(maskmemcmp(mux->mask, lifc->local, v, IPaddrlen) == 0) + goto yes; + goto no; + case Tdata: + off = hl; break; default: - v = mux->val; - for(e = mux->e; v < e; v = ve){ - 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 = 0; + break; } + 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; continue; yes: @@ -744,25 +747,24 @@ yes: if(c != nil){ /* tack on interface address */ 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)); return; } nomatch: /* doesn't match any filter, hand it to the specific protocol handler */ - ip = (Myip4hdr*)bp->rp; - if((ip->vihl & 0xF0) == IP_VER4) { - p = f->t2p[ip->proto]; - } else { - ip6 = (Ip6hdr*)bp->rp; + if(ip6 != nil) p = f->t2p[ip6->proto]; - } - if(p != nil && p->rcv != nil) - (*p->rcv)(p, ifc, bp); else - freeblist(bp); - return; + p = f->t2p[ip4->proto]; + if(p != nil && p->rcv != nil){ + (*p->rcv)(p, ifc, bp); + return; + } + freeblist(bp); } static int @@ -778,11 +780,14 @@ ipmuxsprint(Ipmux *mux, int level, char *buf, int len) n += snprint(buf+n, len-n, "\n"); return n; } - n += snprint(buf+n, len-n, "h[%d:%d]&", - mux->off+((int)mux->skiphdr)*((int)(uintptr)ipoff->data), - mux->off+(((int)mux->skiphdr)*((int)(uintptr)ipoff->data))+mux->len-1); - for(i = 0; i < mux->len; i++) - n += snprint(buf+n, len - n, "%2.2ux", mux->mask[i]); + n += snprint(buf+n, len-n, "%s[%d:%d]", + mux->type == Tdata ? "data": "iph", + mux->off, mux->off+mux->len-1); + if(mux->mask != nil){ + 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, "="); v = mux->val; for(j = 0; j < mux->n; j++){ |
