/**************************************************************** * secpol_xml2bin.c * * Copyright (C) 2005 IBM Corporation * * Author: Reiner Sailer * * Maintained: * Reiner Sailer * Ray Valdez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2 of the * License. * * sHype policy translation tool. This tool takes an XML * policy specification as input and produces a binary * policy file that can be loaded into Xen through the * ACM operations (xensec_tool loadpolicy) interface or at * boot time (grub module parameter) * * indent -i4 -kr -nut */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "secpol_xml2bin.h" #define DEBUG 0 /* primary / secondary policy component setting */ enum policycomponent { CHWALL, STE, NULLPOLICY } primary = NULLPOLICY, secondary = NULLPOLICY; /* general list element for ste and chwall type queues */ struct type_entry { TAILQ_ENTRY(type_entry) entries; char *name; /* name of type from xml file */ type_t mapping; /* type mapping into 16bit */ }; TAILQ_HEAD(tailhead, type_entry) ste_head, chwall_head; /* general list element for all label queues */ enum label_type { VM, RES, ANY }; struct ssid_entry { TAILQ_ENTRY(ssid_entry) entries; char *name; /* label name */ enum label_type type; /* type: VM / RESOURCE LABEL */ u_int32_t num; /* ssid or referenced ssid */ int is_ref; /* if this entry references earlier ssid number */ unsigned char *row; /* index of types (if not a reference) */ }; TAILQ_HEAD(tailhead_ssid, ssid_entry) ste_ssid_head, chwall_ssid_head, conflictsets_head; struct ssid_entry *current_chwall_ssid_p = NULL; struct ssid_entry *current_ste_ssid_p = NULL; struct ssid_entry *current_conflictset_p = NULL; /* which label to assign to dom0 during boot */ char *bootstrap_label; u_int32_t max_ste_ssids = 0; u_int32_t max_chwall_ssids = 0; u_int32_t max_chwall_labels = 0; u_int32_t max_ste_labels = 0; u_int32_t max_conflictsets = 0; char *current_ssid_name; /* store name until structure is allocated */ char *current_conflictset_name; /* store name until structure is allocated */ /* dynamic list of type mappings for STE */ u_int32_t max_ste_types = 0; /* dynamic list of type mappings for CHWALL */ u_int32_t max_chwall_types = 0; /* dynamic list of conflict sets */ int max_conflict_set = 0; /* which policies are defined */ int have_ste = 0; int have_chwall = 0; /* input/output file names */ char *policy_filename = NULL, *binary_filename = NULL, *mapping_filename = NULL, *schema_filename = NULL; char *policy_reference_name = NULL; void walk_labels(xmlNode * start, xmlDocPtr doc, unsigned long state); void usage(char *prg) { printf("Usage: %s [OPTIONS] POLICYNAME\n", prg); printf ("POLICYNAME is the directory name within the policy directory\n"); printf ("that contains the policy files. The default policy directory\n"); printf("is '%s' (see the '-d' option below to change it)\n", POLICY_DIR); printf ("The policy files contained in the POLICYNAME directory must be named:\n"); printf("\tPOLICYNAME-security_policy.xml\n"); printf("\tPOLICYNAME-security_label_template.xml\n\n"); printf("OPTIONS:\n"); printf("\t-d POLICYDIR\n"); printf ("\t\tUse POLICYDIR as the policy directory. This directory must contain\n"); printf("\t\tthe policy schema file 'security_policy.xsd'\n"); exit(EXIT_FAILURE); } /***************** policy-related parsing *********************/ char *type_by_mapping(struct tailhead *head, u_int32_t mapping) { struct type_entry *np; for (np = head->tqh_first; np != NULL; np = np->entries.tqe_next) if (np->mapping == mapping) return np->name; return NULL; } struct type_entry *lookup(struct tailhead *head, char *name) { struct type_entry *np; for (np = head->tqh_first; np != NULL; np = np->entries.tqe_next) if (!(strcmp(np->name, name))) return np; return NULL; } /* enforces single-entry lists */ int add_entry(struct tailhead *head, char *name, type_t mapping) { struct type_entry *e; if (lookup(head, name)) { printf("Error: Type >%s< defined more than once.\n", name); return -EFAULT; /* already in the list */ } if (!(e = malloc(sizeof(struct type_entry)))) return -ENOMEM; e->name = name; e->mapping = mapping; TAILQ_INSERT_TAIL(head, e, entries); return 0; } int totoken(char *tok) { int i; for (i = 0; token[i] != NULL; i++) if (!strcmp(token[i], tok)) return i; return -EFAULT; } /* conflictsets use the same data structure as ssids; since * they are similar in structure (set of types) */ int init_next_conflictset(void) { struct ssid_entry *conflictset = malloc(sizeof(struct ssid_entry)); if (!conflictset) return -ENOMEM; conflictset->name = current_conflictset_name; conflictset->num = max_conflictsets++; conflictset->is_ref = 0; /* n/a for conflictsets */ /** * row: allocate one byte per type; * [i] != 0 --> mapped type >i< is part of the conflictset */ conflictset->row = malloc(max_chwall_types); if (!conflictset->row) return -ENOMEM; memset(conflictset->row, 0, max_chwall_types); TAILQ_INSERT_TAIL(&conflictsets_head, conflictset, entries); current_conflictset_p = conflictset; return 0; } int register_type(xmlNode * cur_node, xmlDocPtr doc, unsigned long state) { xmlChar *text; struct type_entry *e; text = xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!text) { printf("Error reading type name!\n"); return -EFAULT; } switch (state) { case XML2BIN_stetype_S: if (add_entry(&ste_head, (char *) text, max_ste_types)) { xmlFree(text); return -EFAULT; } max_ste_types++; break; case XML2BIN_chwalltype_S: if (add_entry(&chwall_head, (char *) text, max_chwall_types)) { xmlFree(text); return -EFAULT; } max_chwall_types++; break; case XML2BIN_conflictsettype_S: /* a) search the type in the chwall_type list */ e = lookup(&chwall_head, (char *) text); if (e == NULL) { printf("CS type >%s< not a CHWALL type.\n", text); xmlFree(text); return -EFAULT; } /* b) add type entry to the current cs set */ if (current_conflictset_p->row[e->mapping]) { printf ("ERROR: Double entry of type >%s< in conflict set %d.\n", text, current_conflictset_p->num); xmlFree(text); return -EFAULT; } current_conflictset_p->row[e->mapping] = 1; break; default: printf("Incorrect type environment (state = %lx, text = %s).\n", state, text); xmlFree(text); return -EFAULT; } return 0; } void set_component_type(xmlNode * cur_node, enum policycomponent pc) { xmlChar *order; if ((order = xmlGetProp(cur_node, (xmlChar *) PRIMARY_COMPONENT_ATTR_NAME))) { if (strcmp((char *) order, PRIMARY_COMPONENT)) { printf("ERROR: Illegal attribut value >order=%s<.\n", (char *) order); xmlFree(order); exit(EXIT_FAILURE); } if (primary != NULLPOLICY) { printf("ERROR: Primary Policy Component set twice!\n"); exit(EXIT_FAILURE); } primary = pc; xmlFree(order); } } void walk_policy(xmlNode * start, xmlDocPtr doc, unsigned long state) { xmlNode *cur_node = NULL; int code; for (cur_node = start; cur_node; cur_node = cur_node->next) { if ((code = totoken((char *) cur_node->name)) < 0) { printf("Unknown token: >%s<. Aborting.\n", cur_node->name); exit(EXIT_FAILURE); } switch (code) { /* adjust state to new state */ case XML2BIN_SECPOL: case XML2BIN_STETYPES: case XML2BIN_CHWALLTYPES: case XML2BIN_CONFLICTSETS: case XML2BIN_POLICYHEADER: walk_policy(cur_node->children, doc, state | (1 << code)); break; case XML2BIN_POLICYNAME: /* get policy reference name .... */ if (state != XML2BIN_PN_S) { printf("ERROR: >Url< >%s< out of context.\n", (char *) xmlNodeListGetString(doc, cur_node-> xmlChildrenNode, 1)); exit(EXIT_FAILURE); } policy_reference_name = (char *) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!policy_reference_name) { printf("ERROR: empty >policy reference name (Url)children, doc, state | (1 << code)); break; case XML2BIN_CHWALL: if (WRITTEN_AGAINST_ACM_CHWALL_VERSION != ACM_CHWALL_VERSION) { printf ("ERROR: This program was written against another CHWALL version.\n"); exit(EXIT_FAILURE); } have_chwall = 1; set_component_type(cur_node, CHWALL); walk_policy(cur_node->children, doc, state | (1 << code)); break; case XML2BIN_CSTYPE: current_conflictset_name = (char *) xmlGetProp(cur_node, (xmlChar *) "name"); if (!current_conflictset_name) current_conflictset_name = ""; if (init_next_conflictset()) { printf ("ERROR: creating new conflictset structure failed.\n"); exit(EXIT_FAILURE); } walk_policy(cur_node->children, doc, state | (1 << code)); break; case XML2BIN_TYPE: if (register_type(cur_node, doc, state)) exit(EXIT_FAILURE); /* type leaf */ break; case XML2BIN_LABELTEMPLATE: /* handle in second pass */ case XML2BIN_TEXT: case XML2BIN_COMMENT: case XML2BIN_DATE: case XML2BIN_REFERENCE: case XML2BIN_NSURL: /* for future use: where to find global label / type name mappings */ case XML2BIN_URL: /* for future use: where to find policy */ /* leaf - nothing to do */ break; default: printf("Unkonwn token Error (%d) in Policy\n", code); exit(EXIT_FAILURE); } } return; } void init_type_mapping(void) { printf("Creating ssid mappings ...\n"); /* initialize the ste and chwall type lists */ TAILQ_INIT(&ste_head); TAILQ_INIT(&chwall_head); TAILQ_INIT(&conflictsets_head); } void post_type_mapping(void) { struct type_entry *te; struct ssid_entry *se; int i; /* determine primary/secondary policy component orders */ if ((primary == NULLPOLICY) && have_chwall) primary = CHWALL; /* default if not set */ else if ((primary == NULLPOLICY) && have_ste) primary = STE; switch (primary) { case CHWALL: if (have_ste) secondary = STE; /* else default = NULLPOLICY */ break; case STE: if (have_chwall) secondary = CHWALL; /* else default = NULLPOLICY */ break; default: /* NULL/NULL policy */ break; } if (!DEBUG) return; /* print queues */ if (have_ste) { printf("STE-Type queue (%s):\n", (primary == STE) ? "PRIMARY" : "SECONDARY"); for (te = ste_head.tqh_first; te != NULL; te = te->entries.tqe_next) printf("name=%22s, map=%x\n", te->name, te->mapping); } if (have_chwall) { printf("CHWALL-Type queue (%s):\n", (primary == CHWALL) ? "PRIMARY" : "SECONDARY"); for (te = chwall_head.tqh_first; te != NULL; te = te->entries.tqe_next) printf("name=%s, map=%x\n", te->name, te->mapping); printf("Conflictset queue (max=%d):\n", max_conflictsets); for (se = conflictsets_head.tqh_first; se != NULL; se = se->entries.tqe_next) { printf("conflictset name >%s<\n", se->name ? se->name : "NONAME"); for (i = 0; i < max_chwall_types; i++) if (se->row[i]) printf("#%x ", i); printf("\n"); } } } /***************** template-related parsing *********************/ /* add default ssid at head of ssid queues */ int init_ssid_queues(void) { struct ssid_entry *default_ssid_chwall, *default_ssid_ste; default_ssid_chwall = malloc(sizeof(struct ssid_entry)); default_ssid_ste = malloc(sizeof(struct ssid_entry)); if ((!default_ssid_chwall) || (!default_ssid_ste)) return -ENOMEM; /* default chwall ssid */ default_ssid_chwall->name = "DEFAULT"; default_ssid_chwall->num = max_chwall_ssids++; default_ssid_chwall->is_ref = 0; default_ssid_chwall->type = ANY; default_ssid_chwall->row = malloc(max_chwall_types); if (!default_ssid_chwall->row) return -ENOMEM; memset(default_ssid_chwall->row, 0, max_chwall_types); TAILQ_INSERT_TAIL(&chwall_ssid_head, default_ssid_chwall, entries); current_chwall_ssid_p = default_ssid_chwall; max_chwall_labels++; /* default ste ssid */ default_ssid_ste->name = "DEFAULT"; default_ssid_ste->num = max_ste_ssids++; default_ssid_ste->is_ref = 0; default_ssid_ste->type = ANY; default_ssid_ste->row = malloc(max_ste_types); if (!default_ssid_ste->row) return -ENOMEM; memset(default_ssid_ste->row, 0, max_ste_types); TAILQ_INSERT_TAIL(&ste_ssid_head, default_ssid_ste, entries); current_ste_ssid_p = default_ssid_ste; max_ste_labels++; return 0; } int init_next_chwall_ssid(unsigned long state) { struct ssid_entry *ssid = malloc(sizeof(struct ssid_entry)); if (!ssid) return -ENOMEM; ssid->name = current_ssid_name; ssid->num = max_chwall_ssids++; ssid->is_ref = 0; if (state & (1 << XML2BIN_VM)) ssid->type = VM; else ssid->type = RES; /** * row: allocate one byte per type; * [i] != 0 --> mapped type >i< is part of the ssid */ ssid->row = malloc(max_chwall_types); if (!ssid->row) return -ENOMEM; memset(ssid->row, 0, max_chwall_types); TAILQ_INSERT_TAIL(&chwall_ssid_head, ssid, entries); current_chwall_ssid_p = ssid; max_chwall_labels++; return 0; } int init_next_ste_ssid(unsigned long state) { struct ssid_entry *ssid = malloc(sizeof(struct ssid_entry)); if (!ssid) return -ENOMEM; ssid->name = current_ssid_name; ssid->num = max_ste_ssids++; ssid->is_ref = 0; if (state & (1 << XML2BIN_VM)) ssid->type = VM; else ssid->type = RES; /** * row: allocate one byte per type; * [i] != 0 --> mapped type >i< is part of the ssid */ ssid->row = malloc(max_ste_types); if (!ssid->row) return -ENOMEM; memset(ssid->row, 0, max_ste_types); TAILQ_INSERT_TAIL(&ste_ssid_head, ssid, entries); current_ste_ssid_p = ssid; max_ste_labels++; return 0; } /* adds a type to the current ssid */ int add_type(xmlNode * cur_node, xmlDocPtr doc, unsigned long state) { xmlChar *text; struct type_entry *e; text = xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!text) { printf("Error reading type name!\n"); return -EFAULT; } /* same for all: 1. lookup type mapping, 2. mark type in ssid */ switch (state) { case XML2BIN_VM_STE_S: case XML2BIN_RES_STE_S: /* lookup the type mapping and include the type mapping into the array */ if (!(e = lookup(&ste_head, (char *) text))) { printf("ERROR: unknown VM STE type >%s<.\n", text); exit(EXIT_FAILURE); } if (current_ste_ssid_p->row[e->mapping]) printf("Warning: double entry of VM STE type >%s<.\n", text); current_ste_ssid_p->row[e->mapping] = 1; break; case XML2BIN_VM_CHWALL_S: /* lookup the type mapping and include the type mapping into the array */ if (!(e = lookup(&chwall_head, (char *) text))) { printf("ERROR: unknown VM CHWALL type >%s<.\n", text); exit(EXIT_FAILURE); } if (current_chwall_ssid_p->row[e->mapping]) printf("Warning: double entry of VM CHWALL type >%s<.\n", text); current_chwall_ssid_p->row[e->mapping] = 1; break; default: printf("Incorrect type environment (state = %lx, text = %s).\n", state, text); xmlFree(text); return -EFAULT; } return 0; } void set_bootstrap_label(xmlNode * cur_node) { xmlChar *order; if ((order = xmlGetProp(cur_node, (xmlChar *) BOOTSTRAP_LABEL_ATTR_NAME))) bootstrap_label = (char *) order; else { printf("ERROR: No bootstrap label defined!\n"); exit(EXIT_FAILURE); } } void walk_labels(xmlNode * start, xmlDocPtr doc, unsigned long state) { xmlNode *cur_node = NULL; int code; for (cur_node = start; cur_node; cur_node = cur_node->next) { if ((code = totoken((char *) cur_node->name)) < 0) { printf("Unkonwn token: >%s<. Aborting.\n", cur_node->name); exit(EXIT_FAILURE); } switch (code) { /* adjust state to new state */ case XML2BIN_SUBJECTS: set_bootstrap_label(cur_node); /* fall through */ case XML2BIN_SECPOL: case XML2BIN_LABELTEMPLATE: case XML2BIN_VM: case XML2BIN_RES: case XML2BIN_OBJECTS: walk_labels(cur_node->children, doc, state | (1 << code)); break; case XML2BIN_STETYPES: /* create new ssid entry to use and point current to it */ if (init_next_ste_ssid(state)) { printf("ERROR: creating new ste ssid structure failed.\n"); exit(EXIT_FAILURE); } walk_labels(cur_node->children, doc, state | (1 << code)); break; case XML2BIN_CHWALLTYPES: /* create new ssid entry to use and point current to it */ if (init_next_chwall_ssid(state)) { printf ("ERROR: creating new chwall ssid structure failed.\n"); exit(EXIT_FAILURE); } walk_labels(cur_node->children, doc, state | (1 << code)); break; case XML2BIN_TYPE: /* add type to current ssid */ if (add_type(cur_node, doc, state)) exit(EXIT_FAILURE); break; case XML2BIN_NAME: if ((state == XML2BIN_VM_S) || (state == XML2BIN_RES_S)) { current_ssid_name = (char *) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!current_ssid_name) { printf("ERROR: empty >vm/res namename< >%s< out of context (state = 0x%lx.\n", (char *) xmlNodeListGetString(doc, cur_node-> xmlChildrenNode, 1), state); exit(EXIT_FAILURE); } break; case XML2BIN_TEXT: case XML2BIN_COMMENT: case XML2BIN_POLICYHEADER: case XML2BIN_STE: case XML2BIN_CHWALL: break; default: printf("Unkonwn token Error (%d) in Label Template\n", code); exit(EXIT_FAILURE); } } return; } /* * will go away as soon as we have non-static bootstrap ssidref for dom0 */ void fixup_bootstrap_label(struct tailhead_ssid *head, u_int32_t max_types, u_int32_t * max_ssids) { struct ssid_entry *np; int i; /* should not happen if xml / xsd checks work */ if (!bootstrap_label) { printf("ERROR: No bootstrap label defined.\n"); exit(EXIT_FAILURE); } /* search bootstrap_label */ for (np = head->tqh_first; np != NULL; np = np->entries.tqe_next) { if (!strcmp(np->name, bootstrap_label)) { break; } } if (!np) { /* bootstrap label not found */ printf("ERROR: Bootstrap label >%s< not found.\n", bootstrap_label); exit(EXIT_FAILURE); } /* move this entry ahead in the list right after the default entry so it * receives ssidref 1/1 */ TAILQ_REMOVE(head, np, entries); TAILQ_INSERT_AFTER(head, head->tqh_first, np, entries); /* renumber the ssids (we could also just switch places with 1st element) */ for (np = head->tqh_first, i = 0; np != NULL; np = np->entries.tqe_next, i++) np->num = i; } void init_label_mapping(void) { printf("Creating label mappings ...\n"); /* initialize the ste and chwall type lists */ TAILQ_INIT(&chwall_ssid_head); TAILQ_INIT(&ste_ssid_head); /* init with default ssids */ if (init_ssid_queues()) { printf("ERROR adding default ssids.\n"); exit(EXIT_FAILURE); } } void post_label_mapping(void) { struct ssid_entry *np; int i; /* * now sort bootstrap label to the head of the list * (for now), dom0 assumes its label in the first * defined ssidref (1/1). 0/0 is the default non-Label */ if (have_chwall) fixup_bootstrap_label(&chwall_ssid_head, max_chwall_types, &max_chwall_ssids); if (have_ste) fixup_bootstrap_label(&ste_ssid_head, max_ste_types, &max_ste_ssids); if (!DEBUG) return; /* print queues */ if (have_chwall) { printf("CHWALL SSID queue (max ssidrefs=%d):\n", max_chwall_ssids); np = NULL; for (np = chwall_ssid_head.tqh_first; np != NULL; np = np->entries.tqe_next) { printf("SSID #%02u (Label=%s)\n", np->num, np->name); if (np->is_ref) printf("REFERENCE"); else for (i = 0; i < max_chwall_types; i++) if (np->row[i]) printf("#%02d ", i); printf("\n\n"); } } if (have_ste) { printf("STE SSID queue (max ssidrefs=%d):\n", max_ste_ssids); np = NULL; for (np = ste_ssid_head.tqh_first; np != NULL; np = np->entries.tqe_next) { printf("SSID #%02u (Label=%s)\n", np->num, np->name); if (np->is_ref) printf("REFERENCE"); else for (i = 0; i < max_ste_types; i++) if (np->row[i]) printf("#%02d ", i); printf("\n\n"); } } } void create_mappings(xmlDocPtr doc) { xmlNode *doc_root_node = xmlDocGetRootElement(doc); /* walk the XML policy tree and fill in types and labels */ init_type_mapping(); walk_policy(doc_root_node, doc, XML2BIN_NULL); /* first pass: types */ post_type_mapping(); init_label_mapping(); walk_labels(doc_root_node, doc, XML2BIN_NULL); /* second pass: labels */ post_label_mapping(); } /***************** writing the binary policy *********************/ /* * the mapping file is ascii-based since it will likely be used from * within scripts (using awk, grep, etc.); * * We print from high-level to low-level information so that with one * pass, any symbol can be resolved (e.g. Label -> types) */ int write_mapping(char *filename) { struct ssid_entry *e; struct type_entry *t; int i; FILE *file; if ((file = fopen(filename, "w")) == NULL) return -EIO; fprintf(file, "POLICYREFERENCENAME %s\n", policy_reference_name); fprintf(file, "MAGIC %08x\n", ACM_MAGIC); fprintf(file, "POLICY FILE %s\n", policy_filename); fprintf(file, "BINARY FILE %s\n", binary_filename); if (have_chwall) { fprintf(file, "MAX-CHWALL-TYPES %08x\n", max_chwall_types); fprintf(file, "MAX-CHWALL-SSIDS %08x\n", max_chwall_ssids); fprintf(file, "MAX-CHWALL-LABELS %08x\n", max_chwall_labels); } if (have_ste) { fprintf(file, "MAX-STE-TYPES %08x\n", max_ste_types); fprintf(file, "MAX-STE-SSIDS %08x\n", max_ste_ssids); fprintf(file, "MAX-STE-LABELS %08x\n", max_ste_labels); } fprintf(file, "\n"); /* primary / secondary order for combined ssid synthesis/analysis * if no primary is named, then chwall is primary */ switch (primary) { case CHWALL: fprintf(file, "PRIMARY CHWALL\n"); break; case STE: fprintf(file, "PRIMARY STE\n"); break; default: fprintf(file, "PRIMARY NULL\n"); break; } switch (secondary) { case CHWALL: fprintf(file, "SECONDARY CHWALL\n"); break; case STE: fprintf(file, "SECONDARY STE\n"); break; default: fprintf(file, "SECONDARY NULL\n"); break; } fprintf(file, "\n"); /* first labels to ssid mappings */ if (have_chwall) { for (e = chwall_ssid_head.tqh_first; e != NULL; e = e->entries.tqe_next) { fprintf(file, "LABEL->SSID %s CHWALL %-25s %8x\n", (e->type == VM) ? "VM " : ((e->type == RES) ? "RES" : "ANY"), e->name, e->num); } fprintf(file, "\n"); } if (have_ste) { for (e = ste_ssid_head.tqh_first; e != NULL; e = e->entries.tqe_next) { fprintf(file, "LABEL->SSID %s STE %-25s %8x\n", (e->type == VM) ? "VM " : ((e->type == RES) ? "RES" : "ANY"), e->name, e->num); } fprintf(file, "\n"); } /* second ssid to type mappings */ if (have_chwall) { for (e = chwall_ssid_head.tqh_first; e != NULL; e = e->entries.tqe_next) { if (e->is_ref) continue; fprintf(file, "SSID->TYPE CHWALL %08x", e->num); for (i = 0; i < max_chwall_types; i++)
/**
 * \addtogroup uip
 * @{
 */

/**
 * \defgroup uipopt Configuration options for uIP
 * @{
 *
 * uIP is configured using the per-project configuration file
 * "uipopt.h". This file contains all compile-time options for uIP and
 * should be tweaked to match each specific project. The uIP
 * distribution contains a documented example "uipopt.h" that can be
 * copied and modified for each project.
 */

/**
 * \file
 * Configuration options for uIP.
 * \author Adam Dunkels <adam@dunkels.com>
 *
 * This file is used for tweaking various configuration options for
 * uIP. You should make a copy of this file into one of your project's
 * directories instead of editing this example "uipopt.h" file that
 * comes with the uIP distribution.
 */

/*
 * Copyright (c) 2001-2003, Adam Dunkels.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This file is part of the uIP TCP/IP stack.
 *
 * $Id: uipopt.h,v 1.11 2009/04/10 00:37:48 adamdunkels Exp $
 *
 */

#ifndef __UIPOPT_H__
#define __UIPOPT_H__

#ifndef UIP_LITTLE_ENDIAN
#define UIP_LITTLE_ENDIAN  3412
#endif /* UIP_LITTLE_ENDIAN */
#ifndef UIP_BIG_ENDIAN
#define UIP_BIG_ENDIAN     1234
#endif /* UIP_BIG_ENDIAN */

/*------------------------------------------------------------------------------*/

/**
 * \defgroup uipoptstaticconf Static configuration options
 * @{
 *
 * These configuration options can be used for setting the IP address
 * settings statically, but only if UIP_FIXEDADDR is set to 1. The
 * configuration options for a specific node includes IP address,
 * netmask and default router as well as the Ethernet address. The
 * netmask, default router and Ethernet address are applicable only
 * if uIP should be run over Ethernet.
 *
 * This options are meaningful only for the IPv4 code.
 *
 * All of these should be changed to suit your project.
 */

/**
 * Determines if uIP should use a fixed IP address or not.
 *
 * If uIP should use a fixed IP address, the settings are set in the
 * uipopt.h file. If not, the macros uip_sethostaddr(),
 * uip_setdraddr() and uip_setnetmask() should be used instead.
 *
 * \hideinitializer
 */
#define UIP_FIXEDADDR    0

/**
 * Ping IP address assignment.
 *
 * uIP uses a "ping" packets for setting its own IP address if this
 * option is set. If so, uIP will start with an empty IP address and
 * the destination IP address of the first incoming "ping" (ICMP echo)
 * packet will be used for setting the hosts IP address.
 *
 * \note This works only if UIP_FIXEDADDR is 0.
 *
 * \hideinitializer
 */
#ifdef UIP_CONF_PINGADDRCONF
#define UIP_PINGADDRCONF UIP_CONF_PINGADDRCONF
#else /* UIP_CONF_PINGADDRCONF */
#define UIP_PINGADDRCONF 0
#endif /* UIP_CONF_PINGADDRCONF */


/**
 * Specifies if the uIP ARP module should be compiled with a fixed
 * Ethernet MAC address or not.
 *
 * If this configuration option is 0, the macro uip_setethaddr() can
 * be used to specify the Ethernet address at run-time.
 *
 * \hideinitializer
 */
#define UIP_FIXEDETHADDR 0

/** @} */
/*------------------------------------------------------------------------------*/
/**
 * \defgroup uipoptip IP configuration options
 * @{
 *
 */
/**
 * The IP TTL (time to live) of IP packets sent by uIP.
 *
 * This should normally not be changed.
 */
#define UIP_TTL         64

/**
 * The maximum time an IP fragment should wait in the reassembly
 * buffer before it is dropped.
 *
 */
#define UIP_REASS_MAXAGE 60 /*60s*/

/**
 * Turn on support for IP packet reassembly.
 *
 * uIP supports reassembly of fragmented IP packets. This features
 * requires an additional amount of RAM to hold the reassembly buffer
 * and the reassembly code size is approximately 700 bytes.  The
 * reassembly buffer is of the same size as the uip_buf buffer
 * (configured by UIP_BUFSIZE).
 *
 * \note IP packet reassembly is not heavily tested.
 *
 * \hideinitializer
 */
#ifdef UIP_CONF_REASSEMBLY
#define UIP_REASSEMBLY UIP_CONF_REASSEMBLY
#else /* UIP_CONF_REASSEMBLY */
#define UIP_REASSEMBLY 0
#endif /* UIP_CONF_REASSEMBLY */
/** @} */

/*------------------------------------------------------------------------------*/
/**
 * \defgroup uipoptipv6 IPv6 configuration options
 * @{
 *
 */

/** The maximum transmission unit at the IP Layer*/
#define UIP_LINK_MTU 1280

#ifndef UIP_CONF_IPV6
/** Do we use IPv6 or not (default: no) */
#define UIP_CONF_IPV6                 0
#endif

#ifndef UIP_CONF_IPV6_QUEUE_PKT
/** Do we do per %neighbor queuing during address resolution (default: no) */
#define UIP_CONF_IPV6_QUEUE_PKT       0
#endif

#ifndef UIP_CONF_IPV6_CHECKS
/** Do we do IPv6 consistency checks (highly recommended, default: yes) */
#define UIP_CONF_IPV6_CHECKS          1
#endif

#ifndef UIP_CONF_IPV6_REASSEMBLY
/** Do we do IPv6 fragmentation (default: no) */
#define UIP_CONF_IPV6_REASSEMBLY      0
#endif

#ifndef UIP_CONF_NETIF_MAX_ADDRESSES
/** Default number of IPv6 addresses associated to the node's interface */
#define UIP_CONF_NETIF_MAX_ADDRESSES  3
#endif

#ifndef UIP_CONF_ND6_MAX_PREFIXES
/** Default number of IPv6 prefixes associated to the node's interface */
#define UIP_CONF_ND6_MAX_PREFIXES     3
#endif

#ifndef UIP_CONF_ND6_MAX_NEIGHBORS
/** Default number of neighbors that can be stored in the %neighbor cache */
#define UIP_CONF_ND6_MAX_NEIGHBORS    4
#endif

#ifndef UIP_CONF_ND6_MAX_DEFROUTERS
/** Minimum number of default routers */
#define UIP_CONF_ND6_MAX_DEFROUTERS   2
#endif
/** @} */

/*------------------------------------------------------------------------------*/
/**
 * \defgroup uipoptudp UDP configuration options
 * @{
 *
 * \note The UDP support in uIP is still not entirely complete; there
 * is no support for sending or receiving broadcast or multicast
 * packets, but it works well enough to support a number of vital
 * applications such as DNS queries, though
 */

/**
 * Toggles whether UDP support should be compiled in or not.
 *
 * \hideinitializer
 */
#ifdef UIP_CONF_UDP
#define UIP_UDP UIP_CONF_UDP
#else /* UIP_CONF_UDP */
#define UIP_UDP           1
#endif /* UIP_CONF_UDP */

/**
 * Toggles if UDP checksums should be used or not.
 *
 * \note Support for UDP checksums is currently not included in uIP,
 * so this option has no function.
 *
 * \hideinitializer
 */
#ifdef UIP_CONF_UDP_CHECKSUMS
#define UIP_UDP_CHECKSUMS UIP_CONF_UDP_CHECKSUMS
#else
#define UIP_UDP_CHECKSUMS 0
#endif

/**
 * The maximum amount of concurrent UDP connections.
 *
 * \hideinitializer
 */
#ifdef UIP_CONF_UDP_CONNS
#define UIP_UDP_CONNS UIP_CONF_UDP_CONNS
#else /* UIP_CONF_UDP_CONNS */
#define UIP_UDP_CONNS    10
#endif /* UIP_CONF_UDP_CONNS */

/**
 * The name of the function that should be called when UDP datagrams arrive.
 *
 * \hideinitializer
 */


/** @} */
/*------------------------------------------------------------------------------*/
/**
 * \defgroup uipopttcp TCP configuration options
 * @{
 */

/**
 * Toggles whether TCP support should be compiled in or not.
 *
 * \hideinitializer
 */
#ifdef UIP_CONF_TCP
#define UIP_TCP UIP_CONF_TCP
#else /* UIP_CONF_TCP */
#define UIP_TCP           1
#endif /* UIP_CONF_TCP */

/**
 * Determines if support for opening connections from uIP should be
 * compiled in.
 *
 * If the applications that are running on top of uIP for this project
 * do not need to open outgoing TCP connections, this configuration
 * option can be turned off to reduce the code size of uIP.
 *
 * \hideinitializer
 */
#ifndef UIP_CONF_ACTIVE_OPEN
#define UIP_ACTIVE_OPEN 1
#else /* UIP_CONF_ACTIVE_OPEN */
#define UIP_ACTIVE_OPEN UIP_CONF_ACTIVE_OPEN
#endif /* UIP_CONF_ACTIVE_OPEN */

/**
 * The maximum number of simultaneously open TCP connections.
 *
 * Since the TCP connections are statically allocated, turning this
 * configuration knob down results in less RAM used. Each TCP
 * connection requires approximately 30 bytes of memory.
 *
 * \hideinitializer
 */
#ifndef UIP_CONF_MAX_CONNECTIONS
#define UIP_CONNS       10
#else /* UIP_CONF_MAX_CONNECTIONS */
#define UIP_CONNS UIP_CONF_MAX_CONNECTIONS
#endif /* UIP_CONF_MAX_CONNECTIONS */


/**
 * The maximum number of simultaneously listening TCP ports.
 *
 * Each listening TCP port requires 2 bytes of memory.
 *
 * \hideinitializer
 */
#ifndef UIP_CONF_MAX_LISTENPORTS
#define UIP_LISTENPORTS 20
#else /* UIP_CONF_MAX_LISTENPORTS */
#define UIP_LISTENPORTS UIP_CONF_MAX_LISTENPORTS
#endif /* UIP_CONF_MAX_LISTENPORTS */

/**
 * Determines if support for TCP urgent data notification should be
 * compiled in.
 *
 * Urgent data (out-of-band data) is a rarely used TCP feature that
 * very seldom would be required.
 *
 * \hideinitializer
 */
#if !defined(UIP_URGDATA)
#define UIP_URGDATA      0
#endif

/**
 * The initial retransmission timeout counted in timer pulses.
 *
 * This should not be changed.
 */
#if !defined(UIP_RTO)
#define UIP_RTO         3
#endif

/**
 * The maximum number of times a segment should be retransmitted
 * before the connection should be aborted.
 *
 * This should not be changed.
 */
#if !defined(UIP_MAXRTX)
#define UIP_MAXRTX      8
#endif

/**
 * The maximum number of times a SYN segment should be retransmitted
 * before a connection request should be deemed to have been
 * unsuccessful.
 *
 * This should not need to be changed.
 */
#if !defined(UIP_MAXSYNRTX)
#define UIP_MAXSYNRTX      5
#endif

/**
 * The TCP maximum segment size.
 *
 * This is should not be to set to more than
 * UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN.
 */
#ifdef UIP_CONF_TCP_MSS
#define UIP_TCP_MSS UIP_CONF_TCP_MSS
#else
#define UIP_TCP_MSS     (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN)
#endif

/**
 * The size of the advertised receiver's window.
 *
 * Should be set low (i.e., to the size of the uip_buf buffer) if the
 * application is slow to process incoming data, or high (32768 bytes)
 * if the application processes data quickly.
 *
 * \hideinitializer
 */
#ifndef UIP_CONF_RECEIVE_WINDOW
#define UIP_RECEIVE_WINDOW UIP_TCP_MSS
#else
#define UIP_RECEIVE_WINDOW UIP_CONF_RECEIVE_WINDOW
#endif

/**
 * How long a connection should stay in the TIME_WAIT state.
 *
 * This configuration option has no real implication, and it should be
 * left untouched.
 */
#define UIP_TIME_WAIT_TIMEOUT 120


/** @} */
/*------------------------------------------------------------------------------*/
/**
 * \defgroup uipoptarp ARP configuration options
 * @{
 */

/**
 * The size of the ARP table.
 *
 * This option should be set to a larger value if this uIP node will
 * have many connections from the local network.
 *
 * \hideinitializer
 */
#ifdef UIP_CONF_ARPTAB_SIZE
#define UIP_ARPTAB_SIZE UIP_CONF_ARPTAB_SIZE
#else
#define UIP_ARPTAB_SIZE 8
#endif

/**
 * The maximum age of ARP table entries measured in 10ths of seconds.
 *
 * An UIP_ARP_MAXAGE of 120 corresponds to 20 minutes (BSD
 * default).
 */
#define UIP_ARP_MAXAGE 120


/** @} */

/*------------------------------------------------------------------------------*/

/**
 * \defgroup uipoptmac layer 2 options (for ipv6)
 * @{
 */

#define UIP_DEFAULT_PREFIX_LEN 64

/** @} */

/*------------------------------------------------------------------------------*/

/**
 * \defgroup uipoptsics 6lowpan options (for ipv6)
 * @{
 */
/**
 * Timeout for packet reassembly at the 6lowpan layer
 * (should be < 60s)
 */
#ifdef SICSLOWPAN_CONF_MAXAGE
#define SICSLOWPAN_REASS_MAXAGE SICSLOWPAN_CONF_MAXAGE
#else
#define SICSLOWPAN_REASS_MAXAGE 20
#endif

/**
 * Do we compress the IP header or not (default: no)
 */
#ifndef SICSLOWPAN_CONF_COMPRESSION
#define SICSLOWPAN_CONF_COMPRESSION 0
#endif

/**
 * If we use IPHC compression, how many address contexts do we support
 */
#ifndef SICSLOWPAN_CONF_MAX_ADDR_CONTEXTS
#define SICSLOWPAN_CONF_MAX_ADDR_CONTEXTS 1
#endif

/**
 * Do we support 6lowpan fragmentation
 */
#ifndef SICSLOWPAN_CONF_FRAG
#define SICSLOWPAN_CONF_FRAG  0
#endif

/** @} */

/*------------------------------------------------------------------------------*/

/**
 * \defgroup uipoptgeneral General configuration options
 * @{
 */

/**
 * The size of the uIP packet buffer.
 *
 * The uIP packet buffer should not be smaller than 60 bytes, and does
 * not need to be larger than 1514 bytes. Lower size results in lower
 * TCP throughput, larger size results in higher TCP throughput.
 *
 * \hideinitializer
 */
#ifndef UIP_CONF_BUFFER_SIZE
#define UIP_BUFSIZE UIP_LINK_MTU + UIP_LLH_LEN
#else /* UIP_CONF_BUFFER_SIZE */
#define UIP_BUFSIZE UIP_CONF_BUFFER_SIZE
#endif /* UIP_CONF_BUFFER_SIZE */


/**
 * Determines if statistics support should be compiled in.
 *
 * The statistics is useful for debugging and to show the user.
 *
 * \hideinitializer
 */
#ifndef UIP_CONF_STATISTICS
#define UIP_STATISTICS  0
#else /* UIP_CONF_STATISTICS */
#define UIP_STATISTICS UIP_CONF_STATISTICS
#endif /* UIP_CONF_STATISTICS */

/**
 * Determines if logging of certain events should be compiled in.
 *
 * This is useful mostly for debugging. The function uip_log()
 * must be implemented to suit the architecture of the project, if
 * logging is turned on.
 *
 * \hideinitializer
 */
#ifndef UIP_CONF_LOGGING
#define UIP_LOGGING     0
#else /* UIP_CONF_LOGGING */
#define UIP_LOGGING     UIP_CONF_LOGGING
#endif /* UIP_CONF_LOGGING */

/**
 * Broadcast support.
 *
 * This flag configures IP broadcast support. This is useful only
 * together with UDP.
 *
 * \hideinitializer
 *
 */
#ifndef UIP_CONF_BROADCAST
#define UIP_BROADCAST 0
#else /* UIP_CONF_BROADCAST */
#define UIP_BROADCAST UIP_CONF_BROADCAST
#endif /* UIP_CONF_BROADCAST */

/**
 * Print out a uIP log message.
 *
 * This function must be implemented by the module that uses uIP, and
 * is called by uIP whenever a log message is generated.
 */
void uip_log(char *msg);

/**
 * The link level header length.
 *
 * This is the offset into the uip_buf where the IP header can be
 * found. For Ethernet, this should be set to 14. For SLIP, this
 * should be set to 0.
 *
 * \note we probably won't use this constant for other link layers than
 * ethernet as they have variable header length (this is due to variable
 * number and type of address fields and to optional security features)
 * E.g.: 802.15.4 -> 2 + (1/2*4/8) + 0/5/6/10/14
 *       802.11 -> 4 + (6*3/4) + 2
 * \hideinitializer
 */
#ifdef UIP_CONF_LLH_LEN
#define UIP_LLH_LEN UIP_CONF_LLH_LEN
#else /* UIP_LLH_LEN */
#define UIP_LLH_LEN     14
#endif /* UIP_CONF_LLH_LEN */

/** @} */
/*------------------------------------------------------------------------------*/
/**
 * \defgroup uipoptcpu CPU architecture configuration
 * @{
 *
 * The CPU architecture configuration is where the endianess of the
 * CPU on which uIP is to be run is specified. Most CPUs today are
 * little endian, and the most notable exception are the Motorolas
 * which are big endian. The BYTE_ORDER macro should be changed to
 * reflect the CPU architecture on which uIP is to be run.
 */

/**
 * The byte order of the CPU architecture on which uIP is to be run.
 *
 * This option can be either UIP_BIG_ENDIAN (Motorola byte order) or
 * UIP_LITTLE_ENDIAN (Intel byte order).
 *
 * \hideinitializer
 */
#ifdef UIP_CONF_BYTE_ORDER
#define UIP_BYTE_ORDER     UIP_CONF_BYTE_ORDER
#else /* UIP_CONF_BYTE_ORDER */
#define UIP_BYTE_ORDER     UIP_LITTLE_ENDIAN
#endif /* UIP_CONF_BYTE_ORDER */

/** @} */
/*------------------------------------------------------------------------------*/

#include <ff.h>
#include <stdbool.h>
#include <stdint.h>

#include "timer.h"

typedef uint8_t u8_t;
typedef uint16_t u16_t;
typedef uint32_t u32_t;
typedef uint32_t uip_stats_t;

/**
 * \defgroup uipoptapp Application specific configurations
 * @{
 *
 * An uIP application is implemented using a single application
 * function that is called by uIP whenever a TCP/IP event occurs. The
 * name of this function must be registered with uIP at compile time
 * using the UIP_APPCALL definition.
 *
 * uIP applications can store the application state within the
 * uip_conn structure by specifying the type of the application
 * structure by typedef:ing the type uip_tcp_appstate_t and uip_udp_appstate_t.
 *
 * The file containing the definitions must be included in the
 * uipopt.h file.
 *
 * The following example illustrates how this can look.
 \code

 void httpd_appcall(void);
 #define UIP_APPCALL     httpd_appcall

 struct httpd_state {
 u8_t state;
 u16_t count;
 char *dataptr;
 char *script;
 };
 typedef struct httpd_state uip_tcp_appstate_t
 \endcode
*/
#define UIP_UDP_APPCALL uIPManagement_UDPCallback
void UIP_UDP_APPCALL(void);

/**
 * \var #define UIP_APPCALL
 *
 * The name of the application function that uIP should call in
 * response to TCP/IP events.
 *
 */
#define UIP_APPCALL     uIPManagement_TCPCallback
void UIP_APPCALL(void);

/**
 * \var typedef uip_tcp_appstate_t
 *
 * The type of the application state that is to be stored in the
 * uip_conn structure. This usually is typedef:ed to a struct holding
 * application state information.
 */
typedef union
{
	struct
	{
		uint8_t  CurrentState;
		uint8_t  NextState;

		char     FileName[MAX_URI_LENGTH];
		FIL      FileHandle;
		bool     FileOpen;
		uint32_t ACKedFilePos;
		uint16_t SentChunkSize;
	} HTTPServer;

	struct
	{
		uint8_t  CurrentState;
		uint8_t  NextState;

		uint8_t  IssuedCommand;
	} TELNETServer;
} uip_tcp_appstate_t;

/**
 * \var typedef uip_udp_appstate_t
 *
 * The type of the application state that is to be stored in the
 * uip_conn structure. This usually is typedef:ed to a struct holding
 * application state information.
 */
typedef union
{
	struct
	{
		uint8_t      CurrentState;
		struct timer Timeout;

		struct
		{
			uint8_t AllocatedIP[4];
			uint8_t Netmask[4];
			uint8_t GatewayIP[4];
			uint8_t ServerIP[4];
		} DHCPOffer_Data;
	} DHCPClient;
} uip_udp_appstate_t;
/** @} */

#endif /* __UIPOPT_H__ */
/** @} */
/** @} */