netif.c (14502B)
1 #include "u.h" 2 #include "lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "error.h" 7 #include "netif.h" 8 9 static int netown(Netfile*, char*, int); 10 static int openfile(Netif*, int); 11 static char* matchtoken(char*, char*); 12 static char* netmulti(Netif*, Netfile*, uchar*, int); 13 static int parseaddr(uchar*, char*, int); 14 15 int netifdebug; 16 #define dprint(...) if(netifdebug)print(__VA_ARGS__); else USED(netifdebug) 17 18 /* 19 * set up a new network interface 20 */ 21 void 22 netifinit(Netif *nif, char *name, int nfile, ulong limit) 23 { 24 strncpy(nif->name, name, KNAMELEN-1); 25 nif->name[KNAMELEN-1] = 0; 26 nif->nfile = nfile; 27 nif->f = xalloc(nfile*sizeof(Netfile*)); 28 if (nif->f == nil) 29 panic("netifinit: no memory"); 30 nif->limit = limit; 31 } 32 33 #define DD(c,q,nam,n,owner,perm,dp) dprint("%lux.%llux %s\n", q.type, q.path, nam); devdir(c,q,nam,n,owner,perm,dp) 34 35 /* 36 * generate a 3 level directory 37 */ 38 static int 39 netifgen(Chan *c, char *dummy, Dirtab *vp, int dummy1, int i, Dir *dp) 40 { 41 Qid q; 42 Netif *nif = (Netif*)vp; 43 Netfile *f; 44 int t, perm; 45 char *o; 46 47 memset(&q, 0, sizeof q); 48 q.type = QTFILE; 49 q.vers = 0; 50 51 dprint("gen %d %llud %.2d ", c->dri, c->qid.path, i); 52 /* top level directory contains the name of the network */ 53 if(c->qid.path == 0){ 54 switch(i){ 55 case DEVDOTDOT: 56 q.path = 0; 57 q.type = QTDIR; 58 DD(c, q, ".", 0, eve, 0555, dp); 59 break; 60 case 0: 61 q.path = N2ndqid; 62 q.type = QTDIR; 63 strcpy(up->genbuf, nif->name); 64 DD(c, q, up->genbuf, 0, eve, 0555, dp); 65 break; 66 default: 67 dprint("-> -1 (top)\n"); 68 return -1; 69 } 70 return 1; 71 } 72 73 /* second level contains clone plus all the conversations */ 74 t = NETTYPE(c->qid.path); 75 if(t == N2ndqid || t == Ncloneqid || t == Naddrqid || t == Nstatqid || t == Nifstatqid){ 76 switch(i){ 77 case DEVDOTDOT: 78 q.type = QTDIR; 79 q.path = 0; 80 DD(c, q, ".", 0, eve, DMDIR|0555, dp); 81 break; 82 case 0: 83 q.path = Ncloneqid; 84 DD(c, q, "clone", 0, eve, 0666, dp); 85 break; 86 case 1: 87 q.path = Naddrqid; 88 DD(c, q, "addr", 0, eve, 0666, dp); 89 break; 90 case 2: 91 q.path = Nstatqid; 92 DD(c, q, "stats", 0, eve, 0444, dp); 93 break; 94 case 3: 95 q.path = Nifstatqid; 96 DD(c, q, "ifstats", 0, eve, 0444, dp); 97 break; 98 default: 99 i -= 4; 100 if(i >= nif->nfile){ 101 dprint("-> -1 (2d): %d %d\n", i, nif->nfile); 102 return -1; 103 } 104 if(nif->f[i] == 0){ 105 dprint("nif->f[%d] -> 0\n", i); 106 return 0; 107 } 108 q.type = QTDIR; 109 q.path = NETQID(i, N3rdqid); 110 sprint(up->genbuf, "%d", i); 111 DD(c, q, up->genbuf, 0, eve, DMDIR|0555, dp); 112 break; 113 } 114 return 1; 115 } 116 117 /* third level */ 118 f = nif->f[NETID(c->qid.path)]; 119 if(f == 0){ 120 dprint("->f 0\n"); 121 return -1; 122 } 123 if(*f->owner){ 124 o = f->owner; 125 perm = f->mode; 126 } else { 127 o = eve; 128 perm = 0666; 129 } 130 switch(i){ 131 case DEVDOTDOT: 132 q.type = QTDIR; 133 q.path = N2ndqid; 134 strcpy(up->genbuf, nif->name); 135 DD(c, q, up->genbuf, 0, eve, DMDIR|0555, dp); 136 break; 137 case 0: 138 q.path = NETQID(NETID(c->qid.path), Ndataqid); 139 DD(c, q, "data", 0, o, perm, dp); 140 break; 141 case 1: 142 q.path = NETQID(NETID(c->qid.path), Nctlqid); 143 DD(c, q, "ctl", 0, o, perm, dp); 144 break; 145 case 2: 146 q.path = NETQID(NETID(c->qid.path), Nstatqid); 147 DD(c, q, "stats", 0, eve, 0444, dp); 148 break; 149 case 3: 150 q.path = NETQID(NETID(c->qid.path), Ntypeqid); 151 DD(c, q, "type", 0, eve, 0444, dp); 152 break; 153 case 4: 154 q.path = NETQID(NETID(c->qid.path), Nifstatqid); 155 DD(c, q, "ifstats", 0, eve, 0444, dp); 156 break; 157 default: 158 dprint("-> -1 (third)\n"); 159 return -1; 160 } 161 return 1; 162 } 163 164 static void 165 prwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname) 166 { 167 char buf[512], *e, *p; 168 169 if(netifdebug == 0) 170 return; 171 p = buf; 172 e = p + sizeof buf; 173 for(int i = 0; i < nname; i++) 174 p = seprint(p, e, "%s ", name[i]); 175 if(p > buf) 176 p--; 177 *p = 0; 178 print("netifwalk %lld [%s]\n", c->qid.path, buf); 179 } 180 181 Walkqid* 182 netifwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname) 183 { 184 prwalk(nif, c, nc, name, nname); 185 return devwalk(c, nc, name, nname, (Dirtab *)nif, 0, netifgen); 186 } 187 188 Chan* 189 netifopen(Netif *nif, Chan *c, int omode) 190 { 191 int id; 192 Netfile *f; 193 194 dprint("netifopen %p %d\n", nif, c? c->qid.path: -1); 195 id = 0; 196 if(c->qid.type & QTDIR){ 197 if(omode != OREAD) 198 error(Eperm); 199 } else { 200 switch(NETTYPE(c->qid.path)){ 201 case Ndataqid: 202 case Nctlqid: 203 id = NETID(c->qid.path); 204 openfile(nif, id); 205 break; 206 case Ncloneqid: 207 id = openfile(nif, -1); 208 c->qid.path = NETQID(id, Nctlqid); 209 break; 210 default: 211 if(omode != OREAD) 212 error(Ebadarg); 213 } 214 switch(NETTYPE(c->qid.path)){ 215 case Ndataqid: 216 case Nctlqid: 217 f = nif->f[id]; 218 if(netown(f, up->user, omode&7) < 0) 219 error(Eperm); 220 break; 221 } 222 } 223 c->mode = openmode(omode); 224 c->flag |= COPEN; 225 c->offset = 0; 226 c->iounit = qiomaxatomic; 227 return c; 228 } 229 230 long 231 netifread(Netif *nif, Chan *c, void *a, long n, ulong offset) 232 { 233 int i, j; 234 Netfile *f; 235 char *p; 236 237 dprint("netifread %lud %lud\n", c->qid.path, NETTYPE(c->qid.path)); 238 if(c->qid.type&QTDIR) 239 return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen); 240 241 switch(NETTYPE(c->qid.path)){ 242 case Ndataqid: 243 f = nif->f[NETID(c->qid.path)]; 244 return qread(f->in, a, n); 245 case Nctlqid: 246 return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE); 247 case Nstatqid: 248 dprint("netstatqid\n"); 249 p = smalloc(READSTR); 250 j = snprint(p, READSTR, "in: %llud\n", nif->inpackets); 251 j += snprint(p+j, READSTR-j, "link: %d\n", nif->link); 252 j += snprint(p+j, READSTR-j, "out: %llud\n", nif->outpackets); 253 j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs); 254 j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows); 255 j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows); 256 j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames); 257 j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs); 258 j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs); 259 j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom); 260 j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps); 261 j += snprint(p+j, READSTR-j, "addr: "); 262 for(i = 0; i < nif->alen; i++) 263 j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]); 264 snprint(p+j, READSTR-j, "\n"); 265 n = readstr(offset, a, n, p); 266 free(p); 267 return n; 268 case Naddrqid: 269 p = malloc(READSTR); 270 j = 0; 271 for(i = 0; i < nif->alen; i++) 272 j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]); 273 n = readstr(offset, a, n, p); 274 free(p); 275 return n; 276 case Ntypeqid: 277 f = nif->f[NETID(c->qid.path)]; 278 return readnum(offset, a, n, f->type, NUMSIZE); 279 case Nifstatqid: 280 return 0; 281 } 282 error(Ebadarg); 283 return -1; /* not reached */ 284 } 285 286 Block* 287 netifbread(Netif *nif, Chan *c, long n, ulong offset) 288 { 289 if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid) 290 return devbread(c, n, offset); 291 292 return qbread(nif->f[NETID(c->qid.path)]->in, n); 293 } 294 295 /* 296 * make sure this type isn't already in use on this device 297 */ 298 static int 299 typeinuse(Netif *nif, int type) 300 { 301 Netfile *f, **fp, **efp; 302 303 if(type <= 0) 304 return 0; 305 306 efp = &nif->f[nif->nfile]; 307 for(fp = nif->f; fp < efp; fp++){ 308 f = *fp; 309 if(f == 0) 310 continue; 311 if(f->type == type) 312 return 1; 313 } 314 return 0; 315 } 316 317 /* 318 * the devxxx.c that calls us handles writing data, it knows best 319 */ 320 long 321 netifwrite(Netif *nif, Chan *c, void *a, long n) 322 { 323 Netfile *f; 324 int type; 325 char *p, buf[64]; 326 uchar binaddr[Nmaxaddr]; 327 328 if(NETTYPE(c->qid.path) != Nctlqid) 329 error(Eperm); 330 331 if(n >= sizeof(buf)) 332 n = sizeof(buf)-1; 333 memmove(buf, a, n); 334 buf[n] = 0; 335 336 if(waserror()){ 337 QUNLOCK(nif); 338 nexterror(); 339 } 340 341 QLOCK(nif); 342 f = nif->f[NETID(c->qid.path)]; 343 if((p = matchtoken(buf, "connect")) != 0){ 344 type = atoi(p); 345 if(typeinuse(nif, type)) 346 error(Einuse); 347 f->type = type; 348 if(f->type < 0) 349 nif->all++; 350 } else if(matchtoken(buf, "promiscuous")){ 351 if(f->prom == 0){ 352 if(nif->prom == 0 && nif->promiscuous != nil) 353 nif->promiscuous(nif->arg, 1); 354 f->prom = 1; 355 nif->prom++; 356 } 357 } else if((p = matchtoken(buf, "scanbs")) != 0){ 358 /* scan for base stations */ 359 if(f->scan == 0){ 360 type = atoi(p); 361 if(type < 5) 362 type = 5; 363 if(nif->scanbs != nil) 364 nif->scanbs(nif->arg, type); 365 f->scan = type; 366 nif->scan++; 367 } 368 } else if(matchtoken(buf, "bridge")){ 369 f->bridge = 1; 370 } else if(matchtoken(buf, "headersonly")){ 371 f->headersonly = 1; 372 } else if((p = matchtoken(buf, "addmulti")) != 0){ 373 if(parseaddr(binaddr, p, nif->alen) < 0) 374 error("bad address"); 375 p = netmulti(nif, f, binaddr, 1); 376 if(p) 377 error(p); 378 } else if((p = matchtoken(buf, "remmulti")) != 0){ 379 if(parseaddr(binaddr, p, nif->alen) < 0) 380 error("bad address"); 381 p = netmulti(nif, f, binaddr, 0); 382 if(p) 383 error(p); 384 } else 385 n = -1; 386 QUNLOCK(nif); 387 poperror(); 388 return n; 389 } 390 391 int 392 netifwstat(Netif *nif, Chan *c, uchar *db, int n) 393 { 394 Dir *dir; 395 Netfile *f; 396 int m; 397 398 f = nif->f[NETID(c->qid.path)]; 399 if(f == 0) 400 error(Enonexist); 401 402 if(netown(f, up->user, OWRITE) < 0) 403 error(Eperm); 404 405 dir = smalloc(sizeof(Dir)+n); 406 m = convM2D(db, n, &dir[0], (char*)&dir[1]); 407 if(m == 0){ 408 free(dir); 409 error(Eshortstat); 410 } 411 if(!emptystr(dir[0].uid)) 412 strncpy(f->owner, dir[0].uid, KNAMELEN); 413 if(dir[0].mode != ~0UL) 414 f->mode = dir[0].mode; 415 free(dir); 416 return m; 417 } 418 419 int 420 netifstat(Netif *nif, Chan *c, uchar *db, int n) 421 { 422 dprint("netifstat %s nfile %d %lld type=%d\n", nif->name, nif->nfile, c->qid.path, c->type); 423 return devstat(c, db, n, (Dirtab *)nif, 0, netifgen); 424 } 425 426 void 427 netifclose(Netif *nif, Chan *c) 428 { 429 Netfile *f; 430 int t; 431 Netaddr *ap; 432 433 if((c->flag & COPEN) == 0) 434 return; 435 436 t = NETTYPE(c->qid.path); 437 if(t != Ndataqid && t != Nctlqid) 438 return; 439 440 f = nif->f[NETID(c->qid.path)]; 441 QLOCK(f); 442 if(--(f->inuse) == 0){ 443 if(f->prom){ 444 QLOCK(nif); 445 if(--(nif->prom) == 0 && nif->promiscuous != nil) 446 nif->promiscuous(nif->arg, 0); 447 QUNLOCK(nif); 448 f->prom = 0; 449 } 450 if(f->scan){ 451 QLOCK(nif); 452 if(--(nif->scan) == 0 && nif->scanbs != nil) 453 nif->scanbs(nif->arg, 0); 454 QUNLOCK(nif); 455 f->prom = 0; 456 f->scan = 0; 457 } 458 if(f->nmaddr){ 459 QLOCK(nif); 460 t = 0; 461 for(ap = nif->maddr; ap; ap = ap->next){ 462 if(f->maddr[t/8] & (1<<(t%8))) 463 netmulti(nif, f, ap->addr, 0); 464 } 465 QUNLOCK(nif); 466 f->nmaddr = 0; 467 } 468 if(f->type < 0){ 469 QLOCK(nif); 470 --(nif->all); 471 QUNLOCK(nif); 472 } 473 f->owner[0] = 0; 474 print("drop type %.4ux\n", f->type); 475 f->type = 0; 476 f->bridge = 0; 477 f->headersonly = 0; 478 qclose(f->in); 479 } 480 QUNLOCK(f); 481 } 482 483 Lock netlock; 484 485 static int 486 netown(Netfile *p, char *o, int omode) 487 { 488 static int access[] = { 0400, 0200, 0600, 0100 }; 489 int mode; 490 int t; 491 492 lock(&netlock); 493 if(*p->owner){ 494 if(strncmp(o, p->owner, KNAMELEN) == 0) /* User */ 495 mode = p->mode; 496 else if(strncmp(o, eve, KNAMELEN) == 0) /* Bootes is group */ 497 mode = p->mode<<3; 498 else 499 mode = p->mode<<6; /* Other */ 500 501 t = access[omode&3]; 502 if((t & mode) == t){ 503 unlock(&netlock); 504 return 0; 505 } else { 506 unlock(&netlock); 507 return -1; 508 } 509 } 510 strncpy(p->owner, o, KNAMELEN); 511 p->mode = 0660; 512 unlock(&netlock); 513 return 0; 514 } 515 516 /* 517 * Increment the reference count of a network device. 518 * If id < 0, return an unused ether device. 519 */ 520 static int 521 openfile(Netif *nif, int id) 522 { 523 Netfile *f, **fp, **efp; 524 525 if(id >= 0){ 526 f = nif->f[id]; 527 if(f == 0) 528 error(Enodev); 529 QLOCK(f); 530 qreopen(f->in); 531 f->inuse++; 532 QUNLOCK(f); 533 return id; 534 } 535 536 QLOCK(nif); 537 if(waserror()){ 538 QUNLOCK(nif); 539 nexterror(); 540 } 541 efp = &nif->f[nif->nfile]; 542 for(fp = nif->f; fp < efp; fp++){ 543 f = *fp; 544 if(f == 0){ 545 f = malloc(sizeof(Netfile)); 546 if(f == 0) 547 exhausted("memory"); 548 f->in = qopen(nif->limit, Qmsg, 0, 0); 549 if(f->in == nil){ 550 free(f); 551 exhausted("memory"); 552 } 553 *fp = f; 554 QLOCK(f); 555 } else { 556 QLOCK(f); 557 if(f->inuse){ 558 QUNLOCK(f); 559 continue; 560 } 561 } 562 f->inuse = 1; 563 qreopen(f->in); 564 netown(f, up->user, 0); 565 QUNLOCK(f); 566 QUNLOCK(nif); 567 poperror(); 568 return fp - nif->f; 569 } 570 error(Enodev); 571 return -1; /* not reached */ 572 } 573 574 /* 575 * look for a token starting a string, 576 * return a pointer to first non-space char after it 577 */ 578 static char* 579 matchtoken(char *p, char *token) 580 { 581 int n; 582 583 n = strlen(token); 584 if(strncmp(p, token, n)) 585 return 0; 586 p += n; 587 if(*p == 0) 588 return p; 589 if(*p != ' ' && *p != '\t' && *p != '\n') 590 return 0; 591 while(*p == ' ' || *p == '\t' || *p == '\n') 592 p++; 593 return p; 594 } 595 596 void 597 hnputv(void *p, uvlong v) 598 { 599 uchar *a; 600 601 a = p; 602 hnputl(a, v>>32); 603 hnputl(a+4, v); 604 } 605 606 void 607 hnputl(void *p, uint v) 608 { 609 uchar *a; 610 611 a = p; 612 a[0] = v>>24; 613 a[1] = v>>16; 614 a[2] = v>>8; 615 a[3] = v; 616 } 617 618 void 619 hnputs(void *p, ushort v) 620 { 621 uchar *a; 622 623 a = p; 624 a[0] = v>>8; 625 a[1] = v; 626 } 627 628 uvlong 629 nhgetv(void *p) 630 { 631 uchar *a; 632 633 a = p; 634 return ((vlong)nhgetl(a) << 32) | nhgetl(a+4); 635 } 636 637 uint 638 nhgetl(void *p) 639 { 640 uchar *a; 641 642 a = p; 643 return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0); 644 } 645 646 ushort 647 nhgets(void *p) 648 { 649 uchar *a; 650 651 a = p; 652 return (a[0]<<8)|(a[1]<<0); 653 } 654 655 static ulong 656 hash(uchar *a, int len) 657 { 658 ulong sum = 0; 659 660 while(len-- > 0) 661 sum = (sum << 1) + *a++; 662 return sum%Nmhash; 663 } 664 665 int 666 activemulti(Netif *nif, uchar *addr, int alen) 667 { 668 Netaddr *hp; 669 670 for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext) 671 if(memcmp(addr, hp->addr, alen) == 0){ 672 if(hp->ref) 673 return 1; 674 else 675 break; 676 } 677 return 0; 678 } 679 680 static int 681 parseaddr(uchar *to, char *from, int alen) 682 { 683 char nip[4]; 684 char *p; 685 int i; 686 687 p = from; 688 for(i = 0; i < alen; i++){ 689 if(*p == 0) 690 return -1; 691 nip[0] = *p++; 692 if(*p == 0) 693 return -1; 694 nip[1] = *p++; 695 nip[2] = 0; 696 to[i] = strtoul(nip, 0, 16); 697 if(*p == ':') 698 p++; 699 } 700 return 0; 701 } 702 703 /* 704 * keep track of multicast addresses 705 */ 706 static char* 707 netmulti(Netif *nif, Netfile *f, uchar *addr, int add) 708 { 709 Netaddr **l, *ap; 710 int i; 711 ulong h; 712 713 if(nif->multicast == nil) 714 return "interface does not support multicast"; 715 716 l = &nif->maddr; 717 i = 0; 718 for(ap = *l; ap; ap = *l){ 719 if(memcmp(addr, ap->addr, nif->alen) == 0) 720 break; 721 i++; 722 l = &ap->next; 723 } 724 725 if(add){ 726 if(ap == 0){ 727 *l = ap = smalloc(sizeof(*ap)); 728 memmove(ap->addr, addr, nif->alen); 729 ap->next = 0; 730 ap->ref = 1; 731 h = hash(addr, nif->alen); 732 ap->hnext = nif->mhash[h]; 733 nif->mhash[h] = ap; 734 } else { 735 ap->ref++; 736 } 737 if(ap->ref == 1){ 738 nif->nmaddr++; 739 nif->multicast(nif->arg, addr, 1); 740 } 741 if(i < 8*sizeof(f->maddr)){ 742 if((f->maddr[i/8] & (1<<(i%8))) == 0) 743 f->nmaddr++; 744 f->maddr[i/8] |= 1<<(i%8); 745 } 746 } else { 747 if(ap == 0 || ap->ref == 0) 748 return 0; 749 ap->ref--; 750 if(ap->ref == 0){ 751 nif->nmaddr--; 752 nif->multicast(nif->arg, addr, 0); 753 } 754 if(i < 8*sizeof(f->maddr)){ 755 if((f->maddr[i/8] & (1<<(i%8))) != 0) 756 f->nmaddr--; 757 f->maddr[i/8] &= ~(1<<(i%8)); 758 } 759 } 760 return 0; 761 }