ipmux.c 15 KB

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