5 #include "../port/lib.h"
9 #include "../port/error.h"
14 typedef struct Ipmuxrock Ipmuxrock;
15 typedef struct Ipmux Ipmux;
29 * a node in the decision tree
35 uchar type; /* type of field(Txxxx) */
36 uchar len; /* length in bytes of item to compare */
37 uchar n; /* number of items val points to */
38 int off; /* offset of comparison */
41 uchar *e; /* val+n*len*/
42 int ref; /* so we can garbage collect */
47 * someplace to hold per conversation data
54 static int ipmuxsprint(Ipmux*, int, char*, int);
55 static void ipmuxkick(void *x);
56 static void ipmuxfree(Ipmux *f);
61 while(*p == ' ' || *p == '\t')
67 follows(char *p, char c)
85 int type, off, end, len;
89 if(strncmp(p, "ver", 3) == 0){
95 else if(strncmp(p, "dst", 3) == 0){
97 off = offsetof(Ip6hdr, dst[0]);
101 else if(strncmp(p, "src", 3) == 0){
103 off = offsetof(Ip6hdr, src[0]);
107 else if(strncmp(p, "ifc", 3) == 0){
113 else if(strncmp(p, "proto", 5) == 0){
115 off = offsetof(Ip6hdr, proto);
119 else if(strncmp(p, "data", 4) == 0 || strncmp(p, "iph", 3) == 0){
120 if(strncmp(p, "data", 4) == 0) {
132 off = strtoul(p, &p, 0);
141 end = strtoul(p, &p, 0);
154 f = smalloc(sizeof(*f));
168 if(x >= '0' && x <= '9')
170 else if(x >= 'a' && x <= 'f')
172 else if(x >= 'A' && x <= 'F')
182 return (htoi(p[0])<<4) | htoi(p[1]);
186 parseval(uchar *v, char *p, int len)
188 while(*p && len-- > 0){
210 val = follows(p, '=');
215 mask = follows(p, '&');
221 f->mask = smalloc(f->len);
222 parseipmask(f->mask, mask, 0);
226 f->mask = smalloc(f->len);
227 parseval(f->mask, mask, f->len);
232 } else if(f->type == Tver){
233 f->mask = smalloc(f->len);
238 f->n = getfields(val, vals, nelem(vals), 1, "|");
241 f->val = smalloc(f->n*f->len);
243 for(n = 0; n < f->n; n++){
248 if(strcmp(vals[n], "6") == 0)
250 else if(strcmp(vals[n], "4") == 0)
258 if(parseip(v, vals[n]) == -1)
264 parseval(v, vals[n], f->len);
269 f->e = f->val + f->n*f->len;
278 * Compare relative ordering of two ipmuxs. This doesn't compare the
279 * values, just the fields being looked at.
281 * returns: <0 if a is a more specific match
282 * 0 if a and b are matching on the same fields
283 * >0 if b is a more specific match
286 ipmuxcmp(Ipmux *a, Ipmux *b)
290 /* compare types, lesser ones are more important */
291 n = a->type - b->type;
295 /* compare offsets, call earlier ones more specific */
300 /* compare match lengths, longer ones are more specific */
306 * if we get here we have two entries matching
307 * the same bytes of the record. Now check
308 * the mask for equality. Longer masks are
311 if(a->mask != nil && b->mask == nil)
313 if(a->mask == nil && b->mask != nil)
315 if(a->mask != nil && b->mask != nil){
316 n = memcmp(b->mask, a->mask, a->len);
324 * Compare the values of two ipmuxs. We're assuming that ipmuxcmp
325 * returned 0 comparing them.
328 ipmuxvalcmp(Ipmux *a, Ipmux *b)
332 n = b->len*b->n - a->len*a->n;
335 return memcmp(a->val, b->val, a->len*a->n);
339 * add onto an existing ipmux chain in the canonical comparison
343 ipmuxchain(Ipmux **l, Ipmux *f)
345 for(; *l; l = &(*l)->yes)
346 if(ipmuxcmp(f, *l) < 0)
362 nf = smalloc(sizeof *nf);
364 nf->no = ipmuxcopy(f->no);
365 nf->yes = ipmuxcopy(f->yes);
367 nf->mask = smalloc(f->len);
368 memmove(nf->mask, f->mask, f->len);
370 nf->val = smalloc(f->n*f->len);
371 nf->e = nf->val + f->len*f->n;
372 memmove(nf->val, f->val, f->n*f->len);
387 ipmuxtreefree(Ipmux *f)
400 ipmuxmerge(Ipmux *a, Ipmux *b)
412 a->yes = ipmuxmerge(a->yes, b);
413 a->no = ipmuxmerge(a->no, f);
418 b->yes = ipmuxmerge(b->yes, a);
419 b->no = ipmuxmerge(b->no, f);
422 if(ipmuxvalcmp(a, b) == 0){
423 a->yes = ipmuxmerge(a->yes, b->yes);
424 a->no = ipmuxmerge(a->no, b->no);
429 a->no = ipmuxmerge(a->no, b);
434 * remove a chain from a demux tree. This is like merging accept that
435 * we remove instead of insert.
438 ipmuxremove(Ipmux **l, Ipmux *f)
444 return 0; /* we've removed it all */
451 /* *l is maching an earlier field, descend both paths */
452 rv = ipmuxremove(&ft->yes, f);
453 rv += ipmuxremove(&ft->no, f);
457 /* f represents an earlier field than *l, this should be impossible */
461 /* if we get here f and *l are comparing the same fields */
462 if(ipmuxvalcmp(ft, f) != 0){
463 /* different values mean mutually exclusive */
464 return ipmuxremove(&ft->no, f);
467 ipmuxremove(&ft->no, f->no);
469 /* we found a match */
470 if(--(ft->ref) == 0){
472 * a dead node implies the whole yes side is also dead.
473 * since our chain is constrained to be on that side,
476 ipmuxtreefree(ft->yes);
483 * free the rest of the chain. it is constrained to match the
486 return ipmuxremove(&ft->yes, f->yes);
490 * convert to ipv4 filter
502 f->off = offsetof(Ip4hdr, proto);
505 f->off = offsetof(Ip4hdr, dst[0]);
508 f->off = offsetof(Ip4hdr, src[0]);
510 if(f->len != IPaddrlen)
513 for(i = 0; i < f->n; i++){
514 if(isv4(f->val + i*IPaddrlen)){
515 memmove(f->val + n*IPv4addrlen, f->val + i*IPaddrlen + IPv4off, IPv4addrlen);
524 f->len = IPv4addrlen;
526 memmove(f->mask, f->mask+IPv4off, IPv4addrlen);
528 f->e = f->val + f->n*f->len;
530 f->yes = ipmuxconv4(f->yes);
531 f->no = ipmuxconv4(f->no);
537 * connection request is a semi separated list of filters
538 * e.g. ver=4;proto=17;data[0:4]=11aa22bb;ifc=135.104.9.2&255.255.255.0
540 * there's no protection against overlapping specs.
543 ipmuxconnect(Conv *c, char **argv, int argc)
556 n = getfields(argv[1], field, nelem(field), 1, ";");
562 for(i = 0; i < n; i++){
563 mux = parsemux(field[i]);
565 ipmuxtreefree(chain);
568 ipmuxchain(&chain, mux);
574 if(chain->type != Tver) {
575 char ver6[] = "ver=6";
576 mux = parsemux(ver6);
578 mux->no = ipmuxcopy(chain);
581 if(*chain->val == IP_VER4)
582 chain->yes = ipmuxconv4(chain->yes);
584 chain->no = ipmuxconv4(chain->no);
586 /* save a copy of the chain so we can later remove it */
587 mux = ipmuxcopy(chain);
588 r = (Ipmuxrock*)(c->ptcl);
591 /* add the chain to the protocol demultiplexor tree */
593 f->ipmux->priv = ipmuxmerge(f->ipmux->priv, mux);
601 ipmuxstate(Conv *c, char *state, int n)
605 r = (Ipmuxrock*)(c->ptcl);
606 return ipmuxsprint(r->chain, 0, state, n);
614 c->rq = qopen(64*1024, Qmsg, 0, c);
615 c->wq = qopen(64*1024, Qkick, ipmuxkick, c);
616 r = (Ipmuxrock*)(c->ptcl);
621 ipmuxannounce(Conv*, char**, int)
623 return "ipmux does not support announce";
632 r = (Ipmuxrock*)(c->ptcl);
637 ipmove(c->laddr, IPnoaddr);
638 ipmove(c->raddr, IPnoaddr);
643 ipmuxremove(&(c->p->priv), r->chain);
645 ipmuxtreefree(r->chain);
650 * takes a fully formed ip packet and just passes it down
661 Ip4hdr *ih4 = (Ip4hdr*)(bp->rp);
663 if((ih4->vihl & 0xF0) != IP_VER6)
664 ipoput4(c->p->f, bp, 0, ih4->ttl, ih4->tos, nil);
666 ipoput6(c->p->f, bp, 0, ((Ip6hdr*)ih4)->ttl, 0, nil);
671 maskmemcmp(uchar *m, uchar *v, uchar *c, int n)
676 return memcmp(v, c, n) != 0;
678 for(i = 0; i < n; i++)
679 if((v[i] & m[i]) != c[i])
685 ipmuxiput(Proto *p, Ipifc *ifc, Block *bp)
696 ip4 = (Ip4hdr*)bp->rp;
697 if((ip4->vihl & 0xF0) == IP_VER4) {
698 hl = (ip4->vihl&0x0F)<<2;
713 mux = f->ipmux->priv;
717 if(mux->len != IPaddrlen)
719 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
720 for(v = mux->val; v < mux->e; v += IPaddrlen)
721 if(maskmemcmp(mux->mask, lifc->local, v, IPaddrlen) == 0)
732 if(off < 0 || off + mux->len > BLEN(bp))
734 for(v = mux->val; v < mux->e; v += mux->len)
735 if(maskmemcmp(mux->mask, bp->rp + off, v, mux->len) == 0)
748 /* tack on interface address */
749 bp = padblock(bp, IPaddrlen);
752 ipmove(bp->rp, lifc != nil ? lifc->local : IPnoaddr);
753 qpass(c->rq, concatblock(bp));
758 /* doesn't match any filter, hand it to the specific protocol handler */
760 p = f->t2p[ip6->proto];
762 p = f->t2p[ip4->proto];
763 if(p != nil && p->rcv != nil){
764 (*p->rcv)(p, ifc, bp);
771 ipmuxsprint(Ipmux *mux, int level, char *buf, int len)
777 for(i = 0; i < level; i++)
778 n += snprint(buf+n, len-n, " ");
780 n += snprint(buf+n, len-n, "\n");
783 n += snprint(buf+n, len-n, "%s[%d:%d]",
784 mux->type == Tdata ? "data": "iph",
785 mux->off, mux->off+mux->len-1);
786 if(mux->mask != nil){
787 n += snprint(buf+n, len-n, "&");
788 for(i = 0; i < mux->len; i++)
789 n += snprint(buf+n, len - n, "%2.2ux", mux->mask[i]);
791 n += snprint(buf+n, len-n, "=");
793 for(j = 0; j < mux->n; j++){
794 for(i = 0; i < mux->len; i++)
795 n += snprint(buf+n, len - n, "%2.2ux", *v++);
796 n += snprint(buf+n, len-n, "|");
798 n += snprint(buf+n, len-n, "\n");
800 n += ipmuxsprint(mux->no, level, buf+n, len-n);
801 n += ipmuxsprint(mux->yes, level, buf+n, len-n);
806 ipmuxstats(Proto *p, char *buf, int len)
812 n = ipmuxsprint(p->priv, 0, buf, len);
823 ipmux = smalloc(sizeof(Proto));
825 ipmux->name = "ipmux";
826 ipmux->connect = ipmuxconnect;
827 ipmux->announce = ipmuxannounce;
828 ipmux->state = ipmuxstate;
829 ipmux->create = ipmuxcreate;
830 ipmux->close = ipmuxclose;
831 ipmux->rcv = ipmuxiput;
834 ipmux->stats = ipmuxstats;
837 ipmux->ptclsize = sizeof(Ipmuxrock);
839 f->ipmux = ipmux; /* hack for Fsrcvpcol */