5 #include "../port/lib.h"
9 #include "../port/error.h"
14 typedef struct Ipmuxrock Ipmuxrock;
15 typedef struct Ipmux Ipmux;
17 typedef struct Myip4hdr Myip4hdr;
20 uchar vihl; /* Version and header length */
21 uchar tos; /* Type of service */
22 uchar length[2]; /* packet length */
23 uchar id[2]; /* ip->identification */
24 uchar frag[2]; /* Fragment information */
25 uchar ttl; /* Time to live */
26 uchar proto; /* Protocol */
27 uchar cksum[2]; /* Header checksum */
28 uchar src[4]; /* IP source */
29 uchar dst[4]; /* IP destination */
31 uchar data[1]; /* start of data */
45 Cbyte, /* single byte */
46 Cmbyte, /* single byte with mask */
47 Cshort, /* single short */
48 Cmshort, /* single short with mask */
49 Clong, /* single long */
50 Cmlong, /* single long with mask */
66 * a node in the decision tree
72 uchar type; /* type of field(Txxxx) */
73 uchar ctype; /* tupe of comparison(Cxxxx) */
74 uchar len; /* length in bytes of item to compare */
75 uchar n; /* number of items val points to */
76 short off; /* offset of comparison */
77 short eoff; /* end offset of comparison */
78 uchar skiphdr; /* should offset start after ipheader */
81 uchar *e; /* val+n*len*/
83 int ref; /* so we can garbage collect */
88 * someplace to hold per conversation data
95 static int ipmuxsprint(Ipmux*, int, char*, int);
96 static void ipmuxkick(void *x);
101 while(*p == ' ' || *p == '\t')
107 follows(char *p, char c)
125 int type, off, end, len;
129 if(strncmp(p, "dst", 3) == 0){
131 off = (int)(uintptr)(ipoff->dst);
135 else if(strncmp(p, "src", 3) == 0){
137 off = (int)(uintptr)(ipoff->src);
141 else if(strncmp(p, "ifc", 3) == 0){
147 else if(strncmp(p, "proto", 5) == 0){
149 off = (int)(uintptr)&(ipoff->proto);
153 else if(strncmp(p, "data", 4) == 0 || strncmp(p, "iph", 3) == 0){
154 if(strncmp(p, "data", 4) == 0) {
166 off = strtoul(p, &p, 0);
167 if(off < 0 || off > (64-IP4HDR))
175 end = strtoul(p, &p, 0);
188 f = smalloc(sizeof(*f));
207 if(x >= '0' && x <= '9')
209 else if(x >= 'a' && x <= 'f')
211 else if(x >= 'A' && x <= 'F')
221 return (htoi(p[0])<<4) | htoi(p[1]);
225 parseval(uchar *v, char *p, int len)
227 while(*p && len-- > 0){
249 val = follows(p, '=');
254 mask = follows(p, '&');
260 f->mask = smalloc(f->len);
261 v4parseip(f->mask, mask);
265 f->mask = smalloc(f->len);
266 parseval(f->mask, mask, f->len);
274 f->mask = smalloc(f->len);
275 memset(f->mask, 0xff, f->len);
279 f->n = getfields(val, vals, sizeof(vals)/sizeof(char*), 1, "|");
282 f->val = smalloc(f->n*f->len);
284 for(n = 0; n < f->n; n++){
289 v4parseip(v, vals[n]);
294 parseval(v, vals[n], f->len);
300 f->eoff = f->off + f->len;
301 f->e = f->val + f->n*f->len;
306 f->ctype = nomask ? Cbyte : Cmbyte;
309 f->ctype = nomask ? Cshort : Cmshort;
313 f->ctype = nomask ? Cifc : Cmifc;
315 f->ctype = nomask ? Clong : Cmlong;
331 * Compare relative ordering of two ipmuxs. This doesn't compare the
332 * values, just the fields being looked at.
334 * returns: <0 if a is a more specific match
335 * 0 if a and b are matching on the same fields
336 * >0 if b is a more specific match
339 ipmuxcmp(Ipmux *a, Ipmux *b)
343 /* compare types, lesser ones are more important */
344 n = a->type - b->type;
348 /* compare offsets, call earlier ones more specific */
349 n = (a->off+((int)a->skiphdr)*(int)(uintptr)ipoff->data) -
350 (b->off+((int)b->skiphdr)*(int)(uintptr)ipoff->data);
354 /* compare match lengths, longer ones are more specific */
360 * if we get here we have two entries matching
361 * the same bytes of the record. Now check
362 * the mask for equality. Longer masks are
365 if(a->mask != nil && b->mask == nil)
367 if(a->mask == nil && b->mask != nil)
369 if(a->mask != nil && b->mask != nil){
370 n = memcmp(b->mask, a->mask, a->len);
378 * Compare the values of two ipmuxs. We're assuming that ipmuxcmp
379 * returned 0 comparing them.
382 ipmuxvalcmp(Ipmux *a, Ipmux *b)
386 n = b->len*b->n - a->len*a->n;
389 return memcmp(a->val, b->val, a->len*a->n);
393 * add onto an existing ipmux chain in the canonical comparison
397 ipmuxchain(Ipmux **l, Ipmux *f)
399 for(; *l; l = &(*l)->yes)
400 if(ipmuxcmp(f, *l) < 0)
416 nf = smalloc(sizeof *nf);
418 nf->no = ipmuxcopy(f->no);
419 nf->yes = ipmuxcopy(f->yes);
420 nf->val = smalloc(f->n*f->len);
421 nf->e = nf->val + f->len*f->n;
422 memmove(nf->val, f->val, f->n*f->len);
435 ipmuxtreefree(Ipmux *f)
450 ipmuxmerge(Ipmux *a, Ipmux *b)
462 a->yes = ipmuxmerge(a->yes, b);
463 a->no = ipmuxmerge(a->no, f);
468 b->yes = ipmuxmerge(b->yes, a);
469 b->no = ipmuxmerge(b->no, f);
472 if(ipmuxvalcmp(a, b) == 0){
473 a->yes = ipmuxmerge(a->yes, b->yes);
474 a->no = ipmuxmerge(a->no, b->no);
479 a->no = ipmuxmerge(a->no, b);
484 * remove a chain from a demux tree. This is like merging accept that
485 * we remove instead of insert.
488 ipmuxremove(Ipmux **l, Ipmux *f)
494 return 0; /* we've removed it all */
501 /* *l is maching an earlier field, descend both paths */
502 rv = ipmuxremove(&ft->yes, f);
503 rv += ipmuxremove(&ft->no, f);
507 /* f represents an earlier field than *l, this should be impossible */
511 /* if we get here f and *l are comparing the same fields */
512 if(ipmuxvalcmp(ft, f) != 0){
513 /* different values mean mutually exclusive */
514 return ipmuxremove(&ft->no, f);
517 /* we found a match */
518 if(--(ft->ref) == 0){
520 * a dead node implies the whole yes side is also dead.
521 * since our chain is constrained to be on that side,
524 ipmuxtreefree(ft->yes);
531 * free the rest of the chain. it is constrained to match the
534 return ipmuxremove(&ft->yes, f->yes);
538 * connection request is a semi separated list of filters
539 * e.g. proto=17;data[0:4]=11aa22bb;ifc=135.104.9.2&255.255.255.0
541 * there's no protection against overlapping specs.
544 ipmuxconnect(Conv *c, char **argv, int argc)
557 n = getfields(argv[1], field, nelem(field), 1, ";");
563 for(i = 0; i < n; i++){
564 mux = parsemux(field[i]);
566 ipmuxtreefree(chain);
569 ipmuxchain(&chain, mux);
575 /* save a copy of the chain so we can later remove it */
576 mux = ipmuxcopy(chain);
577 r = (Ipmuxrock*)(c->ptcl);
580 /* add the chain to the protocol demultiplexor tree */
582 f->ipmux->priv = ipmuxmerge(f->ipmux->priv, mux);
590 ipmuxstate(Conv *c, char *state, int n)
594 r = (Ipmuxrock*)(c->ptcl);
595 return ipmuxsprint(r->chain, 0, state, n);
603 c->rq = qopen(64*1024, Qmsg, 0, c);
604 c->wq = qopen(64*1024, Qkick, ipmuxkick, c);
605 r = (Ipmuxrock*)(c->ptcl);
610 ipmuxannounce(Conv*, char**, int)
612 return "ipmux does not support announce";
621 r = (Ipmuxrock*)(c->ptcl);
626 ipmove(c->laddr, IPnoaddr);
627 ipmove(c->raddr, IPnoaddr);
632 ipmuxremove(&(c->p->priv), r->chain);
634 ipmuxtreefree(r->chain);
639 * takes a fully formed ip packet and just passes it down
650 Myip4hdr *ih4 = (Myip4hdr*)(bp->rp);
652 if((ih4->vihl & 0xF0) != IP_VER6)
653 ipoput4(c->p->f, bp, 0, ih4->ttl, ih4->tos, nil);
655 ipoput6(c->p->f, bp, 0, ((Ip6hdr*)ih4)->ttl, 0, nil);
660 ipmuxiput(Proto *p, Ipifc *ifc, Block *bp)
664 uchar *m, *h, *v, *e, *ve, *hp;
670 ip = (Myip4hdr*)bp->rp;
671 hl = (ip->vihl&0x0F)<<2;
679 /* run the v4 filter */
682 mux = f->ipmux->priv;
688 hp = h + mux->off + ((int)mux->skiphdr)*hl;
695 if((*hp & *mux->mask) == *mux->val)
699 if(*((ushort*)mux->val) == *(ushort*)hp)
703 if((*(ushort*)hp & (*((ushort*)mux->mask))) == *((ushort*)mux->val))
707 if(*((ulong*)mux->val) == *(ulong*)hp)
711 if((*(ulong*)hp & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
715 if(*((ulong*)mux->val) == *(ulong*)(ifc->lifc->local + IPv4off))
719 if((*(ulong*)(ifc->lifc->local + IPv4off) & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
724 for(e = mux->e; v < e; v = ve){
727 for(ve = v + mux->len; v < ve; v++){
728 if((*hp++ & *m++) != *v)
745 /* tack on interface address */
746 bp = padblock(bp, IPaddrlen);
747 ipmove(bp->rp, ifc->lifc->local);
748 qpass(c->rq, concatblock(bp));
753 /* doesn't match any filter, hand it to the specific protocol handler */
754 ip = (Myip4hdr*)bp->rp;
755 if((ip->vihl & 0xF0) == IP_VER4) {
756 p = f->t2p[ip->proto];
758 ip6 = (Ip6hdr*)bp->rp;
759 p = f->t2p[ip6->proto];
762 (*p->rcv)(p, ifc, bp);
769 ipmuxsprint(Ipmux *mux, int level, char *buf, int len)
775 for(i = 0; i < level; i++)
776 n += snprint(buf+n, len-n, " ");
778 n += snprint(buf+n, len-n, "\n");
781 n += snprint(buf+n, len-n, "h[%d:%d]&",
782 mux->off+((int)mux->skiphdr)*((int)(uintptr)ipoff->data),
783 mux->off+(((int)mux->skiphdr)*((int)(uintptr)ipoff->data))+mux->len-1);
784 for(i = 0; i < mux->len; i++)
785 n += snprint(buf+n, len - n, "%2.2ux", mux->mask[i]);
786 n += snprint(buf+n, len-n, "=");
788 for(j = 0; j < mux->n; j++){
789 for(i = 0; i < mux->len; i++)
790 n += snprint(buf+n, len - n, "%2.2ux", *v++);
791 n += snprint(buf+n, len-n, "|");
793 n += snprint(buf+n, len-n, "\n");
795 n += ipmuxsprint(mux->no, level, buf+n, len-n);
796 n += ipmuxsprint(mux->yes, level, buf+n, len-n);
801 ipmuxstats(Proto *p, char *buf, int len)
807 n = ipmuxsprint(p->priv, 0, buf, len);
818 ipmux = smalloc(sizeof(Proto));
820 ipmux->name = "ipmux";
821 ipmux->connect = ipmuxconnect;
822 ipmux->announce = ipmuxannounce;
823 ipmux->state = ipmuxstate;
824 ipmux->create = ipmuxcreate;
825 ipmux->close = ipmuxclose;
826 ipmux->rcv = ipmuxiput;
829 ipmux->stats = ipmuxstats;
832 ipmux->ptclsize = sizeof(Ipmuxrock);
834 f->ipmux = ipmux; /* hack for Fsrcvpcol */