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