renamed openair-cn to openair3
git-svn-id: http://svn.eurecom.fr/openair4G/trunk@7886 818b1a75-f10b-46b9-bf7c-635c3b92a50f
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
|
||||
AM_CFLAGS = @ADD_CFLAGS@ \
|
||||
-I$(top_srcdir)/COMMON
|
||||
|
||||
noinst_LTLIBRARIES = libhashtable.la
|
||||
libhashtable_la_LDFLAGS = -all-static
|
||||
libhashtable_la_SOURCES = \
|
||||
hashtable.c hashtable.h \
|
||||
obj_hashtable.c obj_hashtable.h
|
||||
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
|
||||
all: libhashtable.a
|
||||
|
||||
libhashtable_OBJECTS = \
|
||||
hashtable.o \
|
||||
obj_hashtable.o \
|
||||
|
||||
|
||||
CFLAGS = \
|
||||
-DUSER_MODE \
|
||||
-DENABLE_USE_MME \
|
||||
-g \
|
||||
-O2 \
|
||||
-Wall \
|
||||
-Werror=implicit-function-declaration
|
||||
|
||||
-include .deps/*.d
|
||||
|
||||
$(libhashtable_OBJECTS): %.o : %.c
|
||||
$(CC) -c $(CFLAGS) -o $@ $<
|
||||
@if ! test -d ".deps" ; then mkdir -p .deps/; fi
|
||||
@$(CC) -MM $(CFLAGS) $*.c > .deps/$*.d
|
||||
@mv -f .deps/$*.d .deps/$*.d.tmp
|
||||
@sed -e 's|.*:|$*.o:|' < .deps/$*.d.tmp > .deps/$*.d
|
||||
@sed -e 's/.*://' -e 's/\\$$//' < .deps/$*.d.tmp | fmt -1 | \
|
||||
sed -e 's/^ *//' -e 's/$$/:/' >> .deps/$*.d
|
||||
@rm -f .deps/$*.d.tmp
|
||||
|
||||
libhashtable.a: $(libhashtable_OBJECTS)
|
||||
$(AR) rcvs $@ $(libhashtable_OBJECTS)
|
||||
|
||||
clean:
|
||||
rm -f libhashtable.a
|
||||
rm -rf .deps/
|
||||
rm -f $(libhashtable_OBJECTS)
|
||||
Executable
+360
@@ -0,0 +1,360 @@
|
||||
/* from: http://en.literateprograms.org/Hash_table_%28C%29#chunk%20def:node
|
||||
* Original licence Creative Commons CC0 1.0 Waiver.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "hashtable.h"
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
char* hashtable_rc_code2string(hashtable_rc_t rcP)
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
switch (rcP) {
|
||||
case HASH_TABLE_OK:
|
||||
return "HASH_TABLE_OK";
|
||||
break;
|
||||
|
||||
case HASH_TABLE_INSERT_OVERWRITTEN_DATA:
|
||||
return "HASH_TABLE_INSERT_OVERWRITTEN_DATA";
|
||||
break;
|
||||
|
||||
case HASH_TABLE_KEY_NOT_EXISTS:
|
||||
return "HASH_TABLE_KEY_NOT_EXISTS";
|
||||
break;
|
||||
|
||||
case HASH_TABLE_KEY_ALREADY_EXISTS:
|
||||
return "HASH_TABLE_KEY_ALREADY_EXISTS";
|
||||
break;
|
||||
|
||||
case HASH_TABLE_BAD_PARAMETER_HASHTABLE:
|
||||
return "HASH_TABLE_BAD_PARAMETER_HASHTABLE";
|
||||
break;
|
||||
|
||||
default:
|
||||
return "UNKNOWN hashtable_rc_t";
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* free int function
|
||||
* hash_free_int_func() is used when this hashtable is used to store int values as data (pointer = value).
|
||||
*/
|
||||
|
||||
void hash_free_int_func(void* memoryP) {}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Default hash function
|
||||
* def_hashfunc() is the default used by hashtable_create() when the user didn't specify one.
|
||||
* This is a simple/naive hash function which adds the key's ASCII char values. It will probably generate lots of collisions on large hash tables.
|
||||
*/
|
||||
|
||||
static hash_size_t def_hashfunc(const uint64_t keyP)
|
||||
{
|
||||
return (hash_size_t)keyP;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Initialisation
|
||||
* hashtable_create() sets up the initial structure of the hash table. The user specified size will be allocated and initialized to NULL.
|
||||
* The user can also specify a hash function. If the hashfunc argument is NULL, a default hash function is used.
|
||||
* If an error occurred, NULL is returned. All other values in the returned hash_table_t pointer should be released with hashtable_destroy().
|
||||
*/
|
||||
hash_table_t *hashtable_create(hash_size_t sizeP, hash_size_t (*hashfuncP)(const uint64_t ), void (*freefuncP)(void*))
|
||||
{
|
||||
hash_table_t *hashtbl;
|
||||
|
||||
if(!(hashtbl=malloc(sizeof(hash_table_t)))) return NULL;
|
||||
|
||||
if(!(hashtbl->nodes=calloc(sizeP, sizeof(hash_node_t*)))) {
|
||||
free(hashtbl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hashtbl->size=sizeP;
|
||||
|
||||
if(hashfuncP) hashtbl->hashfunc=hashfuncP;
|
||||
else hashtbl->hashfunc=def_hashfunc;
|
||||
|
||||
if(freefuncP) hashtbl->freefunc=freefuncP;
|
||||
else hashtbl->freefunc=free;
|
||||
|
||||
return hashtbl;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Cleanup
|
||||
* The hashtable_destroy() walks through the linked lists for each possible hash value, and releases the elements. It also releases the nodes array and the hash_table_t.
|
||||
*/
|
||||
hashtable_rc_t hashtable_destroy(hash_table_t *hashtblP)
|
||||
{
|
||||
hash_size_t n;
|
||||
hash_node_t *node, *oldnode;
|
||||
|
||||
if (hashtblP == NULL) {
|
||||
return HASH_TABLE_BAD_PARAMETER_HASHTABLE;
|
||||
}
|
||||
|
||||
for(n=0; n<hashtblP->size; ++n) {
|
||||
node=hashtblP->nodes[n];
|
||||
|
||||
while(node) {
|
||||
oldnode=node;
|
||||
node=node->next;
|
||||
|
||||
if (oldnode->data) {
|
||||
hashtblP->freefunc(oldnode->data);
|
||||
}
|
||||
|
||||
free(oldnode);
|
||||
}
|
||||
}
|
||||
|
||||
free(hashtblP->nodes);
|
||||
free(hashtblP);
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
hashtable_rc_t hashtable_is_key_exists (hash_table_t *hashtblP, const uint64_t keyP)
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
hash_node_t *node;
|
||||
hash_size_t hash;
|
||||
|
||||
if (hashtblP == NULL) {
|
||||
return HASH_TABLE_BAD_PARAMETER_HASHTABLE;
|
||||
}
|
||||
|
||||
hash=hashtblP->hashfunc(keyP)%hashtblP->size;
|
||||
node=hashtblP->nodes[hash];
|
||||
|
||||
while(node) {
|
||||
if(node->key == keyP) {
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
|
||||
node=node->next;
|
||||
}
|
||||
|
||||
return HASH_TABLE_KEY_NOT_EXISTS;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
hashtable_rc_t hashtable_apply_funct_on_elements (hash_table_t *hashtblP, void functP(uint64_t keyP, void* dataP, void* parameterP), void* parameterP)
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
hash_node_t *node = NULL;
|
||||
unsigned int i = 0;
|
||||
unsigned int num_elements = 0;
|
||||
|
||||
if (hashtblP == NULL) {
|
||||
return HASH_TABLE_BAD_PARAMETER_HASHTABLE;
|
||||
}
|
||||
|
||||
while ((num_elements < hashtblP->num_elements) && (i < hashtblP->size)) {
|
||||
if (hashtblP->nodes[i] != NULL) {
|
||||
node=hashtblP->nodes[i];
|
||||
|
||||
while(node) {
|
||||
num_elements += 1;
|
||||
functP(node->key, node->data, parameterP);
|
||||
node=node->next;
|
||||
}
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Adding a new element
|
||||
* To make sure the hash value is not bigger than size, the result of the user provided hash function is used modulo size.
|
||||
*/
|
||||
hashtable_rc_t hashtable_insert(hash_table_t *hashtblP, const uint64_t keyP, void *dataP)
|
||||
{
|
||||
hash_node_t *node;
|
||||
hash_size_t hash;
|
||||
|
||||
if (hashtblP == NULL) {
|
||||
return HASH_TABLE_BAD_PARAMETER_HASHTABLE;
|
||||
}
|
||||
|
||||
hash=hashtblP->hashfunc(keyP)%hashtblP->size;
|
||||
|
||||
node=hashtblP->nodes[hash];
|
||||
|
||||
while(node) {
|
||||
if(node->key == keyP) {
|
||||
if (node->data) {
|
||||
hashtblP->freefunc(node->data);
|
||||
}
|
||||
|
||||
node->data=dataP;
|
||||
return HASH_TABLE_INSERT_OVERWRITTEN_DATA;
|
||||
}
|
||||
|
||||
node=node->next;
|
||||
}
|
||||
|
||||
if(!(node=malloc(sizeof(hash_node_t)))) return -1;
|
||||
|
||||
node->key=keyP;
|
||||
node->data=dataP;
|
||||
|
||||
if (hashtblP->nodes[hash]) {
|
||||
node->next=hashtblP->nodes[hash];
|
||||
} else {
|
||||
node->next = NULL;
|
||||
}
|
||||
|
||||
hashtblP->nodes[hash]=node;
|
||||
hashtblP->num_elements += 1;
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* To remove an element from the hash table, we just search for it in the linked list for that hash value,
|
||||
* and remove it if it is found. If it was not found, it is an error and -1 is returned.
|
||||
*/
|
||||
hashtable_rc_t hashtable_remove(hash_table_t *hashtblP, const uint64_t keyP)
|
||||
{
|
||||
hash_node_t *node, *prevnode=NULL;
|
||||
hash_size_t hash;
|
||||
|
||||
if (hashtblP == NULL) {
|
||||
return HASH_TABLE_BAD_PARAMETER_HASHTABLE;
|
||||
}
|
||||
|
||||
hash=hashtblP->hashfunc(keyP)%hashtblP->size;
|
||||
node=hashtblP->nodes[hash];
|
||||
|
||||
while(node) {
|
||||
if(node->key == keyP) {
|
||||
if(prevnode) prevnode->next=node->next;
|
||||
else hashtblP->nodes[hash]=node->next;
|
||||
|
||||
if (node->data) {
|
||||
hashtblP->freefunc(node->data);
|
||||
}
|
||||
|
||||
free(node);
|
||||
hashtblP->num_elements -= 1;
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
|
||||
prevnode=node;
|
||||
node=node->next;
|
||||
}
|
||||
|
||||
return HASH_TABLE_KEY_NOT_EXISTS;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Searching for an element is easy. We just search through the linked list for the corresponding hash value.
|
||||
* NULL is returned if we didn't find it.
|
||||
*/
|
||||
hashtable_rc_t hashtable_get(hash_table_t *hashtblP, const uint64_t keyP, void** dataP)
|
||||
{
|
||||
hash_node_t *node;
|
||||
hash_size_t hash;
|
||||
|
||||
if (hashtblP == NULL) {
|
||||
*dataP = NULL;
|
||||
return HASH_TABLE_BAD_PARAMETER_HASHTABLE;
|
||||
}
|
||||
|
||||
hash=hashtblP->hashfunc(keyP)%hashtblP->size;
|
||||
/* fprintf(stderr, "hashtable_get() key=%s, hash=%d\n", key, hash);*/
|
||||
|
||||
node=hashtblP->nodes[hash];
|
||||
|
||||
while(node) {
|
||||
if(node->key == keyP) {
|
||||
*dataP = node->data;
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
|
||||
node=node->next;
|
||||
}
|
||||
|
||||
*dataP = NULL;
|
||||
return HASH_TABLE_KEY_NOT_EXISTS;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Resizing
|
||||
* The number of elements in a hash table is not always known when creating the table.
|
||||
* If the number of elements grows too large, it will seriously reduce the performance of most hash table operations.
|
||||
* If the number of elements are reduced, the hash table will waste memory. That is why we provide a function for resizing the table.
|
||||
* Resizing a hash table is not as easy as a realloc(). All hash values must be recalculated and each element must be inserted into its new position.
|
||||
* We create a temporary hash_table_t object (newtbl) to be used while building the new hashes.
|
||||
* This allows us to reuse hashtable_insert() and hashtable_remove(), when moving the elements to the new table.
|
||||
* After that, we can just free the old table and copy the elements from newtbl to hashtbl.
|
||||
*/
|
||||
|
||||
hashtable_rc_t hashtable_resize(hash_table_t *hashtblP, hash_size_t sizeP)
|
||||
{
|
||||
hash_table_t newtbl;
|
||||
hash_size_t n;
|
||||
hash_node_t *node,*next;
|
||||
|
||||
if (hashtblP == NULL) {
|
||||
return HASH_TABLE_BAD_PARAMETER_HASHTABLE;
|
||||
}
|
||||
|
||||
newtbl.size = sizeP;
|
||||
newtbl.hashfunc = hashtblP->hashfunc;
|
||||
|
||||
if(!(newtbl.nodes=calloc(sizeP, sizeof(hash_node_t*)))) return -1;
|
||||
|
||||
for(n=0; n<hashtblP->size; ++n) {
|
||||
for(node=hashtblP->nodes[n]; node; node=next) {
|
||||
next = node->next;
|
||||
hashtable_insert(&newtbl, node->key, node->data);
|
||||
// Lionel GAUTHIER: BAD CODE TO BE REWRITTEN
|
||||
hashtable_remove(hashtblP, node->key);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
free(hashtblP->nodes);
|
||||
hashtblP->size=newtbl.size;
|
||||
hashtblP->nodes=newtbl.nodes;
|
||||
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
/* from: http://en.literateprograms.org/Hash_table_%28C%29#chunk%20def:node
|
||||
* Original licence Creative Commons CC0 1.0 Waiver.(http://creativecommons.org/publicdomain/zero/1.0/)
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
#ifndef _UTILS_COLLECTION_HASH_TABLE_H_
|
||||
#define _UTILS_COLLECTION_HASH_TABLE_H_
|
||||
#include<stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef size_t hash_size_t;
|
||||
|
||||
typedef enum hashtable_return_code_e {
|
||||
HASH_TABLE_OK = 0,
|
||||
HASH_TABLE_INSERT_OVERWRITTEN_DATA = 1,
|
||||
HASH_TABLE_KEY_NOT_EXISTS = 2,
|
||||
HASH_TABLE_KEY_ALREADY_EXISTS = 3,
|
||||
HASH_TABLE_BAD_PARAMETER_HASHTABLE = 4,
|
||||
HASH_TABLE_SYSTEM_ERROR = 5,
|
||||
HASH_TABLE_CODE_MAX
|
||||
} hashtable_rc_t;
|
||||
|
||||
|
||||
typedef struct hash_node_s {
|
||||
uint64_t key;
|
||||
void *data;
|
||||
struct hash_node_s *next;
|
||||
} hash_node_t;
|
||||
|
||||
typedef struct hash_table_s {
|
||||
hash_size_t size;
|
||||
hash_size_t num_elements;
|
||||
struct hash_node_s **nodes;
|
||||
hash_size_t (*hashfunc)(const uint64_t);
|
||||
void (*freefunc)(void*);
|
||||
} hash_table_t;
|
||||
|
||||
char* hashtable_rc_code2string(hashtable_rc_t rcP);
|
||||
void hash_free_int_func(void* memoryP);
|
||||
hash_table_t *hashtable_create (hash_size_t size, hash_size_t (*hashfunc)(const uint64_t ), void (*freefunc)(void*));
|
||||
hashtable_rc_t hashtable_destroy(hash_table_t *hashtbl);
|
||||
hashtable_rc_t hashtable_is_key_exists (hash_table_t *hashtbl, const uint64_t key);
|
||||
hashtable_rc_t hashtable_apply_funct_on_elements (hash_table_t *hashtblP, void funct(uint64_t keyP, void* dataP, void* parameterP), void* parameterP);
|
||||
hashtable_rc_t hashtable_insert (hash_table_t *hashtbl, const uint64_t key, void *data);
|
||||
hashtable_rc_t hashtable_remove (hash_table_t *hashtbl, const uint64_t key);
|
||||
hashtable_rc_t hashtable_get (hash_table_t *hashtbl, const uint64_t key, void **dataP);
|
||||
hashtable_rc_t hashtable_resize (hash_table_t *hashtbl, hash_size_t size);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
Executable
+319
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "obj_hashtable.h"
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Default hash function
|
||||
* def_hashfunc() is the default used by hashtable_create() when the user didn't specify one.
|
||||
* This is a simple/naive hash function which adds the key's ASCII char values. It will probably generate lots of collisions on large hash tables.
|
||||
*/
|
||||
|
||||
static hash_size_t def_hashfunc(const void *keyP, int key_sizeP)
|
||||
{
|
||||
hash_size_t hash=0;
|
||||
|
||||
while(key_sizeP) hash^=((unsigned char*)keyP)[key_sizeP --];
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Initialisation
|
||||
* hashtable_create() sets up the initial structure of the hash table. The user specified size will be allocated and initialized to NULL.
|
||||
* The user can also specify a hash function. If the hashfunc argument is NULL, a default hash function is used.
|
||||
* If an error occurred, NULL is returned. All other values in the returned obj_hash_table_t pointer should be released with hashtable_destroy().
|
||||
*/
|
||||
obj_hash_table_t *obj_hashtable_create(hash_size_t sizeP, hash_size_t (*hashfuncP)(const void*, int ), void (*freekeyfuncP)(void*), void (*freedatafuncP)(void*))
|
||||
{
|
||||
obj_hash_table_t *hashtbl;
|
||||
|
||||
if(!(hashtbl=malloc(sizeof(obj_hash_table_t)))) return NULL;
|
||||
|
||||
if(!(hashtbl->nodes=calloc(sizeP, sizeof(obj_hash_node_t*)))) {
|
||||
free(hashtbl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hashtbl->size=sizeP;
|
||||
|
||||
if(hashfuncP) hashtbl->hashfunc=hashfuncP;
|
||||
else hashtbl->hashfunc=def_hashfunc;
|
||||
|
||||
if(freekeyfuncP) hashtbl->freekeyfunc=freekeyfuncP;
|
||||
else hashtbl->freekeyfunc=free;
|
||||
|
||||
if(freedatafuncP) hashtbl->freedatafunc=freedatafuncP;
|
||||
else hashtbl->freedatafunc=free;
|
||||
|
||||
return hashtbl;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Cleanup
|
||||
* The hashtable_destroy() walks through the linked lists for each possible hash value, and releases the elements. It also releases the nodes array and the obj_hash_table_t.
|
||||
*/
|
||||
hashtable_rc_t obj_hashtable_destroy(obj_hash_table_t *hashtblP)
|
||||
{
|
||||
hash_size_t n;
|
||||
obj_hash_node_t *node, *oldnode;
|
||||
|
||||
for(n=0; n<hashtblP->size; ++n) {
|
||||
node=hashtblP->nodes[n];
|
||||
|
||||
while(node) {
|
||||
oldnode=node;
|
||||
node=node->next;
|
||||
hashtblP->freekeyfunc(oldnode->key);
|
||||
hashtblP->freedatafunc(oldnode->data);
|
||||
free(oldnode);
|
||||
}
|
||||
}
|
||||
|
||||
free(hashtblP->nodes);
|
||||
free(hashtblP);
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
hashtable_rc_t obj_hashtable_is_key_exists (obj_hash_table_t *hashtblP, void* keyP, int key_sizeP)
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
obj_hash_node_t *node;
|
||||
hash_size_t hash;
|
||||
|
||||
if (hashtblP == NULL) {
|
||||
return HASH_TABLE_BAD_PARAMETER_HASHTABLE;
|
||||
}
|
||||
|
||||
hash=hashtblP->hashfunc(keyP, key_sizeP)%hashtblP->size;
|
||||
node=hashtblP->nodes[hash];
|
||||
|
||||
while(node) {
|
||||
if(node->key == keyP) {
|
||||
return HASH_TABLE_OK;
|
||||
} else if (node->key_size == key_sizeP) {
|
||||
if (memcmp(node->key, keyP, key_sizeP) == 0) {
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
node=node->next;
|
||||
}
|
||||
|
||||
return HASH_TABLE_KEY_NOT_EXISTS;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Adding a new element
|
||||
* To make sure the hash value is not bigger than size, the result of the user provided hash function is used modulo size.
|
||||
*/
|
||||
hashtable_rc_t obj_hashtable_insert(obj_hash_table_t *hashtblP, void* keyP, int key_sizeP, void *dataP)
|
||||
{
|
||||
obj_hash_node_t *node;
|
||||
hash_size_t hash;
|
||||
|
||||
if (hashtblP == NULL) {
|
||||
return HASH_TABLE_BAD_PARAMETER_HASHTABLE;
|
||||
}
|
||||
|
||||
hash=hashtblP->hashfunc(keyP, key_sizeP)%hashtblP->size;
|
||||
node=hashtblP->nodes[hash];
|
||||
|
||||
while(node) {
|
||||
if(node->key == keyP) {
|
||||
if (node->data) {
|
||||
hashtblP->freedatafunc(node->data);
|
||||
}
|
||||
|
||||
node->data=dataP;
|
||||
// waste of memory here (keyP is lost) we should free it now
|
||||
return HASH_TABLE_INSERT_OVERWRITTEN_DATA;
|
||||
}
|
||||
|
||||
node=node->next;
|
||||
}
|
||||
|
||||
if(!(node=malloc(sizeof(obj_hash_node_t)))) return -1;
|
||||
|
||||
node->key=keyP;
|
||||
node->data=dataP;
|
||||
|
||||
if (hashtblP->nodes[hash]) {
|
||||
node->next=hashtblP->nodes[hash];
|
||||
} else {
|
||||
node->next = NULL;
|
||||
}
|
||||
|
||||
hashtblP->nodes[hash]=node;
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* To remove an element from the hash table, we just search for it in the linked list for that hash value,
|
||||
* and remove it if it is found. If it was not found, it is an error and -1 is returned.
|
||||
*/
|
||||
hashtable_rc_t obj_hashtable_remove(obj_hash_table_t *hashtblP, const void* keyP, int key_sizeP)
|
||||
{
|
||||
obj_hash_node_t *node, *prevnode=NULL;
|
||||
hash_size_t hash;
|
||||
|
||||
if (hashtblP == NULL) {
|
||||
return HASH_TABLE_BAD_PARAMETER_HASHTABLE;
|
||||
}
|
||||
|
||||
hash=hashtblP->hashfunc(keyP, key_sizeP)%hashtblP->size;
|
||||
node=hashtblP->nodes[hash];
|
||||
|
||||
while(node) {
|
||||
if ((node->key == keyP) || ((node->key_size == key_sizeP) && (memcmp(node->key, keyP, key_sizeP) == 0))) {
|
||||
if(prevnode) {
|
||||
prevnode->next=node->next;
|
||||
} else {
|
||||
hashtblP->nodes[hash]=node->next;
|
||||
}
|
||||
|
||||
hashtblP->freekeyfunc(node->key);
|
||||
hashtblP->freedatafunc(node->data);
|
||||
free(node);
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
|
||||
prevnode=node;
|
||||
node=node->next;
|
||||
}
|
||||
|
||||
return HASH_TABLE_KEY_NOT_EXISTS;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Searching for an element is easy. We just search through the linked list for the corresponding hash value.
|
||||
* NULL is returned if we didn't find it.
|
||||
*/
|
||||
hashtable_rc_t obj_hashtable_get(obj_hash_table_t *hashtblP, const void* keyP, int key_sizeP, void** dataP)
|
||||
{
|
||||
obj_hash_node_t *node;
|
||||
hash_size_t hash;
|
||||
|
||||
if (hashtblP == NULL) {
|
||||
*dataP = NULL;
|
||||
return HASH_TABLE_BAD_PARAMETER_HASHTABLE;
|
||||
}
|
||||
|
||||
hash=hashtblP->hashfunc(keyP, key_sizeP)%hashtblP->size;
|
||||
node=hashtblP->nodes[hash];
|
||||
|
||||
while(node) {
|
||||
if(node->key == keyP) {
|
||||
*dataP = node->data;
|
||||
return HASH_TABLE_OK;
|
||||
} else if (node->key_size == key_sizeP) {
|
||||
if (memcmp(node->key, keyP, key_sizeP) == 0) {
|
||||
*dataP = node->data;
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
node=node->next;
|
||||
}
|
||||
|
||||
*dataP = NULL;
|
||||
return HASH_TABLE_KEY_NOT_EXISTS;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Function to return all keys of an object hash table
|
||||
*/
|
||||
hashtable_rc_t obj_hashtable_get_keys(obj_hash_table_t *hashtblP, void ** keysP, unsigned int *sizeP)
|
||||
{
|
||||
size_t n = 0;
|
||||
obj_hash_node_t *node = NULL;
|
||||
obj_hash_node_t *next = NULL;
|
||||
|
||||
*sizeP = 0;
|
||||
keysP = calloc(hashtblP->num_elements, sizeof(void *));
|
||||
|
||||
if (keysP) {
|
||||
for(n=0; n<hashtblP->size; ++n) {
|
||||
for(node=hashtblP->nodes[n]; node; node=next) {
|
||||
keysP[*sizeP++] = node->key;
|
||||
next = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
|
||||
return HASH_TABLE_SYSTEM_ERROR;
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
* Resizing
|
||||
* The number of elements in a hash table is not always known when creating the table.
|
||||
* If the number of elements grows too large, it will seriously reduce the performance of most hash table operations.
|
||||
* If the number of elements are reduced, the hash table will waste memory. That is why we provide a function for resizing the table.
|
||||
* Resizing a hash table is not as easy as a realloc(). All hash values must be recalculated and each element must be inserted into its new position.
|
||||
* We create a temporary obj_hash_table_t object (newtbl) to be used while building the new hashes.
|
||||
* This allows us to reuse hashtable_insert() and hashtable_remove(), when moving the elements to the new table.
|
||||
* After that, we can just free the old table and copy the elements from newtbl to hashtbl.
|
||||
*/
|
||||
hashtable_rc_t obj_hashtable_resize(obj_hash_table_t *hashtblP, hash_size_t sizeP)
|
||||
{
|
||||
obj_hash_table_t newtbl;
|
||||
hash_size_t n;
|
||||
obj_hash_node_t *node,*next;
|
||||
|
||||
if (hashtblP == NULL) {
|
||||
return HASH_TABLE_BAD_PARAMETER_HASHTABLE;
|
||||
}
|
||||
|
||||
newtbl.size = sizeP;
|
||||
newtbl.hashfunc = hashtblP->hashfunc;
|
||||
|
||||
if(!(newtbl.nodes=calloc(sizeP, sizeof(obj_hash_node_t*)))) return HASH_TABLE_SYSTEM_ERROR;
|
||||
|
||||
for(n=0; n<hashtblP->size; ++n) {
|
||||
for(node=hashtblP->nodes[n]; node; node=next) {
|
||||
next = node->next;
|
||||
obj_hashtable_insert(&newtbl, node->key, node->key_size, node->data);
|
||||
obj_hashtable_remove(hashtblP, node->key, node->key_size);
|
||||
}
|
||||
}
|
||||
|
||||
free(hashtblP->nodes);
|
||||
hashtblP->size=newtbl.size;
|
||||
hashtblP->nodes=newtbl.nodes;
|
||||
|
||||
return HASH_TABLE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
#ifndef _OBJ_HASH_TABLE_H_
|
||||
#define _OBJ_HASH_TABLE_H_
|
||||
#include<stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "hashtable.h"
|
||||
|
||||
typedef size_t hash_size_t;
|
||||
|
||||
|
||||
typedef struct obj_hash_node_s {
|
||||
int key_size;
|
||||
void *key;
|
||||
void *data;
|
||||
struct obj_hash_node_s *next;
|
||||
} obj_hash_node_t;
|
||||
|
||||
typedef struct obj_hash_table_s {
|
||||
hash_size_t size;
|
||||
hash_size_t num_elements;
|
||||
struct obj_hash_node_s **nodes;
|
||||
hash_size_t (*hashfunc)(const void*, int);
|
||||
void (*freekeyfunc)(void*);
|
||||
void (*freedatafunc)(void*);
|
||||
} obj_hash_table_t;
|
||||
|
||||
obj_hash_table_t *obj_hashtable_create (hash_size_t size, hash_size_t (*hashfunc)(const void*, int ), void (*freekeyfunc)(void*), void (*freedatafunc)(void*));
|
||||
hashtable_rc_t obj_hashtable_destroy (obj_hash_table_t *hashtblP);
|
||||
hashtable_rc_t obj_hashtable_is_key_exists (obj_hash_table_t *hashtblP, void* keyP, int key_sizeP);
|
||||
hashtable_rc_t obj_hashtable_insert (obj_hash_table_t *hashtblP, void* keyP, int key_sizeP, void *dataP);
|
||||
hashtable_rc_t obj_hashtable_remove (obj_hash_table_t *hashtblP, const void* keyP, int key_sizeP);
|
||||
hashtable_rc_t obj_hashtable_get (obj_hash_table_t *hashtblP, const void* keyP, int key_sizeP, void ** dataP);
|
||||
hashtable_rc_t obj_hashtable_get_keys(obj_hash_table_t *hashtblP, void ** keysP, unsigned int *sizeP);
|
||||
hashtable_rc_t obj_hashtable_resize (obj_hash_table_t *hashtblP, hash_size_t sizeP);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
SUBDIRS = . HASHTABLE
|
||||
|
||||
AM_CFLAGS = @ADD_CFLAGS@ \
|
||||
-I$(top_srcdir)/COMMON \
|
||||
-I$(top_srcdir)/NAS/EURECOM-NAS/src/api/network \
|
||||
-I$(top_srcdir)/NAS/EURECOM-NAS/src/include \
|
||||
-I$(top_srcdir)/SGW-LITE \
|
||||
-I$(top_srcdir)/INTERTASK_INTERFACE
|
||||
|
||||
AM_YFLAGS = -d
|
||||
|
||||
noinst_LTLIBRARIES = libutils.la
|
||||
libutils_la_LDFLAGS = -all-static
|
||||
libutils_la_SOURCES = \
|
||||
conversions.h conversions.c \
|
||||
enum_string.h enum_string.c \
|
||||
log.c log.h \
|
||||
mme_config.c mme_config.h \
|
||||
mcc_mnc_itu.c mcc_mnc_itu.h \
|
||||
mme_default_values.h \
|
||||
queue.h tree.h
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "conversions.h"
|
||||
|
||||
static const char hex_to_ascii_table[16] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
};
|
||||
|
||||
static const signed char ascii_to_hex_table[0x100] = {
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
|
||||
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
||||
};
|
||||
|
||||
void hexa_to_ascii(uint8_t *from, char *to, size_t length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < length; i++) {
|
||||
uint8_t upper = (from[i] & 0xf0) >> 4;
|
||||
uint8_t lower = from[i] & 0x0f;
|
||||
to[2 * i] = hex_to_ascii_table[upper];
|
||||
to[2 * i + 1] = hex_to_ascii_table[lower];
|
||||
}
|
||||
}
|
||||
|
||||
int ascii_to_hex(uint8_t *dst, const char *h)
|
||||
{
|
||||
const unsigned char *hex = (const unsigned char *) h;
|
||||
unsigned i = 0;
|
||||
|
||||
for (;;) {
|
||||
int high, low;
|
||||
|
||||
while (*hex && isspace(*hex))
|
||||
hex++;
|
||||
|
||||
if (!*hex)
|
||||
return 1;
|
||||
|
||||
high = ascii_to_hex_table[*hex++];
|
||||
|
||||
if (high < 0)
|
||||
return 0;
|
||||
|
||||
while (*hex && isspace(*hex))
|
||||
hex++;
|
||||
|
||||
if (!*hex)
|
||||
return 0;
|
||||
|
||||
low = ascii_to_hex_table[*hex++];
|
||||
|
||||
if (low < 0)
|
||||
return 0;
|
||||
|
||||
dst[i++] = (high << 4) | low;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
|
||||
#include "assertions.h"
|
||||
|
||||
#ifndef CONVERSIONS_H_
|
||||
#define CONVERSIONS_H_
|
||||
|
||||
/* Endianness conversions for 16 and 32 bits integers from host to network order */
|
||||
#if (BYTE_ORDER == LITTLE_ENDIAN)
|
||||
# define hton_int32(x) \
|
||||
(((x & 0x000000FF) << 24) | ((x & 0x0000FF00) << 8) | \
|
||||
((x & 0x00FF0000) >> 8) | ((x & 0xFF000000) >> 24))
|
||||
|
||||
# define hton_int16(x) \
|
||||
(((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8)
|
||||
|
||||
# define ntoh_int32_buf(bUF) \
|
||||
((*(bUF)) << 24) | ((*((bUF) + 1)) << 16) | ((*((bUF) + 2)) << 8) \
|
||||
| (*((bUF) + 3))
|
||||
#else
|
||||
# define hton_int32(x) (x)
|
||||
# define hton_int16(x) (x)
|
||||
#endif
|
||||
|
||||
#define IN_ADDR_TO_BUFFER(X,bUFF) INT32_TO_BUFFER((X).s_addr,(char*)bUFF)
|
||||
|
||||
#define IN6_ADDR_TO_BUFFER(X,bUFF) \
|
||||
do { \
|
||||
((uint8_t*)(bUFF))[0] = (X).s6_addr[0]; \
|
||||
((uint8_t*)(bUFF))[1] = (X).s6_addr[1]; \
|
||||
((uint8_t*)(bUFF))[2] = (X).s6_addr[2]; \
|
||||
((uint8_t*)(bUFF))[3] = (X).s6_addr[3]; \
|
||||
((uint8_t*)(bUFF))[4] = (X).s6_addr[4]; \
|
||||
((uint8_t*)(bUFF))[5] = (X).s6_addr[5]; \
|
||||
((uint8_t*)(bUFF))[6] = (X).s6_addr[6]; \
|
||||
((uint8_t*)(bUFF))[7] = (X).s6_addr[7]; \
|
||||
((uint8_t*)(bUFF))[8] = (X).s6_addr[8]; \
|
||||
((uint8_t*)(bUFF))[9] = (X).s6_addr[9]; \
|
||||
((uint8_t*)(bUFF))[10] = (X).s6_addr[10]; \
|
||||
((uint8_t*)(bUFF))[11] = (X).s6_addr[11]; \
|
||||
((uint8_t*)(bUFF))[12] = (X).s6_addr[12]; \
|
||||
((uint8_t*)(bUFF))[13] = (X).s6_addr[13]; \
|
||||
((uint8_t*)(bUFF))[14] = (X).s6_addr[14]; \
|
||||
((uint8_t*)(bUFF))[15] = (X).s6_addr[15]; \
|
||||
} while(0)
|
||||
|
||||
#define BUFFER_TO_INT8(buf, x) (x = ((buf)[0]))
|
||||
|
||||
#define INT8_TO_BUFFER(x, buf) ((buf)[0] = (x))
|
||||
|
||||
/* Convert an integer on 16 bits to the given bUFFER */
|
||||
#define INT16_TO_BUFFER(x, buf) \
|
||||
do { \
|
||||
(buf)[0] = (x) >> 8; \
|
||||
(buf)[1] = (x); \
|
||||
} while(0)
|
||||
|
||||
/* Convert an array of char containing vALUE to x */
|
||||
#define BUFFER_TO_INT16(buf, x) \
|
||||
do { \
|
||||
x = ((buf)[0] << 8) | \
|
||||
((buf)[1]); \
|
||||
} while(0)
|
||||
|
||||
/* Convert an integer on 32 bits to the given bUFFER */
|
||||
#define INT32_TO_BUFFER(x, buf) \
|
||||
do { \
|
||||
(buf)[0] = (x) >> 24; \
|
||||
(buf)[1] = (x) >> 16; \
|
||||
(buf)[2] = (x) >> 8; \
|
||||
(buf)[3] = (x); \
|
||||
} while(0)
|
||||
|
||||
/* Convert an array of char containing vALUE to x */
|
||||
#define BUFFER_TO_INT32(buf, x) \
|
||||
do { \
|
||||
x = ((buf)[0] << 24) | \
|
||||
((buf)[1] << 16) | \
|
||||
((buf)[2] << 8) | \
|
||||
((buf)[3]); \
|
||||
} while(0)
|
||||
|
||||
/* Convert an integer on 32 bits to an octet string from aSN1c tool */
|
||||
#define INT32_TO_OCTET_STRING(x, aSN) \
|
||||
do { \
|
||||
(aSN)->buf = calloc(4, sizeof(uint8_t)); \
|
||||
INT32_TO_BUFFER(x, ((aSN)->buf)); \
|
||||
(aSN)->size = 4; \
|
||||
} while(0)
|
||||
|
||||
#define INT32_TO_BIT_STRING(x, aSN) \
|
||||
do { \
|
||||
INT32_TO_OCTET_STRING(x, aSN); \
|
||||
(aSN)->bits_unused = 0; \
|
||||
} while(0)
|
||||
|
||||
#define INT16_TO_OCTET_STRING(x, aSN) \
|
||||
do { \
|
||||
(aSN)->buf = calloc(2, sizeof(uint8_t)); \
|
||||
(aSN)->size = 2; \
|
||||
INT16_TO_BUFFER(x, (aSN)->buf); \
|
||||
} while(0)
|
||||
|
||||
#define INT8_TO_OCTET_STRING(x, aSN) \
|
||||
do { \
|
||||
(aSN)->buf = calloc(1, sizeof(uint8_t)); \
|
||||
(aSN)->size = 1; \
|
||||
INT8_TO_BUFFER(x, (aSN)->buf); \
|
||||
} while(0)
|
||||
|
||||
#define MME_CODE_TO_OCTET_STRING INT8_TO_OCTET_STRING
|
||||
#define M_TMSI_TO_OCTET_STRING INT32_TO_OCTET_STRING
|
||||
#define MME_GID_TO_OCTET_STRING INT16_TO_OCTET_STRING
|
||||
|
||||
#define OCTET_STRING_TO_INT8(aSN, x) \
|
||||
do { \
|
||||
DevCheck((aSN)->size == 1, (aSN)->size, 0, 0); \
|
||||
BUFFER_TO_INT8((aSN)->buf, x); \
|
||||
} while(0)
|
||||
|
||||
#define OCTET_STRING_TO_INT16(aSN, x) \
|
||||
do { \
|
||||
DevCheck((aSN)->size == 2, (aSN)->size, 0, 0); \
|
||||
BUFFER_TO_INT16((aSN)->buf, x); \
|
||||
} while(0)
|
||||
|
||||
#define OCTET_STRING_TO_INT32(aSN, x) \
|
||||
do { \
|
||||
DevCheck((aSN)->size == 4, (aSN)->size, 0, 0); \
|
||||
BUFFER_TO_INT32((aSN)->buf, x); \
|
||||
} while(0)
|
||||
|
||||
#define BIT_STRING_TO_INT32(aSN, x) \
|
||||
do { \
|
||||
DevCheck((aSN)->bits_unused == 0, (aSN)->bits_unused, 0, 0); \
|
||||
OCTET_STRING_TO_INT32(aSN, x); \
|
||||
} while(0)
|
||||
|
||||
#define BIT_STRING_TO_CELL_IDENTITY(aSN, vALUE) \
|
||||
do { \
|
||||
DevCheck((aSN)->bits_unused == 4, (aSN)->bits_unused, 4, 0); \
|
||||
vALUE = ((aSN)->buf[0] << 20) | ((aSN)->buf[1] << 12) | \
|
||||
((aSN)->buf[2] << 4) | (aSN)->buf[3]; \
|
||||
} while(0)
|
||||
|
||||
#define MCC_HUNDREDS(vALUE) \
|
||||
((vALUE) / 100)
|
||||
/* When MNC is only composed of 2 digits, set the hundreds unit to 0xf */
|
||||
#define MNC_HUNDREDS(vALUE, mNCdIGITlENGTH) \
|
||||
( mNCdIGITlENGTH == 2 ? 15 : (vALUE) / 100)
|
||||
#define MCC_MNC_DECIMAL(vALUE) \
|
||||
(((vALUE) / 10) % 10)
|
||||
#define MCC_MNC_DIGIT(vALUE) \
|
||||
((vALUE) % 10)
|
||||
|
||||
#define MCC_TO_BUFFER(mCC, bUFFER) \
|
||||
do { \
|
||||
DevAssert(bUFFER != NULL); \
|
||||
(bUFFER)[0] = MCC_HUNDREDS(mCC); \
|
||||
(bUFFER)[1] = MCC_MNC_DECIMAL(mCC); \
|
||||
(bUFFER)[2] = MCC_MNC_DIGIT(mCC); \
|
||||
} while(0)
|
||||
|
||||
#define MCC_MNC_TO_PLMNID(mCC, mNC, mNCdIGITlENGTH, oCTETsTRING) \
|
||||
do { \
|
||||
(oCTETsTRING)->buf = calloc(3, sizeof(uint8_t)); \
|
||||
(oCTETsTRING)->buf[0] = (MCC_MNC_DECIMAL(mCC) << 4) | MCC_HUNDREDS(mCC); \
|
||||
(oCTETsTRING)->buf[1] = (MNC_HUNDREDS(mNC,mNCdIGITlENGTH) << 4) | MCC_MNC_DIGIT(mCC); \
|
||||
(oCTETsTRING)->buf[2] = (MCC_MNC_DIGIT(mNC) << 4) | MCC_MNC_DECIMAL(mNC); \
|
||||
(oCTETsTRING)->size = 3; \
|
||||
} while(0)
|
||||
|
||||
#define MCC_MNC_TO_TBCD(mCC, mNC, mNCdIGITlENGTH, tBCDsTRING) \
|
||||
do { \
|
||||
char _buf[3]; \
|
||||
DevAssert((mNCdIGITlENGTH == 3) || (mNCdIGITlENGTH == 2)); \
|
||||
_buf[0] = (MCC_MNC_DECIMAL(mCC) << 4) | MCC_HUNDREDS(mCC); \
|
||||
_buf[1] = (MNC_HUNDREDS(mNC,mNCdIGITlENGTH) << 4) | MCC_MNC_DIGIT(mCC);\
|
||||
_buf[2] = (MCC_MNC_DIGIT(mNC) << 4) | MCC_MNC_DECIMAL(mNC); \
|
||||
OCTET_STRING_fromBuf(tBCDsTRING, _buf, 3); \
|
||||
} while(0)
|
||||
|
||||
#define TBCD_TO_MCC_MNC(tBCDsTRING, mCC, mNC, mNCdIGITlENGTH) \
|
||||
do { \
|
||||
int mNC_hundred; \
|
||||
DevAssert((tBCDsTRING)->size == 3); \
|
||||
mNC_hundred = (((tBCDsTRING)->buf[1] & 0xf0) >> 4); \
|
||||
if (mNC_hundred == 0xf) { \
|
||||
mNC_hundred = 0; \
|
||||
mNCdIGITlENGTH = 2; \
|
||||
} else { \
|
||||
mNCdIGITlENGTH = 3; \
|
||||
} \
|
||||
mCC = (((((tBCDsTRING)->buf[0]) & 0xf0) >> 4) * 10) + \
|
||||
((((tBCDsTRING)->buf[0]) & 0x0f) * 100) + \
|
||||
(((tBCDsTRING)->buf[1]) & 0x0f); \
|
||||
mNC = (mNC_hundred * 100) + \
|
||||
((((tBCDsTRING)->buf[2]) & 0xf0) >> 4) + \
|
||||
((((tBCDsTRING)->buf[2]) & 0x0f) * 10); \
|
||||
} while(0)
|
||||
|
||||
#define TBCD_TO_PLMN_T(tBCDsTRING, pLMN) \
|
||||
do { \
|
||||
DevAssert((tBCDsTRING)->size == 3); \
|
||||
(pLMN)->MCCdigit2 = (((tBCDsTRING)->buf[0] & 0xf0) >> 4); \
|
||||
(pLMN)->MCCdigit3 = ((tBCDsTRING)->buf[0] & 0x0f); \
|
||||
(pLMN)->MCCdigit1 = (tBCDsTRING)->buf[1] & 0x0f; \
|
||||
(pLMN)->MNCdigit3 = (((tBCDsTRING)->buf[1] & 0xf0) >> 4) == 0xF \
|
||||
? 0 : (((tBCDsTRING)->buf[1] & 0xf0) >> 4); \
|
||||
(pLMN)->MNCdigit2 = (((tBCDsTRING)->buf[2] & 0xf0) >> 4); \
|
||||
(pLMN)->MNCdigit1 = ((tBCDsTRING)->buf[2] & 0x0f); \
|
||||
} while(0)
|
||||
|
||||
#define PLMN_T_TO_TBCD(pLMN, tBCDsTRING, mNClENGTH) \
|
||||
do { \
|
||||
tBCDsTRING[0] = (pLMN.MCCdigit2 << 4) | pLMN.MCCdigit1; \
|
||||
/* ambiguous (think about len 2) */ \
|
||||
if (mNClENGTH == 2) { \
|
||||
tBCDsTRING[1] = (0x0F << 4) | pLMN.MCCdigit3; \
|
||||
tBCDsTRING[2] = (pLMN.MNCdigit2 << 4) | pLMN.MNCdigit1; \
|
||||
} else { \
|
||||
tBCDsTRING[1] = (pLMN.MNCdigit3 << 4) | pLMN.MCCdigit3; \
|
||||
tBCDsTRING[2] = (pLMN.MNCdigit2 << 4) | pLMN.MNCdigit1; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define PLMN_T_TO_MCC_MNC(pLMN, mCC, mNC, mNCdIGITlENGTH) \
|
||||
do { \
|
||||
mCC = pLMN.MCCdigit3 * 100 + pLMN.MCCdigit2 * 10 + pLMN.MCCdigit1; \
|
||||
mNCdIGITlENGTH = (pLMN.MNCdigit3 == 0xF ? 2 : 3); \
|
||||
mNC = (mNCdIGITlENGTH == 2 ? 0 : pLMN.MNCdigit3 * 100) \
|
||||
+ pLMN.MNCdigit2 * 10 + pLMN.MNCdigit1; \
|
||||
} while(0)
|
||||
|
||||
/* TS 36.413 v10.9.0 section 9.2.1.37:
|
||||
* Macro eNB ID:
|
||||
* Equal to the 20 leftmost bits of the Cell
|
||||
* Identity IE contained in the E-UTRAN CGI
|
||||
* IE (see subclause 9.2.1.38) of each cell
|
||||
* served by the eNB.
|
||||
*/
|
||||
#define MACRO_ENB_ID_TO_BIT_STRING(mACRO, bITsTRING) \
|
||||
do { \
|
||||
(bITsTRING)->buf = calloc(3, sizeof(uint8_t)); \
|
||||
(bITsTRING)->buf[0] = ((mACRO) >> 12); \
|
||||
(bITsTRING)->buf[1] = (mACRO) >> 4; \
|
||||
(bITsTRING)->buf[2] = ((mACRO) & 0x0f) << 4; \
|
||||
(bITsTRING)->size = 3; \
|
||||
(bITsTRING)->bits_unused = 4; \
|
||||
} while(0)
|
||||
/*
|
||||
/* TS 36.413 v10.9.0 section 9.2.1.38:
|
||||
* E-UTRAN CGI/Cell Identity
|
||||
* The leftmost bits of the Cell
|
||||
* Identity correspond to the eNB
|
||||
* ID (defined in subclause 9.2.1.37).
|
||||
*/
|
||||
#define MACRO_ENB_ID_TO_CELL_IDENTITY(mACRO, cELL_iD, bITsTRING) \
|
||||
do { \
|
||||
(bITsTRING)->buf = calloc(4, sizeof(uint8_t)); \
|
||||
(bITsTRING)->buf[0] = ((mACRO) >> 12); \
|
||||
(bITsTRING)->buf[1] = (mACRO) >> 4; \
|
||||
(bITsTRING)->buf[2] = (((mACRO) & 0x0f) << 4) | ((cELL_iD) >> 4); \
|
||||
(bITsTRING)->buf[3] = ((cELL_iD) & 0x0f) << 4; \
|
||||
(bITsTRING)->size = 4; \
|
||||
(bITsTRING)->bits_unused = 4; \
|
||||
} while(0)
|
||||
|
||||
/* Used to format an uint32_t containing an ipv4 address */
|
||||
#define IPV4_ADDR "%u.%u.%u.%u"
|
||||
#define IPV4_ADDR_FORMAT(aDDRESS) \
|
||||
(uint8_t)((aDDRESS) & 0x000000ff), \
|
||||
(uint8_t)(((aDDRESS) & 0x0000ff00) >> 8 ), \
|
||||
(uint8_t)(((aDDRESS) & 0x00ff0000) >> 16), \
|
||||
(uint8_t)(((aDDRESS) & 0xff000000) >> 24)
|
||||
|
||||
#define IPV4_ADDR_DISPLAY_8(aDDRESS) \
|
||||
(aDDRESS)[0], (aDDRESS)[1], (aDDRESS)[2], (aDDRESS)[3]
|
||||
|
||||
#define TAC_TO_ASN1 INT16_TO_OCTET_STRING
|
||||
#define GTP_TEID_TO_ASN1 INT32_TO_OCTET_STRING
|
||||
#define OCTET_STRING_TO_TAC OCTET_STRING_TO_INT16
|
||||
|
||||
void hexa_to_ascii(uint8_t *from, char *to, size_t length);
|
||||
|
||||
int ascii_to_hex(uint8_t *dst, const char *h);
|
||||
|
||||
#endif /* CONVERSIONS_H_ */
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common_types.h"
|
||||
|
||||
#include "enum_string.h"
|
||||
|
||||
enum_to_string_t rat_to_string[NUMBER_OF_RAT_TYPE] = {
|
||||
{ RAT_WLAN, "WLAN" },
|
||||
{ RAT_VIRTUAL, "VIRUTAL" },
|
||||
{ RAT_UTRAN, "UTRAN" },
|
||||
{ RAT_GERAN, "GERAN" },
|
||||
{ RAT_GAN, "GAN" },
|
||||
{ RAT_HSPA_EVOLUTION, "HSPA_EVOLUTION" },
|
||||
{ RAT_EUTRAN, "E-UTRAN" },
|
||||
{ RAT_CDMA2000_1X, "CDMA2000_1X" },
|
||||
{ RAT_HRPD, "HRPD" },
|
||||
{ RAT_UMB, "UMB" },
|
||||
{ RAT_EHRPD, "EHRPD" },
|
||||
};
|
||||
|
||||
enum_to_string_t network_access_mode_to_string[NAM_MAX] = {
|
||||
{ NAM_PACKET_AND_CIRCUIT, "PACKET AND CIRCUIT" },
|
||||
{ NAM_RESERVED, "RESERVED" },
|
||||
{ NAM_ONLY_PACKET, "ONLY PACKET" },
|
||||
};
|
||||
|
||||
enum_to_string_t all_apn_conf_ind_to_string[ALL_APN_MAX] = {
|
||||
{ ALL_APN_CONFIGURATIONS_INCLUDED, "ALL APN CONFIGURATIONS INCLUDED" },
|
||||
{ MODIFIED_ADDED_APN_CONFIGURATIONS_INCLUDED, "MODIFIED ADDED APN CONFIGURATIONS INCLUDED" },
|
||||
};
|
||||
|
||||
enum_to_string_t pdn_type_to_string[IP_MAX] = {
|
||||
{ IPv4, "IPv4" },
|
||||
{ IPv6, "IPv6" },
|
||||
{ IPv4_AND_v6, "IPv4 and IPv6" },
|
||||
{ IPv4_OR_v6, "IPv4 or IPv6" },
|
||||
};
|
||||
|
||||
static int
|
||||
compare_values(const void *m1, const void *m2)
|
||||
{
|
||||
enum_to_string_t *mi1 = (enum_to_string_t *) m1;
|
||||
enum_to_string_t *mi2 = (enum_to_string_t *) m2;
|
||||
return (mi1->enum_value - mi2->enum_value);
|
||||
}
|
||||
|
||||
char *enum_to_string(int enum_val, enum_to_string_t *string_table, int nb_element)
|
||||
{
|
||||
enum_to_string_t *res;
|
||||
enum_to_string_t temp;
|
||||
|
||||
temp.enum_value = enum_val;
|
||||
res = bsearch(&temp, string_table, nb_element,
|
||||
sizeof(enum_to_string_t), compare_values);
|
||||
|
||||
if (res == NULL) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
return res->enum_value_name;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
#ifndef ENUM_STRING_H_
|
||||
#define ENUM_STRING_H_
|
||||
|
||||
typedef struct {
|
||||
int enum_value;
|
||||
char *enum_value_name;
|
||||
} enum_to_string_t;
|
||||
|
||||
extern enum_to_string_t network_access_mode_to_string[NAM_MAX];
|
||||
extern enum_to_string_t rat_to_string[NUMBER_OF_RAT_TYPE];
|
||||
extern enum_to_string_t pdn_type_to_string[IP_MAX];
|
||||
|
||||
char *enum_to_string(int enum_val, enum_to_string_t *string_table, int nb_element);
|
||||
#define ACCESS_MODE_TO_STRING(vAL) \
|
||||
enum_to_string((int)vAL, network_access_mode_to_string, \
|
||||
sizeof(network_access_mode_to_string) / sizeof(enum_to_string_t))
|
||||
#define PDN_TYPE_TO_STRING(vAL) \
|
||||
enum_to_string((int)vAL, pdn_type_to_string, \
|
||||
sizeof(pdn_type_to_string) / sizeof(enum_to_string_t))
|
||||
|
||||
#endif /* ENUM_STRING_H_ */
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
#if HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "log.h"
|
||||
|
||||
/* mme log */
|
||||
int log_enabled = 0;
|
||||
|
||||
int log_init(const mme_config_t *mme_config_p,
|
||||
log_specific_init_t specific_init)
|
||||
{
|
||||
if (mme_config_p->verbosity_level == 1) {
|
||||
log_enabled = 1;
|
||||
} else if (mme_config_p->verbosity_level == 2) {
|
||||
log_enabled = 1;
|
||||
} else {
|
||||
log_enabled = 0;
|
||||
}
|
||||
|
||||
return specific_init(mme_config_p->verbosity_level);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
#include "mme_config.h"
|
||||
|
||||
#ifndef LOG_H_
|
||||
#define LOG_H_
|
||||
|
||||
/* asn1c debug */
|
||||
extern int asn_debug;
|
||||
extern int asn1_xer_print;
|
||||
extern int fd_g_debug_lvl;
|
||||
|
||||
typedef int (*log_specific_init_t)(int log_level);
|
||||
|
||||
int log_init(const mme_config_t *mme_config,
|
||||
log_specific_init_t specific_init);
|
||||
|
||||
#endif /* LOG_H_ */
|
||||
Executable
+1825
File diff suppressed because it is too large
Load Diff
Executable
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
/*****************************************************************************
|
||||
|
||||
Source mcc_mnc.h
|
||||
|
||||
Version 0.1
|
||||
|
||||
Date {2014/10/02
|
||||
|
||||
Product
|
||||
|
||||
Subsystem
|
||||
|
||||
Author Lionel GAUTHIER
|
||||
|
||||
Description Defines the MCC/MNC list delivered by the ITU
|
||||
|
||||
*****************************************************************************/
|
||||
#ifndef __MCC_MNC_H__
|
||||
#define __MCC_MNC_H__
|
||||
|
||||
|
||||
typedef struct mcc_mnc_list_s {
|
||||
uint16_t mcc;
|
||||
char mnc[4];
|
||||
} mcc_mnc_list_t;
|
||||
|
||||
int find_mnc_length(const char mcc_digit1P,
|
||||
const char mcc_digit2P,
|
||||
const char mcc_digit3P,
|
||||
const char mnc_digit1P,
|
||||
const char mnc_digit2P,
|
||||
const char mnc_digit3P);
|
||||
#endif
|
||||
@@ -0,0 +1,724 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <libconfig.h>
|
||||
|
||||
#include <arpa/inet.h> /* To provide inet_addr */
|
||||
|
||||
#include "intertask_interface.h"
|
||||
#include "assertions.h"
|
||||
#include "msc.h"
|
||||
#include "mme_config.h"
|
||||
#include "spgw_config.h"
|
||||
#include "intertask_interface_conf.h"
|
||||
|
||||
mme_config_t mme_config;
|
||||
|
||||
int mme_config_find_mnc_length(const char mcc_digit1P,
|
||||
const char mcc_digit2P,
|
||||
const char mcc_digit3P,
|
||||
const char mnc_digit1P,
|
||||
const char mnc_digit2P,
|
||||
const char mnc_digit3P)
|
||||
{
|
||||
|
||||
uint16_t mcc = 100*mcc_digit1P + 10*mcc_digit2P + mcc_digit3P;
|
||||
uint16_t mnc3= 100*mnc_digit1P + 10*mnc_digit2P + mnc_digit3P;
|
||||
uint16_t mnc2= 10*mnc_digit1P + mnc_digit2P;
|
||||
int plmn_index = 0;
|
||||
|
||||
AssertFatal((mcc_digit1P >= 0) && (mcc_digit1P <= 9)
|
||||
&& (mcc_digit2P >= 0) && (mcc_digit2P <= 9)
|
||||
&& (mcc_digit3P >= 0) && (mcc_digit3P <= 9) ,
|
||||
"BAD MCC PARAMETER (%d%d%d)!\n",
|
||||
mcc_digit1P, mcc_digit2P, mcc_digit3P);
|
||||
|
||||
AssertFatal((mnc_digit2P >= 0) && (mnc_digit2P <= 9)
|
||||
&& (mnc_digit1P >= 0) && (mnc_digit1P <= 9) ,
|
||||
"BAD MNC PARAMETER (%d.%d.%d)!\n",
|
||||
mnc_digit1P, mnc_digit2P, mnc_digit3P);
|
||||
|
||||
|
||||
while (plmn_index < mme_config.gummei.nb_plmns) {
|
||||
if (mme_config.gummei.plmn_mcc[plmn_index] == mcc) {
|
||||
if ((mme_config.gummei.plmn_mnc[plmn_index] == mnc2) &&
|
||||
(mme_config.gummei.plmn_mnc_len[plmn_index] == 2)) {
|
||||
return 2;
|
||||
} else if ((mme_config.gummei.plmn_mnc[plmn_index] == mnc3) &&
|
||||
(mme_config.gummei.plmn_mnc_len[plmn_index] == 3)) {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
plmn_index += 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void mme_config_init(mme_config_t *mme_config_p)
|
||||
{
|
||||
memset(mme_config_p, 0, sizeof(mme_config_t));
|
||||
|
||||
pthread_rwlock_init(&mme_config_p->rw_lock, NULL);
|
||||
|
||||
mme_config_p->verbosity_level = 0;
|
||||
mme_config_p->config_file = NULL;
|
||||
mme_config_p->max_eNBs = MAX_NUMBER_OF_ENB;
|
||||
mme_config_p->max_ues = MAX_NUMBER_OF_UE;
|
||||
|
||||
mme_config_p->emergency_attach_supported = 0;
|
||||
mme_config_p->unauthenticated_imsi_supported = 0;
|
||||
|
||||
/* Timer configuration */
|
||||
mme_config_p->gtpv1u_config.port_number = GTPV1_U_PORT_NUMBER;
|
||||
mme_config_p->s1ap_config.port_number = S1AP_PORT_NUMBER;
|
||||
/* IP configuration */
|
||||
mme_config_p->ipv4.sgw_ip_address_for_S1u_S12_S4_up = inet_addr(DEFAULT_SGW_IP_ADDRESS_FOR_S1U_S12_S4_UP);
|
||||
|
||||
mme_config_p->ipv4.mme_interface_name_for_S1_MME = DEFAULT_MME_INTERFACE_NAME_FOR_S1_MME;
|
||||
mme_config_p->ipv4.mme_ip_address_for_S1_MME = inet_addr(DEFAULT_MME_IP_ADDRESS_FOR_S1_MME);
|
||||
|
||||
mme_config_p->ipv4.mme_interface_name_for_S11 = DEFAULT_MME_INTERFACE_NAME_FOR_S11;
|
||||
mme_config_p->ipv4.mme_ip_address_for_S11 = inet_addr(DEFAULT_MME_IP_ADDRESS_FOR_S11);
|
||||
|
||||
mme_config_p->ipv4.sgw_ip_address_for_S11 = inet_addr(DEFAULT_SGW_IP_ADDRESS_FOR_S11);
|
||||
|
||||
mme_config_p->s6a_config.conf_file = S6A_CONF_FILE;
|
||||
|
||||
mme_config_p->itti_config.queue_size = ITTI_QUEUE_MAX_ELEMENTS;
|
||||
mme_config_p->itti_config.log_file = NULL;
|
||||
|
||||
mme_config_p->sctp_config.in_streams = SCTP_IN_STREAMS;
|
||||
mme_config_p->sctp_config.out_streams = SCTP_OUT_STREAMS;
|
||||
|
||||
mme_config_p->relative_capacity = RELATIVE_CAPACITY;
|
||||
|
||||
mme_config_p->mme_statistic_timer = MME_STATISTIC_TIMER_S;
|
||||
|
||||
mme_config_p->gummei.nb_mme_gid = 1;
|
||||
mme_config_p->gummei.mme_gid = calloc(1, sizeof(*mme_config_p->gummei.mme_gid));
|
||||
mme_config_p->gummei.mme_gid[0] = MMEGID;
|
||||
mme_config_p->gummei.nb_mmec = 1;
|
||||
mme_config_p->gummei.mmec = calloc(1, sizeof(*mme_config_p->gummei.mmec));
|
||||
mme_config_p->gummei.mmec[0] = MMEC;
|
||||
|
||||
/* Set the TAI */
|
||||
mme_config_p->gummei.nb_plmns = 1;
|
||||
mme_config_p->gummei.plmn_mcc = calloc(1, sizeof(*mme_config_p->gummei.plmn_mcc));
|
||||
mme_config_p->gummei.plmn_mnc = calloc(1, sizeof(*mme_config_p->gummei.plmn_mnc));
|
||||
mme_config_p->gummei.plmn_mnc_len = calloc(1, sizeof(*mme_config_p->gummei.plmn_mnc_len));
|
||||
mme_config_p->gummei.plmn_tac = calloc(1, sizeof(*mme_config_p->gummei.plmn_tac));
|
||||
|
||||
|
||||
mme_config_p->gummei.plmn_mcc[0] = PLMN_MCC;
|
||||
mme_config_p->gummei.plmn_mnc[0] = PLMN_MNC;
|
||||
mme_config_p->gummei.plmn_mnc_len[0] = PLMN_MNC_LEN;
|
||||
mme_config_p->gummei.plmn_tac[0] = PLMN_TAC;
|
||||
|
||||
mme_config_p->s1ap_config.outcome_drop_timer_sec = S1AP_OUTCOME_TIMER_DEFAULT;
|
||||
}
|
||||
|
||||
int mme_system(char *command_pP, int abort_on_errorP)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (command_pP) {
|
||||
fprintf(stdout, "system command: %s\n",command_pP);
|
||||
ret = system(command_pP);
|
||||
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "ERROR in system command %s: %d\n",
|
||||
command_pP,ret);
|
||||
|
||||
if (abort_on_errorP) {
|
||||
exit(-1); // may be not exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int config_parse_file(mme_config_t *mme_config_p)
|
||||
{
|
||||
config_t cfg;
|
||||
config_setting_t *setting_mme = NULL;
|
||||
config_setting_t *setting = NULL;
|
||||
config_setting_t *subsetting = NULL;
|
||||
config_setting_t *sub2setting = NULL;
|
||||
|
||||
long int alongint;
|
||||
int i, num;
|
||||
char *astring = NULL;
|
||||
char *address = NULL;
|
||||
char *cidr = NULL;
|
||||
|
||||
const char* tac = NULL;
|
||||
const char* mcc = NULL;
|
||||
const char* mnc = NULL;
|
||||
|
||||
char *sgw_ip_address_for_S1u_S12_S4_up = NULL;
|
||||
char *mme_interface_name_for_S1_MME = NULL;
|
||||
char *mme_ip_address_for_S1_MME = NULL;
|
||||
char *mme_interface_name_for_S11 = NULL;
|
||||
char *mme_ip_address_for_S11 = NULL;
|
||||
char *sgw_ip_address_for_S11 = NULL;
|
||||
char system_cmd[256];
|
||||
|
||||
config_init(&cfg);
|
||||
|
||||
if(mme_config_p->config_file != NULL) {
|
||||
/* Read the file. If there is an error, report it and exit. */
|
||||
if(! config_read_file(&cfg, mme_config_p->config_file)) {
|
||||
fprintf(stdout, "ERROR: %s:%d - %s\n", mme_config_p->config_file, config_error_line(&cfg), config_error_text(&cfg));
|
||||
config_destroy(&cfg);
|
||||
AssertFatal (1 == 0, "Failed to parse MME configuration file %s!\n", mme_config_p->config_file);
|
||||
}
|
||||
} else {
|
||||
fprintf(stdout, "ERROR No MME configuration file provided!\n");
|
||||
config_destroy(&cfg);
|
||||
AssertFatal (0, "No MME configuration file provided!\n");
|
||||
}
|
||||
|
||||
setting_mme = config_lookup(&cfg, MME_CONFIG_STRING_MME_CONFIG);
|
||||
|
||||
if(setting_mme != NULL) {
|
||||
// GENERAL MME SETTINGS
|
||||
if( (config_setting_lookup_string( setting_mme, MME_CONFIG_STRING_REALM, (const char **)&astring) )) {
|
||||
mme_config_p->realm = strdup(astring);
|
||||
mme_config_p->realm_length = strlen(mme_config_p->realm);
|
||||
}
|
||||
|
||||
if( (config_setting_lookup_int( setting_mme, MME_CONFIG_STRING_MAXENB, &alongint) )) {
|
||||
mme_config_p->max_eNBs = (uint32_t)alongint;
|
||||
}
|
||||
|
||||
if( (config_setting_lookup_int( setting_mme, MME_CONFIG_STRING_MAXUE, &alongint) )) {
|
||||
mme_config_p->max_ues = (uint32_t)alongint;
|
||||
}
|
||||
|
||||
if( (config_setting_lookup_int( setting_mme, MME_CONFIG_STRING_RELATIVE_CAPACITY, &alongint) )) {
|
||||
mme_config_p->relative_capacity = (uint8_t)alongint;
|
||||
}
|
||||
|
||||
if( (config_setting_lookup_int( setting_mme, MME_CONFIG_STRING_STATISTIC_TIMER, &alongint) )) {
|
||||
mme_config_p->mme_statistic_timer = (uint32_t)alongint;
|
||||
}
|
||||
|
||||
if( (config_setting_lookup_string( setting_mme, MME_CONFIG_STRING_EMERGENCY_ATTACH_SUPPORTED, (const char **)&astring) )) {
|
||||
if (strcasecmp(astring , "yes") == 0)
|
||||
mme_config_p->emergency_attach_supported = 1;
|
||||
else
|
||||
mme_config_p->emergency_attach_supported = 0;
|
||||
}
|
||||
|
||||
if( (config_setting_lookup_string( setting_mme, MME_CONFIG_STRING_UNAUTHENTICATED_IMSI_SUPPORTED, (const char **)&astring) )) {
|
||||
if (strcasecmp(astring , "yes") == 0)
|
||||
mme_config_p->unauthenticated_imsi_supported = 1;
|
||||
else
|
||||
mme_config_p->unauthenticated_imsi_supported = 0;
|
||||
}
|
||||
|
||||
if( (config_setting_lookup_string( setting_mme, MME_CONFIG_STRING_ASN1_VERBOSITY, (const char **)&astring) )) {
|
||||
if (strcasecmp(astring , MME_CONFIG_STRING_ASN1_VERBOSITY_NONE) == 0)
|
||||
mme_config_p->verbosity_level = 0;
|
||||
else if (strcasecmp(astring , MME_CONFIG_STRING_ASN1_VERBOSITY_ANNOYING) == 0)
|
||||
mme_config_p->verbosity_level = 2;
|
||||
else if (strcasecmp(astring , MME_CONFIG_STRING_ASN1_VERBOSITY_INFO) == 0)
|
||||
mme_config_p->verbosity_level = 1;
|
||||
else
|
||||
mme_config_p->verbosity_level = 0;
|
||||
}
|
||||
|
||||
// ITTI SETTING
|
||||
setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_INTERTASK_INTERFACE_CONFIG);
|
||||
|
||||
if (setting != NULL) {
|
||||
if( (config_setting_lookup_int( setting, MME_CONFIG_STRING_INTERTASK_INTERFACE_QUEUE_SIZE, &alongint) )) {
|
||||
mme_config_p->itti_config.queue_size = (uint32_t)alongint;
|
||||
}
|
||||
}
|
||||
|
||||
// S6A SETTING
|
||||
setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_S6A_CONFIG);
|
||||
|
||||
if (setting != NULL) {
|
||||
if( (config_setting_lookup_string( setting, MME_CONFIG_STRING_S6A_CONF_FILE_PATH, (const char **)&astring) )) {
|
||||
if (astring != NULL)
|
||||
mme_config_p->s6a_config.conf_file = strdup(astring);
|
||||
}
|
||||
|
||||
if( (config_setting_lookup_string( setting, MME_CONFIG_STRING_S6A_HSS_HOSTNAME, (const char **)&astring) )) {
|
||||
if (astring != NULL)
|
||||
mme_config_p->s6a_config.hss_host_name = strdup(astring);
|
||||
else
|
||||
AssertFatal (1 == 0,
|
||||
"You have to provide a valid HSS hostname %s=...\n",
|
||||
MME_CONFIG_STRING_S6A_HSS_HOSTNAME);
|
||||
}
|
||||
}
|
||||
|
||||
// SCTP SETTING
|
||||
setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_SCTP_CONFIG);
|
||||
|
||||
if (setting != NULL) {
|
||||
if( (config_setting_lookup_int( setting, MME_CONFIG_STRING_SCTP_INSTREAMS, &alongint) )) {
|
||||
mme_config_p->sctp_config.in_streams = (uint16_t)alongint;
|
||||
}
|
||||
|
||||
if( (config_setting_lookup_int( setting, MME_CONFIG_STRING_SCTP_OUTSTREAMS, &alongint) )) {
|
||||
mme_config_p->sctp_config.out_streams = (uint16_t)alongint;
|
||||
}
|
||||
}
|
||||
|
||||
// S1AP SETTING
|
||||
setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_S1AP_CONFIG);
|
||||
|
||||
if (setting != NULL) {
|
||||
if( (config_setting_lookup_int( setting, MME_CONFIG_STRING_S1AP_OUTCOME_TIMER, &alongint) )) {
|
||||
mme_config_p->s1ap_config.outcome_drop_timer_sec = (uint8_t)alongint;
|
||||
}
|
||||
|
||||
if( (config_setting_lookup_int( setting, MME_CONFIG_STRING_S1AP_PORT, &alongint) )) {
|
||||
mme_config_p->s1ap_config.port_number = (uint16_t)alongint;
|
||||
}
|
||||
}
|
||||
|
||||
// GUMMEI SETTING
|
||||
setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_GUMMEI_CONFIG);
|
||||
|
||||
if (setting != NULL) {
|
||||
subsetting = config_setting_get_member (setting, MME_CONFIG_STRING_MME_CODE);
|
||||
|
||||
if (subsetting != NULL) {
|
||||
num = config_setting_length(subsetting);
|
||||
|
||||
if (mme_config_p->gummei.nb_mmec != num) {
|
||||
if (mme_config_p->gummei.mmec != NULL) {
|
||||
free(mme_config_p->gummei.mmec);
|
||||
}
|
||||
|
||||
mme_config_p->gummei.mmec = calloc(num, sizeof(*mme_config_p->gummei.mmec));
|
||||
}
|
||||
|
||||
mme_config_p->gummei.nb_mmec = num;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
mme_config_p->gummei.mmec[i] = config_setting_get_int_elem(subsetting, i);
|
||||
}
|
||||
}
|
||||
|
||||
subsetting = config_setting_get_member (setting, MME_CONFIG_STRING_MME_GID);
|
||||
|
||||
if (subsetting != NULL) {
|
||||
num = config_setting_length(subsetting);
|
||||
|
||||
if (mme_config_p->gummei.nb_mme_gid != num) {
|
||||
if (mme_config_p->gummei.mme_gid != NULL) {
|
||||
free(mme_config_p->gummei.mme_gid);
|
||||
}
|
||||
|
||||
mme_config_p->gummei.mme_gid = calloc(num, sizeof(*mme_config_p->gummei.mme_gid));
|
||||
}
|
||||
|
||||
mme_config_p->gummei.nb_mme_gid = num;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
mme_config_p->gummei.mme_gid[i] = config_setting_get_int_elem(subsetting, i);
|
||||
}
|
||||
}
|
||||
|
||||
subsetting = config_setting_get_member (setting, MME_CONFIG_STRING_TAI_LIST);
|
||||
|
||||
if (subsetting != NULL) {
|
||||
num = config_setting_length(subsetting);
|
||||
|
||||
if (mme_config_p->gummei.nb_plmns != num) {
|
||||
if (mme_config_p->gummei.plmn_mcc != NULL) free(mme_config_p->gummei.plmn_mcc);
|
||||
|
||||
if (mme_config_p->gummei.plmn_mnc != NULL) free(mme_config_p->gummei.plmn_mnc);
|
||||
|
||||
if (mme_config_p->gummei.plmn_mnc_len != NULL) free(mme_config_p->gummei.plmn_mnc_len);
|
||||
|
||||
if (mme_config_p->gummei.plmn_tac != NULL) free(mme_config_p->gummei.plmn_tac);
|
||||
|
||||
mme_config_p->gummei.plmn_mcc = calloc(num, sizeof(*mme_config_p->gummei.plmn_mcc));
|
||||
mme_config_p->gummei.plmn_mnc = calloc(num, sizeof(*mme_config_p->gummei.plmn_mnc));
|
||||
mme_config_p->gummei.plmn_mnc_len = calloc(num, sizeof(*mme_config_p->gummei.plmn_mnc_len));
|
||||
mme_config_p->gummei.plmn_tac = calloc(num, sizeof(*mme_config_p->gummei.plmn_tac));
|
||||
}
|
||||
|
||||
mme_config_p->gummei.nb_plmns = num;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
sub2setting = config_setting_get_elem(subsetting, i);
|
||||
|
||||
if (sub2setting != NULL) {
|
||||
if( (config_setting_lookup_string( sub2setting, MME_CONFIG_STRING_MCC, &mcc) )) {
|
||||
mme_config_p->gummei.plmn_mcc[i] = (uint16_t)atoi(mcc);
|
||||
}
|
||||
|
||||
if( (config_setting_lookup_string( sub2setting, MME_CONFIG_STRING_MNC, &mnc) )) {
|
||||
mme_config_p->gummei.plmn_mnc[i] = (uint16_t)atoi(mnc);
|
||||
mme_config_p->gummei.plmn_mnc_len[i] = strlen(mnc);
|
||||
AssertFatal((mme_config_p->gummei.plmn_mnc_len[i] == 2) || (mme_config_p->gummei.plmn_mnc_len[i] == 3),
|
||||
"Bad MNC length %u, must be 2 or 3", mme_config_p->gummei.plmn_mnc_len[i]);
|
||||
}
|
||||
|
||||
if( (config_setting_lookup_string( sub2setting, MME_CONFIG_STRING_TAC, &tac) )) {
|
||||
mme_config_p->gummei.plmn_tac[i] = (uint16_t)atoi(tac);
|
||||
AssertFatal(mme_config_p->gummei.plmn_tac[i] != 0,
|
||||
"TAC must not be 0");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NETWORK INTERFACE SETTING
|
||||
setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_NETWORK_INTERFACES_CONFIG);
|
||||
|
||||
if(setting != NULL) {
|
||||
if( (
|
||||
config_setting_lookup_string( setting, MME_CONFIG_STRING_INTERFACE_NAME_FOR_S1_MME,
|
||||
(const char **)&mme_interface_name_for_S1_MME)
|
||||
&& config_setting_lookup_string( setting, MME_CONFIG_STRING_IPV4_ADDRESS_FOR_S1_MME,
|
||||
(const char **)&mme_ip_address_for_S1_MME)
|
||||
&& config_setting_lookup_string( setting, MME_CONFIG_STRING_INTERFACE_NAME_FOR_S11_MME,
|
||||
(const char **)&mme_interface_name_for_S11)
|
||||
&& config_setting_lookup_string( setting, MME_CONFIG_STRING_IPV4_ADDRESS_FOR_S11_MME,
|
||||
(const char **)&mme_ip_address_for_S11)
|
||||
)
|
||||
) {
|
||||
mme_config_p->ipv4.mme_interface_name_for_S1_MME = strdup(mme_interface_name_for_S1_MME);
|
||||
cidr = strdup(mme_ip_address_for_S1_MME);
|
||||
address = strtok(cidr, "/");
|
||||
IPV4_STR_ADDR_TO_INT_NWBO ( address, mme_config_p->ipv4.mme_ip_address_for_S1_MME, "BAD IP ADDRESS FORMAT FOR MME S1_MME !\n" )
|
||||
free(cidr);
|
||||
|
||||
mme_config_p->ipv4.mme_interface_name_for_S11 = strdup(mme_interface_name_for_S11);
|
||||
cidr = strdup(mme_ip_address_for_S11);
|
||||
address = strtok(cidr, "/");
|
||||
IPV4_STR_ADDR_TO_INT_NWBO ( address, mme_config_p->ipv4.mme_ip_address_for_S11, "BAD IP ADDRESS FORMAT FOR MME S11 !\n" )
|
||||
free(cidr);
|
||||
|
||||
if (strncasecmp("tun",mme_config_p->ipv4.mme_interface_name_for_S1_MME, strlen("tun")) == 0) {
|
||||
if (snprintf(system_cmd, 256,
|
||||
"ip link set %s down ;openvpn --rmtun --dev %s",
|
||||
mme_config_p->ipv4.mme_interface_name_for_S1_MME,
|
||||
mme_config_p->ipv4.mme_interface_name_for_S1_MME
|
||||
) > 0) {
|
||||
mme_system(system_cmd, 1);
|
||||
} else {
|
||||
fprintf(stderr, "Del %s\n", mme_config_p->ipv4.mme_interface_name_for_S1_MME);
|
||||
}
|
||||
|
||||
if (snprintf(system_cmd, 256,
|
||||
"openvpn --mktun --dev %s;sync;ifconfig %s up;sync",
|
||||
mme_config_p->ipv4.mme_interface_name_for_S1_MME,
|
||||
mme_config_p->ipv4.mme_interface_name_for_S1_MME) > 0) {
|
||||
mme_system(system_cmd, 1);
|
||||
} else {
|
||||
fprintf(stderr, "Create %s\n", mme_config_p->ipv4.mme_interface_name_for_S1_MME);
|
||||
}
|
||||
|
||||
if (snprintf(system_cmd, 256,
|
||||
"ip -4 addr add %s dev %s",
|
||||
mme_ip_address_for_S1_MME,
|
||||
mme_config_p->ipv4.mme_interface_name_for_S1_MME) > 0) {
|
||||
mme_system(system_cmd, 1);
|
||||
} else {
|
||||
fprintf(stderr, "Set IPv4 address on %s\n", mme_config_p->ipv4.mme_interface_name_for_S1_MME);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// NAS SETTING
|
||||
setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_NAS_CONFIG);
|
||||
|
||||
if (setting != NULL) {
|
||||
subsetting = config_setting_get_member (setting, MME_CONFIG_STRING_NAS_SUPPORTED_INTEGRITY_ALGORITHM_LIST);
|
||||
|
||||
if (subsetting != NULL) {
|
||||
num = config_setting_length(subsetting);
|
||||
|
||||
if (num <= 8) {
|
||||
for (i = 0; i < num; i++) {
|
||||
astring = config_setting_get_string_elem(subsetting, i);
|
||||
|
||||
if (strcmp("EIA0", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
|
||||
else if (strcmp("EIA1", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA1;
|
||||
else if (strcmp("EIA2", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA2;
|
||||
else if (strcmp("EIA3", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
|
||||
else if (strcmp("EIA4", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
|
||||
else if (strcmp("EIA5", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
|
||||
else if (strcmp("EIA6", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
|
||||
else if (strcmp("EIA7", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
|
||||
}
|
||||
|
||||
for (i = num; i < 8; i++) {
|
||||
mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subsetting = config_setting_get_member (setting, MME_CONFIG_STRING_NAS_SUPPORTED_CIPHERING_ALGORITHM_LIST);
|
||||
|
||||
if (subsetting != NULL) {
|
||||
num = config_setting_length(subsetting);
|
||||
|
||||
if (num <= 8) {
|
||||
for (i = 0; i < num; i++) {
|
||||
astring = config_setting_get_string_elem(subsetting, i);
|
||||
|
||||
if (strcmp("EEA0", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
|
||||
else if (strcmp("EEA1", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA1;
|
||||
else if (strcmp("EEA2", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA2;
|
||||
else if (strcmp("EEA3", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
|
||||
else if (strcmp("EEA4", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
|
||||
else if (strcmp("EEA5", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
|
||||
else if (strcmp("EEA6", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
|
||||
else if (strcmp("EEA7", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
|
||||
}
|
||||
|
||||
for (i = num; i < 8; i++) {
|
||||
mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
setting = config_lookup(&cfg, SGW_CONFIG_STRING_SGW_CONFIG);
|
||||
|
||||
if(setting != NULL) {
|
||||
subsetting = config_setting_get_member (setting, SGW_CONFIG_STRING_NETWORK_INTERFACES_CONFIG);
|
||||
|
||||
if(subsetting != NULL) {
|
||||
if( (
|
||||
config_setting_lookup_string( subsetting, SGW_CONFIG_STRING_SGW_IPV4_ADDRESS_FOR_S1U_S12_S4_UP,
|
||||
(const char **)&sgw_ip_address_for_S1u_S12_S4_up)
|
||||
&& config_setting_lookup_string( subsetting, SGW_CONFIG_STRING_SGW_IPV4_ADDRESS_FOR_S11,
|
||||
(const char **)&sgw_ip_address_for_S11)
|
||||
&& config_setting_lookup_int( subsetting, SGW_CONFIG_STRING_SGW_PORT_FOR_S1U_S12_S4_UP, &alongint)
|
||||
)
|
||||
) {
|
||||
cidr = strdup(sgw_ip_address_for_S1u_S12_S4_up);
|
||||
address = strtok(cidr, "/");
|
||||
IPV4_STR_ADDR_TO_INT_NWBO ( address, mme_config_p->ipv4.sgw_ip_address_for_S1u_S12_S4_up, "BAD IP ADDRESS FORMAT FOR SGW S1u_S12_S4 !\n" )
|
||||
free(cidr);
|
||||
|
||||
cidr = strdup(sgw_ip_address_for_S11);
|
||||
address = strtok(cidr, "/");
|
||||
IPV4_STR_ADDR_TO_INT_NWBO ( address, mme_config_p->ipv4.sgw_ip_address_for_S11, "BAD IP ADDRESS FORMAT FOR SGW S11 !\n" )
|
||||
free(cidr);
|
||||
|
||||
mme_config_p->gtpv1u_config.port_number = (uint16_t) alongint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DISPLAY_ARRAY(size, format, args...) \
|
||||
do { \
|
||||
int i; \
|
||||
for (i = 0; i < size; i++) { \
|
||||
fprintf(stdout, format, args); \
|
||||
if ((i != (size - 1)) && ((i + 1) % 10 == 0)) \
|
||||
{ \
|
||||
fprintf(stdout, "\n "); \
|
||||
} \
|
||||
} \
|
||||
if (i > 0) \
|
||||
fprintf(stdout, "\n"); \
|
||||
} while(0)
|
||||
|
||||
static void config_display(mme_config_t *mme_config_p)
|
||||
{
|
||||
int j;
|
||||
|
||||
fprintf(stdout, "==== EURECOM %s v%s ====\n", PACKAGE_NAME, PACKAGE_VERSION);
|
||||
fprintf(stdout, "Configuration:\n");
|
||||
fprintf(stdout, "- File ...............: %s\n", mme_config_p->config_file);
|
||||
fprintf(stdout, "- Verbosity level ....: %d\n", mme_config_p->verbosity_level);
|
||||
fprintf(stdout, "- Realm ..............: %s\n", mme_config_p->realm);
|
||||
fprintf(stdout, "- Max eNBs ...........: %u\n", mme_config_p->max_eNBs);
|
||||
fprintf(stdout, "- Max UEs ............: %u\n", mme_config_p->max_ues);
|
||||
fprintf(stdout, "- Emergency support ..: %s\n", mme_config_p->emergency_attach_supported == 0 ? "FALSE" : "TRUE");
|
||||
fprintf(stdout, "- Unauth IMSI support : %s\n", mme_config_p->unauthenticated_imsi_supported == 0 ? "FALSE" : "TRUE");
|
||||
fprintf(stdout, "- Relative capa ......: %u\n\n", mme_config_p->relative_capacity);
|
||||
fprintf(stdout, "- Statistics timer ...: %u (seconds)\n\n", mme_config_p->mme_statistic_timer);
|
||||
fprintf(stdout, "- S1-U:\n");
|
||||
fprintf(stdout, " port number ......: %d\n", mme_config_p->gtpv1u_config.port_number);
|
||||
fprintf(stdout, "- S1-MME:\n");
|
||||
fprintf(stdout, " port number ......: %d\n", mme_config_p->s1ap_config.port_number);
|
||||
fprintf(stdout, "- IP:\n");
|
||||
//fprintf(stdout, " s1-u iface .......: %s\n", mme_config_p->ipv4.sgw_interface_name_for_S1u_S12_S4_up);
|
||||
fprintf(stdout, " s1-u ip ..........: %s\n",
|
||||
inet_ntoa(*((struct in_addr *)&mme_config_p->ipv4.sgw_ip_address_for_S1u_S12_S4_up)));
|
||||
//fprintf(stdout, " sgi iface ........: %s\n", mme_config_p->ipv4.pgw_interface_name_for_SGI);
|
||||
//fprintf(stdout, " sgi ip ...........: %s/%d\n",
|
||||
// inet_ntoa(*((struct in_addr *)&mme_config_p->ipv4.pgw_ip_addr_for_SGI)),
|
||||
// mme_config_p->ipv4.pgw_ip_netmask_for_SGI);
|
||||
fprintf(stdout, " s1-MME iface .....: %s\n", mme_config_p->ipv4.mme_interface_name_for_S1_MME);
|
||||
fprintf(stdout, " s1-MME ip ........: %s\n",
|
||||
inet_ntoa(*((struct in_addr *)&mme_config_p->ipv4.mme_ip_address_for_S1_MME)));
|
||||
//fprintf(stdout, " s11 S-GW iface ...: %s\n", mme_config_p->ipv4.sgw_interface_name_for_S11);
|
||||
//fprintf(stdout, " s11 S-GW ip ......: %s/%d\n",
|
||||
// inet_ntoa(*((struct in_addr *)&mme_config_p->ipv4.sgw_ip_address_for_S11)),
|
||||
// mme_config_p->ipv4.sgw_ip_netmask_for_S11);
|
||||
fprintf(stdout, " s11 MME iface ....: %s\n", mme_config_p->ipv4.mme_interface_name_for_S11);
|
||||
fprintf(stdout, " s11 S-GW ip ......: %s\n",
|
||||
inet_ntoa(*((struct in_addr *)&mme_config_p->ipv4.mme_ip_address_for_S11)));
|
||||
fprintf(stdout, "- ITTI:\n");
|
||||
fprintf(stdout, " queue size .......: %u (bytes)\n", mme_config_p->itti_config.queue_size);
|
||||
fprintf(stdout, " log file .........: %s\n", mme_config_p->itti_config.log_file);
|
||||
fprintf(stdout, "- SCTP:\n");
|
||||
fprintf(stdout, " in streams .......: %u\n", mme_config_p->sctp_config.in_streams);
|
||||
fprintf(stdout, " out streams ......: %u\n", mme_config_p->sctp_config.out_streams);
|
||||
fprintf(stdout, "- GUMMEI:\n");
|
||||
fprintf(stdout, " mme group ids ....:\n ");
|
||||
DISPLAY_ARRAY(mme_config_p->gummei.nb_mme_gid, "| %u ", mme_config_p->gummei.mme_gid[i]);
|
||||
fprintf(stdout, " mme codes ........:\n ");
|
||||
DISPLAY_ARRAY(mme_config_p->gummei.nb_mmec, "| %u ", mme_config_p->gummei.mmec[i]);
|
||||
fprintf(stdout, " plmns ............: (mcc.mnc:tac)\n");
|
||||
|
||||
for (j= 0; j < mme_config_p->gummei.nb_plmns; j++) {
|
||||
if (mme_config_p->gummei.plmn_mnc_len[j] ==2 ) {
|
||||
fprintf(stdout, " %3u.%3u:%u\n",
|
||||
mme_config_p->gummei.plmn_mcc[j],
|
||||
mme_config_p->gummei.plmn_mnc[j],
|
||||
mme_config_p->gummei.plmn_tac[j]);
|
||||
} else {
|
||||
fprintf(stdout, " %3u.%03u:%u\n",
|
||||
mme_config_p->gummei.plmn_mcc[j],
|
||||
mme_config_p->gummei.plmn_mnc[j],
|
||||
mme_config_p->gummei.plmn_tac[j]);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stdout, "- S6A:\n");
|
||||
fprintf(stdout, " conf file ........: %s\n", mme_config_p->s6a_config.conf_file);
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stdout, "==== EURECOM %s v%s ====\n", PACKAGE_NAME, PACKAGE_VERSION);
|
||||
fprintf(stdout, "Please report any bug to: %s\n\n", PACKAGE_BUGREPORT);
|
||||
fprintf(stdout, "Usage: oaisim_mme [options]\n\n");
|
||||
fprintf(stdout, "Available options:\n");
|
||||
fprintf(stdout, "-h Print this help and return\n");
|
||||
fprintf(stdout, "-c<path>\n");
|
||||
fprintf(stdout, " Set the configuration file for mme\n");
|
||||
fprintf(stdout, " See template in UTILS/CONF\n");
|
||||
fprintf(stdout, "-K<file>\n");
|
||||
fprintf(stdout, " Output intertask messages to provided file\n");
|
||||
fprintf(stdout, "-V Print %s version and return\n", PACKAGE_NAME);
|
||||
fprintf(stdout, "-v[1-2] Debug level:\n");
|
||||
fprintf(stdout, " 1 -> ASN1 XER printf on and ASN1 debug off\n");
|
||||
fprintf(stdout, " 2 -> ASN1 XER printf on and ASN1 debug on\n");
|
||||
}
|
||||
|
||||
extern void
|
||||
nwGtpv1uDisplayBanner(void);
|
||||
|
||||
int config_parse_opt_line(int argc, char *argv[], mme_config_t *mme_config_p)
|
||||
{
|
||||
int c;
|
||||
mme_config_init(mme_config_p);
|
||||
|
||||
/* Parsing command line */
|
||||
while ((c = getopt (argc, argv, "O:c:hi:K:v:V")) != -1) {
|
||||
switch (c) {
|
||||
case 'O':
|
||||
case 'c': {
|
||||
/* Store the given configuration file. If no file is given,
|
||||
* then the default values will be used.
|
||||
*/
|
||||
int config_file_len = 0;
|
||||
config_file_len = strlen(optarg);
|
||||
mme_config_p->config_file = malloc(sizeof(char) * (config_file_len + 1));
|
||||
memcpy(mme_config_p->config_file, optarg, config_file_len);
|
||||
mme_config_p->config_file[config_file_len] = '\0';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'v': {
|
||||
mme_config_p->verbosity_level = atoi(optarg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'V': {
|
||||
fprintf(stdout, "==== EURECOM %s v%s ====\n"
|
||||
"Please report any bug to: %s\n", PACKAGE_NAME, PACKAGE_VERSION,
|
||||
PACKAGE_BUGREPORT);
|
||||
exit(0);
|
||||
nwGtpv1uDisplayBanner();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'K':
|
||||
mme_config_p->itti_config.log_file = strdup(optarg);
|
||||
break;
|
||||
|
||||
case 'h': /* Fall through */
|
||||
default:
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the configuration file using libconfig */
|
||||
if (config_parse_file(mme_config_p) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Display the configuration */
|
||||
config_display(mme_config_p);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "mme_default_values.h"
|
||||
|
||||
#ifndef MME_CONFIG_H_
|
||||
#define MME_CONFIG_H_
|
||||
|
||||
#define MME_CONFIG_STRING_MME_CONFIG "MME"
|
||||
#define MME_CONFIG_STRING_REALM "REALM"
|
||||
#define MME_CONFIG_STRING_MAXENB "MAXENB"
|
||||
#define MME_CONFIG_STRING_MAXUE "MAXUE"
|
||||
#define MME_CONFIG_STRING_RELATIVE_CAPACITY "RELATIVE_CAPACITY"
|
||||
#define MME_CONFIG_STRING_STATISTIC_TIMER "MME_STATISTIC_TIMER"
|
||||
#define MME_CONFIG_STRING_EMERGENCY_ATTACH_SUPPORTED "EMERGENCY_ATTACH_SUPPORTED"
|
||||
#define MME_CONFIG_STRING_UNAUTHENTICATED_IMSI_SUPPORTED "UNAUTHENTICATED_IMSI_SUPPORTED"
|
||||
|
||||
#define MME_CONFIG_STRING_INTERTASK_INTERFACE_CONFIG "INTERTASK_INTERFACE"
|
||||
#define MME_CONFIG_STRING_INTERTASK_INTERFACE_QUEUE_SIZE "ITTI_QUEUE_SIZE"
|
||||
|
||||
#define MME_CONFIG_STRING_S6A_CONFIG "S6A"
|
||||
#define MME_CONFIG_STRING_S6A_CONF_FILE_PATH "S6A_CONF"
|
||||
#define MME_CONFIG_STRING_S6A_HSS_HOSTNAME "HSS_HOSTNAME"
|
||||
|
||||
#define MME_CONFIG_STRING_SCTP_CONFIG "SCTP"
|
||||
#define MME_CONFIG_STRING_SCTP_INSTREAMS "SCTP_INSTREAMS"
|
||||
#define MME_CONFIG_STRING_SCTP_OUTSTREAMS "SCTP_OUTSTREAMS"
|
||||
|
||||
|
||||
#define MME_CONFIG_STRING_S1AP_CONFIG "S1AP"
|
||||
#define MME_CONFIG_STRING_S1AP_OUTCOME_TIMER "S1AP_OUTCOME_TIMER"
|
||||
#define MME_CONFIG_STRING_S1AP_PORT "S1AP_PORT"
|
||||
|
||||
#define MME_CONFIG_STRING_GUMMEI_CONFIG "GUMMEI"
|
||||
#define MME_CONFIG_STRING_MME_CODE "MME_CODE"
|
||||
#define MME_CONFIG_STRING_MME_GID "MME_GID"
|
||||
#define MME_CONFIG_STRING_TAI_LIST "TAI_LIST"
|
||||
#define MME_CONFIG_STRING_MCC "MCC"
|
||||
#define MME_CONFIG_STRING_MNC "MNC"
|
||||
#define MME_CONFIG_STRING_TAC "TAC"
|
||||
|
||||
#define MME_CONFIG_STRING_NETWORK_INTERFACES_CONFIG "NETWORK_INTERFACES"
|
||||
#define MME_CONFIG_STRING_INTERFACE_NAME_FOR_S1_MME "MME_INTERFACE_NAME_FOR_S1_MME"
|
||||
#define MME_CONFIG_STRING_IPV4_ADDRESS_FOR_S1_MME "MME_IPV4_ADDRESS_FOR_S1_MME"
|
||||
#define MME_CONFIG_STRING_INTERFACE_NAME_FOR_S11_MME "MME_INTERFACE_NAME_FOR_S11_MME"
|
||||
#define MME_CONFIG_STRING_IPV4_ADDRESS_FOR_S11_MME "MME_IPV4_ADDRESS_FOR_S11_MME"
|
||||
|
||||
#define MME_CONFIG_STRING_ASN1_VERBOSITY "VERBOSITY"
|
||||
#define MME_CONFIG_STRING_ASN1_VERBOSITY_NONE "none"
|
||||
#define MME_CONFIG_STRING_ASN1_VERBOSITY_ANNOYING "annoying"
|
||||
#define MME_CONFIG_STRING_ASN1_VERBOSITY_INFO "info"
|
||||
|
||||
#define MME_CONFIG_STRING_NAS_CONFIG "NAS"
|
||||
#define MME_CONFIG_STRING_NAS_SUPPORTED_INTEGRITY_ALGORITHM_LIST "ORDERED_SUPPORTED_INTEGRITY_ALGORITHM_LIST"
|
||||
#define MME_CONFIG_STRING_NAS_SUPPORTED_CIPHERING_ALGORITHM_LIST "ORDERED_SUPPORTED_CIPHERING_ALGORITHM_LIST"
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EEA0 0b000
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EEA1 0b001
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EEA2 0b010
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EEA3 0b011
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EEA4 0b100
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EEA5 0b101
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EEA6 0b110
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EEA7 0b111
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EIA0 0b000
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EIA1 0b001
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EIA2 0b010
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EIA3 0b011
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EIA4 0b100
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EIA5 0b101
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EIA6 0b110
|
||||
#define NAS_CONFIG_SECURITY_ALGORITHMS_EIA7 0b111
|
||||
|
||||
|
||||
|
||||
typedef struct mme_config_s {
|
||||
/* Reader/writer lock for this configuration */
|
||||
pthread_rwlock_t rw_lock;
|
||||
|
||||
uint8_t verbosity_level;
|
||||
|
||||
char *config_file;
|
||||
char *realm;
|
||||
int realm_length;
|
||||
|
||||
uint32_t max_eNBs;
|
||||
uint32_t max_ues;
|
||||
|
||||
uint8_t relative_capacity;
|
||||
|
||||
uint32_t mme_statistic_timer;
|
||||
|
||||
uint8_t emergency_attach_supported;
|
||||
uint8_t unauthenticated_imsi_supported;
|
||||
|
||||
struct {
|
||||
uint16_t nb_mme_gid;
|
||||
uint16_t *mme_gid;
|
||||
|
||||
uint16_t nb_mmec;
|
||||
uint8_t *mmec;
|
||||
|
||||
uint8_t nb_plmns;
|
||||
uint16_t *plmn_mcc;
|
||||
uint16_t *plmn_mnc;
|
||||
uint16_t *plmn_mnc_len;
|
||||
uint16_t *plmn_tac;
|
||||
} gummei;
|
||||
|
||||
struct {
|
||||
uint16_t in_streams;
|
||||
uint16_t out_streams;
|
||||
} sctp_config;
|
||||
struct {
|
||||
uint16_t port_number;
|
||||
} gtpv1u_config;
|
||||
struct {
|
||||
uint16_t port_number;
|
||||
uint8_t outcome_drop_timer_sec;
|
||||
} s1ap_config;
|
||||
struct {
|
||||
uint32_t sgw_ip_address_for_S1u_S12_S4_up;
|
||||
|
||||
char *mme_interface_name_for_S1_MME;
|
||||
uint32_t mme_ip_address_for_S1_MME;
|
||||
|
||||
char *mme_interface_name_for_S11;
|
||||
uint32_t mme_ip_address_for_S11;
|
||||
|
||||
uint32_t sgw_ip_address_for_S11;
|
||||
} ipv4;
|
||||
struct {
|
||||
char *conf_file;
|
||||
char *hss_host_name;
|
||||
} s6a_config;
|
||||
struct {
|
||||
uint32_t queue_size;
|
||||
char *log_file;
|
||||
} itti_config;
|
||||
|
||||
struct {
|
||||
uint8_t prefered_integrity_algorithm[8];
|
||||
uint8_t prefered_ciphering_algorithm[8];
|
||||
} nas_config;
|
||||
|
||||
|
||||
|
||||
} mme_config_t;
|
||||
|
||||
extern mme_config_t mme_config;
|
||||
|
||||
int mme_config_find_mnc_length(const char mcc_digit1P,
|
||||
const char mcc_digit2P,
|
||||
const char mcc_digit3P,
|
||||
const char mnc_digit1P,
|
||||
const char mnc_digit2P,
|
||||
const char mnc_digit3P);
|
||||
int config_parse_opt_line(int argc, char *argv[], mme_config_t *mme_config);
|
||||
|
||||
#define config_read_lock(mMEcONFIG) pthread_rwlock_rdlock(&(mMEcONFIG)->rw_lock)
|
||||
#define config_write_lock(mMEcONFIG) pthread_rwlock_wrlock(&(mMEcONFIG)->rw_lock)
|
||||
#define config_unlock(mMEcONFIG) pthread_rwlock_unlock(&(mMEcONFIG)->rw_lock)
|
||||
|
||||
//int yyparse(struct mme_config_s *mme_config_p);
|
||||
|
||||
#endif /* MME_CONFIG_H_ */
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2015, EURECOM (www.eurecom.fr)
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those
|
||||
* of the authors and should not be interpreted as representing official policies,
|
||||
* either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
#ifndef MME_DEFAULT_VALUES_H_
|
||||
#define MME_DEFAULT_VALUES_H_
|
||||
|
||||
/*******************************************************************************
|
||||
* Timer Constants
|
||||
******************************************************************************/
|
||||
#define MME_STATISTIC_TIMER_S (60)
|
||||
|
||||
/*******************************************************************************
|
||||
* GTPV1 User Plane Constants
|
||||
******************************************************************************/
|
||||
#define GTPV1_U_PORT_NUMBER (2152)
|
||||
|
||||
/*******************************************************************************
|
||||
* S1AP Constants
|
||||
******************************************************************************/
|
||||
|
||||
#define S1AP_PORT_NUMBER (36412) ///< S1AP SCTP IANA ASSIGNED Port Number
|
||||
#define S1AP_SCTP_PPID (18) ///< S1AP SCTP Payload Protocol Identifier (PPID)
|
||||
|
||||
#define S1AP_OUTCOME_TIMER_DEFAULT (5) ///< S1AP Outcome drop timer (s)
|
||||
|
||||
/*******************************************************************************
|
||||
* S6A Constants
|
||||
******************************************************************************/
|
||||
|
||||
#define S6A_CONF_FILE "../S6A/freediameter/s6a.conf"
|
||||
|
||||
/*******************************************************************************
|
||||
* SCTP Constants
|
||||
******************************************************************************/
|
||||
|
||||
#define SCTP_RECV_BUFFER_SIZE (1 << 16)
|
||||
#define SCTP_OUT_STREAMS (32)
|
||||
#define SCTP_IN_STREAMS (32)
|
||||
#define SCTP_MAX_ATTEMPTS (5)
|
||||
|
||||
/*******************************************************************************
|
||||
* MME global definitions
|
||||
******************************************************************************/
|
||||
|
||||
#define MAX_NUMBER_OF_ENB (2)
|
||||
#define MAX_NUMBER_OF_UE (64)
|
||||
|
||||
#define MMEC (0)
|
||||
#define MMEGID (0)
|
||||
#define PLMN_MCC (208)
|
||||
#define PLMN_MNC (34)
|
||||
#define PLMN_MNC_LEN (2)
|
||||
#define PLMN_TAC (0)
|
||||
|
||||
#define RELATIVE_CAPACITY (15)
|
||||
|
||||
/*******************************************************************************
|
||||
* IPv4 Constants
|
||||
******************************************************************************/
|
||||
|
||||
/* Default network card to use for IPV4 packets forwarding.
|
||||
* up stands for user-plane.
|
||||
* cp stands for control-plane
|
||||
*
|
||||
* +-----------+ +------+ +-----------+
|
||||
* | eNB +------+ | ovs | VLAN 1+------+ MME |
|
||||
* | |cpenb0+------------------+cpmme0| |
|
||||
* | +------+ |bridge| +------+ |
|
||||
* | |upenb0+-------+ | | |
|
||||
* +-----------+------+ | | | +-+------+--+
|
||||
* +---|--+ |s11mme|
|
||||
* | +---+--+
|
||||
* | VLAN3 | (optional)
|
||||
* | +---+--+
|
||||
* | |s11sgw|
|
||||
* | +-+------+--+
|
||||
* | | S+P-GW |
|
||||
* | VLAN2 +------+ +--------+
|
||||
* +----------+upsgw0| |pgwsgi0 +
|
||||
* +------+ +--------+
|
||||
* | |
|
||||
* +-----------+
|
||||
*/
|
||||
|
||||
#define DEFAULT_SGW_INTERFACE_NAME_FOR_S11 ("s11sgw")
|
||||
#define DEFAULT_SGW_IP_ADDRESS_FOR_S11 ("192.168.10.1")
|
||||
#define DEFAULT_SGW_IP_NETMASK_FOR_S11 24
|
||||
|
||||
#define DEFAULT_SGW_INTERFACE_NAME_FOR_S1U_S12_S4_UP ("upsgw0")
|
||||
#define DEFAULT_SGW_IP_ADDRESS_FOR_S1U_S12_S4_UP ("192.168.1.1")
|
||||
#define DEFAULT_SGW_IP_NETMASK_FOR_S1U_S12_S4_UP 24
|
||||
|
||||
#define DEFAULT_SGW_INTERFACE_NAME_FOR_S5_S8_UP ("upsgw1")
|
||||
#define DEFAULT_SGW_IP_ADDRESS_FOR_S5_S8_UP ("192.168.5.2")
|
||||
#define DEFAULT_SGW_IP_NETMASK_FOR_S5_S8_UP 24
|
||||
|
||||
#define DEFAULT_PGW_INTERFACE_NAME_FOR_S5_S8 ("uppgw0")
|
||||
#define DEFAULT_PGW_IP_ADDRESS_FOR_S5_S8 ("192.168.5.1")
|
||||
#define DEFAULT_PGW_IP_NETMASK_FOR_S5_S8 24
|
||||
|
||||
#define DEFAULT_PGW_INTERFACE_NAME_FOR_SGI ("eth0")
|
||||
#define DEFAULT_PGW_IP_ADDR_FOR_SGI ("192.168.14.17")
|
||||
#define DEFAULT_PGW_IP_NETMASK_FOR_SGI 24
|
||||
|
||||
#define DEFAULT_MME_INTERFACE_NAME_FOR_S11 ("s11mme") ///< MME control plane interface
|
||||
#define DEFAULT_MME_IP_ADDRESS_FOR_S11 ("192.168.10.2") ///< MME control plane IP address
|
||||
#define DEFAULT_MME_IP_NETMASK_FOR_S11 24;
|
||||
|
||||
#define DEFAULT_MME_INTERFACE_NAME_FOR_S1_MME ("cpmme0") ///< MME control plane interface
|
||||
#define DEFAULT_MME_IP_ADDRESS_FOR_S1_MME ("192.168.11.1") ///< MME control plane IP address
|
||||
#define DEFAULT_MME_IP_NETMASK_FOR_S1_MME 24;
|
||||
|
||||
|
||||
#endif /* MME_DEFAULT_VALUES_H_ */
|
||||
@@ -0,0 +1,570 @@
|
||||
/* For development only : */
|
||||
%debug
|
||||
%error-verbose
|
||||
|
||||
%parse-param {struct mme_config_s *mme_config_p}
|
||||
|
||||
/* Keep track of location */
|
||||
%locations
|
||||
%defines
|
||||
%pure-parser
|
||||
|
||||
%{
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "mme_config.h"
|
||||
#include "mme_parser.h"
|
||||
|
||||
void yyerror (YYLTYPE *llocp, struct mme_config_s *mme_config_p, const char *s);
|
||||
|
||||
extern int yywrap();
|
||||
extern int yylex();
|
||||
|
||||
/* The Lex parser prototype */
|
||||
int fddlex(YYSTYPE *lvalp, YYLTYPE *llocp);
|
||||
%}
|
||||
|
||||
/* Values returned by lex for token */
|
||||
%union {
|
||||
char *string; /* The string is allocated by strdup in lex.*/
|
||||
int integer; /* Store integer values */
|
||||
}
|
||||
|
||||
%token LEX_ERROR
|
||||
|
||||
%token <string> QSTRING
|
||||
%token <integer> INTEGER
|
||||
|
||||
%token EMERGENCY_ATTACH_SUPPORTED
|
||||
%token UNAUTHENTICATED_IMSI_SUPPORTED
|
||||
%token S6A_CONF
|
||||
%token MAX_UE
|
||||
%token MAX_ENB
|
||||
%token ITTI_QUEUE_SIZE
|
||||
%token SCTP_INSTREAMS
|
||||
%token SCTP_OUTSTREAMS
|
||||
%token MME_CODE
|
||||
%token MME_GID
|
||||
%token PLMN_Y
|
||||
%token REALM
|
||||
%token RELATIVE_CAP
|
||||
%token S1AP_OUTCOME_TIMER
|
||||
%token MME_STATISTIC_TIMER
|
||||
%token SGW_INTERFACE_NAME_FOR_S11
|
||||
%token SGW_IP_ADDRESS_FOR_S11
|
||||
%token SGW_IP_NETMASK_FOR_S11
|
||||
%token SGW_INTERFACE_NAME_FOR_S1U_S12_S4_UP
|
||||
%token SGW_IP_ADDRESS_FOR_S1U_S12_S4_UP
|
||||
%token SGW_IP_NETMASK_FOR_S1U_S12_S4_UP
|
||||
%token SGW_INTERFACE_NAME_FOR_S5_S8_UP
|
||||
%token SGW_IP_ADDRESS_FOR_S5_S8_UP
|
||||
%token SGW_IP_NETMASK_FOR_S5_S8_UP
|
||||
%token PGW_INTERFACE_NAME_FOR_S5_S8
|
||||
%token PGW_IP_ADDRESS_FOR_S5_S8
|
||||
%token PGW_IP_NETMASK_FOR_S5_S8
|
||||
%token PGW_INTERFACE_NAME_FOR_SGI
|
||||
%token PGW_IP_ADDR_FOR_SGI
|
||||
%token PGW_IP_NETMASK_FOR_SGI
|
||||
%token MME_INTERFACE_NAME_FOR_S1_MME
|
||||
%token MME_IP_ADDRESS_FOR_S1_MME
|
||||
%token MME_IP_NETMASK_FOR_S1_MME
|
||||
%token MME_INTERFACE_NAME_FOR_S11_MME
|
||||
%token MME_IP_ADDRESS_FOR_S11_MME
|
||||
%token MME_IP_NETMASK_FOR_S11_MME
|
||||
|
||||
%%
|
||||
conffile: /* If options not provided, we will default values */
|
||||
| conffile s6aconf
|
||||
| conffile emergency_attach_supported
|
||||
| conffile unauthenticated_imsi_supported
|
||||
| conffile maxenb
|
||||
| conffile maxue
|
||||
| conffile mmec
|
||||
| conffile mmegid
|
||||
| conffile plmn
|
||||
| conffile realm
|
||||
| conffile relative_capacity
|
||||
| conffile itti_queue_size
|
||||
| conffile s1ap_outcome_timer
|
||||
| conffile mme_statistic_timer
|
||||
| conffile sgw_interface_name_for_S11
|
||||
| conffile sgw_ip_address_for_S11
|
||||
| conffile sgw_ip_netmask_for_S11
|
||||
| conffile sgw_interface_name_for_S1u_S12_S4_up
|
||||
| conffile sgw_ip_address_for_S1u_S12_S4_up
|
||||
| conffile sgw_ip_netmask_for_S1u_S12_S4_up
|
||||
| conffile sgw_interface_name_for_S5_S8_up
|
||||
| conffile sgw_ip_address_for_S5_S8_up
|
||||
| conffile sgw_ip_netmask_for_S5_S8_up
|
||||
| conffile pgw_interface_name_for_S5_S8
|
||||
| conffile pgw_ip_address_for_S5_S8
|
||||
| conffile pgw_ip_netmask_for_S5_S8
|
||||
| conffile pgw_interface_name_for_SGI
|
||||
| conffile pgw_ip_addr_for_SGI
|
||||
| conffile pgw_ip_netmask_for_SGI
|
||||
| conffile mme_interface_name_for_S1_MME
|
||||
| conffile mme_ip_address_for_S1_MME
|
||||
| conffile mme_ip_netmask_for_S1_MME
|
||||
| conffile mme_interface_name_for_S11_MME
|
||||
| conffile mme_ip_address_for_S11_MME
|
||||
| conffile mme_ip_netmask_for_S11_MME
|
||||
| conffile sctp_instreams
|
||||
| conffile sctp_outstreams
|
||||
| conffile errors
|
||||
{
|
||||
yyerror(&yylloc, mme_config_p, "An error occurred while parsing the configuration file");
|
||||
return EINVAL;
|
||||
}
|
||||
;
|
||||
|
||||
emergency_attach_supported: EMERGENCY_ATTACH_SUPPORTED '=' INTEGER ';'
|
||||
{
|
||||
if ($3 != 0 && $3 != 1) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value (possible values are 0 or 1");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->emergency_attach_supported = $3;
|
||||
};
|
||||
|
||||
unauthenticated_imsi_supported: UNAUTHENTICATED_IMSI_SUPPORTED '=' INTEGER ';'
|
||||
{
|
||||
if ($3 != 0 && $3 != 1) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value (possible values are 0 or 1");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->unauthenticated_imsi_supported = $3;
|
||||
};
|
||||
|
||||
mme_statistic_timer: MME_STATISTIC_TIMER '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->mme_statistic_timer = $3;
|
||||
}
|
||||
;
|
||||
|
||||
s1ap_outcome_timer: S1AP_OUTCOME_TIMER '=' INTEGER ';'
|
||||
{
|
||||
/* 0 is not a valid value here */
|
||||
if ($3 <= 0 || $3 >= (1 << (8 * sizeof(mme_config_p->s1ap_config.outcome_drop_timer_sec)))) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->s1ap_config.outcome_drop_timer_sec = $3;
|
||||
}
|
||||
;
|
||||
|
||||
s6aconf: S6A_CONF '=' QSTRING ';'
|
||||
{
|
||||
mme_config_p->s6a_config.conf_file = $3;
|
||||
}
|
||||
;
|
||||
|
||||
maxenb: MAX_ENB '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->max_eNBs = (uint32_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
maxue: MAX_UE '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->max_ues = (uint32_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
mmec: MME_CODE '=' mmeclist ';'
|
||||
;
|
||||
|
||||
mmeclist:
|
||||
{
|
||||
yyerror(&yylloc, mme_config_p, "Empty value is not allowed");
|
||||
return EINVAL;
|
||||
}
|
||||
| mmecvalue ',' mmeclist
|
||||
| mmecvalue
|
||||
;
|
||||
|
||||
mmecvalue: INTEGER
|
||||
{
|
||||
if (mme_config_p->gummei.nb_mmec == 256) {
|
||||
yyerror(&yylloc, mme_config_p, "Reached maximum number of mmec");
|
||||
return EINVAL;
|
||||
}
|
||||
if ($1 >= 256) {
|
||||
yyerror(&yylloc, mme_config_p, "Value exceed limit <= 255");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->gummei.nb_mmec++;
|
||||
mme_config_p->gummei.mmec = realloc(mme_config_p->gummei.mmec,
|
||||
mme_config_p->gummei.nb_mmec * sizeof(uint8_t));
|
||||
mme_config_p->gummei.mmec[mme_config_p->gummei.nb_mmec-1] = (uint8_t)$1;
|
||||
}
|
||||
;
|
||||
|
||||
mmegid: MME_GID '=' mmegidlist ';'
|
||||
;
|
||||
|
||||
mmegidlist:
|
||||
{
|
||||
yyerror(&yylloc, mme_config_p, "Empty value is not allowed");
|
||||
return EINVAL;
|
||||
}
|
||||
| mmegidvalue
|
||||
| mmegidvalue ',' mmegidlist
|
||||
;
|
||||
|
||||
mmegidvalue: INTEGER
|
||||
{
|
||||
if (mme_config_p->gummei.nb_mme_gid == 65535) {
|
||||
yyerror(&yylloc, mme_config_p, "Reached maximum number of mmegid");
|
||||
return EINVAL;
|
||||
}
|
||||
if ($1 >= 65356) {
|
||||
yyerror(&yylloc, mme_config_p, "Value exceed limit <= 65535");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->gummei.nb_mme_gid++;
|
||||
mme_config_p->gummei.mme_gid = realloc(mme_config_p->gummei.mme_gid,
|
||||
mme_config_p->gummei.nb_mme_gid * sizeof(uint16_t));
|
||||
mme_config_p->gummei.mme_gid[mme_config_p->gummei.nb_mme_gid-1] = (uint16_t)$1;
|
||||
}
|
||||
;
|
||||
|
||||
realm: REALM '=' QSTRING ';'
|
||||
{
|
||||
mme_config_p->realm = $3;
|
||||
mme_config_p->realm_length = strlen(mme_config_p->realm);
|
||||
}
|
||||
;
|
||||
|
||||
plmn: PLMN_Y '=' plmnlist ';'
|
||||
;
|
||||
|
||||
plmnlist:
|
||||
{
|
||||
yyerror(&yylloc, mme_config_p, "Empty value is not allowed");
|
||||
return EINVAL;
|
||||
}
|
||||
| plmnvalue
|
||||
| plmnvalue ',' plmnlist
|
||||
;
|
||||
|
||||
plmnvalue: INTEGER '.' INTEGER ':' INTEGER
|
||||
{
|
||||
if (mme_config_p->gummei.nb_plmns == 32) {
|
||||
yyerror(&yylloc, mme_config_p, "Reached maximum number of plmns");
|
||||
return EINVAL;
|
||||
}
|
||||
if ($1 > 999 || $3 > 999) {
|
||||
yyerror(&yylloc, mme_config_p, "PLMN value exceed limit <= 999");
|
||||
return EINVAL;
|
||||
}
|
||||
if ($5 > 65535) {
|
||||
yyerror(&yylloc, mme_config_p, "TAC value exceed limit <= 65535");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->gummei.nb_plmns++;
|
||||
mme_config_p->gummei.plmn_mcc = realloc(mme_config_p->gummei.plmn_mcc,
|
||||
mme_config_p->gummei.nb_plmns * sizeof(uint16_t));
|
||||
mme_config_p->gummei.plmn_mnc = realloc(mme_config_p->gummei.plmn_mnc,
|
||||
mme_config_p->gummei.nb_plmns * sizeof(uint16_t));
|
||||
mme_config_p->gummei.plmn_tac = realloc(mme_config_p->gummei.plmn_tac,
|
||||
mme_config_p->gummei.nb_plmns * sizeof(uint16_t));
|
||||
mme_config_p->gummei.plmn_mcc[mme_config_p->gummei.nb_plmns-1] = (uint16_t)$1;
|
||||
mme_config_p->gummei.plmn_mnc[mme_config_p->gummei.nb_plmns-1] = (uint16_t)$3;
|
||||
mme_config_p->gummei.plmn_tac[mme_config_p->gummei.nb_plmns-1] = (uint16_t)$5;
|
||||
}
|
||||
;
|
||||
|
||||
relative_capacity: RELATIVE_CAP '=' INTEGER ';'
|
||||
{
|
||||
if ($3 >= 256 || $3 < 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Value exceed limit <= 255");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->relative_capacity = $3;
|
||||
}
|
||||
;
|
||||
|
||||
itti_queue_size: ITTI_QUEUE_SIZE '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->itti_config.queue_size = (uint32_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
sctp_instreams: SCTP_INSTREAMS '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else if ($3 > UINT16_MAX) {
|
||||
yyerror(&yylloc, mme_config_p, "Value exceed container");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->sctp_config.in_streams = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
sctp_outstreams: SCTP_OUTSTREAMS '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else if ($3 > UINT16_MAX) {
|
||||
yyerror(&yylloc, mme_config_p, "Value exceed container");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->sctp_config.out_streams = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
sgw_interface_name_for_S11: SGW_INTERFACE_NAME_FOR_S11 '=' QSTRING ';'
|
||||
{
|
||||
mme_config_p->ipv4.sgw_interface_name_for_S11 = $3;
|
||||
}
|
||||
;
|
||||
|
||||
sgw_ip_address_for_S11: SGW_IP_ADDRESS_FOR_S11 '=' QSTRING ';'
|
||||
{
|
||||
struct in_addr inp;
|
||||
if ( inet_aton($3, &inp ) < 0 ) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else {
|
||||
mme_config_p->ipv4.sgw_ip_address_for_S11 = inp.s_addr;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
sgw_ip_netmask_for_S11: SGW_IP_NETMASK_FOR_S11 '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else if ($3 > UINT16_MAX) {
|
||||
yyerror(&yylloc, mme_config_p, "Value exceed container");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->ipv4.sgw_ip_netmask_for_S11 = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
sgw_interface_name_for_S1u_S12_S4_up: SGW_INTERFACE_NAME_FOR_S1U_S12_S4_UP '=' QSTRING ';'
|
||||
{
|
||||
mme_config_p->ipv4.sgw_interface_name_for_S1u_S12_S4_up = $3;
|
||||
}
|
||||
;
|
||||
|
||||
sgw_ip_address_for_S1u_S12_S4_up: SGW_IP_ADDRESS_FOR_S1U_S12_S4_UP '=' QSTRING ';'
|
||||
{
|
||||
struct in_addr inp;
|
||||
if ( inet_aton($3, &inp ) < 0 ) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else {
|
||||
mme_config_p->ipv4.sgw_ip_address_for_S1u_S12_S4_up = inp.s_addr;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
sgw_ip_netmask_for_S1u_S12_S4_up: SGW_IP_NETMASK_FOR_S1U_S12_S4_UP '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else if ($3 > UINT16_MAX) {
|
||||
yyerror(&yylloc, mme_config_p, "Value exceed container");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->ipv4.sgw_ip_netmask_for_S1u_S12_S4_up = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
sgw_interface_name_for_S5_S8_up: SGW_INTERFACE_NAME_FOR_S5_S8_UP '=' QSTRING ';'
|
||||
{
|
||||
mme_config_p->ipv4.sgw_interface_name_for_S5_S8_up = $3;
|
||||
}
|
||||
;
|
||||
|
||||
sgw_ip_address_for_S5_S8_up: SGW_IP_ADDRESS_FOR_S5_S8_UP '=' QSTRING ';'
|
||||
{
|
||||
struct in_addr inp;
|
||||
if ( inet_aton($3, &inp ) < 0 ) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else {
|
||||
mme_config_p->ipv4.sgw_ip_address_for_S5_S8_up = inp.s_addr;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
sgw_ip_netmask_for_S5_S8_up: SGW_IP_NETMASK_FOR_S5_S8_UP '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else if ($3 > UINT16_MAX) {
|
||||
yyerror(&yylloc, mme_config_p, "Value exceed container");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->ipv4.sgw_ip_netmask_for_S5_S8_up = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
pgw_interface_name_for_S5_S8: PGW_INTERFACE_NAME_FOR_S5_S8 '=' QSTRING ';'
|
||||
{
|
||||
mme_config_p->ipv4.pgw_interface_name_for_S5_S8 = $3;
|
||||
}
|
||||
;
|
||||
|
||||
pgw_ip_address_for_S5_S8: PGW_IP_ADDRESS_FOR_S5_S8 '=' QSTRING ';'
|
||||
{
|
||||
struct in_addr inp;
|
||||
if ( inet_aton($3, &inp ) < 0 ) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else {
|
||||
mme_config_p->ipv4.pgw_ip_address_for_S5_S8 = inp.s_addr;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
pgw_ip_netmask_for_S5_S8: PGW_IP_NETMASK_FOR_S5_S8 '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else if ($3 > UINT16_MAX) {
|
||||
yyerror(&yylloc, mme_config_p, "Value exceed container");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->ipv4.pgw_ip_netmask_for_S5_S8 = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
pgw_interface_name_for_SGI: PGW_INTERFACE_NAME_FOR_SGI '=' QSTRING ';'
|
||||
{
|
||||
mme_config_p->ipv4.pgw_interface_name_for_SGI = $3;
|
||||
}
|
||||
;
|
||||
|
||||
pgw_ip_addr_for_SGI: PGW_IP_ADDR_FOR_SGI '=' QSTRING ';'
|
||||
{
|
||||
struct in_addr inp;
|
||||
if ( inet_aton($3, &inp ) < 0 ) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else {
|
||||
mme_config_p->ipv4.pgw_ip_addr_for_SGI = inp.s_addr;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
pgw_ip_netmask_for_SGI: PGW_IP_NETMASK_FOR_SGI '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else if ($3 > UINT16_MAX) {
|
||||
yyerror(&yylloc, mme_config_p, "Value exceed container");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->ipv4.pgw_ip_netmask_for_SGI = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
mme_interface_name_for_S1_MME: MME_INTERFACE_NAME_FOR_S1_MME '=' QSTRING ';'
|
||||
{
|
||||
mme_config_p->ipv4.mme_interface_name_for_S1_MME = $3;
|
||||
}
|
||||
;
|
||||
|
||||
mme_ip_address_for_S1_MME: MME_IP_ADDRESS_FOR_S1_MME '=' QSTRING ';'
|
||||
{
|
||||
struct in_addr inp;
|
||||
if ( inet_aton($3, &inp ) < 0 ) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else {
|
||||
mme_config_p->ipv4.mme_ip_address_for_S1_MME = inp.s_addr;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
mme_ip_netmask_for_S1_MME: MME_IP_NETMASK_FOR_S1_MME '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else if ($3 > UINT16_MAX) {
|
||||
yyerror(&yylloc, mme_config_p, "Value exceed container");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->ipv4.mme_ip_netmask_for_S1_MME = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
mme_interface_name_for_S11_MME: MME_INTERFACE_NAME_FOR_S11_MME '=' QSTRING ';'
|
||||
{
|
||||
mme_config_p->ipv4.mme_interface_name_for_S11 = $3;
|
||||
}
|
||||
;
|
||||
|
||||
mme_ip_address_for_S11_MME: MME_IP_ADDRESS_FOR_S11_MME '=' QSTRING ';'
|
||||
{
|
||||
struct in_addr inp;
|
||||
if ( inet_aton($3, &inp ) < 0 ) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else {
|
||||
mme_config_p->ipv4.mme_ip_address_for_S11 = inp.s_addr;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
mme_ip_netmask_for_S11_MME: MME_IP_NETMASK_FOR_S11_MME '=' INTEGER ';'
|
||||
{
|
||||
if ($3 <= 0) {
|
||||
yyerror(&yylloc, mme_config_p, "Invalid value");
|
||||
return EINVAL;
|
||||
} else if ($3 > UINT16_MAX) {
|
||||
yyerror(&yylloc, mme_config_p, "Value exceed container");
|
||||
return EINVAL;
|
||||
}
|
||||
mme_config_p->ipv4.mme_ip_netmask_for_S11 = (uint16_t)$3;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
/* Lexical or syntax error */
|
||||
errors: LEX_ERROR
|
||||
| error
|
||||
;
|
||||
%%
|
||||
|
||||
void yyerror(YYLTYPE *llocp, struct mme_config_s *mme_config_p, const char *str)
|
||||
{
|
||||
fprintf(stderr, "Error in %s ( on line %i column %i -> line %i column %i) : %s\n",
|
||||
mme_config_p->config_file, llocp->first_line, llocp->first_column,
|
||||
llocp->last_line, llocp->last_column, str);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
|
||||
/* Lex configuration parser.
|
||||
*
|
||||
* This file defines the token for parsing the configuration file
|
||||
*
|
||||
* Note : This module is NOT thread-safe. All processing must be done from one thread only.
|
||||
*/
|
||||
%{
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mme_config.h"
|
||||
/* Include yacc tokens definitions */
|
||||
#include "mme_parser.h"
|
||||
|
||||
/* Update the column information */
|
||||
#ifdef DEBUG_LEX
|
||||
#define YY_USER_ACTION { \
|
||||
yylloc->first_column = yylloc->last_column + 1; \
|
||||
yylloc->last_column = yylloc->first_column + yyleng - 1; \
|
||||
TRACE_DEBUG(FULL, \
|
||||
"(%d:%d-%d:%d) matched rule %d, length=%d, txt='%s'\n", \
|
||||
yylloc->first_line, yylloc->first_column, \
|
||||
yylloc->last_line, yylloc->last_column, \
|
||||
yy_act, yyleng, yytext); \
|
||||
}
|
||||
#else /* DEBUG_LEX */
|
||||
#define YY_USER_ACTION { \
|
||||
yylloc->first_column = yylloc->last_column + 1; \
|
||||
yylloc->last_column = yylloc->first_column + yyleng - 1; \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define YY_NO_INPUT
|
||||
%}
|
||||
|
||||
%option bison-bridge bison-locations
|
||||
%option noyywrap
|
||||
%option nounput
|
||||
|
||||
/* Quoted string. Multilines do not match. */
|
||||
qstring \"[^\"\n]*\"
|
||||
|
||||
%%
|
||||
|
||||
/* List of patterns and actions */
|
||||
|
||||
<*>\n {
|
||||
/* Update the line count */
|
||||
yylloc->first_line++;
|
||||
yylloc->last_line++;
|
||||
yylloc->last_column=0;
|
||||
}
|
||||
|
||||
<*>([[:space:]]{-}[\n])+ ; /* Eat all spaces, not new lines */
|
||||
<*>#.*$ ; /* Eat all comments */
|
||||
|
||||
{qstring} {
|
||||
/* First copy the string without the quotes for use in the yacc parser */
|
||||
if ((yylval->string = strdup(yytext+1)) == NULL) { /* This allocates one useless tail char but... it's easier :D */
|
||||
return LEX_ERROR;/* on error, trig an error in yacc parser */
|
||||
}
|
||||
yylval->string[yyleng-2] = '\0';
|
||||
|
||||
/* the yacc parser will check the string is valid */
|
||||
return QSTRING;
|
||||
}
|
||||
|
||||
[[:digit:]]+ {
|
||||
/* Convert this to an integer value */
|
||||
int ret = sscanf(yytext, "%i", &yylval->integer);
|
||||
if (ret != 1) {
|
||||
/* No matching: an error occurred */
|
||||
fprintf(stderr, "Unable to convert the value '%s' to a valid number: %s\n",
|
||||
yytext, strerror(errno));
|
||||
return LEX_ERROR; /* trig an error in yacc parser */
|
||||
/* Maybe we could REJECT instead of failing here? */
|
||||
}
|
||||
return INTEGER;
|
||||
}
|
||||
|
||||
/* Full words tokens (keywords) */
|
||||
(?i:"EMERGENCY_ATTACH_SUPPORTED") { return EMERGENCY_ATTACH_SUPPORTED; }
|
||||
(?i:"UNAUTHENTICATED_IMSI_SUPPORTED") { return UNAUTHENTICATED_IMSI_SUPPORTED; }
|
||||
(?i:"MAXENB") { return MAX_ENB; }
|
||||
(?i:"MAXUE") { return MAX_UE; }
|
||||
(?i:"S6A_CONF") { return S6A_CONF; }
|
||||
(?i:"ITTI_QUEUE_SIZE") { return ITTI_QUEUE_SIZE; }
|
||||
(?i:"SCTP_INSTREAMS") { return SCTP_INSTREAMS; }
|
||||
(?i:"SCTP_OUTSTREAMS") { return SCTP_OUTSTREAMS; }
|
||||
(?i:"MME_CODE") { return MME_CODE; }
|
||||
(?i:"MME_GID") { return MME_GID; }
|
||||
(?i:"REALM") { return REALM; }
|
||||
(?i:"PLMN") { return PLMN_Y; }
|
||||
(?i:"RELATIVE_CAPACITY") { return RELATIVE_CAP; }
|
||||
(?i:"S1AP_OUTCOME_TIMER") { return S1AP_OUTCOME_TIMER; }
|
||||
(?i:"MME_STATISTIC_TIMER") { return MME_STATISTIC_TIMER; }
|
||||
(?i:"SGW_INTERFACE_NAME_FOR_S11") { return SGW_INTERFACE_NAME_FOR_S11; }
|
||||
(?i:"SGW_IP_ADDRESS_FOR_S11") { return SGW_IP_ADDRESS_FOR_S11; }
|
||||
(?i:"SGW_IP_NETMASK_FOR_S11") { return SGW_IP_NETMASK_FOR_S11; }
|
||||
(?i:"SGW_INTERFACE_NAME_FOR_S1U_S12_S4_UP") { return SGW_INTERFACE_NAME_FOR_S1U_S12_S4_UP; }
|
||||
(?i:"SGW_IP_ADDRESS_FOR_S1U_S12_S4_UP") { return SGW_IP_ADDRESS_FOR_S1U_S12_S4_UP; }
|
||||
(?i:"SGW_IP_NETMASK_FOR_S1U_S12_S4_UP") { return SGW_IP_NETMASK_FOR_S1U_S12_S4_UP; }
|
||||
(?i:"SGW_INTERFACE_NAME_FOR_S5_S8_UP") { return SGW_INTERFACE_NAME_FOR_S5_S8_UP; }
|
||||
(?i:"SGW_IP_ADDRESS_FOR_S5_S8_UP") { return SGW_IP_ADDRESS_FOR_S5_S8_UP; }
|
||||
(?i:"SGW_IP_NETMASK_FOR_S5_S8_UP") { return SGW_IP_NETMASK_FOR_S5_S8_UP; }
|
||||
(?i:"PGW_INTERFACE_NAME_FOR_S5_S8") { return PGW_INTERFACE_NAME_FOR_S5_S8; }
|
||||
(?i:"PGW_IP_ADDRESS_FOR_S5_S8") { return PGW_IP_ADDRESS_FOR_S5_S8; }
|
||||
(?i:"PGW_IP_NETMASK_FOR_S5_S8") { return PGW_IP_NETMASK_FOR_S5_S8; }
|
||||
(?i:"PGW_INTERFACE_NAME_FOR_SGI") { return PGW_INTERFACE_NAME_FOR_SGI; }
|
||||
(?i:"PGW_IP_ADDR_FOR_SGI") { return PGW_IP_ADDR_FOR_SGI; }
|
||||
(?i:"PGW_IP_NETMASK_FOR_SGI") { return PGW_IP_NETMASK_FOR_SGI; }
|
||||
(?i:"MME_INTERFACE_NAME_FOR_S1_MME") { return MME_INTERFACE_NAME_FOR_S1_MME; }
|
||||
(?i:"MME_IP_ADDRESS_FOR_S1_MME") { return MME_IP_ADDRESS_FOR_S1_MME; }
|
||||
(?i:"MME_IP_NETMASK_FOR_S1_MME") { return MME_IP_NETMASK_FOR_S1_MME; }
|
||||
(?i:"MME_INTERFACE_NAME_FOR_S11_MME") { return MME_INTERFACE_NAME_FOR_S11_MME; }
|
||||
(?i:"MME_IP_ADDRESS_FOR_S11_MME") { return MME_IP_ADDRESS_FOR_S11_MME; }
|
||||
(?i:"MME_IP_NETMASK_FOR_S11_MME") { return MME_IP_NETMASK_FOR_S11_MME; }
|
||||
|
||||
/* Valid single characters for yyparse */
|
||||
<*>[.=,:;{}] { return yytext[0]; }
|
||||
|
||||
<*>[[:alnum:]]+ | /* This rule is only useful to print a complete token in error messages */
|
||||
/* Unrecognized character */
|
||||
<*>. {
|
||||
fprintf(stderr, "Unrecognized text on line %d col %d: '%s'.\n",
|
||||
yylloc->first_line, yylloc->first_column, yytext);
|
||||
return LEX_ERROR;
|
||||
}
|
||||
|
||||
%%
|
||||
@@ -0,0 +1,592 @@
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines five types of data structures: singly-linked lists,
|
||||
* lists, simple queues, tail queues, and circular queues.
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The
|
||||
* elements are singly linked for minimum space and pointer manipulation
|
||||
* overhead at the expense of O(n) removal for arbitrary elements. New
|
||||
* elements can be added to the list after an existing element or at the
|
||||
* head of the list. Elements being removed from the head of the list
|
||||
* should use the explicit macro for this purpose for optimum
|
||||
* efficiency. A singly-linked list may only be traversed in the forward
|
||||
* direction. Singly-linked lists are ideal for applications with large
|
||||
* datasets and few or no removals or for implementing a LIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A simple queue is headed by a pair of pointers, one the head of the
|
||||
* list and the other to the tail of the list. The elements are singly
|
||||
* linked to save space, so elements can only be removed from the
|
||||
* head of the list. New elements can be added to the list after
|
||||
* an existing element, at the head of the list, or at the end of the
|
||||
* list. A simple queue may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the list.
|
||||
* A circle queue may be traversed in either direction, but has a more
|
||||
* complex end of list detection.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
* SLIST LIST STAILQ TAILQ CIRCLEQ
|
||||
* _HEAD + + + + +
|
||||
* _HEAD_INITIALIZER + + + + +
|
||||
* _ENTRY + + + + +
|
||||
* _INIT + + + + +
|
||||
* _EMPTY + + + + +
|
||||
* _FIRST + + + + +
|
||||
* _NEXT + + + + +
|
||||
* _PREV - - - + +
|
||||
* _LAST - - + + +
|
||||
* _FOREACH + + + + +
|
||||
* _FOREACH_REVERSE - - - + +
|
||||
* _INSERT_HEAD + + + + +
|
||||
* _INSERT_BEFORE - + - + +
|
||||
* _INSERT_AFTER + + + + +
|
||||
* _INSERT_TAIL - - + + +
|
||||
* _REMOVE_HEAD + - + - -
|
||||
* _REMOVE + + + + +
|
||||
*/
|
||||
|
||||
/*
|
||||
* List definitions.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define LIST_INIT(head) do { \
|
||||
(head)->lh_first = NULL; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
(elm)->field.le_next = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->lh_first); \
|
||||
(var); \
|
||||
(var) = ((var)->field.le_next))
|
||||
|
||||
/*
|
||||
* List access methods.
|
||||
*/
|
||||
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
|
||||
/*
|
||||
* Singly-linked List definitions.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_INIT(head) do { \
|
||||
(head)->slh_first = NULL; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||
(slistelm)->field.sle_next = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (head)->slh_first; \
|
||||
(head)->slh_first = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->slh_first == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = (head)->slh_first; \
|
||||
while(curelm->field.sle_next != (elm)) \
|
||||
curelm = curelm->field.sle_next; \
|
||||
curelm->field.sle_next = \
|
||||
curelm->field.sle_next->field.sle_next; \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
|
||||
|
||||
/*
|
||||
* Singly-linked List access methods.
|
||||
*/
|
||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue declarations.
|
||||
*/
|
||||
#define STAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *stqh_first; /* first element */ \
|
||||
struct type **stqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).stqh_first }
|
||||
|
||||
#define STAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *stqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue functions.
|
||||
*/
|
||||
#define STAILQ_INIT(head) do { \
|
||||
(head)->stqh_first = NULL; \
|
||||
(head)->stqh_last = &(head)->stqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
|
||||
(head)->stqh_last = &(elm)->field.stqe_next; \
|
||||
(head)->stqh_first = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.stqe_next = NULL; \
|
||||
*(head)->stqh_last = (elm); \
|
||||
(head)->stqh_last = &(elm)->field.stqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
|
||||
(head)->stqh_last = &(elm)->field.stqe_next; \
|
||||
(listelm)->field.stqe_next = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
|
||||
(head)->stqh_last = &(head)->stqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->stqh_first == (elm)) { \
|
||||
STAILQ_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
struct type *curelm = (head)->stqh_first; \
|
||||
while (curelm->field.stqe_next != (elm)) \
|
||||
curelm = curelm->field.stqe_next; \
|
||||
if ((curelm->field.stqe_next = \
|
||||
curelm->field.stqe_next->field.stqe_next) == NULL) \
|
||||
(head)->stqh_last = &(curelm)->field.stqe_next; \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define STAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->stqh_first); \
|
||||
(var); \
|
||||
(var) = ((var)->field.stqe_next))
|
||||
|
||||
#define STAILQ_CONCAT(head1, head2) do { \
|
||||
if (!STAILQ_EMPTY((head2))) { \
|
||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue access methods.
|
||||
*/
|
||||
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
|
||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
||||
|
||||
|
||||
/*
|
||||
* Simple queue definitions.
|
||||
*/
|
||||
#define SIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqh_first; /* first element */ \
|
||||
struct type **sqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).sqh_first }
|
||||
|
||||
#define SIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple queue functions.
|
||||
*/
|
||||
#define SIMPLEQ_INIT(head) do { \
|
||||
(head)->sqh_first = NULL; \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(head)->sqh_first = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqe_next = NULL; \
|
||||
*(head)->sqh_last = (elm); \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(listelm)->field.sqe_next = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SIMPLEQ_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->sqh_first == (elm)) { \
|
||||
SIMPLEQ_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
struct type *curelm = (head)->sqh_first; \
|
||||
while (curelm->field.sqe_next != (elm)) \
|
||||
curelm = curelm->field.sqe_next; \
|
||||
if ((curelm->field.sqe_next = \
|
||||
curelm->field.sqe_next->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(curelm)->field.sqe_next; \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define SIMPLEQ_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->sqh_first); \
|
||||
(var); \
|
||||
(var) = ((var)->field.sqe_next))
|
||||
|
||||
/*
|
||||
* Simple queue access methods.
|
||||
*/
|
||||
#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL)
|
||||
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
|
||||
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define _TAILQ_HEAD(name, type, qual) \
|
||||
struct name { \
|
||||
qual type *tqh_first; /* first element */ \
|
||||
qual type *qual *tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,)
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define _TAILQ_ENTRY(type, qual) \
|
||||
struct { \
|
||||
qual type *tqe_next; /* next element */ \
|
||||
qual type *qual *tqe_prev; /* address of previous next element */\
|
||||
}
|
||||
#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,)
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_INIT(head) do { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(head)->tqh_first->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->tqh_first); \
|
||||
(var); \
|
||||
(var) = ((var)->field.tqe_next))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
|
||||
(var); \
|
||||
(var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
|
||||
|
||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
||||
if (!TAILQ_EMPTY(head2)) { \
|
||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
TAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
/*
|
||||
* Tail queue access methods.
|
||||
*/
|
||||
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
|
||||
|
||||
/*
|
||||
* Circular queue definitions.
|
||||
*/
|
||||
#define CIRCLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *cqh_first; /* first element */ \
|
||||
struct type *cqh_last; /* last element */ \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_HEAD_INITIALIZER(head) \
|
||||
{ (void *)&head, (void *)&head }
|
||||
|
||||
#define CIRCLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *cqe_next; /* next element */ \
|
||||
struct type *cqe_prev; /* previous element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue functions.
|
||||
*/
|
||||
#define CIRCLEQ_INIT(head) do { \
|
||||
(head)->cqh_first = (void *)(head); \
|
||||
(head)->cqh_last = (void *)(head); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev = (listelm); \
|
||||
if ((listelm)->field.cqe_next == (void *)(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||
(listelm)->field.cqe_next = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm); \
|
||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||
if ((listelm)->field.cqe_prev == (void *)(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||
(listelm)->field.cqe_prev = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||
(elm)->field.cqe_prev = (void *)(head); \
|
||||
if ((head)->cqh_last == (void *)(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||
(head)->cqh_first = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (void *)(head); \
|
||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||
if ((head)->cqh_first == (void *)(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(head)->cqh_last->field.cqe_next = (elm); \
|
||||
(head)->cqh_last = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
||||
if ((elm)->field.cqe_next == (void *)(head)) \
|
||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||
else \
|
||||
(elm)->field.cqe_next->field.cqe_prev = \
|
||||
(elm)->field.cqe_prev; \
|
||||
if ((elm)->field.cqe_prev == (void *)(head)) \
|
||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||
else \
|
||||
(elm)->field.cqe_prev->field.cqe_next = \
|
||||
(elm)->field.cqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define CIRCLEQ_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->cqh_first); \
|
||||
(var) != (const void *)(head); \
|
||||
(var) = ((var)->field.cqe_next))
|
||||
|
||||
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
|
||||
for ((var) = ((head)->cqh_last); \
|
||||
(var) != (const void *)(head); \
|
||||
(var) = ((var)->field.cqe_prev))
|
||||
|
||||
/*
|
||||
* Circular queue access methods.
|
||||
*/
|
||||
#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
|
||||
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
|
||||
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
|
||||
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
|
||||
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
|
||||
|
||||
#define CIRCLEQ_LOOP_NEXT(head, elm, field) \
|
||||
(((elm)->field.cqe_next == (void *)(head)) \
|
||||
? ((head)->cqh_first) \
|
||||
: (elm->field.cqe_next))
|
||||
#define CIRCLEQ_LOOP_PREV(head, elm, field) \
|
||||
(((elm)->field.cqe_prev == (void *)(head)) \
|
||||
? ((head)->cqh_last) \
|
||||
: (elm->field.cqe_prev))
|
||||
|
||||
#endif /* sys/queue.h */
|
||||
@@ -0,0 +1,678 @@
|
||||
/*
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_TREE_H_
|
||||
#define _SYS_TREE_H_
|
||||
|
||||
/*
|
||||
* This file defines data structures for different types of trees:
|
||||
* splay trees and red-black trees.
|
||||
*
|
||||
* A splay tree is a self-organizing data structure. Every operation
|
||||
* on the tree causes a splay to happen. The splay moves the requested
|
||||
* node to the root of the tree and partly rebalances it.
|
||||
*
|
||||
* This has the benefit that request locality causes faster lookups as
|
||||
* the requested nodes move to the top of the tree. On the other hand,
|
||||
* every lookup causes memory writes.
|
||||
*
|
||||
* The Balance Theorem bounds the total access time for m operations
|
||||
* and n inserts on an initially empty tree as O((m + n)lg n). The
|
||||
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
|
||||
*
|
||||
* A red-black tree is a binary search tree with the node color as an
|
||||
* extra attribute. It fulfills a set of conditions:
|
||||
* - every search path from the root to a leaf consists of the
|
||||
* same number of black nodes,
|
||||
* - each red node (except for the root) has a black parent,
|
||||
* - each leaf node is black.
|
||||
*
|
||||
* Every operation on a red-black tree is bounded as O(lg n).
|
||||
* The maximum height of a red-black tree is 2lg (n+1).
|
||||
*/
|
||||
|
||||
#define SPLAY_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sph_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define SPLAY_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define SPLAY_INIT(root) do { \
|
||||
(root)->sph_root = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *spe_left; /* left element */ \
|
||||
struct type *spe_right; /* right element */ \
|
||||
}
|
||||
|
||||
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
|
||||
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
|
||||
#define SPLAY_ROOT(head) (head)->sph_root
|
||||
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
|
||||
|
||||
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
|
||||
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_LINKLEFT(head, tmp, field) do { \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
|
||||
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
|
||||
} while (0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
|
||||
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
|
||||
void name##_SPLAY(struct name *, struct type *); \
|
||||
void name##_SPLAY_MINMAX(struct name *, int); \
|
||||
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
|
||||
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return(NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) \
|
||||
return (head->sph_root); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
name##_SPLAY(head, elm); \
|
||||
if (SPLAY_RIGHT(elm, field) != NULL) { \
|
||||
elm = SPLAY_RIGHT(elm, field); \
|
||||
while (SPLAY_LEFT(elm, field) != NULL) { \
|
||||
elm = SPLAY_LEFT(elm, field); \
|
||||
} \
|
||||
} else \
|
||||
elm = NULL; \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_MIN_MAX(struct name *head, int val) \
|
||||
{ \
|
||||
name##_SPLAY_MINMAX(head, val); \
|
||||
return (SPLAY_ROOT(head)); \
|
||||
}
|
||||
|
||||
/* Main splay operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define SPLAY_GENERATE(name, type, field, cmp) \
|
||||
struct type * \
|
||||
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
|
||||
} else { \
|
||||
int __comp; \
|
||||
name##_SPLAY(head, elm); \
|
||||
__comp = (cmp)(elm, (head)->sph_root); \
|
||||
if(__comp < 0) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
|
||||
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_LEFT((head)->sph_root, field) = NULL; \
|
||||
} else if (__comp > 0) { \
|
||||
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
SPLAY_LEFT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
|
||||
} else \
|
||||
return ((head)->sph_root); \
|
||||
} \
|
||||
(head)->sph_root = (elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
struct type * \
|
||||
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *__tmp; \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return (NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) { \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
} else { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
|
||||
name##_SPLAY(head, elm); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
void \
|
||||
name##_SPLAY(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
int __comp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while ((__comp = (cmp)(elm, (head)->sph_root))) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) > 0){ \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
} \
|
||||
\
|
||||
/* Splay with either the minimum or the maximum element \
|
||||
* Used to find minimum or maximum element in tree. \
|
||||
*/ \
|
||||
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while (1) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp > 0) { \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
}
|
||||
|
||||
#define SPLAY_NEGINF -1
|
||||
#define SPLAY_INF 1
|
||||
|
||||
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
|
||||
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
|
||||
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
|
||||
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
|
||||
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
|
||||
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
|
||||
|
||||
#define SPLAY_FOREACH(x, name, head) \
|
||||
for ((x) = SPLAY_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = SPLAY_NEXT(name, head, x))
|
||||
|
||||
/* Macros that define a red-back tree */
|
||||
#define RB_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *rbh_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define RB_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define RB_INIT(root) do { \
|
||||
(root)->rbh_root = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define RB_BLACK 0
|
||||
#define RB_RED 1
|
||||
#define RB_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *rbe_left; /* left element */ \
|
||||
struct type *rbe_right; /* right element */ \
|
||||
struct type *rbe_parent; /* parent element */ \
|
||||
int rbe_color; /* node color */ \
|
||||
}
|
||||
|
||||
#define RB_LEFT(elm, field) (elm)->field.rbe_left
|
||||
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
|
||||
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
|
||||
#define RB_COLOR(elm, field) (elm)->field.rbe_color
|
||||
#define RB_ROOT(head) (head)->rbh_root
|
||||
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
|
||||
|
||||
#define RB_SET(elm, parent, field) do { \
|
||||
RB_PARENT(elm, field) = parent; \
|
||||
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
|
||||
RB_COLOR(elm, field) = RB_RED; \
|
||||
} while (0)
|
||||
|
||||
#define RB_SET_BLACKRED(black, red, field) do { \
|
||||
RB_COLOR(black, field) = RB_BLACK; \
|
||||
RB_COLOR(red, field) = RB_RED; \
|
||||
} while (0)
|
||||
|
||||
#ifndef RB_AUGMENT
|
||||
#define RB_AUGMENT(x)
|
||||
#endif
|
||||
|
||||
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_RIGHT(elm, field); \
|
||||
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \
|
||||
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_LEFT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (0)
|
||||
|
||||
#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_LEFT(elm, field); \
|
||||
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \
|
||||
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_RIGHT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
#define RB_PROTOTYPE(name, type, field, cmp) \
|
||||
void name##_RB_INSERT_COLOR(struct name *, struct type *); \
|
||||
void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
|
||||
struct type *name##_RB_REMOVE(struct name *, struct type *); \
|
||||
struct type *name##_RB_INSERT(struct name *, struct type *); \
|
||||
struct type *name##_RB_FIND(struct name *, struct type *); \
|
||||
struct type *name##_RB_NEXT(struct type *); \
|
||||
struct type *name##_RB_MINMAX(struct name *, int); \
|
||||
\
|
||||
|
||||
/* Main rb operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define RB_GENERATE(name, type, field, cmp) \
|
||||
void \
|
||||
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *parent, *gparent, *tmp; \
|
||||
while ((parent = RB_PARENT(elm, field)) && \
|
||||
RB_COLOR(parent, field) == RB_RED) { \
|
||||
gparent = RB_PARENT(parent, field); \
|
||||
if (parent == RB_LEFT(gparent, field)) { \
|
||||
tmp = RB_RIGHT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field);\
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_RIGHT(parent, field) == elm) { \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_RIGHT(head, gparent, tmp, field); \
|
||||
} else { \
|
||||
tmp = RB_LEFT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field);\
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_LEFT(parent, field) == elm) { \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_LEFT(head, gparent, tmp, field); \
|
||||
} \
|
||||
} \
|
||||
RB_COLOR(head->rbh_root, field) = RB_BLACK; \
|
||||
} \
|
||||
\
|
||||
void \
|
||||
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
|
||||
elm != RB_ROOT(head)) { \
|
||||
if (RB_LEFT(parent, field) == elm) { \
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} else { \
|
||||
if (RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
|
||||
struct type *oleft; \
|
||||
if ((oleft = RB_LEFT(tmp, field)))\
|
||||
RB_COLOR(oleft, field) = RB_BLACK;\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_RIGHT(head, tmp, oleft, field);\
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_RIGHT(tmp, field)) \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} else { \
|
||||
if (RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
|
||||
struct type *oright; \
|
||||
if ((oright = RB_RIGHT(tmp, field)))\
|
||||
RB_COLOR(oright, field) = RB_BLACK;\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_LEFT(head, tmp, oright, field);\
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_LEFT(tmp, field)) \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (elm) \
|
||||
RB_COLOR(elm, field) = RB_BLACK; \
|
||||
} \
|
||||
\
|
||||
struct type * \
|
||||
name##_RB_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *child, *parent, *old = elm; \
|
||||
int color; \
|
||||
if (RB_LEFT(elm, field) == NULL) \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
else if (RB_RIGHT(elm, field) == NULL) \
|
||||
child = RB_LEFT(elm, field); \
|
||||
else { \
|
||||
struct type *left; \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while ((left = RB_LEFT(elm, field))) \
|
||||
elm = left; \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) { \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = child; \
|
||||
if (RB_PARENT(elm, field) == old) \
|
||||
parent = elm; \
|
||||
(elm)->field = (old)->field; \
|
||||
if (RB_PARENT(old, field)) { \
|
||||
if (RB_LEFT(RB_PARENT(old, field), field) == old)\
|
||||
RB_LEFT(RB_PARENT(old, field), field) = elm;\
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(old, field), field) = elm;\
|
||||
RB_AUGMENT(RB_PARENT(old, field)); \
|
||||
} else \
|
||||
RB_ROOT(head) = elm; \
|
||||
RB_PARENT(RB_LEFT(old, field), field) = elm; \
|
||||
if (RB_RIGHT(old, field)) \
|
||||
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
|
||||
if (parent) { \
|
||||
left = parent; \
|
||||
do { \
|
||||
RB_AUGMENT(left); \
|
||||
} while ((left = RB_PARENT(left, field))); \
|
||||
} \
|
||||
goto color; \
|
||||
} \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) { \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = child; \
|
||||
color: \
|
||||
if (color == RB_BLACK) \
|
||||
name##_RB_REMOVE_COLOR(head, parent, child); \
|
||||
return (old); \
|
||||
} \
|
||||
\
|
||||
/* Inserts a node into the RB tree */ \
|
||||
struct type * \
|
||||
name##_RB_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
struct type *parent = NULL; \
|
||||
int comp = 0; \
|
||||
tmp = RB_ROOT(head); \
|
||||
while (tmp) { \
|
||||
parent = tmp; \
|
||||
comp = (cmp)(elm, parent); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
RB_SET(elm, parent, field); \
|
||||
if (parent != NULL) { \
|
||||
if (comp < 0) \
|
||||
RB_LEFT(parent, field) = elm; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = elm; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
{ \
|
||||
RB_ROOT(head) = elm; \
|
||||
} \
|
||||
name##_RB_INSERT_COLOR(head, elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
struct type * \
|
||||
name##_RB_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
int comp; \
|
||||
while (tmp) { \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
struct type * \
|
||||
name##_RB_NEXT(struct type *elm) \
|
||||
{ \
|
||||
if (RB_RIGHT(elm, field)) { \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while (RB_LEFT(elm, field)) \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
} else { \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else { \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
struct type * \
|
||||
name##_RB_MINMAX(struct name *head, int val) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *parent = NULL; \
|
||||
while (tmp) { \
|
||||
parent = tmp; \
|
||||
if (val < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
} \
|
||||
return (parent); \
|
||||
}
|
||||
|
||||
#define RB_NEGINF -1
|
||||
#define RB_INF 1
|
||||
|
||||
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
|
||||
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
|
||||
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
|
||||
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
|
||||
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
||||
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
||||
|
||||
#define RB_FOREACH(x, name, head) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_NEXT(x))
|
||||
|
||||
#endif /* _SYS_TREE_H_ */
|
||||
Reference in New Issue
Block a user