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 }