plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

ndbipinfo.c (4880B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <bio.h>
      4 #include <ndb.h>
      5 #include <ip.h>
      6 
      7 enum
      8 {
      9 	Ffound=	1<<0,
     10 	Fignore=	1<<1,
     11 	Faddr=	1<<2
     12 };
     13 
     14 static Ndbtuple*	filter(Ndb *db, Ndbtuple *t, Ndbtuple *f);
     15 static Ndbtuple*	mkfilter(int argc, char **argv);
     16 static int		filtercomplete(Ndbtuple *f);
     17 static int		prefixlen(uchar *ip);
     18 static Ndbtuple*	subnet(Ndb *db, uchar *net, Ndbtuple *f, int prefix);
     19 
     20 /* make a filter to be used in filter */
     21 static Ndbtuple*
     22 mkfilter(int argc, char **argv)
     23 {
     24 	Ndbtuple *t, *first, *last;
     25 	char *p;
     26 
     27 	last = first = nil;
     28 	while(argc-- > 0){
     29 		t = ndbnew(0, 0);
     30 		if(first)
     31 			last->entry = t;
     32 		else
     33 			first = t;
     34 		last = t;
     35 		p = *argv++;
     36 		if(*p == '@'){
     37 			t->ptr |= Faddr;
     38 			p++;
     39 		}
     40 		strncpy(t->attr, p, sizeof(t->attr)-1);
     41 	}
     42 	return first;
     43 }
     44 
     45 /* return true if every pair of filter has been used */
     46 static int
     47 filtercomplete(Ndbtuple *f)
     48 {
     49 	for(; f; f = f->entry)
     50 		if((f->ptr & Fignore) == 0)
     51 			return 0;
     52 	return 1;
     53 }
     54 
     55 /* set the attribute of all entries in a tuple */
     56 static Ndbtuple*
     57 setattr(Ndbtuple *t, char *attr)
     58 {
     59 	Ndbtuple *nt;
     60 
     61 	for(nt = t; nt; nt = nt->entry)
     62 		strcpy(nt->attr, attr);
     63 	return t;
     64 }
     65 
     66 /*
     67  *  return only the attr/value pairs in t maching the filter, f.
     68  *  others are freed.  line structure is preserved.
     69  */
     70 static Ndbtuple*
     71 filter(Ndb *db, Ndbtuple *t, Ndbtuple *f)
     72 {
     73 	Ndbtuple *nt, *nf, *next;
     74 
     75 	/* filter out what we don't want */
     76 	for(nt = t; nt; nt = next){
     77 		next = nt->entry;
     78 
     79 		/* look through filter */
     80 		for(nf = f; nf != nil; nf = nf->entry){
     81 			if(!(nf->ptr&Fignore) && strcmp(nt->attr, nf->attr) == 0)
     82 				break;
     83 		}
     84 		if(nf == nil){
     85 			/* remove nt from t */
     86 			t = ndbdiscard(t, nt);
     87 		} else {
     88 			if(nf->ptr & Faddr)
     89 				t = ndbsubstitute(t, nt, setattr(ndbgetipaddr(db, nt->val), nt->attr));
     90 			nf->ptr |= Ffound;
     91 		}
     92 	}
     93 
     94 	/* remember filter etnries that matched */
     95 	for(nf = f; nf != nil; nf = nf->entry)
     96 		if(nf->ptr & Ffound)
     97 			nf->ptr = (nf->ptr & ~Ffound) | Fignore;
     98 
     99 	return t;
    100 }
    101 
    102 static int
    103 prefixlen(uchar *ip)
    104 {
    105 	int y, i;
    106 
    107 	for(y = IPaddrlen-1; y >= 0; y--)
    108 		for(i = 8; i > 0; i--)
    109 			if(ip[y] & (1<<(8-i)))
    110 				return y*8 + i;
    111 	return 0;
    112 }
    113 
    114 /*
    115  *  look through a containing subset
    116  */
    117 static Ndbtuple*
    118 subnet(Ndb *db, uchar *net, Ndbtuple *f, int prefix)
    119 {
    120 	Ndbs s;
    121 	Ndbtuple *t, *nt, *xt;
    122 	char netstr[128];
    123 	uchar mask[IPaddrlen];
    124 	int masklen;
    125 
    126 	t = nil;
    127 	sprint(netstr, "%I", net);
    128 	nt = ndbsearch(db, &s, "ip", netstr);
    129 	while(nt != nil){
    130 		xt = ndbfindattr(nt, nt, "ipnet");
    131 		if(xt){
    132 			xt = ndbfindattr(nt, nt, "ipmask");
    133 			if(xt)
    134 				parseipmask(mask, xt->val);
    135 			else
    136 				ipmove(mask, defmask(net));
    137 			masklen = prefixlen(mask);
    138 			if(masklen <= prefix)
    139 				t = ndbconcatenate(t, filter(db, nt, f));
    140 		} else
    141 			ndbfree(nt);
    142 		nt = ndbsnext(&s, "ip", netstr);
    143 	}
    144 	return t;
    145 }
    146 
    147 /*
    148  *  fill in all the requested attributes for a system.
    149  *  if the system's entry doesn't have all required,
    150  *  walk through successively more inclusive networks
    151  *  for inherited attributes.
    152  */
    153 Ndbtuple*
    154 ndbipinfo(Ndb *db, char *attr, char *val, char **alist, int n)
    155 {
    156 	Ndbtuple *t, *nt, *f;
    157 	Ndbs s;
    158 	char *ipstr;
    159 	uchar net[IPaddrlen];
    160 	uchar ip[IPaddrlen];
    161 	int prefix, smallestprefix;
    162 	int force;
    163 
    164 	/* just in case */
    165 	fmtinstall('I', eipfmt);
    166 	fmtinstall('M', eipfmt);
    167 
    168 	/* get needed attributes */
    169 	f = mkfilter(n, alist);
    170 
    171 	/*
    172 	 *  first look for a matching entry with an ip address
    173 	 */
    174 	t = nil;
    175 	ipstr = ndbgetvalue(db, &s, attr, val, "ip", &nt);
    176 	if(ipstr == nil){
    177 		/* none found, make one up */
    178 		if(strcmp(attr, "ip") != 0)
    179 			return nil;
    180 		t = ndbnew("ip", val);
    181 		t->line = t;
    182 		t->entry = nil;
    183 		parseip(net, val);
    184 	} else {
    185 		/* found one */
    186 		while(nt != nil){
    187 			nt = ndbreorder(nt, s.t);
    188 			t = ndbconcatenate(t, nt);
    189 			nt = ndbsnext(&s, attr, val);
    190 		}
    191 		parseip(net, ipstr);
    192 		free(ipstr);
    193 	}
    194 	ipmove(ip, net);
    195 	t = filter(db, t, f);
    196 
    197 	/*
    198 	 *  now go through subnets to fill in any missing attributes
    199 	 */
    200 	if(isv4(net)){
    201 		prefix = 127;
    202 		smallestprefix = 100;
    203 		force = 0;
    204 	} else {
    205 		/* in v6, the last 8 bytes have no structure (we hope) */
    206 		prefix = 64;
    207 		smallestprefix = 2;
    208 		memset(net+8, 0, 8);
    209 		force = 1;
    210 	}
    211 
    212 	/*
    213 	 *  to find a containing network, keep turning off
    214 	 *  the lower bit and look for a network with
    215 	 *  that address and a shorter mask.  tedius but
    216 	 *  complete, we may need to find a trick to speed this up.
    217 	 */
    218 	for(; prefix >= smallestprefix; prefix--){
    219 		if(filtercomplete(f))
    220 			break;
    221 		if(!force && (net[prefix/8] & (1<<(7-(prefix%8)))) == 0)
    222 			continue;
    223 		force = 0;
    224 		net[prefix/8] &= ~(1<<(7-(prefix%8)));
    225 		t = ndbconcatenate(t, subnet(db, net, f, prefix));
    226 	}
    227 
    228 	/*
    229 	 *  if there's an unfulfilled ipmask, make one up
    230 	 */
    231 	nt = ndbfindattr(f, f, "ipmask");
    232 	if(nt && !(nt->ptr & Fignore)){
    233 		char x[64];
    234 
    235 		snprint(x, sizeof(x), "%M", defmask(ip));
    236 		t = ndbconcatenate(t, ndbnew("ipmask", x));
    237 	}
    238 
    239 	ndbfree(f);
    240 	return t;
    241 }