|  | /* | 
|  | * 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; | 
|  | } |