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