+/* 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;
+}