X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=src%2Fcommon%2Fsrc%2Frt_generic_static.c;fp=src%2Fcommon%2Fsrc%2Frt_generic_static.c;h=26e907ec140802bc228f84ede8efb1f28b571131;hb=fd9cc7a5b3355146388ebdf4d558cb284c66c5f1;hp=0000000000000000000000000000000000000000;hpb=008bc5b4414cebe48cecf63e8c817a02f7c3ce74;p=ric-plt%2Flib%2Frmr.git diff --git a/src/common/src/rt_generic_static.c b/src/common/src/rt_generic_static.c new file mode 100644 index 0000000..26e907e --- /dev/null +++ b/src/common/src/rt_generic_static.c @@ -0,0 +1,466 @@ +// :vi sw=4 ts=4 noet: +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 AT&T Intellectual Property. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +================================================================================== +*/ + +/* + Mnemonic: rt_generic_static.c + Abstract: These are route table functions which are not specific to the + underlying protocol. rtable_static, and rtable_nng_static + have transport provider specific code. + + This file must be included before the nng/nano specific file as + it defines types. + + Author: E. Scott Daniels + Date: 5 February 2019 +*/ + +#ifndef rt_generic_static_c +#define rt_generic_static_c + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + Passed to a symtab foreach callback to construct a list of pointers from + a current symtab. +*/ +typedef struct thing_list { + int nalloc; + int nused; + void** things; +} thing_list_t; + + +/* + Given a message type create a route table entry and add to the hash keyed on the + message type. Once in the hash, endpoints can be added with uta_add_ep. Size + is the number of group slots to allocate in the entry. +*/ +static rtable_ent_t* uta_add_rte( route_table_t* rt, int mtype, int nrrgroups ) { + rtable_ent_t* rte; + + if( rt == NULL ) { + return NULL; + } + + if( (rte = (rtable_ent_t *) malloc( sizeof( *rte ) )) == NULL ) { + fprintf( stderr, "rmr_add_rte: malloc failed for entry\n" ); + return NULL; + } + memset( rte, 0, sizeof( *rte ) ); + + if( nrrgroups <= 0 ) { + nrrgroups = 10; + } + + if( (rte->rrgroups = (rrgroup_t **) malloc( sizeof( rrgroup_t * ) * nrrgroups )) == NULL ) { + fprintf( stderr, "rmr_add_rte: malloc failed for rrgroup array\n" ); + free( rte ); + return NULL; + } + memset( rte->rrgroups, 0, sizeof( rrgroup_t *) * nrrgroups ); + rte->nrrgroups = nrrgroups; + + rmr_sym_map( rt->hash, mtype, rte ); // add to hash using numeric mtype as key + + if( DEBUG ) fprintf( stderr, "[DBUG] route table entry created: mt=%d groups=%d\n", mtype, nrrgroups ); + return rte; +} + +/* + Parse a single record recevied from the route table generator, or read + from a static route table file. Start records cause a new table to + be started (if a partial table was received it is discarded. Table + entry records are added to the currenly 'in progress' table, and an + end record causes the in progress table to be finalised and the + currently active table is replaced. +*/ +static void parse_rt_rec( uta_ctx_t* ctx, char* buf, int vlevel ) { + int i; + int ntoks; // number of tokens found in something + int ngtoks; + int grp; // group number + rtable_ent_t* rte; // route table entry added + char* tokens[128]; + char* gtokens[64]; // groups + char* tok; // pointer into a token or string + + if( ! buf ) { + return; + } + + while( *buf && isspace( *buf ) ) { // skip leading whitespace + buf++; + } + for( tok = buf + (strlen( buf ) - 1); tok > buf && isspace( *tok ); tok-- ); // trim trailing spaces too + *(tok+1) = 0; + + if( (ntoks = uta_tokenise( buf, tokens, 128, '|' )) > 0 ) { + switch( *(tokens[0]) ) { + case 0: // ignore blanks + // fallthrough + case '#': // and comment lines + break; + + case 'n': // newrt|{start|end} + if( strcmp( tokens[1], "end" ) == 0 ) { // wrap up the table we were building + if( ctx->new_rtable ) { + uta_rt_drop( ctx->old_rtable ); // time to drop one that was previously replaced + ctx->old_rtable = ctx->rtable; // currently active becomes old and allowed to 'drain' + ctx->rtable = ctx->new_rtable; // one we've been adding to becomes active + ctx->new_rtable = NULL; + if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] end of route table noticed\n" ); + } else { + if( DEBUG > 1 ) fprintf( stderr, "[DBUG] end of route table noticed, but one was not started!\n" ); + ctx->new_rtable = NULL; + } + } else { // start a new table. + if( ctx->new_rtable != NULL ) { // one in progress? this forces it out + if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] new table; dropping incomplete table\n" ); + uta_rt_drop( ctx->new_rtable ); + } + + if( ctx->rtable ) { + ctx->new_rtable = uta_rt_clone( ctx->rtable ); // create by cloning endpoint entries from active table + } else { + ctx->new_rtable = uta_rt_init( ); // don't have one yet, just crate empty + } + if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] start of route table noticed\n" ); + } + break; + + case 'r': // assume rt entry + if( ! ctx->new_rtable ) { // bad sequence, or malloc issue earlier; ignore siliently + break; + } + + if( ((tok = strchr( tokens[1], ',' )) == NULL ) || // no sender names + (uta_has_str( tokens[1], ctx->my_name, ',', 127) >= 0) || // our name isn't in the list + has_myip( tokens[1], ctx->ip_list, ',', 127 ) ) { // the list has one of our IP addresses + + if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] create rte for mtype=%s\n", tokens[1] ); + + if( (ngtoks = uta_tokenise( tokens[2], gtokens, 64, ';' )) > 0 ) { // split last field by groups first + rte = uta_add_rte( ctx->new_rtable, atoi( tokens[1] ), ngtoks ); // get/create entry for message type + for( grp = 0; grp < ngtoks; grp++ ) { + if( (ntoks = uta_tokenise( gtokens[grp], tokens, 64, ',' )) > 0 ) { + for( i = 0; i < ntoks; i++ ) { + if( DEBUG > 1 || (vlevel > 1)) fprintf( stderr, "[DBUG] add endpoint %s\n", tokens[i] ); + uta_add_ep( ctx->new_rtable, rte, tokens[i], grp ); + } + } + } + } + } else { + if( DEBUG || (vlevel > 2) ) + fprintf( stderr, "entry not included, sender not matched: %s\n", tokens[1] ); + } + + break; + + default: + if( DEBUG ) fprintf( stderr, "[WRN] rmr_rtc: unrecognised request: %s\n", tokens[0] ); + break; + } + } +} + +/* + This function attempts to open a static route table in order to create a 'seed' + table during initialisation. The environment variable RMR_SEED_RT is expected + to contain the necessary path to the file. If missing, or if the file is empty, + no route table will be available until one is received from the generator. + + This function is probably most useful for testing situations, or extreme + cases where the routes are static. +*/ +static void read_static_rt( uta_ctx_t* ctx, int vlevel ) { + int i; + char* fname; + char* fbuf; // buffer with file contents + char* rec; // start of the record + char* eor; // end of the record + int rcount = 0; // record count for debug + + if( (fname = getenv( ENV_SEED_RT )) == NULL ) { + return; + } + + if( (fbuf = uta_fib( fname ) ) == NULL ) { // read file into a single buffer + fprintf( stderr, "[WRN] seed route table could not be opened: %s: %s\n", fname, strerror( errno ) ); + return; + } + + if( DEBUG ) fprintf( stderr, "[INFO] seed route table successfully opened: %s\n", fname ); + for( rec = fbuf; rec && *rec; rec = eor+1 ) { + rcount++; + if( (eor = strchr( rec, '\n' )) != NULL ) { + *eor = 0; + } + + parse_rt_rec( ctx, rec, vlevel ); + } + + if( DEBUG ) fprintf( stderr, "[INFO] seed route table successfully parsed: %d records\n", rcount ); + free( fbuf ); +} + +/* + Callback driven for each named thing in a symtab. We collect the pointers to those + things for later use (cloning). +*/ +static void collect_things( void* st, void* entry, char const* name, void* thing, void* vthing_list ) { + thing_list_t* tl; + + if( (tl = (thing_list_t *) vthing_list) == NULL ) { + return; + } + + if( thing == NULL ) { + return; + } + + tl->things[tl->nused++] = thing; // save a reference to the thing +} + +/* + Called to delete a route table entry struct. We delete the array of endpoint + pointers, but NOT the endpoints referenced as those are referenced from + multiple entries. +*/ +static void del_rte( void* st, void* entry, char const* name, void* thing, void* data ) { + rtable_ent_t* rte; + int i; + + if( (rte = (rtable_ent_t *) thing) == NULL ) { + return; + } + + if( rte->rrgroups ) { // clean up the round robin groups + for( i = 0; i < rte->nrrgroups; i++ ) { + if( rte->rrgroups[i] ) { + free( rte->rrgroups[i]->epts ); // ditch list of endpoint pointers (end points are reused; don't trash them) + } + } + + free( rte->rrgroups ); + } + + free( rte ); // finally, drop the potato +} + +/* + Read an entire file into a buffer. We assume for route table files + they will be smallish and so this won't be a problem. + Returns a pointer to the buffer, or nil. Caller must free. + Terminates the buffer with a nil character for string processing. + + If we cannot stat the file, we assume it's empty or missing and return + an empty buffer, as opposed to a nil, so the caller can generate defaults + or error if an empty/missing file isn't tolerated. +*/ +static char* uta_fib( char* fname ) { + struct stat stats; + off_t fsize = 8192; // size of the file + off_t nread; // number of bytes read + int fd; + char* buf; // input buffer + + if( (fd = open( fname, O_RDONLY )) >= 0 ) { + if( fstat( fd, &stats ) >= 0 ) { + if( stats.st_size <= 0 ) { // empty file + close( fd ); + fd = -1; + } else { + fsize = stats.st_size; // stat ok, save the file size + } + } else { + fsize = 8192; // stat failed, we'll leave the file open and try to read a default max of 8k + } + } + + if( fd < 0 ) { // didn't open or empty + if( (buf = (char *) malloc( sizeof( char ) * 1 )) == NULL ) { + return NULL; + } + + *buf = 0; + return buf; + } + + // add a size limit check here + + if( (buf = (char *) malloc( sizeof( char ) * fsize + 2 )) == NULL ) { // enough to add nil char to make string + close( fd ); + errno = ENOMEM; + return NULL; + } + + nread = read( fd, buf, fsize ); + if( nread < 0 || nread > fsize ) { // failure of some kind + free( buf ); + errno = EFBIG; // likely too much to handle + close( fd ); + return NULL; + } + + buf[nread] = 0; + + close( fd ); + return buf; +} + +/* + Create and initialise a route table; Returns a pointer to the table struct. +*/ +static route_table_t* uta_rt_init( ) { + route_table_t* rt; + + if( (rt = (route_table_t *) malloc( sizeof( route_table_t ) )) == NULL ) { + return NULL; + } + + if( (rt->hash = rmr_sym_alloc( 509 )) == NULL ) { // modest size, prime + free( rt ); + return NULL; + } + + return rt; +} + +/* + Clone (sort of) an existing route table. This is done to preserve the endpoint + names referenced in a table (and thus existing sessions) when a new set + of message type to endpoint name mappings is received. A new route table + with only endpoint name references is returned based on the active table in + the context. +*/ +static route_table_t* uta_rt_clone( route_table_t* srt ) { + endpoint_t* ep; // an endpoint + route_table_t* nrt; // new route table + route_table_t* art; // active route table + void* sst; // source symtab + void* nst; // new symtab + thing_list_t things; + int i; + + if( srt == NULL ) { + return NULL; + } + + if( (nrt = (route_table_t *) malloc( sizeof( *nrt ) )) == NULL ) { + return NULL; + } + + if( (nrt->hash = rmr_sym_alloc( 509 )) == NULL ) { // modest size, prime + free( nrt ); + return NULL; + } + + things.nalloc = 2048; + things.nused = 0; + things.things = (void **) malloc( sizeof( void * ) * things.nalloc ); + if( things.things == NULL ) { + free( nrt->hash ); + free( nrt ); + return NULL; + } + + sst = srt->hash; // convenience pointers (src symtab) + nst = nrt->hash; + + rmr_sym_foreach_class( sst, 1, collect_things, &things ); // collect the named endpoints in the active table + + for( i = 0; i < things.nused; i++ ) { + ep = (endpoint_t *) things.things[i]; + rmr_sym_put( nst, ep->name, 1, ep ); // slam this one into the new table + } + + free( things.things ); + return nrt; +} + +/* + Given a name, find the endpoint struct in the provided route table. +*/ +static endpoint_t* uta_get_ep( route_table_t* rt, char const* ep_name ) { + + if( rt == NULL || rt->hash == NULL || ep_name == NULL || *ep_name == 0 ) { + return NULL; + } + + return rmr_sym_get( rt->hash, ep_name, 1 ); +} + +/* + Drop the given route table. Purge all type 0 entries, then drop the symtab itself. +*/ +static void uta_rt_drop( route_table_t* rt ) { + if( rt == NULL ) { + return; + } + + rmr_sym_foreach_class( rt->hash, 0, del_rte, NULL ); // free each rte referenced by the hash, but NOT the endpoints + rmr_sym_free( rt->hash ); // free all of the hash related data + free( rt ); +} + +/* + Look up and return the pointer to the endpoint stuct matching the given name. + If not in the hash, a new endpoint is created, added to the hash. Should always + return a pointer. +*/ +static endpoint_t* rt_ensure_ep( route_table_t* rt, char const* ep_name ) { + endpoint_t* ep; + + if( !rt || !ep_name || ! *ep_name ) { + fprintf( stderr, "[WARN] rmr: rt_ensure: internal mishap, something undefined rt=%p ep_name=%p\n", rt, ep_name ); + errno = EINVAL; + return NULL; + } + + if( (ep = uta_get_ep( rt, ep_name )) == NULL ) { // not there yet, make + if( (ep = (endpoint_t *) malloc( sizeof( *ep ) )) == NULL ) { + fprintf( stderr, "[WARN] rmr: rt_ensure: malloc failed for endpoint creation: %s\n", ep_name ); + errno = ENOMEM; + return NULL; + } + + ep->open = 0; // not connected + ep->addr = uta_h2ip( ep_name ); + ep->name = strdup( ep_name ); + + rmr_sym_put( rt->hash, ep_name, 1, ep ); + } + + return ep; +} + + +#endif