Importing all of DRL, including ulogd and all of its files.
[distributedratelimiting.git] / drl / config.c
diff --git a/drl/config.c b/drl/config.c
new file mode 100644 (file)
index 0000000..1c56ce8
--- /dev/null
@@ -0,0 +1,427 @@
+/* See the DRL-LICENSE file for this file's software license. */
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <errno.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "raterouter.h"
+#include "ratetypes.h"
+#include "config.h"
+#include "util.h"
+#include "logging.h"
+
+void free_ident(ident_config *ident) {
+    /* Free peers. */
+    while (ident->peers) {
+        ident_peer *tofree = ident->peers;
+        ident->peers = ident->peers->next;
+        free(tofree);
+    }
+
+    /* Free members. */
+    while (ident->members) {
+        ident_member *tofree = ident->members;
+        ident->members = ident->members->next;
+        free(tofree);
+    }
+
+    /* Free ident. */
+    free(ident);
+}
+
+void free_ident_list(ident_config *list) {
+    ident_config *tofree;
+
+    while (list) {
+        tofree = list;
+        list = list->next;
+        free_ident(tofree);
+    }
+}
+
+static int xid_filter(const struct dirent *d) {
+    if (atoi(d->d_name) > 0)
+        return 1;
+    else
+        return 0;
+}
+
+int get_eligible_leaves(drl_instance_t *instance) {
+    struct dirent **names;
+    int count, i;
+    leaf_t *leaves = NULL;
+    map_handle leaf_map = allocate_map();
+
+    if (leaf_map == NULL) {
+        return ENOMEM;
+    }
+
+    count = scandir("/proc/virtual", &names, xid_filter, alphasort);
+
+    if (count < 1) {
+        return 1;
+    }
+
+    leaves = malloc(count * sizeof(leaf_t));
+    if (leaves == NULL) {
+        /* Couldn't allocate leaves array.  Need to free names memory. */
+        while (count--) {
+            free(names[count]);
+        }
+        free(names);
+
+        return ENOMEM;
+    }
+
+    for (i = 0; i < count; ++i) {
+        leaves[i].xid = atoi(names[i]->d_name);
+        leaves[i].parent = NULL;
+        
+        free(names[i]);
+
+        map_insert(leaf_map, &leaves[i].xid, sizeof(leaves[i].xid), &leaves[i]);
+    }
+
+    free(names);
+
+    instance->leaf_map = leaf_map;
+    instance->leaves = leaves;
+    instance->leaf_count = count;
+
+    return 0;
+}
+
+static int parse_common(xmlDocPtr doc, xmlNodePtr ident, ident_config *common) {
+    xmlChar *id;
+    xmlChar *limit;
+    xmlChar *commfabric;
+    xmlChar *branch;
+    xmlChar *accounting;
+    xmlChar *ewma;
+    xmlChar *intervals;
+    xmlNodePtr fields = ident->children;
+    ident_peer *current = NULL;
+
+    /* Make sure no required fields are missing. */
+    id = xmlGetProp(ident, (const xmlChar *) "id");
+    if (id == NULL) {
+        printlog(LOG_CRITICAL, "Ident missing globally unique identifier.\n");
+        return EINVAL;
+    } else {
+        common->id = atoi((const char *) id);
+        xmlFree(id);
+    }
+
+    limit = xmlGetProp(ident, (const xmlChar *) "limit");
+    if (limit == NULL) {
+        printlog(LOG_CRITICAL, "Ident missing global rate limit.\n");
+        return EINVAL;
+    } else {
+        common->limit = atoi((const char *) limit);
+        xmlFree(limit);
+    }
+
+    commfabric = xmlGetProp(ident, (const xmlChar *) "commfabric");
+    if (commfabric == NULL) {
+        printlog(LOG_CRITICAL, "Ident missing comm fabric specifier.\n");
+        return EINVAL;
+    } else {
+        if (!xmlStrcmp(commfabric, (const xmlChar *) "MESH")) {
+            common->commfabric = COMM_MESH;
+        } else if (!xmlStrcmp(commfabric, (const xmlChar *) "GOSSIP")) {
+            common->commfabric = COMM_GOSSIP;
+        } else {
+            printlog(LOG_CRITICAL, "Unknown/invalid comm fabric.\n");
+            xmlFree(commfabric);
+            return EINVAL;
+        }
+        xmlFree(commfabric);
+    }
+
+    /* Only care about branching factor if we're using gossip. */
+    if (common->commfabric == COMM_GOSSIP) {
+        branch = xmlGetProp(ident, (const xmlChar *) "branch");
+        if (branch == NULL) {
+            printlog(LOG_CRITICAL, "Ident missing gossip branch.\n");
+            return EINVAL;
+        } else {
+            common->branch = atoi((const char *) branch);
+            xmlFree(branch);
+        }
+    }
+
+    accounting = xmlGetProp(ident, (const xmlChar *) "accounting");
+    if (accounting == NULL) {
+        printlog(LOG_CRITICAL, "Ident missing accounting.\n");
+        return EINVAL;
+    } else {
+        if (!xmlStrcmp(accounting, (const xmlChar *) "STANDARD")) {
+            common->accounting = ACT_STANDARD;
+        } else if (!xmlStrcmp(accounting, (const xmlChar *) "SAMPLEHOLD")) {
+            common->accounting = ACT_SAMPLEHOLD;
+        } else if (!xmlStrcmp(accounting, (const xmlChar *) "SIMPLE")) {
+            common->accounting = ACT_SIMPLE;
+        } else {
+            printlog(LOG_CRITICAL, "Unknown/invalid accounting table.\n");
+            xmlFree(accounting);
+            return EINVAL;
+        }
+        xmlFree(accounting);
+    }
+
+    ewma = xmlGetProp(ident, (const xmlChar *) "ewma");
+    if (ewma == NULL) {
+        printlog(LOG_CRITICAL, "Ident missing ewma weight.\n");
+        return EINVAL;
+    } else {
+        common->fixed_ewma_weight = atof((const char *) ewma);
+        xmlFree(ewma);
+    }
+
+    intervals = xmlGetProp(ident, (const xmlChar *) "intervals");
+    if (intervals == NULL) {
+        printlog(LOG_CRITICAL, "Ident missing interval count.\n");
+        return EINVAL;
+    } else {
+        common->intervals = atoi((const char *) intervals);
+        xmlFree(intervals);
+    }
+
+    while (fields != NULL) {
+        if((!xmlStrcmp(fields->name, (const xmlChar *) "peer"))) {
+            xmlChar *ip = xmlNodeListGetString(doc, fields->children, 1);
+            if (current == NULL) {
+                /* No peers yet. */
+                common->peers = malloc(sizeof(ident_peer));
+                if (common->peers == NULL) {
+                    return ENOMEM;
+                }
+                common->peers->ip = inet_addr((const char *) ip);
+                common->peers->next = NULL;
+                common->peer_count += 1;
+                current = common->peers;
+            } else {
+                /* Add it to the list. */
+                current->next = malloc(sizeof(ident_peer));
+                if (current->next == NULL) {
+                    return ENOMEM;
+                }
+                current = current->next;
+                current->ip = inet_addr((const char *) ip);
+                common->peer_count += 1;
+                current->next = NULL;
+            }
+            xmlFree(ip);
+        }
+        fields = fields->next;
+    }
+
+    if (common->peers == NULL) {
+        printlog(LOG_CRITICAL, "Must have at least one peer.\n");
+        return EINVAL;
+    }
+
+    /* No errors. */
+    return 0;
+}
+
+static ident_config *parse_machine(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) {
+    ident_config *common = malloc(sizeof(ident_config));
+
+    if (common == NULL) {
+        return NULL;
+    }
+
+    memset(common, 0, sizeof(ident_config));
+    if (parse_common(doc, ident, common)) {
+        free_ident(common);
+        return NULL;
+    }
+
+    /* No further information needed for machine-level identities. */
+    common->type = IDENT_MACHINE;
+    common->members = NULL;
+    common->next = NULL;
+
+    if (configs->last_machine == NULL) {
+        configs->machines = common;
+        configs->last_machine = common;
+    } else {
+        configs->last_machine->next = common;
+        configs->last_machine = common;
+    }
+
+    configs->machine_count += 1;
+
+    return common;
+}
+
+static ident_config *parse_set(xmlDocPtr doc, xmlNodePtr ident, parsed_configs *configs) {
+    xmlNodePtr fields = ident->children;
+    ident_config *common = malloc(sizeof(ident_config));
+    ident_member *current = NULL;
+
+    if (common == NULL) {
+        return NULL;
+    }
+
+    memset(common, 0, sizeof(ident_config));
+    if (parse_common(doc, ident, common)) {
+        free_ident(common);
+        return NULL;
+    }
+
+    while (fields != NULL) {
+        ident_member *member = NULL;
+
+        if (!xmlStrcmp(fields->name, (const xmlChar *) "xid")) {
+            xmlChar *xid = xmlNodeListGetString(doc, fields->children, 1);
+
+            if (atoi((const char *) xid) >= 0) {
+                member = malloc(sizeof(ident_member));
+                if (member == NULL) {
+                    free_ident(common);
+                    xmlFree(xid);
+                    return NULL;
+                }
+                member->type = MEMBER_XID;
+                sscanf((const char *) xid, "%x", &member->value);
+                member->next = NULL;
+            } else {
+                free_ident(common);
+                xmlFree(xid);
+                return NULL;
+            }
+
+            xmlFree(xid);
+        } else if (!xmlStrcmp(fields->name, (const xmlChar *) "guid")) {
+            xmlChar *guid = xmlNodeListGetString(doc, fields->children, 1);
+
+            if (atoi((const char *) guid) >= 0) {
+                member = malloc(sizeof(ident_member));
+                if (member == NULL) {
+                    free_ident(common);
+                    xmlFree(guid);
+                    return NULL;
+                }
+                member->type = MEMBER_GUID;
+                member->value = atoi((const char *) guid);
+                member->next = NULL;
+            } else {
+                free_ident(common);
+                xmlFree(guid);
+                return NULL;
+            }
+
+            xmlFree(guid);
+        }
+
+        if (member) {
+            if (common->members == NULL) {
+                common->members = member;
+                current = member;
+            } else {
+                current->next = member;
+                current = member;
+            }
+        }
+
+        fields = fields->next;
+    }
+
+    /* A sliver set must have at least one member (xid or guid) or else it is
+     * meaningless. */
+    if (common->members == NULL) {
+        free_ident(common);
+        return NULL;
+    }
+
+    common->type = IDENT_SET;
+    common->next = NULL;
+
+    if (configs->last_set == NULL) {
+        configs->sets = common;
+        configs->last_set = common;
+    } else {
+        configs->last_set->next = common;
+        configs->last_set = common;
+    }
+
+    configs->set_count += 1;
+
+    return common;
+}
+
+int parse_drl_config(const char *configfile, parsed_configs *configs) {
+    xmlDocPtr doc;
+    xmlNodePtr drl, ident;
+
+    configs->machines = NULL;
+    configs->sets = NULL;
+    configs->last_machine = NULL;
+    configs->last_set = NULL;
+    configs->machine_count = 0;
+    configs->set_count = 0;
+
+    if(!(doc = xmlParseFile(configfile))){
+        printlog(LOG_CRITICAL, "Config file (%s) not parsed successfully.\n", configfile);
+        xmlFreeDoc(doc);
+        return EIO;
+    }
+
+    if(!(drl = xmlDocGetRootElement(doc))){
+        printlog(LOG_CRITICAL, "Config file (%s) has no root element.\n", configfile);
+        xmlFreeDoc(doc);
+        return EIO;
+    }
+    if(xmlStrcmp(drl->name, (const xmlChar *) "drl")){
+        printlog(LOG_CRITICAL, "Config file (%s) of the wrong type, root node != drl\n", configfile);
+        xmlFreeDoc(doc);
+        return EIO;
+    }
+
+    ident = drl->children;
+    while(ident != NULL) {
+        ident_config *new = NULL;
+
+        if((!xmlStrcmp(ident->name, (const xmlChar *) "machine"))) {
+            new = parse_machine(doc, ident, configs);
+        } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "set"))) {
+            new = parse_set(doc, ident, configs);
+        } else if ((!xmlStrcmp(ident->name, (const xmlChar *) "text"))) {
+            /* libxml seems to wrap everything inside two 'text's. */
+            ident = ident->next;
+            continue;
+        }
+
+        if (new == NULL) {
+            /* FIXME: Make this more descriptive. :) */
+            printlog(LOG_CRITICAL, "Error occurred while parsing...\n");
+
+            free_ident_list(configs->machines);
+            free_ident_list(configs->sets);
+
+            configs->machines = NULL;
+            configs->sets = NULL;
+            configs->last_machine = NULL;
+            configs->last_set = NULL;
+
+            xmlFreeDoc(doc);
+            return 1;
+        }
+
+        ident = ident->next;
+    }
+
+    xmlFreeDoc(doc);
+
+    /* Return the list of parsed identities. */
+    return 0;
+}