| /*  | 
 |  * This file is part of the UCB release of Plan 9. It is subject to the license | 
 |  * terms in the LICENSE file found in the top-level directory of this | 
 |  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
 |  * part of the UCB release of Plan 9, including this file, may be copied, | 
 |  * modified, propagated, or distributed except according to the terms contained | 
 |  * in the LICENSE file. | 
 |  */ | 
 | #include <stdlib.h> | 
 |  | 
 | #include <stdio.h> | 
 | #include <parlib/parlib.h> | 
 | #include <unistd.h> | 
 | #include <signal.h> | 
 | #include <fcntl.h> | 
 | #include <iplib/iplib.h> | 
 | #include <ndblib/ndb.h> | 
 |  | 
 | enum | 
 | { | 
 | 	Ffound=	1<<0, | 
 | 	Fignore=1<<1, | 
 | 	Faddr=	1<<2, | 
 | }; | 
 |  | 
 | static struct ndbtuple*	filter(struct ndb *db, struct ndbtuple *t, | 
 | 				      struct ndbtuple *f); | 
 | static struct ndbtuple*	mkfilter(int argc, char **argv); | 
 | static int		filtercomplete(struct ndbtuple *f); | 
 | static struct ndbtuple*	toipaddr(struct ndb *db, struct ndbtuple *t); | 
 | static int		prefixlen(uint8_t *ip); | 
 | static struct ndbtuple*	subnet(struct ndb *db, uint8_t *net, | 
 | 				      struct ndbtuple *f, int prefix); | 
 |  | 
 | /* make a filter to be used in filter */ | 
 | static struct ndbtuple* | 
 | mkfilter(int argc, char **argv) | 
 | { | 
 | 	struct ndbtuple *t, *first, *last; | 
 | 	char *p; | 
 |  | 
 | 	last = first = NULL; | 
 | 	while(argc-- > 0){ | 
 | 		t = ndbnew(0, 0); | 
 | 		if(first) | 
 | 			last->entry = t; | 
 | 		else | 
 | 			first = t; | 
 | 		last = t; | 
 | 		p = *argv++; | 
 | 		if (*p == '@'){		/* @attr=val ? */ | 
 | 			t->ptr |= Faddr;/* return resolved address(es) */ | 
 | 			p++; | 
 | 		} | 
 | 		strncpy(t->attr, p, sizeof(t->attr)-1); | 
 | 	} | 
 | 	ndbsetmalloctag(first, getcallerpc(&argc)); | 
 | 	return first; | 
 | } | 
 |  | 
 | /* return true if every pair of filter has been used */ | 
 | static int | 
 | filtercomplete(struct ndbtuple *f) | 
 | { | 
 | 	for(; f; f = f->entry) | 
 | 		if((f->ptr & Fignore) == 0) | 
 | 			return 0; | 
 | 	return 1; | 
 | } | 
 |  | 
 | /* set the attribute of all entries in a tuple */ | 
 | static struct ndbtuple* | 
 | setattr(struct ndbtuple *t, char *attr) | 
 | { | 
 | 	struct ndbtuple *nt; | 
 |  | 
 | 	for(nt = t; nt; nt = nt->entry) | 
 | 		strcpy(nt->attr, attr); | 
 | 	return t; | 
 | } | 
 |  | 
 | /* | 
 |  *  return only the attr/value pairs in t maching the filter, f. | 
 |  *  others are freed.  line structure is preserved. | 
 |  */ | 
 | static struct ndbtuple* | 
 | filter(struct ndb *db, struct ndbtuple *t, struct ndbtuple *f) | 
 | { | 
 | 	struct ndbtuple *nt, *nf, *next; | 
 |  | 
 | 	/* filter out what we don't want */ | 
 | 	for(nt = t; nt; nt = next){ | 
 | 		next = nt->entry; | 
 |  | 
 | 		/* look through filter */ | 
 | 		for(nf = f; nf != NULL; nf = nf->entry){ | 
 | 			if (!(nf->ptr&Fignore) && strcmp(nt->attr, nf->attr) == | 
 | 			   0) | 
 | 				break; | 
 | 		} | 
 | 		if(nf == NULL){ | 
 | 			/* remove nt from t */ | 
 | 			t = ndbdiscard(t, nt); | 
 | 		} else { | 
 | 			if (nf->ptr & Faddr) | 
 | 				t = ndbsubstitute(t, nt, | 
 | 						  setattr(ndbgetipaddr(db, | 
 | 								       nt->val), | 
 | 							  nt->attr)); | 
 | 			nf->ptr |= Ffound; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* remember filter etnries that matched */ | 
 | 	for(nf = f; nf != NULL; nf = nf->entry) | 
 | 		if(nf->ptr & Ffound) | 
 | 			nf->ptr = (nf->ptr & ~Ffound) | Fignore; | 
 |  | 
 | 	ndbsetmalloctag(t, getcallerpc(&db)); | 
 | 	return t; | 
 | } | 
 |  | 
 | static int | 
 | prefixlen(uint8_t *ip) | 
 | { | 
 | 	int y, i; | 
 |  | 
 | 	for(y = IPaddrlen-1; y >= 0; y--) | 
 | 		for(i = 8; i > 0; i--) | 
 | 			if(ip[y] & (1<<(8-i))) | 
 | 				return y*8 + i; | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* | 
 |  *  look through a containing subset | 
 |  */ | 
 | static struct ndbtuple* | 
 | subnet(struct ndb *db, uint8_t *net, struct ndbtuple *f, int prefix) | 
 | { | 
 | 	struct ndbs s; | 
 | 	struct ndbtuple *t, *nt, *xt; | 
 | 	char netstr[128]; | 
 | 	uint8_t mask[IPaddrlen]; | 
 | 	int masklen; | 
 |  | 
 | 	t = NULL; | 
 | 	sprintf(netstr, "%I", net); | 
 | 	nt = ndbsearch(db, &s, "ip", netstr); | 
 | 	while(nt != NULL){ | 
 | 		xt = ndbfindattr(nt, nt, "ipnet"); | 
 | 		if(xt){ | 
 | 			xt = ndbfindattr(nt, nt, "ipmask"); | 
 | 			if(xt) | 
 | 				parseipmask(mask, xt->val); | 
 | 			else | 
 | 				ipmove(mask, defmask(net)); | 
 | 			masklen = prefixlen(mask); | 
 | 			if(masklen <= prefix){ | 
 | 				t = ndbconcatenate(t, filter(db, nt, f)); | 
 | 				nt = NULL; | 
 | 			} | 
 | 		} | 
 | 		ndbfree(nt); | 
 | 		nt = ndbsnext(&s, "ip", netstr); | 
 | 	} | 
 | 	ndbsetmalloctag(t, getcallerpc(&db)); | 
 | 	return t; | 
 | } | 
 |  | 
 | /* | 
 |  *  fill in all the requested attributes for a system. | 
 |  *  if the system's entry doesn't have all required, | 
 |  *  walk through successively more inclusive networks | 
 |  *  for inherited attributes. | 
 |  */ | 
 | struct ndbtuple* | 
 | ndbipinfo(struct ndb *db, char *attr, char *val, char **alist, int n) | 
 | { | 
 | 	struct ndbtuple *t, *nt, *f; | 
 | 	struct ndbs s; | 
 | 	char *ipstr; | 
 | 	uint8_t net[IPaddrlen], ip[IPaddrlen]; | 
 | 	int prefix, smallestprefix, force; | 
 | 	int64_t r; | 
 |  | 
 | #if 0 | 
 | 	/* just in case */ | 
 | 	fmtinstall('I', eipfmt); | 
 | 	fmtinstall('M', eipfmt); | 
 | #endif | 
 |  | 
 | 	/* get needed attributes */ | 
 | 	f = mkfilter(n, alist); | 
 |  | 
 | 	/* | 
 | 	 *  first look for a matching entry with an ip address | 
 | 	 */ | 
 | 	t = NULL; | 
 | 	ipstr = ndbgetvalue(db, &s, attr, val, "ip", &nt); | 
 | 	if(ipstr == NULL){ | 
 | 		/* none found, make one up */ | 
 | 		if(strcmp(attr, "ip") != 0) { | 
 | 			ndbfree(f); | 
 | 			return NULL;	 | 
 | 		} | 
 | 		t = ndbnew("ip", val); | 
 | 		t->line = t; | 
 | 		t->entry = NULL; | 
 | 		r = parseip(net, val); | 
 | 		if(r == -1) | 
 | 			ndbfree(t); | 
 | 	} else { | 
 | 		/* found one */ | 
 | 		while(nt != NULL){ | 
 | 			nt = ndbreorder(nt, s.t); | 
 | 			t = ndbconcatenate(t, nt); | 
 | 			nt = ndbsnext(&s, attr, val); | 
 | 		} | 
 | 		r = parseip(net, ipstr); | 
 | 		free(ipstr); | 
 | 	} | 
 | 	if(r < 0){ | 
 | 		ndbfree(f); | 
 | 		return NULL; | 
 | 	} | 
 | 	ipmove(ip, net); | 
 | 	t = filter(db, t, f); | 
 |  | 
 | 	/* | 
 | 	 *  now go through subnets to fill in any missing attributes | 
 | 	 */ | 
 | 	if(isv4(net)){ | 
 | 		prefix = 127; | 
 | 		smallestprefix = 100; | 
 | 		force = 0; | 
 | 	} else { | 
 | 		/* in v6, the last 8 bytes have no structure (we hope) */ | 
 | 		prefix = 64; | 
 | 		smallestprefix = 2; | 
 | 		memset(net+8, 0, 8); | 
 | 		force = 1; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *  to find a containing network, keep turning off | 
 | 	 *  the lower bit and look for a network with | 
 | 	 *  that address and a shorter mask.  tedius but | 
 | 	 *  complete, we may need to find a trick to speed this up. | 
 | 	 */ | 
 | 	for(; prefix >= smallestprefix; prefix--){ | 
 | 		if(filtercomplete(f)) | 
 | 			break; | 
 | 		if(!force && (net[prefix/8] & (1<<(7-(prefix%8)))) == 0) | 
 | 			continue; | 
 | 		force = 0; | 
 | 		net[prefix/8] &= ~(1<<(7-(prefix%8))); | 
 | 		t = ndbconcatenate(t, subnet(db, net, f, prefix)); | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *  if there's an unfulfilled ipmask, make one up | 
 | 	 */ | 
 | 	nt = ndbfindattr(f, f, "ipmask"); | 
 | 	if(nt && !(nt->ptr & Fignore)){ | 
 | 		char x[64]; | 
 |  | 
 | 		snprintf(x, sizeof(x), "%M", defmask(ip)); | 
 | 		t = ndbconcatenate(t, ndbnew("ipmask", x)); | 
 | 	} | 
 |  | 
 | 	ndbfree(f); | 
 | 	ndbsetmalloctag(t, getcallerpc(&db)); | 
 | 	return t; | 
 | } |