ipmux.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       ipmux.c (15010B)
       ---
            1 /*
            2  * IP packet filter
            3  */
            4 #include "u.h"
            5 #include "lib.h"
            6 #include "mem.h"
            7 #include "dat.h"
            8 #include "fns.h"
            9 #include "error.h"
           10 
           11 #include "ip.h"
           12 #include "ipv6.h"
           13 
           14 typedef struct Ipmuxrock  Ipmuxrock;
           15 typedef struct Ipmux      Ipmux;
           16 
           17 typedef struct Myip4hdr Myip4hdr;
           18 struct Myip4hdr
           19 {
           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 */
           30 
           31         uchar        data[1];        /* start of data */
           32 };
           33 Myip4hdr *ipoff = 0;
           34 
           35 enum
           36 {
           37         Tproto,
           38         Tdata,
           39         Tiph,
           40         Tdst,
           41         Tsrc,
           42         Tifc,
           43 
           44         Cother = 0,
           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 */
           51         Cifc,
           52         Cmifc,
           53 };
           54 
           55 char *ftname[] = 
           56 {
           57 [Tproto]        "proto",
           58 [Tdata]                "data",
           59 [Tiph]                 "iph",
           60 [Tdst]                "dst",
           61 [Tsrc]                "src",
           62 [Tifc]                "ifc",
           63 };
           64 
           65 /*
           66  *  a node in the decision tree
           67  */
           68 struct Ipmux
           69 {
           70         Ipmux        *yes;
           71         Ipmux        *no;
           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 */
           79         uchar        *val;
           80         uchar        *mask;
           81         uchar        *e;                /* val+n*len*/
           82 
           83         int        ref;                /* so we can garbage collect */
           84         Conv        *conv;
           85 };
           86 
           87 /*
           88  *  someplace to hold per conversation data
           89  */
           90 struct Ipmuxrock
           91 {
           92         Ipmux        *chain;
           93 };
           94 
           95 static int        ipmuxsprint(Ipmux*, int, char*, int);
           96 static void        ipmuxkick(void *x);
           97 
           98 static char*
           99 skipwhite(char *p)
          100 {
          101         while(*p == ' ' || *p == '\t')
          102                 p++;
          103         return p;
          104 }
          105 
          106 static char*
          107 follows(char *p, char c)
          108 {
          109         char *f;
          110 
          111         f = strchr(p, c);
          112         if(f == nil)
          113                 return nil;
          114         *f++ = 0;
          115         f = skipwhite(f);
          116         if(*f == 0)
          117                 return nil;
          118         return f;
          119 }
          120 
          121 static Ipmux*
          122 parseop(char **pp)
          123 {
          124         char *p = *pp;
          125         int type, off, end, len;
          126         Ipmux *f;
          127 
          128         p = skipwhite(p);
          129         if(strncmp(p, "dst", 3) == 0){
          130                 type = Tdst;
          131                 off = (ulong)(ipoff->dst);
          132                 len = IPv4addrlen;
          133                 p += 3;
          134         }
          135         else if(strncmp(p, "src", 3) == 0){
          136                 type = Tsrc;
          137                 off = (ulong)(ipoff->src);
          138                 len = IPv4addrlen;
          139                 p += 3;
          140         }
          141         else if(strncmp(p, "ifc", 3) == 0){
          142                 type = Tifc;
          143                 off = -IPv4addrlen;
          144                 len = IPv4addrlen;
          145                 p += 3;
          146         }
          147         else if(strncmp(p, "proto", 5) == 0){
          148                 type = Tproto;
          149                 off = (ulong)&(ipoff->proto);
          150                 len = 1;
          151                 p += 5;
          152         }
          153         else if(strncmp(p, "data", 4) == 0 || strncmp(p, "iph", 3) == 0){
          154                 if(strncmp(p, "data", 4) == 0) {
          155                         type = Tdata;
          156                         p += 4;
          157                 }
          158                 else {
          159                         type = Tiph;
          160                         p += 3;
          161                 }
          162                 p = skipwhite(p);
          163                 if(*p != '[')
          164                         return nil;
          165                 p++;
          166                 off = strtoul(p, &p, 0);
          167                 if(off < 0 || off > (64-IP4HDR))
          168                         return nil;
          169                 p = skipwhite(p);
          170                 if(*p != ':')
          171                         end = off;
          172                 else {
          173                         p++;
          174                         p = skipwhite(p);
          175                         end = strtoul(p, &p, 0);
          176                         if(end < off)
          177                                 return nil;
          178                         p = skipwhite(p);
          179                 }
          180                 if(*p != ']')
          181                         return nil;
          182                 p++;
          183                 len = end - off + 1;
          184         }
          185         else
          186                 return nil;
          187 
          188         f = smalloc(sizeof(*f));
          189         f->type = type;
          190         f->len = len;
          191         f->off = off;
          192         f->val = nil;
          193         f->mask = nil;
          194         f->n = 1;
          195         f->ref = 1;
          196         if(type == Tdata)
          197                 f->skiphdr = 1;
          198         else
          199                 f->skiphdr = 0;
          200 
          201         return f;        
          202 }
          203 
          204 static int
          205 htoi(char x)
          206 {
          207         if(x >= '0' && x <= '9')
          208                 x -= '0';
          209         else if(x >= 'a' && x <= 'f')
          210                 x -= 'a' - 10;
          211         else if(x >= 'A' && x <= 'F')
          212                 x -= 'A' - 10;
          213         else
          214                 x = 0;
          215         return x;
          216 }
          217 
          218 static int
          219 hextoi(char *p)
          220 {
          221         return (htoi(p[0])<<4) | htoi(p[1]);
          222 }
          223 
          224 static void
          225 parseval(uchar *v, char *p, int len)
          226 {
          227         while(*p && len-- > 0){
          228                 *v++ = hextoi(p);
          229                 p += 2;
          230         }
          231 }
          232 
          233 static Ipmux*
          234 parsemux(char *p)
          235 {
          236         int n, nomask;
          237         Ipmux *f;
          238         char *val;
          239         char *mask;
          240         char *vals[20];
          241         uchar *v;
          242 
          243         /* parse operand */
          244         f = parseop(&p);
          245         if(f == nil)
          246                 return nil;
          247 
          248         /* find value */
          249         val = follows(p, '=');
          250         if(val == nil)
          251                 goto parseerror;
          252 
          253         /* parse mask */
          254         mask = follows(p, '&');
          255         if(mask != nil){
          256                 switch(f->type){
          257                 case Tsrc:
          258                 case Tdst:
          259                 case Tifc:
          260                         f->mask = smalloc(f->len);
          261                         v4parseip(f->mask, mask);
          262                         break;
          263                 case Tdata:
          264                 case Tiph:
          265                         f->mask = smalloc(f->len);
          266                         parseval(f->mask, mask, f->len);
          267                         break;
          268                 default:
          269                         goto parseerror;
          270                 }
          271                 nomask = 0;
          272         } else {
          273                 nomask = 1;
          274                 f->mask = smalloc(f->len);
          275                 memset(f->mask, 0xff, f->len);
          276         }
          277 
          278         /* parse vals */
          279         f->n = getfields(val, vals, sizeof(vals)/sizeof(char*), 1, "|");
          280         if(f->n == 0)
          281                 goto parseerror;
          282         f->val = smalloc(f->n*f->len);
          283         v = f->val;
          284         for(n = 0; n < f->n; n++){
          285                 switch(f->type){
          286                 case Tsrc:
          287                 case Tdst:
          288                 case Tifc:
          289                         v4parseip(v, vals[n]);
          290                         break;
          291                 case Tproto:
          292                 case Tdata:
          293                 case Tiph:
          294                         parseval(v, vals[n], f->len);
          295                         break;
          296                 }
          297                 v += f->len;
          298         }
          299 
          300         f->eoff = f->off + f->len;
          301         f->e = f->val + f->n*f->len;
          302         f->ctype = Cother;
          303         if(f->n == 1){
          304                 switch(f->len){
          305                 case 1:
          306                         f->ctype = nomask ? Cbyte : Cmbyte;
          307                         break;
          308                 case 2:
          309                         f->ctype = nomask ? Cshort : Cmshort;
          310                         break;
          311                 case 4:
          312                         if(f->type == Tifc)
          313                                 f->ctype = nomask ? Cifc : Cmifc;
          314                         else
          315                                 f->ctype = nomask ? Clong : Cmlong;
          316                         break;
          317                 }
          318         }
          319         return f;
          320 
          321 parseerror:
          322         if(f->mask)
          323                 free(f->mask);
          324         if(f->val)
          325                 free(f->val);
          326         free(f);
          327         return nil;
          328 }
          329 
          330 /*
          331  *  Compare relative ordering of two ipmuxs.  This doesn't compare the
          332  *  values, just the fields being looked at.  
          333  *
          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
          337  */
          338 static int
          339 ipmuxcmp(Ipmux *a, Ipmux *b)
          340 {
          341         int n;
          342 
          343         /* compare types, lesser ones are more important */
          344         n = a->type - b->type;
          345         if(n != 0)
          346                 return n;
          347 
          348         /* compare offsets, call earlier ones more specific */
          349         n = (a->off+((int)a->skiphdr)*(ulong)ipoff->data) - 
          350                 (b->off+((int)b->skiphdr)*(ulong)ipoff->data);
          351         if(n != 0)
          352                 return n;
          353 
          354         /* compare match lengths, longer ones are more specific */
          355         n = b->len - a->len;
          356         if(n != 0)
          357                 return n;
          358 
          359         /*
          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
          363          *  more specific.
          364          */
          365         if(a->mask != nil && b->mask == nil)
          366                 return -1;
          367         if(a->mask == nil && b->mask != nil)
          368                 return 1;
          369         if(a->mask != nil && b->mask != nil){
          370                 n = memcmp(b->mask, a->mask, a->len);
          371                 if(n != 0)
          372                         return n;
          373         }
          374         return 0;
          375 }
          376 
          377 /*
          378  *  Compare the values of two ipmuxs.  We're assuming that ipmuxcmp
          379  *  returned 0 comparing them.
          380  */
          381 static int
          382 ipmuxvalcmp(Ipmux *a, Ipmux *b)
          383 {
          384         int n;
          385 
          386         n = b->len*b->n - a->len*a->n;
          387         if(n != 0)
          388                 return n;
          389         return memcmp(a->val, b->val, a->len*a->n);
          390 } 
          391 
          392 /*
          393  *  add onto an existing ipmux chain in the canonical comparison
          394  *  order
          395  */
          396 static void
          397 ipmuxchain(Ipmux **l, Ipmux *f)
          398 {
          399         for(; *l; l = &(*l)->yes)
          400                 if(ipmuxcmp(f, *l) < 0)
          401                         break;
          402         f->yes = *l;
          403         *l = f;
          404 }
          405 
          406 /*
          407  *  copy a tree
          408  */
          409 static Ipmux*
          410 ipmuxcopy(Ipmux *f)
          411 {
          412         Ipmux *nf;
          413 
          414         if(f == nil)
          415                 return nil;
          416         nf = smalloc(sizeof *nf);
          417         *nf = *f;
          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);
          423         return nf;
          424 }
          425 
          426 static void
          427 ipmuxfree(Ipmux *f)
          428 {
          429         if(f->val != nil)
          430                 free(f->val);
          431         free(f);
          432 }
          433 
          434 static void
          435 ipmuxtreefree(Ipmux *f)
          436 {
          437         if(f == nil)
          438                 return;
          439         if(f->no != nil)
          440                 ipmuxfree(f->no);
          441         if(f->yes != nil)
          442                 ipmuxfree(f->yes);
          443         ipmuxfree(f);
          444 }
          445 
          446 /*
          447  *  merge two trees
          448  */
          449 static Ipmux*
          450 ipmuxmerge(Ipmux *a, Ipmux *b)
          451 {
          452         int n;
          453         Ipmux *f;
          454 
          455         if(a == nil)
          456                 return b;
          457         if(b == nil)
          458                 return a;
          459         n = ipmuxcmp(a, b);
          460         if(n < 0){
          461                 f = ipmuxcopy(b);
          462                 a->yes = ipmuxmerge(a->yes, b);
          463                 a->no = ipmuxmerge(a->no, f);
          464                 return a;
          465         }
          466         if(n > 0){
          467                 f = ipmuxcopy(a);
          468                 b->yes = ipmuxmerge(b->yes, a);
          469                 b->no = ipmuxmerge(b->no, f);
          470                 return b;
          471         }
          472         if(ipmuxvalcmp(a, b) == 0){
          473                 a->yes = ipmuxmerge(a->yes, b->yes);
          474                 a->no = ipmuxmerge(a->no, b->no);
          475                 a->ref++;
          476                 ipmuxfree(b);
          477                 return a;
          478         }
          479         a->no = ipmuxmerge(a->no, b);
          480         return a;
          481 }
          482 
          483 /*
          484  *  remove a chain from a demux tree.  This is like merging accept that
          485  *  we remove instead of insert.
          486  */
          487 static int
          488 ipmuxremove(Ipmux **l, Ipmux *f)
          489 {
          490         int n, rv;
          491         Ipmux *ft;
          492 
          493         if(f == nil)
          494                 return 0;                /* we've removed it all */
          495         if(*l == nil)
          496                 return -1;
          497 
          498         ft = *l;
          499         n = ipmuxcmp(ft, f);
          500         if(n < 0){
          501                 /* *l is maching an earlier field, descend both paths */
          502                 rv = ipmuxremove(&ft->yes, f);
          503                 rv += ipmuxremove(&ft->no, f);
          504                 return rv;
          505         }
          506         if(n > 0){
          507                 /* f represents an earlier field than *l, this should be impossible */
          508                 return -1;
          509         }
          510 
          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);
          515         }
          516 
          517         /* we found a match */
          518         if(--(ft->ref) == 0){
          519                 /*
          520                  *  a dead node implies the whole yes side is also dead.
          521                  *  since our chain is constrained to be on that side,
          522                  *  we're done.
          523                  */
          524                 ipmuxtreefree(ft->yes);
          525                 *l = ft->no;
          526                 ipmuxfree(ft);
          527                 return 0;
          528         }
          529 
          530         /*
          531          *  free the rest of the chain.  it is constrained to match the
          532          *  yes side.
          533          */
          534         return ipmuxremove(&ft->yes, f->yes);
          535 }
          536 
          537 /*
          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
          540  *
          541  *  there's no protection against overlapping specs.
          542  */
          543 static char*
          544 ipmuxconnect(Conv *c, char **argv, int argc)
          545 {
          546         int i, n;
          547         char *field[10];
          548         Ipmux *mux, *chain;
          549         Ipmuxrock *r;
          550         Fs *f;
          551 
          552         f = c->p->f;
          553 
          554         if(argc != 2)
          555                 return Ebadarg;
          556 
          557         n = getfields(argv[1], field, nelem(field), 1, ";");
          558         if(n <= 0)
          559                 return Ebadarg;
          560 
          561         chain = nil;
          562         mux = nil;
          563         for(i = 0; i < n; i++){
          564                 mux = parsemux(field[i]);
          565                 if(mux == nil){
          566                         ipmuxtreefree(chain);
          567                         return Ebadarg;
          568                 }
          569                 ipmuxchain(&chain, mux);
          570         }
          571         if(chain == nil)
          572                 return Ebadarg;
          573         mux->conv = c;
          574 
          575         /* save a copy of the chain so we can later remove it */
          576         mux = ipmuxcopy(chain);
          577         r = (Ipmuxrock*)(c->ptcl);
          578         r->chain = chain;
          579 
          580         /* add the chain to the protocol demultiplexor tree */
          581         WLOCK(f);
          582         f->ipmux->priv = ipmuxmerge(f->ipmux->priv, mux);
          583         WUNLOCK(f);
          584 
          585         Fsconnected(c, nil);
          586         return nil;
          587 }
          588 
          589 static int
          590 ipmuxstate(Conv *c, char *state, int n)
          591 {
          592         Ipmuxrock *r;
          593         
          594         r = (Ipmuxrock*)(c->ptcl);
          595         return ipmuxsprint(r->chain, 0, state, n);
          596 }
          597 
          598 static void
          599 ipmuxcreate(Conv *c)
          600 {
          601         Ipmuxrock *r;
          602 
          603         c->rq = qopen(64*1024, Qmsg, 0, c);
          604         c->wq = qopen(64*1024, Qkick, ipmuxkick, c);
          605         r = (Ipmuxrock*)(c->ptcl);
          606         r->chain = nil;
          607 }
          608 
          609 static char*
          610 ipmuxannounce(Conv* _, char** __, int ___)
          611 {
          612         return "ipmux does not support announce";
          613 }
          614 
          615 static void
          616 ipmuxclose(Conv *c)
          617 {
          618         Ipmux *i;
          619         Ipmuxrock *r;
          620         Fs *f = c->p->f;
          621 
          622         r = (Ipmuxrock*)(c->ptcl);
          623 
          624         qclose(c->rq);
          625         qclose(c->wq);
          626         qclose(c->eq);
          627         ipmove(c->laddr, IPnoaddr);
          628         ipmove(c->raddr, IPnoaddr);
          629         c->lport = 0;
          630         c->rport = 0;
          631 
          632         WLOCK(f);
          633         i = (Ipmux *)c->p->priv;
          634         ipmuxremove(&i, r->chain);
          635         WUNLOCK(f);
          636         ipmuxtreefree(r->chain);
          637         r->chain = nil;
          638 }
          639 
          640 /*
          641  *  takes a fully formed ip packet and just passes it down
          642  *  the stack
          643  */
          644 static void
          645 ipmuxkick(void *x)
          646 {
          647         Conv *c = x;
          648         Block *bp;
          649 
          650         bp = qget(c->wq);
          651         if(bp != nil) {
          652                 Myip4hdr *ih4 = (Myip4hdr*)(bp->rp);
          653 
          654                 if((ih4->vihl & 0xF0) != IP_VER6)
          655                         ipoput4(c->p->f, bp, 0, ih4->ttl, ih4->tos, nil);
          656                 else
          657                         ipoput6(c->p->f, bp, 0, ((Ip6hdr*)ih4)->ttl, 0, nil);
          658         }
          659 }
          660 
          661 static void
          662 ipmuxiput(Proto *p, Ipifc *ifc, Block *bp)
          663 {
          664         int len, hl;
          665         Fs *f = p->f;
          666         uchar *m, *h, *v, *e, *ve, *hp;
          667         Conv *c;
          668         Ipmux *mux;
          669         Myip4hdr *ip;
          670         Ip6hdr *ip6;
          671 
          672         ip = (Myip4hdr*)bp->rp;
          673         hl = (ip->vihl&0x0F)<<2;
          674 
          675         if(p->priv == nil)
          676                 goto nomatch;
          677 
          678         h = bp->rp;
          679         len = BLEN(bp);
          680 
          681         /* run the v4 filter */
          682         RLOCK(f);
          683         c = nil;
          684         mux = f->ipmux->priv;
          685         while(mux != nil){
          686                 if(mux->eoff > len){
          687                         mux = mux->no;
          688                         continue;
          689                 }
          690                 hp = h + mux->off + ((int)mux->skiphdr)*hl;
          691                 switch(mux->ctype){
          692                 case Cbyte:
          693                         if(*mux->val == *hp)
          694                                 goto yes;
          695                         break;
          696                 case Cmbyte:
          697                         if((*hp & *mux->mask) == *mux->val)
          698                                 goto yes;
          699                         break;
          700                 case Cshort:
          701                         if(*((ushort*)mux->val) == *(ushort*)hp)
          702                                 goto yes;
          703                         break;
          704                 case Cmshort:
          705                         if((*(ushort*)hp & (*((ushort*)mux->mask))) == *((ushort*)mux->val))
          706                                 goto yes;
          707                         break;
          708                 case Clong:
          709                         if(*((ulong*)mux->val) == *(ulong*)hp)
          710                                 goto yes;
          711                         break;
          712                 case Cmlong:
          713                         if((*(ulong*)hp & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
          714                                 goto yes;
          715                         break;
          716                 case Cifc:
          717                         if(*((ulong*)mux->val) == *(ulong*)(ifc->lifc->local + IPv4off))
          718                                 goto yes;
          719                         break;
          720                 case Cmifc:
          721                         if((*(ulong*)(ifc->lifc->local + IPv4off) & (*((ulong*)mux->mask))) == *((ulong*)mux->val))
          722                                 goto yes;
          723                         break;
          724                 default:
          725                         v = mux->val;
          726                         for(e = mux->e; v < e; v = ve){
          727                                 m = mux->mask;
          728                                 hp = h + mux->off;
          729                                 for(ve = v + mux->len; v < ve; v++){
          730                                         if((*hp++ & *m++) != *v)
          731                                                 break;
          732                                 }
          733                                 if(v == ve)
          734                                         goto yes;
          735                         }
          736                 }
          737                 mux = mux->no;
          738                 continue;
          739 yes:
          740                 if(mux->conv != nil)
          741                         c = mux->conv;
          742                 mux = mux->yes;
          743         }
          744         RUNLOCK(f);
          745 
          746         if(c != nil){
          747                 /* tack on interface address */
          748                 bp = padblock(bp, IPaddrlen);
          749                 ipmove(bp->rp, ifc->lifc->local);
          750                 bp = concatblock(bp);
          751                 if(bp != nil)
          752                         if(qpass(c->rq, bp) < 0)
          753                                 print("Q");
          754                 return;
          755         }
          756 
          757 nomatch:
          758         /* doesn't match any filter, hand it to the specific protocol handler */
          759         ip = (Myip4hdr*)bp->rp;
          760         if((ip->vihl & 0xF0) == IP_VER4) {
          761                 p = f->t2p[ip->proto];
          762         } else {
          763                 ip6 = (Ip6hdr*)bp->rp;
          764                 p = f->t2p[ip6->proto];
          765         }
          766         if(p && p->rcv)
          767                 (*p->rcv)(p, ifc, bp);
          768         else
          769                 freeblist(bp);
          770         return;
          771 }
          772 
          773 static int
          774 ipmuxsprint(Ipmux *mux, int level, char *buf, int len)
          775 {
          776         int i, j, n;
          777         uchar *v;
          778 
          779         n = 0;
          780         for(i = 0; i < level; i++)
          781                 n += snprint(buf+n, len-n, " ");
          782         if(mux == nil){
          783                 n += snprint(buf+n, len-n, "\n");
          784                 return n;
          785         }
          786         n += snprint(buf+n, len-n, "h[%d:%d]&", 
          787                mux->off+((int)mux->skiphdr)*((int)ipoff->data), 
          788                mux->off+(((int)mux->skiphdr)*((int)ipoff->data))+mux->len-1);
          789         for(i = 0; i < mux->len; i++)
          790                 n += snprint(buf+n, len - n, "%2.2ux", mux->mask[i]);
          791         n += snprint(buf+n, len-n, "=");
          792         v = mux->val;
          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, "|");
          797         }
          798         n += snprint(buf+n, len-n, "\n");
          799         level++;
          800         n += ipmuxsprint(mux->no, level, buf+n, len-n);
          801         n += ipmuxsprint(mux->yes, level, buf+n, len-n);
          802         return n;
          803 }
          804 
          805 static int
          806 ipmuxstats(Proto *p, char *buf, int len)
          807 {
          808         int n;
          809         Fs *f = p->f;
          810 
          811         RLOCK(f);
          812         n = ipmuxsprint(p->priv, 0, buf, len);
          813         RUNLOCK(f);
          814 
          815         return n;
          816 }
          817 
          818 void
          819 ipmuxinit(Fs *f)
          820 {
          821         Proto *ipmux;
          822 
          823         ipmux = smalloc(sizeof(Proto));
          824         ipmux->priv = nil;
          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;
          832         ipmux->ctl = nil;
          833         ipmux->advise = nil;
          834         ipmux->stats = ipmuxstats;
          835         ipmux->ipproto = -1;
          836         ipmux->nc = 64;
          837         ipmux->ptclsize = sizeof(Ipmuxrock);
          838 
          839         f->ipmux = ipmux;                        /* hack for Fsrcvpcol */
          840 
          841         Fsproto(f, ipmux);
          842 }