Importing all of DRL, including ulogd and all of its files.
[distributedratelimiting.git] / drl / config.c
1 /* See the DRL-LICENSE file for this file's software license. */
2
3 #include <arpa/inet.h>
4 #include <dirent.h>
5 #include <errno.h>
6 #include <libxml/xmlmemory.h>
7 #include <libxml/parser.h>
8 #include <netinet/in.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/socket.h>
13
14 #include "raterouter.h"
15 #include "ratetypes.h"
16 #include "config.h"
17 #include "util.h"
18 #include "logging.h"
19
20 void free_ident(ident_config *ident) {
21     /* Free peers. */
22     while (ident->peers) {
23         ident_peer *tofree = ident->peers;
24         ident->peers = ident->peers->next;
25         free(tofree);
26     }
27
28     /* Free members. */
29     while (ident->members) {
30         ident_member *tofree = ident->members;
31         ident->members = ident->members->next;
32         free(tofree);
33     }
34
35     /* Free ident. */
36     free(ident);
37 }
38
39 void free_ident_list(ident_config *list) {
40     ident_config *tofree;
41
42     while (list) {
43         tofree = list;
44         list = list->next;
45         free_ident(tofree);
46     }
47 }
48
49 static int xid_filter(const struct dirent *d) {
50     if (atoi(d->d_name) > 0)
51         return 1;
52     else
53         return 0;
54 }
55
56 int get_eligible_leaves(drl_instance_t *instance) {
57     struct dirent **names;
58     int count, i;
59     leaf_t *leaves = NULL;
60     map_handle leaf_map = allocate_map();
61
62     if (leaf_map == NULL) {
63         return ENOMEM;
64     }
65
66     count = scandir("/proc/virtual", &names, xid_filter, alphasort);
67
68     if (count < 1) {
69         return 1;
70     }
71
72     leaves = malloc(count * sizeof(leaf_t));
73     if (leaves == NULL) {
74         /* Couldn't allocate leaves array.  Need to free names memory. */
75         while (count--) {
76             free(names[count]);
77         }
78         free(names);
79
80         return ENOMEM;
81     }
82
83     for (i = 0; i < count; ++i) {
84         leaves[i].xid = atoi(names[i]->d_name);
85         leaves[i].parent = NULL;
86         
87         free(names[i]);
88
89         map_insert(leaf_map, &leaves[i].xid, sizeof(leaves[i].xid), &leaves[i]);
90     }
91
92     free(names);
93
94     instance->leaf_map = leaf_map;
95     instance->leaves = leaves;
96     instance->leaf_count = count;
97
98     return 0;
99 }
100
101 static int parse_common(xmlDocPtr doc, xmlNodePtr ident, ident_config *common) {
102     xmlChar *id;
103     xmlChar *limit;
104     xmlChar *commfabric;
105     xmlChar *branch;
106     xmlChar *accounting;
107     xmlChar *ewma;
108     xmlChar *intervals;
109     xmlNodePtr fields = ident->children;
110     ident_peer *current = NULL;
111
112     /* Make sure no required fields are missing. */
113     id = xmlGetProp(ident, (const xmlChar *) "id");
114     if (id == NULL) {
115         printlog(LOG_CRITICAL, "Ident missing globally unique identifier.\n");
116         return EINVAL;
117     } else {
118         common->id = atoi((const char *) id);
119         xmlFree(id);
120     }
121
122     limit = xmlGetProp(ident, (const xmlChar *) "limit");
123     if (limit == NULL) {
124         printlog(LOG_CRITICAL, "Ident missing global rate limit.\n");
125         return EINVAL;
126     } else {
127         common->limit = atoi((const char *) limit);
128         xmlFree(limit);
129     }
130
131     commfabric = xmlGetProp(ident, (const xmlChar *) "commfabric");
132     if (commfabric == NULL) {
133         printlog(LOG_CRITICAL, "Ident missing comm fabric specifier.\n");
134         return EINVAL;
135     } else {
136         if (!xmlStrcmp(commfabric, (const xmlChar *) "MESH")) {
137             common->commfabric = COMM_MESH;
138         } else if (!xmlStrcmp(commfabric, (const xmlChar *) "GOSSIP")) {
139             common->commfabric = COMM_GOSSIP;
140         } else {
141             printlog(LOG_CRITICAL, "Unknown/invalid comm fabric.\n");
142             xmlFree(commfabric);
143             return EINVAL;
144         }
145         xmlFree(commfabric);
146     }
147
148     /* Only care about branching factor if we're using gossip. */
149     if (common->commfabric == COMM_GOSSIP) {
150         branch = xmlGetProp(ident, (const xmlChar *) "branch");
151         if (branch == NULL) {
152             printlog(LOG_CRITICAL, "Ident missing gossip branch.\n");
153             return EINVAL;
154         } else {
155             common->branch = atoi((const char *) branch);
156             xmlFree(branch);
157         }
158     }
159
160     accounting = xmlGetProp(ident, (const xmlChar *) "accounting");
161     if (accounting == NULL) {
162         printlog(LOG_CRITICAL, "Ident missing accounting.\n");
163         return EINVAL;
164     } else {
165         if (!xmlStrcmp(accounting, (const xmlChar *) "STANDARD")) {
166             common->accounting = ACT_STANDARD;
167         } else if (!xmlStrcmp(accounting, (const xmlChar *) "SAMPLEHOLD")) {
168             common->accounting = ACT_SAMPLEHOLD;
169         } else if (!xmlStrcmp(accounting, (const xmlChar *) "SIMPLE")) {
170             common->accounting = ACT_SIMPLE;
171         } else {
172             printlog(LOG_CRITICAL, "Unknown/invalid accounting table.\n");
173             xmlFree(accounting);
174             return EINVAL;
175         }
176         xmlFree(accounting);
177     }
178
179     ewma = xmlGetProp(ident, (const xmlChar *) "ewma");
180     if (ewma == NULL) {
181         printlog(LOG_CRITICAL, "Ident missing ewma weight.\n");
182         return EINVAL;
183     } else {
184         common->fixed_ewma_weight = atof((const char *) ewma);
185         xmlFree(ewma);
186     }
187
188     intervals = xmlGetProp(ident, (const xmlChar *) "intervals");
189     if (intervals == NULL) {
190         printlog(LOG_CRITICAL, "Ident missing interval count.\n");
191         return EINVAL;
192     } else {
193         common->intervals = atoi((const char *) intervals);
194         xmlFree(intervals);
195     }
196
197     while (fields != NULL) {
198         if((!xmlStrcmp(fields->name, (const xmlChar *) "peer"))) {
199             xmlChar *ip = xmlNodeListGetString(doc, fields->children, 1);
200             if (current == NULL) {
201                 /* No peers yet. */
202                 common->peers = malloc(sizeof(ident_peer));
203                 if (common->peers == NULL) {
204                     return ENOMEM;
205                 }
206                 common->peers->ip = inet_addr((const char *) ip);
207                 common->peers->next = NULL;
208                 common->peer_count += 1;
209                 current = common->peers;
210             } else {
211                 /* Add it to the list. */
212                 current->next = malloc(sizeof(ident_peer));
213                 if (current->next == NULL) {
214                     return ENOMEM;
215                 }
216                 current = current->next;
217                 current->ip = inet_addr((const char *) ip);
218                 common->peer_count += 1;
219                 current->next = NULL;
220             }
221             xmlFree(ip);
222         }
223         fields = fields->next;
224     }
225
226     if (common->peers == NULL) {
227         printlog(LOG_CRITICAL, "Must have at least one peer.\n");
228         return EINVAL;
229     }
230
231     /* No errors. */
232     return 0;
233 }
234
235 static ident_config *parse_machine(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) {
236     ident_config *common = malloc(sizeof(ident_config));
237
238     if (common == NULL) {
239         return NULL;
240     }
241
242     memset(common, 0, sizeof(ident_config));
243     if (parse_common(doc, ident, common)) {
244         free_ident(common);
245         return NULL;
246     }
247
248     /* No further information needed for machine-level identities. */
249     common->type = IDENT_MACHINE;
250     common->members = NULL;
251     common->next = NULL;
252
253     if (configs->last_machine == NULL) {
254         configs->machines = common;
255         configs->last_machine = common;
256     } else {
257         configs->last_machine->next = common;
258         configs->last_machine = common;
259     }
260
261     configs->machine_count += 1;
262
263     return common;
264 }
265
266 static ident_config *parse_set(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) {
267     xmlNodePtr fields = ident->children;
268     ident_config *common = malloc(sizeof(ident_config));
269     ident_member *current = NULL;
270
271     if (common == NULL) {
272         return NULL;
273     }
274
275     memset(common, 0, sizeof(ident_config));
276     if (parse_common(doc, ident, common)) {
277         free_ident(common);
278         return NULL;
279     }
280
281     while (fields != NULL) {
282         ident_member *member = NULL;
283
284         if (!xmlStrcmp(fields->name, (const xmlChar *) "xid")) {
285             xmlChar *xid = xmlNodeListGetString(doc, fields->children, 1);
286
287             if (atoi((const char *) xid) >= 0) {
288                 member = malloc(sizeof(ident_member));
289                 if (member == NULL) {
290                     free_ident(common);
291                     xmlFree(xid);
292                     return NULL;
293                 }
294                 member->type = MEMBER_XID;
295                 sscanf((const char *) xid, "%x", &member->value);
296                 member->next = NULL;
297             } else {
298                 free_ident(common);
299                 xmlFree(xid);
300                 return NULL;
301             }
302
303             xmlFree(xid);
304         } else if (!xmlStrcmp(fields->name, (const xmlChar *) "guid")) {
305             xmlChar *guid = xmlNodeListGetString(doc, fields->children, 1);
306
307             if (atoi((const char *) guid) >= 0) {
308                 member = malloc(sizeof(ident_member));
309                 if (member == NULL) {
310                     free_ident(common);
311                     xmlFree(guid);
312                     return NULL;
313                 }
314                 member->type = MEMBER_GUID;
315                 member->value = atoi((const char *) guid);
316                 member->next = NULL;
317             } else {
318                 free_ident(common);
319                 xmlFree(guid);
320                 return NULL;
321             }
322
323             xmlFree(guid);
324         }
325
326         if (member) {
327             if (common->members == NULL) {
328                 common->members = member;
329                 current = member;
330             } else {
331                 current->next = member;
332                 current = member;
333             }
334         }
335
336         fields = fields->next;
337     }
338
339     /* A sliver set must have at least one member (xid or guid) or else it is
340      * meaningless. */
341     if (common->members == NULL) {
342         free_ident(common);
343         return NULL;
344     }
345
346     common->type = IDENT_SET;
347     common->next = NULL;
348
349     if (configs->last_set == NULL) {
350         configs->sets = common;
351         configs->last_set = common;
352     } else {
353         configs->last_set->next = common;
354         configs->last_set = common;
355     }
356
357     configs->set_count += 1;
358
359     return common;
360 }
361
362 int parse_drl_config(const char *configfile, parsed_configs *configs) {
363     xmlDocPtr doc;
364     xmlNodePtr drl, ident;
365
366     configs->machines = NULL;
367     configs->sets = NULL;
368     configs->last_machine = NULL;
369     configs->last_set = NULL;
370     configs->machine_count = 0;
371     configs->set_count = 0;
372
373     if(!(doc = xmlParseFile(configfile))){
374         printlog(LOG_CRITICAL, "Config file (%s) not parsed successfully.\n", configfile);
375         xmlFreeDoc(doc);
376         return EIO;
377     }
378
379     if(!(drl = xmlDocGetRootElement(doc))){
380         printlog(LOG_CRITICAL, "Config file (%s) has no root element.\n", configfile);
381         xmlFreeDoc(doc);
382         return EIO;
383     }
384     if(xmlStrcmp(drl->name, (const xmlChar *) "drl")){
385         printlog(LOG_CRITICAL, "Config file (%s) of the wrong type, root node != drl\n", configfile);
386         xmlFreeDoc(doc);
387         return EIO;
388     }
389
390     ident = drl->children;
391     while(ident != NULL) {
392         ident_config *new = NULL;
393
394         if((!xmlStrcmp(ident->name, (const xmlChar *) "machine"))) {
395             new = parse_machine(doc, ident, configs);
396         } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "set"))) {
397             new = parse_set(doc, ident, configs);
398         } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "text"))) {
399             /* libxml seems to wrap everything inside two 'text's. */
400             ident = ident->next;
401             continue;
402         }
403
404         if (new == NULL) {
405             /* FIXME: Make this more descriptive. :) */
406             printlog(LOG_CRITICAL, "Error occurred while parsing...\n");
407
408             free_ident_list(configs->machines);
409             free_ident_list(configs->sets);
410
411             configs->machines = NULL;
412             configs->sets = NULL;
413             configs->last_machine = NULL;
414             configs->last_set = NULL;
415
416             xmlFreeDoc(doc);
417             return 1;
418         }
419
420         ident = ident->next;
421     }
422
423     xmlFreeDoc(doc);
424
425     /* Return the list of parsed identities. */
426     return 0;
427 }