vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

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 }