Added an 'independent' option for set identites that will put them under the
[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 *mainloop_intervals;
109     xmlChar *communication_intervals;
110     xmlChar *independent;
111     xmlNodePtr fields = ident->children;
112     ident_peer *current = NULL;
113
114     /* Make sure no required fields are missing. */
115     id = xmlGetProp(ident, (const xmlChar *) "id");
116     if (id == NULL) {
117         printlog(LOG_CRITICAL, "Ident missing globally unique identifier.\n");
118         return EINVAL;
119     } else {
120         common->id = atoi((const char *) id);
121         xmlFree(id);
122     }
123
124     limit = xmlGetProp(ident, (const xmlChar *) "limit");
125     if (limit == NULL) {
126         printlog(LOG_CRITICAL, "Ident missing global rate limit.\n");
127         return EINVAL;
128     } else {
129         common->limit = atoi((const char *) limit);
130         xmlFree(limit);
131     }
132
133     commfabric = xmlGetProp(ident, (const xmlChar *) "commfabric");
134     if (commfabric == NULL) {
135         printlog(LOG_CRITICAL, "Ident missing comm fabric specifier.\n");
136         return EINVAL;
137     } else {
138         if (!xmlStrcmp(commfabric, (const xmlChar *) "MESH")) {
139             common->commfabric = COMM_MESH;
140         } else if (!xmlStrcmp(commfabric, (const xmlChar *) "GOSSIP")) {
141             common->commfabric = COMM_GOSSIP;
142         } else {
143             printlog(LOG_CRITICAL, "Unknown/invalid comm fabric.\n");
144             xmlFree(commfabric);
145             return EINVAL;
146         }
147         xmlFree(commfabric);
148     }
149
150     /* Only care about branching factor if we're using gossip. */
151     if (common->commfabric == COMM_GOSSIP) {
152         branch = xmlGetProp(ident, (const xmlChar *) "branch");
153         if (branch == NULL) {
154             printlog(LOG_CRITICAL, "Ident missing gossip branch.\n");
155             return EINVAL;
156         } else {
157             common->branch = atoi((const char *) branch);
158             xmlFree(branch);
159         }
160     }
161
162     accounting = xmlGetProp(ident, (const xmlChar *) "accounting");
163     if (accounting == NULL) {
164         printlog(LOG_CRITICAL, "Ident missing accounting.\n");
165         return EINVAL;
166     } else {
167         if (!xmlStrcmp(accounting, (const xmlChar *) "STANDARD")) {
168             common->accounting = ACT_STANDARD;
169         } else if (!xmlStrcmp(accounting, (const xmlChar *) "SAMPLEHOLD")) {
170             common->accounting = ACT_SAMPLEHOLD;
171         } else if (!xmlStrcmp(accounting, (const xmlChar *) "SIMPLE")) {
172             common->accounting = ACT_SIMPLE;
173         } else if (!xmlStrcmp(accounting, (const xmlChar *) "MULTIPLEINTERVAL")) {
174             common->accounting = ACT_MULTIPLE;
175         } else {
176             printlog(LOG_CRITICAL, "Unknown/invalid accounting table.\n");
177             xmlFree(accounting);
178             return EINVAL;
179         }
180         xmlFree(accounting);
181     }
182
183     ewma = xmlGetProp(ident, (const xmlChar *) "ewma");
184     if (ewma == NULL) {
185         printlog(LOG_CRITICAL, "Ident missing ewma weight.\n");
186         return EINVAL;
187     } else {
188         common->fixed_ewma_weight = atof((const char *) ewma);
189         xmlFree(ewma);
190     }
191
192     mainloop_intervals = xmlGetProp(ident, (const xmlChar *) "loop_intervals");
193     if (mainloop_intervals == NULL) {
194         printlog(LOG_WARN, "Ident id: %d missing loop_intervals, assuming 1.\n", common->id);
195         common->mainloop_intervals = 1;
196     } else {
197         common->mainloop_intervals = atoi((const char *) mainloop_intervals);
198         xmlFree(mainloop_intervals);
199     }
200
201     communication_intervals = xmlGetProp(ident, (const xmlChar *) "comm_intervals");
202     if (communication_intervals == NULL) {
203         printlog(LOG_WARN, "Ident id: %d missing comm_intervals, assuming 1.\n", common->id);
204         common->communication_intervals = 1;
205     } else {
206         common->communication_intervals = atoi((const char *) communication_intervals);
207         xmlFree(communication_intervals);
208     }
209
210     independent = xmlGetProp(ident, (const xmlChar *) "independent");
211     if (independent == NULL) {
212         common->independent = 0;
213     } else {
214         common->independent = atoi((const char *) independent);
215         xmlFree(independent);
216     }
217
218     while (fields != NULL) {
219         if((!xmlStrcmp(fields->name, (const xmlChar *) "peer"))) {
220             xmlChar *ip = xmlNodeListGetString(doc, fields->children, 1);
221             if (current == NULL) {
222                 /* No peers yet. */
223                 common->peers = malloc(sizeof(ident_peer));
224                 if (common->peers == NULL) {
225                     return ENOMEM;
226                 }
227                 common->peers->ip = inet_addr((const char *) ip);
228                 common->peers->next = NULL;
229                 common->peer_count += 1;
230                 current = common->peers;
231             } else {
232                 /* Add it to the list. */
233                 current->next = malloc(sizeof(ident_peer));
234                 if (current->next == NULL) {
235                     return ENOMEM;
236                 }
237                 current = current->next;
238                 current->ip = inet_addr((const char *) ip);
239                 common->peer_count += 1;
240                 current->next = NULL;
241             }
242             xmlFree(ip);
243         }
244         fields = fields->next;
245     }
246
247     if (common->peers == NULL) {
248         printlog(LOG_CRITICAL, "Must have at least one peer.\n");
249         return EINVAL;
250     }
251
252     /* No errors. */
253     return 0;
254 }
255
256 static ident_config *parse_machine(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) {
257     ident_config *common = malloc(sizeof(ident_config));
258
259     if (common == NULL) {
260         return NULL;
261     }
262
263     memset(common, 0, sizeof(ident_config));
264     if (parse_common(doc, ident, common)) {
265         free_ident(common);
266         return NULL;
267     }
268
269     /* No further information needed for machine-level identities. */
270     common->type = IDENT_MACHINE;
271     common->members = NULL;
272     common->next = NULL;
273
274     if (configs->last_machine == NULL) {
275         configs->machines = common;
276         configs->last_machine = common;
277     } else {
278         configs->last_machine->next = common;
279         configs->last_machine = common;
280     }
281
282     configs->machine_count += 1;
283
284     return common;
285 }
286
287 static ident_config *parse_set(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) {
288     xmlNodePtr fields = ident->children;
289     ident_config *common = malloc(sizeof(ident_config));
290     ident_member *current = NULL;
291
292     if (common == NULL) {
293         return NULL;
294     }
295
296     memset(common, 0, sizeof(ident_config));
297     if (parse_common(doc, ident, common)) {
298         free_ident(common);
299         return NULL;
300     }
301
302     while (fields != NULL) {
303         ident_member *member = NULL;
304
305         if (!xmlStrcmp(fields->name, (const xmlChar *) "xid")) {
306             xmlChar *xid = xmlNodeListGetString(doc, fields->children, 1);
307
308             if (atoi((const char *) xid) >= 0) {
309                 member = malloc(sizeof(ident_member));
310                 if (member == NULL) {
311                     free_ident(common);
312                     xmlFree(xid);
313                     return NULL;
314                 }
315                 member->type = MEMBER_XID;
316                 sscanf((const char *) xid, "%x", &member->value);
317                 member->next = NULL;
318             } else {
319                 free_ident(common);
320                 xmlFree(xid);
321                 return NULL;
322             }
323
324             xmlFree(xid);
325         } else if (!xmlStrcmp(fields->name, (const xmlChar *) "guid")) {
326             xmlChar *guid = xmlNodeListGetString(doc, fields->children, 1);
327
328             if (atoi((const char *) guid) >= 0) {
329                 member = malloc(sizeof(ident_member));
330                 if (member == NULL) {
331                     free_ident(common);
332                     xmlFree(guid);
333                     return NULL;
334                 }
335                 member->type = MEMBER_GUID;
336                 member->value = atoi((const char *) guid);
337                 member->next = NULL;
338             } else {
339                 free_ident(common);
340                 xmlFree(guid);
341                 return NULL;
342             }
343
344             xmlFree(guid);
345         }
346
347         if (member) {
348             if (common->members == NULL) {
349                 common->members = member;
350                 current = member;
351             } else {
352                 current->next = member;
353                 current = member;
354             }
355         }
356
357         fields = fields->next;
358     }
359
360     /* A sliver set must have at least one member (xid or guid) or else it is
361      * meaningless. */
362     if (common->members == NULL) {
363         free_ident(common);
364         return NULL;
365     }
366
367     common->type = IDENT_SET;
368     common->next = NULL;
369
370     if (configs->last_set == NULL) {
371         configs->sets = common;
372         configs->last_set = common;
373     } else {
374         configs->last_set->next = common;
375         configs->last_set = common;
376     }
377
378     configs->set_count += 1;
379
380     return common;
381 }
382
383 int parse_drl_config(const char *configfile, parsed_configs *configs) {
384     xmlDocPtr doc;
385     xmlNodePtr drl, ident;
386
387     configs->machines = NULL;
388     configs->sets = NULL;
389     configs->last_machine = NULL;
390     configs->last_set = NULL;
391     configs->machine_count = 0;
392     configs->set_count = 0;
393
394     if(!(doc = xmlParseFile(configfile))){
395         printlog(LOG_CRITICAL, "Config file (%s) not parsed successfully.\n", configfile);
396         xmlFreeDoc(doc);
397         return EIO;
398     }
399
400     if(!(drl = xmlDocGetRootElement(doc))){
401         printlog(LOG_CRITICAL, "Config file (%s) has no root element.\n", configfile);
402         xmlFreeDoc(doc);
403         return EIO;
404     }
405     if(xmlStrcmp(drl->name, (const xmlChar *) "drl")){
406         printlog(LOG_CRITICAL, "Config file (%s) of the wrong type, root node != drl\n", configfile);
407         xmlFreeDoc(doc);
408         return EIO;
409     }
410
411     ident = drl->children;
412     while(ident != NULL) {
413         ident_config *new = NULL;
414
415         if((!xmlStrcmp(ident->name, (const xmlChar *) "machine"))) {
416             new = parse_machine(doc, ident, configs);
417         } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "set"))) {
418             new = parse_set(doc, ident, configs);
419         } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "text"))) {
420             /* libxml seems to wrap everything inside two 'text's. */
421             ident = ident->next;
422             continue;
423         }
424
425         if (new == NULL) {
426             /* FIXME: Make this more descriptive. :) */
427             printlog(LOG_CRITICAL, "Error occurred while parsing...\n");
428
429             free_ident_list(configs->machines);
430             free_ident_list(configs->sets);
431
432             configs->machines = NULL;
433             configs->sets = NULL;
434             configs->last_machine = NULL;
435             configs->last_set = NULL;
436
437             xmlFreeDoc(doc);
438             return 1;
439         }
440
441         ident = ident->next;
442     }
443
444     xmlFreeDoc(doc);
445
446     /* Return the list of parsed identities. */
447     return 0;
448 }