xref: /plan9-contrib/sys/src/9k/port/netif.c (revision 406c76facc4b13aa2a55454bf4091aab9f03da22)
19ef1f84bSDavid du Colombier #include	"u.h"
29ef1f84bSDavid du Colombier #include	"../port/lib.h"
39ef1f84bSDavid du Colombier #include	"mem.h"
49ef1f84bSDavid du Colombier #include	"dat.h"
59ef1f84bSDavid du Colombier #include	"fns.h"
69ef1f84bSDavid du Colombier #include	"../port/error.h"
79ef1f84bSDavid du Colombier 
89ef1f84bSDavid du Colombier #include	"../port/netif.h"
99ef1f84bSDavid du Colombier 
109ef1f84bSDavid du Colombier static int netown(Netfile*, char*, int);
119ef1f84bSDavid du Colombier static int openfile(Netif*, int);
129ef1f84bSDavid du Colombier static char* matchtoken(char*, char*);
139ef1f84bSDavid du Colombier static char* netmulti(Netif*, Netfile*, uchar*, int);
149ef1f84bSDavid du Colombier static int parseaddr(uchar*, char*, int);
159ef1f84bSDavid du Colombier 
169ef1f84bSDavid du Colombier /*
179ef1f84bSDavid du Colombier  *  set up a new network interface
189ef1f84bSDavid du Colombier  */
199ef1f84bSDavid du Colombier void
netifinit(Netif * nif,char * name,int nfile,ulong limit)209ef1f84bSDavid du Colombier netifinit(Netif *nif, char *name, int nfile, ulong limit)
219ef1f84bSDavid du Colombier {
229ef1f84bSDavid du Colombier 	strncpy(nif->name, name, KNAMELEN-1);
239ef1f84bSDavid du Colombier 	nif->name[KNAMELEN-1] = 0;
249ef1f84bSDavid du Colombier 	nif->nfile = nfile;
259ef1f84bSDavid du Colombier 	nif->f = malloc(nfile*sizeof(Netfile*));
269ef1f84bSDavid du Colombier 	if(nif->f == nil)
279ef1f84bSDavid du Colombier 		panic("netifinit: no memory");
289ef1f84bSDavid du Colombier 	memset(nif->f, 0, nfile*sizeof(Netfile*));
299ef1f84bSDavid du Colombier 	nif->limit = limit;
309ef1f84bSDavid du Colombier }
319ef1f84bSDavid du Colombier 
329ef1f84bSDavid du Colombier /*
339ef1f84bSDavid du Colombier  *  generate a 3 level directory
349ef1f84bSDavid du Colombier  */
359ef1f84bSDavid du Colombier static int
netifgen(Chan * c,char *,Dirtab * vp,int,int i,Dir * dp)369ef1f84bSDavid du Colombier netifgen(Chan *c, char*, Dirtab *vp, int, int i, Dir *dp)
379ef1f84bSDavid du Colombier {
389ef1f84bSDavid du Colombier 	Qid q;
399ef1f84bSDavid du Colombier 	Netif *nif = (Netif*)vp;
409ef1f84bSDavid du Colombier 	Netfile *f;
419ef1f84bSDavid du Colombier 	int t;
429ef1f84bSDavid du Colombier 	int perm;
439ef1f84bSDavid du Colombier 	char *o;
449ef1f84bSDavid du Colombier 
459ef1f84bSDavid du Colombier 	q.type = QTFILE;
469ef1f84bSDavid du Colombier 	q.vers = 0;
479ef1f84bSDavid du Colombier 
489ef1f84bSDavid du Colombier 	/* top level directory contains the name of the network */
499ef1f84bSDavid du Colombier 	if(c->qid.path == 0){
509ef1f84bSDavid du Colombier 		switch(i){
519ef1f84bSDavid du Colombier 		case DEVDOTDOT:
529ef1f84bSDavid du Colombier 			q.path = 0;
539ef1f84bSDavid du Colombier 			q.type = QTDIR;
549ef1f84bSDavid du Colombier 			devdir(c, q, ".", 0, eve, 0555, dp);
559ef1f84bSDavid du Colombier 			break;
569ef1f84bSDavid du Colombier 		case 0:
579ef1f84bSDavid du Colombier 			q.path = N2ndqid;
589ef1f84bSDavid du Colombier 			q.type = QTDIR;
599ef1f84bSDavid du Colombier 			strcpy(up->genbuf, nif->name);
609ef1f84bSDavid du Colombier 			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
619ef1f84bSDavid du Colombier 			break;
629ef1f84bSDavid du Colombier 		default:
639ef1f84bSDavid du Colombier 			return -1;
649ef1f84bSDavid du Colombier 		}
659ef1f84bSDavid du Colombier 		return 1;
669ef1f84bSDavid du Colombier 	}
679ef1f84bSDavid du Colombier 
689ef1f84bSDavid du Colombier 	/* second level contains clone plus all the conversations */
699ef1f84bSDavid du Colombier 	t = NETTYPE(c->qid.path);
709ef1f84bSDavid du Colombier 	if(t == N2ndqid || t == Ncloneqid || t == Naddrqid){
719ef1f84bSDavid du Colombier 		switch(i) {
729ef1f84bSDavid du Colombier 		case DEVDOTDOT:
739ef1f84bSDavid du Colombier 			q.type = QTDIR;
749ef1f84bSDavid du Colombier 			q.path = 0;
759ef1f84bSDavid du Colombier 			devdir(c, q, ".", 0, eve, DMDIR|0555, dp);
769ef1f84bSDavid du Colombier 			break;
779ef1f84bSDavid du Colombier 		case 0:
789ef1f84bSDavid du Colombier 			q.path = Ncloneqid;
799ef1f84bSDavid du Colombier 			devdir(c, q, "clone", 0, eve, 0666, dp);
809ef1f84bSDavid du Colombier 			break;
819ef1f84bSDavid du Colombier 		case 1:
829ef1f84bSDavid du Colombier 			q.path = Naddrqid;
839ef1f84bSDavid du Colombier 			devdir(c, q, "addr", 0, eve, 0666, dp);
849ef1f84bSDavid du Colombier 			break;
859ef1f84bSDavid du Colombier 		case 2:
869ef1f84bSDavid du Colombier 			q.path = Nstatqid;
879ef1f84bSDavid du Colombier 			devdir(c, q, "stats", 0, eve, 0444, dp);
889ef1f84bSDavid du Colombier 			break;
899ef1f84bSDavid du Colombier 		case 3:
909ef1f84bSDavid du Colombier 			q.path = Nifstatqid;
919ef1f84bSDavid du Colombier 			devdir(c, q, "ifstats", 0, eve, 0444, dp);
929ef1f84bSDavid du Colombier 			break;
939ef1f84bSDavid du Colombier 		default:
949ef1f84bSDavid du Colombier 			i -= 4;
959ef1f84bSDavid du Colombier 			if(i >= nif->nfile)
969ef1f84bSDavid du Colombier 				return -1;
979ef1f84bSDavid du Colombier 			if(nif->f[i] == 0)
989ef1f84bSDavid du Colombier 				return 0;
999ef1f84bSDavid du Colombier 			q.type = QTDIR;
1009ef1f84bSDavid du Colombier 			q.path = NETQID(i, N3rdqid);
101*406c76faSDavid du Colombier 			snprint(up->genbuf, sizeof up->genbuf, "%d", i);
1029ef1f84bSDavid du Colombier 			devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp);
1039ef1f84bSDavid du Colombier 			break;
1049ef1f84bSDavid du Colombier 		}
1059ef1f84bSDavid du Colombier 		return 1;
1069ef1f84bSDavid du Colombier 	}
1079ef1f84bSDavid du Colombier 
1089ef1f84bSDavid du Colombier 	/* third level */
1099ef1f84bSDavid du Colombier 	f = nif->f[NETID(c->qid.path)];
1109ef1f84bSDavid du Colombier 	if(f == 0)
1119ef1f84bSDavid du Colombier 		return 0;
1129ef1f84bSDavid du Colombier 	if(*f->owner){
1139ef1f84bSDavid du Colombier 		o = f->owner;
1149ef1f84bSDavid du Colombier 		perm = f->mode;
1159ef1f84bSDavid du Colombier 	} else {
1169ef1f84bSDavid du Colombier 		o = eve;
1179ef1f84bSDavid du Colombier 		perm = 0666;
1189ef1f84bSDavid du Colombier 	}
1199ef1f84bSDavid du Colombier 	switch(i){
1209ef1f84bSDavid du Colombier 	case DEVDOTDOT:
1219ef1f84bSDavid du Colombier 		q.type = QTDIR;
1229ef1f84bSDavid du Colombier 		q.path = N2ndqid;
1239ef1f84bSDavid du Colombier 		strcpy(up->genbuf, nif->name);
1249ef1f84bSDavid du Colombier 		devdir(c, q, up->genbuf, 0, eve, DMDIR|0555, dp);
1259ef1f84bSDavid du Colombier 		break;
1269ef1f84bSDavid du Colombier 	case 0:
1279ef1f84bSDavid du Colombier 		q.path = NETQID(NETID(c->qid.path), Ndataqid);
1289ef1f84bSDavid du Colombier 		devdir(c, q, "data", 0, o, perm, dp);
1299ef1f84bSDavid du Colombier 		break;
1309ef1f84bSDavid du Colombier 	case 1:
1319ef1f84bSDavid du Colombier 		q.path = NETQID(NETID(c->qid.path), Nctlqid);
1329ef1f84bSDavid du Colombier 		devdir(c, q, "ctl", 0, o, perm, dp);
1339ef1f84bSDavid du Colombier 		break;
1349ef1f84bSDavid du Colombier 	case 2:
1359ef1f84bSDavid du Colombier 		q.path = NETQID(NETID(c->qid.path), Nstatqid);
1369ef1f84bSDavid du Colombier 		devdir(c, q, "stats", 0, eve, 0444, dp);
1379ef1f84bSDavid du Colombier 		break;
1389ef1f84bSDavid du Colombier 	case 3:
1399ef1f84bSDavid du Colombier 		q.path = NETQID(NETID(c->qid.path), Ntypeqid);
1409ef1f84bSDavid du Colombier 		devdir(c, q, "type", 0, eve, 0444, dp);
1419ef1f84bSDavid du Colombier 		break;
1429ef1f84bSDavid du Colombier 	case 4:
1439ef1f84bSDavid du Colombier 		q.path = NETQID(NETID(c->qid.path), Nifstatqid);
1449ef1f84bSDavid du Colombier 		devdir(c, q, "ifstats", 0, eve, 0444, dp);
1459ef1f84bSDavid du Colombier 		break;
1469ef1f84bSDavid du Colombier 	default:
1479ef1f84bSDavid du Colombier 		return -1;
1489ef1f84bSDavid du Colombier 	}
1499ef1f84bSDavid du Colombier 	return 1;
1509ef1f84bSDavid du Colombier }
1519ef1f84bSDavid du Colombier 
1529ef1f84bSDavid du Colombier Walkqid*
netifwalk(Netif * nif,Chan * c,Chan * nc,char ** name,int nname)1539ef1f84bSDavid du Colombier netifwalk(Netif *nif, Chan *c, Chan *nc, char **name, int nname)
1549ef1f84bSDavid du Colombier {
1559ef1f84bSDavid du Colombier 	return devwalk(c, nc, name, nname, (Dirtab *)nif, 0, netifgen);
1569ef1f84bSDavid du Colombier }
1579ef1f84bSDavid du Colombier 
1589ef1f84bSDavid du Colombier Chan*
netifopen(Netif * nif,Chan * c,int omode)1599ef1f84bSDavid du Colombier netifopen(Netif *nif, Chan *c, int omode)
1609ef1f84bSDavid du Colombier {
1619ef1f84bSDavid du Colombier 	int id;
1629ef1f84bSDavid du Colombier 	Netfile *f;
1639ef1f84bSDavid du Colombier 
1649ef1f84bSDavid du Colombier 	id = 0;
1659ef1f84bSDavid du Colombier 	if(c->qid.type & QTDIR){
1669ef1f84bSDavid du Colombier 		if(omode != OREAD)
1679ef1f84bSDavid du Colombier 			error(Eperm);
1689ef1f84bSDavid du Colombier 	} else {
1699ef1f84bSDavid du Colombier 		switch(NETTYPE(c->qid.path)){
1709ef1f84bSDavid du Colombier 		case Ndataqid:
1719ef1f84bSDavid du Colombier 		case Nctlqid:
1729ef1f84bSDavid du Colombier 			id = NETID(c->qid.path);
1739ef1f84bSDavid du Colombier 			openfile(nif, id);
1749ef1f84bSDavid du Colombier 			break;
1759ef1f84bSDavid du Colombier 		case Ncloneqid:
1769ef1f84bSDavid du Colombier 			id = openfile(nif, -1);
1779ef1f84bSDavid du Colombier 			c->qid.path = NETQID(id, Nctlqid);
1789ef1f84bSDavid du Colombier 			break;
1799ef1f84bSDavid du Colombier 		default:
1809ef1f84bSDavid du Colombier 			if(omode != OREAD)
1819ef1f84bSDavid du Colombier 				error(Ebadarg);
1829ef1f84bSDavid du Colombier 		}
1839ef1f84bSDavid du Colombier 		switch(NETTYPE(c->qid.path)){
1849ef1f84bSDavid du Colombier 		case Ndataqid:
1859ef1f84bSDavid du Colombier 		case Nctlqid:
1869ef1f84bSDavid du Colombier 			f = nif->f[id];
1879ef1f84bSDavid du Colombier 			if(netown(f, up->user, omode&7) < 0)
1889ef1f84bSDavid du Colombier 				error(Eperm);
1899ef1f84bSDavid du Colombier 			break;
1909ef1f84bSDavid du Colombier 		}
1919ef1f84bSDavid du Colombier 	}
1929ef1f84bSDavid du Colombier 	c->mode = openmode(omode);
1939ef1f84bSDavid du Colombier 	c->flag |= COPEN;
1949ef1f84bSDavid du Colombier 	c->offset = 0;
1959ef1f84bSDavid du Colombier 	c->iounit = qiomaxatomic;
1969ef1f84bSDavid du Colombier 	return c;
1979ef1f84bSDavid du Colombier }
1989ef1f84bSDavid du Colombier 
1999ef1f84bSDavid du Colombier long
netifread(Netif * nif,Chan * c,void * a,long n,vlong off)2009ef1f84bSDavid du Colombier netifread(Netif *nif, Chan *c, void *a, long n, vlong off)
2019ef1f84bSDavid du Colombier {
2029ef1f84bSDavid du Colombier 	int i, j;
2039ef1f84bSDavid du Colombier 	Netfile *f;
2049ef1f84bSDavid du Colombier 	char *p;
2059ef1f84bSDavid du Colombier 	long offset;
2069ef1f84bSDavid du Colombier 
2079ef1f84bSDavid du Colombier 	if(c->qid.type & QTDIR)
2089ef1f84bSDavid du Colombier 		return devdirread(c, a, n, (Dirtab*)nif, 0, netifgen);
2099ef1f84bSDavid du Colombier 
2109ef1f84bSDavid du Colombier 	offset = off;
2119ef1f84bSDavid du Colombier 	switch(NETTYPE(c->qid.path)){
2129ef1f84bSDavid du Colombier 	case Ndataqid:
2139ef1f84bSDavid du Colombier 		f = nif->f[NETID(c->qid.path)];
2149ef1f84bSDavid du Colombier 		return qread(f->iq, a, n);
2159ef1f84bSDavid du Colombier 	case Nctlqid:
2169ef1f84bSDavid du Colombier 		return readnum(offset, a, n, NETID(c->qid.path), NUMSIZE);
2179ef1f84bSDavid du Colombier 	case Nstatqid:
2189ef1f84bSDavid du Colombier 		p = malloc(READSTR);
219*406c76faSDavid du Colombier 		if(p == nil)
220*406c76faSDavid du Colombier 			error(Enomem);
221*406c76faSDavid du Colombier 		j = snprint(p, READSTR, "in: %llud\n", nif->inpackets);
2229ef1f84bSDavid du Colombier 		j += snprint(p+j, READSTR-j, "link: %d\n", nif->link);
223*406c76faSDavid du Colombier 		j += snprint(p+j, READSTR-j, "out: %llud\n", nif->outpackets);
2249ef1f84bSDavid du Colombier 		j += snprint(p+j, READSTR-j, "crc errs: %d\n", nif->crcs);
2259ef1f84bSDavid du Colombier 		j += snprint(p+j, READSTR-j, "overflows: %d\n", nif->overflows);
2269ef1f84bSDavid du Colombier 		j += snprint(p+j, READSTR-j, "soft overflows: %d\n", nif->soverflows);
2279ef1f84bSDavid du Colombier 		j += snprint(p+j, READSTR-j, "framing errs: %d\n", nif->frames);
2289ef1f84bSDavid du Colombier 		j += snprint(p+j, READSTR-j, "buffer errs: %d\n", nif->buffs);
2299ef1f84bSDavid du Colombier 		j += snprint(p+j, READSTR-j, "output errs: %d\n", nif->oerrs);
2309ef1f84bSDavid du Colombier 		j += snprint(p+j, READSTR-j, "prom: %d\n", nif->prom);
2319ef1f84bSDavid du Colombier 		j += snprint(p+j, READSTR-j, "mbps: %d\n", nif->mbps);
2329ef1f84bSDavid du Colombier 		j += snprint(p+j, READSTR-j, "addr: ");
2339ef1f84bSDavid du Colombier 		for(i = 0; i < nif->alen; i++)
2349ef1f84bSDavid du Colombier 			j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
2359ef1f84bSDavid du Colombier 		snprint(p+j, READSTR-j, "\n");
2369ef1f84bSDavid du Colombier 		n = readstr(offset, a, n, p);
2379ef1f84bSDavid du Colombier 		free(p);
2389ef1f84bSDavid du Colombier 		return n;
2399ef1f84bSDavid du Colombier 	case Naddrqid:
2409ef1f84bSDavid du Colombier 		p = malloc(READSTR);
241*406c76faSDavid du Colombier 		if(p == nil)
242*406c76faSDavid du Colombier 			error(Enomem);
2439ef1f84bSDavid du Colombier 		j = 0;
2449ef1f84bSDavid du Colombier 		for(i = 0; i < nif->alen; i++)
2459ef1f84bSDavid du Colombier 			j += snprint(p+j, READSTR-j, "%2.2ux", nif->addr[i]);
2469ef1f84bSDavid du Colombier 		n = readstr(offset, a, n, p);
2479ef1f84bSDavid du Colombier 		free(p);
2489ef1f84bSDavid du Colombier 		return n;
2499ef1f84bSDavid du Colombier 	case Ntypeqid:
2509ef1f84bSDavid du Colombier 		f = nif->f[NETID(c->qid.path)];
2519ef1f84bSDavid du Colombier 		return readnum(offset, a, n, f->type, NUMSIZE);
2529ef1f84bSDavid du Colombier 	case Nifstatqid:
2539ef1f84bSDavid du Colombier 		return 0;
2549ef1f84bSDavid du Colombier 	}
2559ef1f84bSDavid du Colombier 	error(Ebadarg);
2569ef1f84bSDavid du Colombier 	return -1;	/* not reached */
2579ef1f84bSDavid du Colombier }
2589ef1f84bSDavid du Colombier 
2599ef1f84bSDavid du Colombier Block*
netifbread(Netif * nif,Chan * c,long n,vlong offset)2609ef1f84bSDavid du Colombier netifbread(Netif *nif, Chan *c, long n, vlong offset)
2619ef1f84bSDavid du Colombier {
2629ef1f84bSDavid du Colombier 	if((c->qid.type & QTDIR) || NETTYPE(c->qid.path) != Ndataqid)
2639ef1f84bSDavid du Colombier 		return devbread(c, n, offset);
2649ef1f84bSDavid du Colombier 
2659ef1f84bSDavid du Colombier 	return qbread(nif->f[NETID(c->qid.path)]->iq, n);
2669ef1f84bSDavid du Colombier }
2679ef1f84bSDavid du Colombier 
2689ef1f84bSDavid du Colombier /*
2699ef1f84bSDavid du Colombier  *  make sure this type isn't already in use on this device
2709ef1f84bSDavid du Colombier  */
2719ef1f84bSDavid du Colombier static int
typeinuse(Netif * nif,int type)2729ef1f84bSDavid du Colombier typeinuse(Netif *nif, int type)
2739ef1f84bSDavid du Colombier {
2749ef1f84bSDavid du Colombier 	Netfile *f, **fp, **efp;
2759ef1f84bSDavid du Colombier 
2769ef1f84bSDavid du Colombier 	if(type <= 0)
2779ef1f84bSDavid du Colombier 		return 0;
2789ef1f84bSDavid du Colombier 
2799ef1f84bSDavid du Colombier 	efp = &nif->f[nif->nfile];
2809ef1f84bSDavid du Colombier 	for(fp = nif->f; fp < efp; fp++){
2819ef1f84bSDavid du Colombier 		f = *fp;
2829ef1f84bSDavid du Colombier 		if(f == 0)
2839ef1f84bSDavid du Colombier 			continue;
2849ef1f84bSDavid du Colombier 		if(f->type == type)
2859ef1f84bSDavid du Colombier 			return 1;
2869ef1f84bSDavid du Colombier 	}
2879ef1f84bSDavid du Colombier 	return 0;
2889ef1f84bSDavid du Colombier }
2899ef1f84bSDavid du Colombier 
2909ef1f84bSDavid du Colombier /*
2919ef1f84bSDavid du Colombier  *  the devxxx.c that calls us handles writing data, it knows best
2929ef1f84bSDavid du Colombier  */
2939ef1f84bSDavid du Colombier long
netifwrite(Netif * nif,Chan * c,void * a,long n)2949ef1f84bSDavid du Colombier netifwrite(Netif *nif, Chan *c, void *a, long n)
2959ef1f84bSDavid du Colombier {
2969ef1f84bSDavid du Colombier 	Netfile *f;
2979ef1f84bSDavid du Colombier 	int type;
2989ef1f84bSDavid du Colombier 	char *p, buf[64];
2999ef1f84bSDavid du Colombier 	uchar binaddr[Nmaxaddr];
3009ef1f84bSDavid du Colombier 
3019ef1f84bSDavid du Colombier 	if(NETTYPE(c->qid.path) != Nctlqid)
3029ef1f84bSDavid du Colombier 		error(Eperm);
3039ef1f84bSDavid du Colombier 
3049ef1f84bSDavid du Colombier 	if(n >= sizeof(buf))
3059ef1f84bSDavid du Colombier 		n = sizeof(buf)-1;
3069ef1f84bSDavid du Colombier 	memmove(buf, a, n);
3079ef1f84bSDavid du Colombier 	buf[n] = 0;
3089ef1f84bSDavid du Colombier 
3099ef1f84bSDavid du Colombier 	if(waserror()){
3109ef1f84bSDavid du Colombier 		qunlock(nif);
3119ef1f84bSDavid du Colombier 		nexterror();
3129ef1f84bSDavid du Colombier 	}
3139ef1f84bSDavid du Colombier 
3149ef1f84bSDavid du Colombier 	qlock(nif);
3159ef1f84bSDavid du Colombier 	f = nif->f[NETID(c->qid.path)];
3169ef1f84bSDavid du Colombier 	if((p = matchtoken(buf, "connect")) != 0){
3179ef1f84bSDavid du Colombier 		type = atoi(p);
3189ef1f84bSDavid du Colombier 		if(typeinuse(nif, type))
3199ef1f84bSDavid du Colombier 			error(Einuse);
3209ef1f84bSDavid du Colombier 		f->type = type;
3219ef1f84bSDavid du Colombier 		if(f->type < 0)
3229ef1f84bSDavid du Colombier 			nif->all++;
3239ef1f84bSDavid du Colombier 	} else if(matchtoken(buf, "promiscuous")){
3249ef1f84bSDavid du Colombier 		if(f->prom == 0){
3259ef1f84bSDavid du Colombier 			if(nif->prom == 0 && nif->promiscuous != nil)
3269ef1f84bSDavid du Colombier 				nif->promiscuous(nif->arg, 1);
3279ef1f84bSDavid du Colombier 			f->prom = 1;
3289ef1f84bSDavid du Colombier 			nif->prom++;
3299ef1f84bSDavid du Colombier 		}
3309ef1f84bSDavid du Colombier 	} else if((p = matchtoken(buf, "scanbs")) != 0){
3319ef1f84bSDavid du Colombier 		/* scan for base stations */
3329ef1f84bSDavid du Colombier 		if(f->scan == 0){
3339ef1f84bSDavid du Colombier 			type = atoi(p);
3349ef1f84bSDavid du Colombier 			if(type < 5)
3359ef1f84bSDavid du Colombier 				type = 5;
3369ef1f84bSDavid du Colombier 			if(nif->scanbs != nil)
3379ef1f84bSDavid du Colombier 				nif->scanbs(nif->arg, type);
3389ef1f84bSDavid du Colombier 			f->scan = type;
3399ef1f84bSDavid du Colombier 			nif->scan++;
3409ef1f84bSDavid du Colombier 		}
3419ef1f84bSDavid du Colombier 	} else if(matchtoken(buf, "bridge")){
3429ef1f84bSDavid du Colombier 		f->bridge = 1;
3439ef1f84bSDavid du Colombier 	} else if(matchtoken(buf, "headersonly")){
3449ef1f84bSDavid du Colombier 		f->headersonly = 1;
3459ef1f84bSDavid du Colombier 	} else if((p = matchtoken(buf, "addmulti")) != 0){
3469ef1f84bSDavid du Colombier 		if(parseaddr(binaddr, p, nif->alen) < 0)
3479ef1f84bSDavid du Colombier 			error("bad address");
3489ef1f84bSDavid du Colombier 		p = netmulti(nif, f, binaddr, 1);
3499ef1f84bSDavid du Colombier 		if(p)
3509ef1f84bSDavid du Colombier 			error(p);
3519ef1f84bSDavid du Colombier 	} else if((p = matchtoken(buf, "remmulti")) != 0){
3529ef1f84bSDavid du Colombier 		if(parseaddr(binaddr, p, nif->alen) < 0)
3539ef1f84bSDavid du Colombier 			error("bad address");
3549ef1f84bSDavid du Colombier 		p = netmulti(nif, f, binaddr, 0);
3559ef1f84bSDavid du Colombier 		if(p)
3569ef1f84bSDavid du Colombier 			error(p);
3579ef1f84bSDavid du Colombier 	} else
3589ef1f84bSDavid du Colombier 		n = -1;
3599ef1f84bSDavid du Colombier 	qunlock(nif);
3609ef1f84bSDavid du Colombier 	poperror();
3619ef1f84bSDavid du Colombier 	return n;
3629ef1f84bSDavid du Colombier }
3639ef1f84bSDavid du Colombier 
3649ef1f84bSDavid du Colombier long
netifwstat(Netif * nif,Chan * c,uchar * db,long n)3659ef1f84bSDavid du Colombier netifwstat(Netif *nif, Chan *c, uchar *db, long n)
3669ef1f84bSDavid du Colombier {
3679ef1f84bSDavid du Colombier 	Dir *dir;
3689ef1f84bSDavid du Colombier 	Netfile *f;
3699ef1f84bSDavid du Colombier 	int l;
3709ef1f84bSDavid du Colombier 
3719ef1f84bSDavid du Colombier 	f = nif->f[NETID(c->qid.path)];
3729ef1f84bSDavid du Colombier 	if(f == 0)
3739ef1f84bSDavid du Colombier 		error(Enonexist);
3749ef1f84bSDavid du Colombier 
3759ef1f84bSDavid du Colombier 	if(netown(f, up->user, OWRITE) < 0)
3769ef1f84bSDavid du Colombier 		error(Eperm);
3779ef1f84bSDavid du Colombier 
3789ef1f84bSDavid du Colombier 	dir = smalloc(sizeof(Dir)+n);
3799ef1f84bSDavid du Colombier 	l = convM2D(db, n, &dir[0], (char*)&dir[1]);
3809ef1f84bSDavid du Colombier 	if(l == 0){
3819ef1f84bSDavid du Colombier 		free(dir);
3829ef1f84bSDavid du Colombier 		error(Eshortstat);
3839ef1f84bSDavid du Colombier 	}
3849ef1f84bSDavid du Colombier 	if(!emptystr(dir[0].uid))
3859ef1f84bSDavid du Colombier 		strncpy(f->owner, dir[0].uid, KNAMELEN);
3869ef1f84bSDavid du Colombier 	if(dir[0].mode != ~0UL)
3879ef1f84bSDavid du Colombier 		f->mode = dir[0].mode;
3889ef1f84bSDavid du Colombier 	free(dir);
3899ef1f84bSDavid du Colombier 	return l;
3909ef1f84bSDavid du Colombier }
3919ef1f84bSDavid du Colombier 
3929ef1f84bSDavid du Colombier long
netifstat(Netif * nif,Chan * c,uchar * db,long n)3939ef1f84bSDavid du Colombier netifstat(Netif *nif, Chan *c, uchar *db, long n)
3949ef1f84bSDavid du Colombier {
3959ef1f84bSDavid du Colombier 	return devstat(c, db, n, (Dirtab *)nif, 0, netifgen);
3969ef1f84bSDavid du Colombier }
3979ef1f84bSDavid du Colombier 
3989ef1f84bSDavid du Colombier void
netifclose(Netif * nif,Chan * c)3999ef1f84bSDavid du Colombier netifclose(Netif *nif, Chan *c)
4009ef1f84bSDavid du Colombier {
4019ef1f84bSDavid du Colombier 	Netfile *f;
4029ef1f84bSDavid du Colombier 	int t;
4039ef1f84bSDavid du Colombier 	Netaddr *ap;
4049ef1f84bSDavid du Colombier 
4059ef1f84bSDavid du Colombier 	if((c->flag & COPEN) == 0)
4069ef1f84bSDavid du Colombier 		return;
4079ef1f84bSDavid du Colombier 
4089ef1f84bSDavid du Colombier 	t = NETTYPE(c->qid.path);
4099ef1f84bSDavid du Colombier 	if(t != Ndataqid && t != Nctlqid)
4109ef1f84bSDavid du Colombier 		return;
4119ef1f84bSDavid du Colombier 
4129ef1f84bSDavid du Colombier 	f = nif->f[NETID(c->qid.path)];
4139ef1f84bSDavid du Colombier 	qlock(f);
4149ef1f84bSDavid du Colombier 	if(--(f->inuse) == 0){
4159ef1f84bSDavid du Colombier 		if(f->prom){
4169ef1f84bSDavid du Colombier 			qlock(nif);
4179ef1f84bSDavid du Colombier 			if(--(nif->prom) == 0 && nif->promiscuous != nil)
4189ef1f84bSDavid du Colombier 				nif->promiscuous(nif->arg, 0);
4199ef1f84bSDavid du Colombier 			qunlock(nif);
4209ef1f84bSDavid du Colombier 			f->prom = 0;
4219ef1f84bSDavid du Colombier 		}
4229ef1f84bSDavid du Colombier 		if(f->scan){
4239ef1f84bSDavid du Colombier 			qlock(nif);
4249ef1f84bSDavid du Colombier 			if(--(nif->scan) == 0 && nif->scanbs != nil)
4259ef1f84bSDavid du Colombier 				nif->scanbs(nif->arg, 0);
4269ef1f84bSDavid du Colombier 			qunlock(nif);
4279ef1f84bSDavid du Colombier 			f->prom = 0;
4289ef1f84bSDavid du Colombier 			f->scan = 0;
4299ef1f84bSDavid du Colombier 		}
4309ef1f84bSDavid du Colombier 		if(f->nmaddr){
4319ef1f84bSDavid du Colombier 			qlock(nif);
4329ef1f84bSDavid du Colombier 			t = 0;
4339ef1f84bSDavid du Colombier 			for(ap = nif->maddr; ap; ap = ap->next){
4349ef1f84bSDavid du Colombier 				if(f->maddr[t/8] & (1<<(t%8)))
4359ef1f84bSDavid du Colombier 					netmulti(nif, f, ap->addr, 0);
4369ef1f84bSDavid du Colombier 			}
4379ef1f84bSDavid du Colombier 			qunlock(nif);
4389ef1f84bSDavid du Colombier 			f->nmaddr = 0;
4399ef1f84bSDavid du Colombier 		}
4409ef1f84bSDavid du Colombier 		if(f->type < 0){
4419ef1f84bSDavid du Colombier 			qlock(nif);
4429ef1f84bSDavid du Colombier 			--(nif->all);
4439ef1f84bSDavid du Colombier 			qunlock(nif);
4449ef1f84bSDavid du Colombier 		}
4459ef1f84bSDavid du Colombier 		f->owner[0] = 0;
4469ef1f84bSDavid du Colombier 		f->type = 0;
4479ef1f84bSDavid du Colombier 		f->bridge = 0;
4489ef1f84bSDavid du Colombier 		f->headersonly = 0;
4499ef1f84bSDavid du Colombier 		qclose(f->iq);
4509ef1f84bSDavid du Colombier 	}
4519ef1f84bSDavid du Colombier 	qunlock(f);
4529ef1f84bSDavid du Colombier }
4539ef1f84bSDavid du Colombier 
4549ef1f84bSDavid du Colombier Lock netlock;
4559ef1f84bSDavid du Colombier 
4569ef1f84bSDavid du Colombier static int
netown(Netfile * p,char * o,int omode)4579ef1f84bSDavid du Colombier netown(Netfile *p, char *o, int omode)
4589ef1f84bSDavid du Colombier {
4599ef1f84bSDavid du Colombier 	static int access[] = { 0400, 0200, 0600, 0100 };
4609ef1f84bSDavid du Colombier 	int mode;
4619ef1f84bSDavid du Colombier 	int t;
4629ef1f84bSDavid du Colombier 
4639ef1f84bSDavid du Colombier 	lock(&netlock);
4649ef1f84bSDavid du Colombier 	if(*p->owner){
4659ef1f84bSDavid du Colombier 		if(strncmp(o, p->owner, KNAMELEN) == 0)	/* User */
4669ef1f84bSDavid du Colombier 			mode = p->mode;
4679ef1f84bSDavid du Colombier 		else if(strncmp(o, eve, KNAMELEN) == 0)	/* Bootes is group */
4689ef1f84bSDavid du Colombier 			mode = p->mode<<3;
4699ef1f84bSDavid du Colombier 		else
4709ef1f84bSDavid du Colombier 			mode = p->mode<<6;		/* Other */
4719ef1f84bSDavid du Colombier 
4729ef1f84bSDavid du Colombier 		t = access[omode&3];
4739ef1f84bSDavid du Colombier 		if((t & mode) == t){
4749ef1f84bSDavid du Colombier 			unlock(&netlock);
4759ef1f84bSDavid du Colombier 			return 0;
4769ef1f84bSDavid du Colombier 		} else {
4779ef1f84bSDavid du Colombier 			unlock(&netlock);
4789ef1f84bSDavid du Colombier 			return -1;
4799ef1f84bSDavid du Colombier 		}
4809ef1f84bSDavid du Colombier 	}
4819ef1f84bSDavid du Colombier 	strncpy(p->owner, o, KNAMELEN);
4829ef1f84bSDavid du Colombier 	p->mode = 0660;
4839ef1f84bSDavid du Colombier 	unlock(&netlock);
4849ef1f84bSDavid du Colombier 	return 0;
4859ef1f84bSDavid du Colombier }
4869ef1f84bSDavid du Colombier 
4879ef1f84bSDavid du Colombier /*
4889ef1f84bSDavid du Colombier  *  Increment the reference count of a network device.
4899ef1f84bSDavid du Colombier  *  If id < 0, return an unused ether device.
4909ef1f84bSDavid du Colombier  */
4919ef1f84bSDavid du Colombier static int
openfile(Netif * nif,int id)4929ef1f84bSDavid du Colombier openfile(Netif *nif, int id)
4939ef1f84bSDavid du Colombier {
4949ef1f84bSDavid du Colombier 	Netfile *f, **fp, **efp;
4959ef1f84bSDavid du Colombier 
4969ef1f84bSDavid du Colombier 	if(id >= 0){
4979ef1f84bSDavid du Colombier 		f = nif->f[id];
4989ef1f84bSDavid du Colombier 		if(f == 0)
4999ef1f84bSDavid du Colombier 			error(Enodev);
5009ef1f84bSDavid du Colombier 		qlock(f);
5019ef1f84bSDavid du Colombier 		qreopen(f->iq);
5029ef1f84bSDavid du Colombier 		f->inuse++;
5039ef1f84bSDavid du Colombier 		qunlock(f);
5049ef1f84bSDavid du Colombier 		return id;
5059ef1f84bSDavid du Colombier 	}
5069ef1f84bSDavid du Colombier 
5079ef1f84bSDavid du Colombier 	qlock(nif);
5089ef1f84bSDavid du Colombier 	if(waserror()){
5099ef1f84bSDavid du Colombier 		qunlock(nif);
5109ef1f84bSDavid du Colombier 		nexterror();
5119ef1f84bSDavid du Colombier 	}
5129ef1f84bSDavid du Colombier 	efp = &nif->f[nif->nfile];
5139ef1f84bSDavid du Colombier 	for(fp = nif->f; fp < efp; fp++){
5149ef1f84bSDavid du Colombier 		f = *fp;
5159ef1f84bSDavid du Colombier 		if(f == 0){
5169ef1f84bSDavid du Colombier 			f = malloc(sizeof(Netfile));
5179ef1f84bSDavid du Colombier 			if(f == 0)
5189ef1f84bSDavid du Colombier 				exhausted("memory");
5199ef1f84bSDavid du Colombier 			f->iq = qopen(nif->limit, Qmsg, 0, 0);
5209ef1f84bSDavid du Colombier 			if(f->iq == nil){
5219ef1f84bSDavid du Colombier 				free(f);
5229ef1f84bSDavid du Colombier 				exhausted("memory");
5239ef1f84bSDavid du Colombier 			}
5249ef1f84bSDavid du Colombier 			*fp = f;
5259ef1f84bSDavid du Colombier 			qlock(f);
5269ef1f84bSDavid du Colombier 		} else {
5279ef1f84bSDavid du Colombier 			qlock(f);
5289ef1f84bSDavid du Colombier 			if(f->inuse){
5299ef1f84bSDavid du Colombier 				qunlock(f);
5309ef1f84bSDavid du Colombier 				continue;
5319ef1f84bSDavid du Colombier 			}
5329ef1f84bSDavid du Colombier 		}
5339ef1f84bSDavid du Colombier 		f->inuse = 1;
5349ef1f84bSDavid du Colombier 		qreopen(f->iq);
5359ef1f84bSDavid du Colombier 		netown(f, up->user, 0);
5369ef1f84bSDavid du Colombier 		qunlock(f);
5379ef1f84bSDavid du Colombier 		qunlock(nif);
5389ef1f84bSDavid du Colombier 		poperror();
5399ef1f84bSDavid du Colombier 		return fp - nif->f;
5409ef1f84bSDavid du Colombier 	}
5419ef1f84bSDavid du Colombier 	error(Enodev);
5429ef1f84bSDavid du Colombier 	return -1;	/* not reached */
5439ef1f84bSDavid du Colombier }
5449ef1f84bSDavid du Colombier 
5459ef1f84bSDavid du Colombier /*
5469ef1f84bSDavid du Colombier  *  look for a token starting a string,
5479ef1f84bSDavid du Colombier  *  return a pointer to first non-space char after it
5489ef1f84bSDavid du Colombier  */
5499ef1f84bSDavid du Colombier static char*
matchtoken(char * p,char * token)5509ef1f84bSDavid du Colombier matchtoken(char *p, char *token)
5519ef1f84bSDavid du Colombier {
5529ef1f84bSDavid du Colombier 	int n;
5539ef1f84bSDavid du Colombier 
5549ef1f84bSDavid du Colombier 	n = strlen(token);
5559ef1f84bSDavid du Colombier 	if(strncmp(p, token, n))
5569ef1f84bSDavid du Colombier 		return 0;
5579ef1f84bSDavid du Colombier 	p += n;
5589ef1f84bSDavid du Colombier 	if(*p == 0)
5599ef1f84bSDavid du Colombier 		return p;
5609ef1f84bSDavid du Colombier 	if(*p != ' ' && *p != '\t' && *p != '\n')
5619ef1f84bSDavid du Colombier 		return 0;
5629ef1f84bSDavid du Colombier 	while(*p == ' ' || *p == '\t' || *p == '\n')
5639ef1f84bSDavid du Colombier 		p++;
5649ef1f84bSDavid du Colombier 	return p;
5659ef1f84bSDavid du Colombier }
5669ef1f84bSDavid du Colombier 
5679ef1f84bSDavid du Colombier void
hnputv(void * p,uvlong v)5689ef1f84bSDavid du Colombier hnputv(void *p, uvlong v)
5699ef1f84bSDavid du Colombier {
5709ef1f84bSDavid du Colombier 	uchar *a;
5719ef1f84bSDavid du Colombier 
5729ef1f84bSDavid du Colombier 	a = p;
5739ef1f84bSDavid du Colombier 	hnputl(a, v>>32);
5749ef1f84bSDavid du Colombier 	hnputl(a+4, v);
5759ef1f84bSDavid du Colombier }
5769ef1f84bSDavid du Colombier 
5779ef1f84bSDavid du Colombier void
hnputl(void * p,uint v)5789ef1f84bSDavid du Colombier hnputl(void *p, uint v)
5799ef1f84bSDavid du Colombier {
5809ef1f84bSDavid du Colombier 	uchar *a;
5819ef1f84bSDavid du Colombier 
5829ef1f84bSDavid du Colombier 	a = p;
5839ef1f84bSDavid du Colombier 	a[0] = v>>24;
5849ef1f84bSDavid du Colombier 	a[1] = v>>16;
5859ef1f84bSDavid du Colombier 	a[2] = v>>8;
5869ef1f84bSDavid du Colombier 	a[3] = v;
5879ef1f84bSDavid du Colombier }
5889ef1f84bSDavid du Colombier 
5899ef1f84bSDavid du Colombier void
hnputs(void * p,ushort v)5909ef1f84bSDavid du Colombier hnputs(void *p, ushort v)
5919ef1f84bSDavid du Colombier {
5929ef1f84bSDavid du Colombier 	uchar *a;
5939ef1f84bSDavid du Colombier 
5949ef1f84bSDavid du Colombier 	a = p;
5959ef1f84bSDavid du Colombier 	a[0] = v>>8;
5969ef1f84bSDavid du Colombier 	a[1] = v;
5979ef1f84bSDavid du Colombier }
5989ef1f84bSDavid du Colombier 
5999ef1f84bSDavid du Colombier uvlong
nhgetv(void * p)6009ef1f84bSDavid du Colombier nhgetv(void *p)
6019ef1f84bSDavid du Colombier {
6029ef1f84bSDavid du Colombier 	uchar *a;
6039ef1f84bSDavid du Colombier 
6049ef1f84bSDavid du Colombier 	a = p;
6059ef1f84bSDavid du Colombier 	return ((vlong)nhgetl(a) << 32) | nhgetl(a+4);
6069ef1f84bSDavid du Colombier }
6079ef1f84bSDavid du Colombier 
6089ef1f84bSDavid du Colombier uint
nhgetl(void * p)6099ef1f84bSDavid du Colombier nhgetl(void *p)
6109ef1f84bSDavid du Colombier {
6119ef1f84bSDavid du Colombier 	uchar *a;
6129ef1f84bSDavid du Colombier 
6139ef1f84bSDavid du Colombier 	a = p;
6149ef1f84bSDavid du Colombier 	return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
6159ef1f84bSDavid du Colombier }
6169ef1f84bSDavid du Colombier 
6179ef1f84bSDavid du Colombier ushort
nhgets(void * p)6189ef1f84bSDavid du Colombier nhgets(void *p)
6199ef1f84bSDavid du Colombier {
6209ef1f84bSDavid du Colombier 	uchar *a;
6219ef1f84bSDavid du Colombier 
6229ef1f84bSDavid du Colombier 	a = p;
6239ef1f84bSDavid du Colombier 	return (a[0]<<8)|(a[1]<<0);
6249ef1f84bSDavid du Colombier }
6259ef1f84bSDavid du Colombier 
6269ef1f84bSDavid du Colombier static ulong
hash(uchar * a,int len)6279ef1f84bSDavid du Colombier hash(uchar *a, int len)
6289ef1f84bSDavid du Colombier {
6299ef1f84bSDavid du Colombier 	ulong sum = 0;
6309ef1f84bSDavid du Colombier 
6319ef1f84bSDavid du Colombier 	while(len-- > 0)
6329ef1f84bSDavid du Colombier 		sum = (sum << 1) + *a++;
6339ef1f84bSDavid du Colombier 	return sum%Nmhash;
6349ef1f84bSDavid du Colombier }
6359ef1f84bSDavid du Colombier 
6369ef1f84bSDavid du Colombier int
activemulti(Netif * nif,uchar * addr,int alen)6379ef1f84bSDavid du Colombier activemulti(Netif *nif, uchar *addr, int alen)
6389ef1f84bSDavid du Colombier {
6399ef1f84bSDavid du Colombier 	Netaddr *hp;
6409ef1f84bSDavid du Colombier 
6419ef1f84bSDavid du Colombier 	for(hp = nif->mhash[hash(addr, alen)]; hp; hp = hp->hnext)
6429ef1f84bSDavid du Colombier 		if(memcmp(addr, hp->addr, alen) == 0){
6439ef1f84bSDavid du Colombier 			if(hp->ref)
6449ef1f84bSDavid du Colombier 				return 1;
6459ef1f84bSDavid du Colombier 			else
6469ef1f84bSDavid du Colombier 				break;
6479ef1f84bSDavid du Colombier 		}
6489ef1f84bSDavid du Colombier 	return 0;
6499ef1f84bSDavid du Colombier }
6509ef1f84bSDavid du Colombier 
6519ef1f84bSDavid du Colombier static int
parseaddr(uchar * to,char * from,int alen)6529ef1f84bSDavid du Colombier parseaddr(uchar *to, char *from, int alen)
6539ef1f84bSDavid du Colombier {
6549ef1f84bSDavid du Colombier 	char nip[4];
6559ef1f84bSDavid du Colombier 	char *p;
6569ef1f84bSDavid du Colombier 	int i;
6579ef1f84bSDavid du Colombier 
6589ef1f84bSDavid du Colombier 	p = from;
6599ef1f84bSDavid du Colombier 	for(i = 0; i < alen; i++){
6609ef1f84bSDavid du Colombier 		if(*p == 0)
6619ef1f84bSDavid du Colombier 			return -1;
6629ef1f84bSDavid du Colombier 		nip[0] = *p++;
6639ef1f84bSDavid du Colombier 		if(*p == 0)
6649ef1f84bSDavid du Colombier 			return -1;
6659ef1f84bSDavid du Colombier 		nip[1] = *p++;
6669ef1f84bSDavid du Colombier 		nip[2] = 0;
6679ef1f84bSDavid du Colombier 		to[i] = strtoul(nip, 0, 16);
6689ef1f84bSDavid du Colombier 		if(*p == ':')
6699ef1f84bSDavid du Colombier 			p++;
6709ef1f84bSDavid du Colombier 	}
6719ef1f84bSDavid du Colombier 	return 0;
6729ef1f84bSDavid du Colombier }
6739ef1f84bSDavid du Colombier 
6749ef1f84bSDavid du Colombier /*
6759ef1f84bSDavid du Colombier  *  keep track of multicast addresses
6769ef1f84bSDavid du Colombier  */
6779ef1f84bSDavid du Colombier static char*
netmulti(Netif * nif,Netfile * f,uchar * addr,int add)6789ef1f84bSDavid du Colombier netmulti(Netif *nif, Netfile *f, uchar *addr, int add)
6799ef1f84bSDavid du Colombier {
6809ef1f84bSDavid du Colombier 	Netaddr **l, *ap;
6819ef1f84bSDavid du Colombier 	int i;
6829ef1f84bSDavid du Colombier 	ulong h;
6839ef1f84bSDavid du Colombier 
6849ef1f84bSDavid du Colombier 	if(nif->multicast == nil)
6859ef1f84bSDavid du Colombier 		return "interface does not support multicast";
6869ef1f84bSDavid du Colombier 
6879ef1f84bSDavid du Colombier 	l = &nif->maddr;
6889ef1f84bSDavid du Colombier 	i = 0;
6899ef1f84bSDavid du Colombier 	for(ap = *l; ap; ap = *l){
6909ef1f84bSDavid du Colombier 		if(memcmp(addr, ap->addr, nif->alen) == 0)
6919ef1f84bSDavid du Colombier 			break;
6929ef1f84bSDavid du Colombier 		i++;
6939ef1f84bSDavid du Colombier 		l = &ap->next;
6949ef1f84bSDavid du Colombier 	}
6959ef1f84bSDavid du Colombier 
6969ef1f84bSDavid du Colombier 	if(add){
6979ef1f84bSDavid du Colombier 		if(ap == 0){
6989ef1f84bSDavid du Colombier 			*l = ap = smalloc(sizeof(*ap));
6999ef1f84bSDavid du Colombier 			memmove(ap->addr, addr, nif->alen);
7009ef1f84bSDavid du Colombier 			ap->next = 0;
7019ef1f84bSDavid du Colombier 			ap->ref = 1;
7029ef1f84bSDavid du Colombier 			h = hash(addr, nif->alen);
7039ef1f84bSDavid du Colombier 			ap->hnext = nif->mhash[h];
7049ef1f84bSDavid du Colombier 			nif->mhash[h] = ap;
7059ef1f84bSDavid du Colombier 		} else {
7069ef1f84bSDavid du Colombier 			ap->ref++;
7079ef1f84bSDavid du Colombier 		}
7089ef1f84bSDavid du Colombier 		if(ap->ref == 1){
7099ef1f84bSDavid du Colombier 			nif->nmaddr++;
7109ef1f84bSDavid du Colombier 			nif->multicast(nif->arg, addr, 1);
7119ef1f84bSDavid du Colombier 		}
7129ef1f84bSDavid du Colombier 		if(i < 8*sizeof(f->maddr)){
7139ef1f84bSDavid du Colombier 			if((f->maddr[i/8] & (1<<(i%8))) == 0)
7149ef1f84bSDavid du Colombier 				f->nmaddr++;
7159ef1f84bSDavid du Colombier 			f->maddr[i/8] |= 1<<(i%8);
7169ef1f84bSDavid du Colombier 		}
7179ef1f84bSDavid du Colombier 	} else {
7189ef1f84bSDavid du Colombier 		if(ap == 0 || ap->ref == 0)
7199ef1f84bSDavid du Colombier 			return 0;
7209ef1f84bSDavid du Colombier 		ap->ref--;
7219ef1f84bSDavid du Colombier 		if(ap->ref == 0){
7229ef1f84bSDavid du Colombier 			nif->nmaddr--;
7239ef1f84bSDavid du Colombier 			nif->multicast(nif->arg, addr, 0);
7249ef1f84bSDavid du Colombier 		}
7259ef1f84bSDavid du Colombier 		if(i < 8*sizeof(f->maddr)){
7269ef1f84bSDavid du Colombier 			if((f->maddr[i/8] & (1<<(i%8))) != 0)
7279ef1f84bSDavid du Colombier 				f->nmaddr--;
7289ef1f84bSDavid du Colombier 			f->maddr[i/8] &= ~(1<<(i%8));
7299ef1f84bSDavid du Colombier 		}
7309ef1f84bSDavid du Colombier 	}
7319ef1f84bSDavid du Colombier 	return 0;
7329ef1f84bSDavid du Colombier }
733