3 ==================================================================================
4 Copyright (c) 2019 Nokia
5 Copyright (c) 2018-2019 AT&T Intellectual Property.
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
11 http://www.apache.org/licenses/LICENSE-2.0
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18 ==================================================================================
22 Mnemonic: rt_generic_static.c
23 Abstract: These are route table functions which are not specific to the
24 underlying protocol. rtable_static, and rtable_nng_static
25 have transport provider specific code.
27 This file must be included before the nng/nano specific file as
30 Author: E. Scott Daniels
34 #ifndef rt_generic_static_c
35 #define rt_generic_static_c
44 #include <sys/types.h>
50 Passed to a symtab foreach callback to construct a list of pointers from
53 typedef struct thing_list {
61 Given a message type create a route table entry and add to the hash keyed on the
62 message type. Once in the hash, endpoints can be added with uta_add_ep. Size
63 is the number of group slots to allocate in the entry.
65 static rtable_ent_t* uta_add_rte( route_table_t* rt, int mtype, int nrrgroups ) {
72 if( (rte = (rtable_ent_t *) malloc( sizeof( *rte ) )) == NULL ) {
73 fprintf( stderr, "rmr_add_rte: malloc failed for entry\n" );
76 memset( rte, 0, sizeof( *rte ) );
78 if( nrrgroups <= 0 ) {
82 if( (rte->rrgroups = (rrgroup_t **) malloc( sizeof( rrgroup_t * ) * nrrgroups )) == NULL ) {
83 fprintf( stderr, "rmr_add_rte: malloc failed for rrgroup array\n" );
87 memset( rte->rrgroups, 0, sizeof( rrgroup_t *) * nrrgroups );
88 rte->nrrgroups = nrrgroups;
90 rmr_sym_map( rt->hash, mtype, rte ); // add to hash using numeric mtype as key
92 if( DEBUG ) fprintf( stderr, "[DBUG] route table entry created: mt=%d groups=%d\n", mtype, nrrgroups );
97 Parse a single record recevied from the route table generator, or read
98 from a static route table file. Start records cause a new table to
99 be started (if a partial table was received it is discarded. Table
100 entry records are added to the currenly 'in progress' table, and an
101 end record causes the in progress table to be finalised and the
102 currently active table is replaced.
104 static void parse_rt_rec( uta_ctx_t* ctx, char* buf, int vlevel ) {
106 int ntoks; // number of tokens found in something
108 int grp; // group number
109 rtable_ent_t* rte; // route table entry added
111 char* gtokens[64]; // groups
112 char* tok; // pointer into a token or string
118 while( *buf && isspace( *buf ) ) { // skip leading whitespace
121 for( tok = buf + (strlen( buf ) - 1); tok > buf && isspace( *tok ); tok-- ); // trim trailing spaces too
124 if( (ntoks = uta_tokenise( buf, tokens, 128, '|' )) > 0 ) {
125 switch( *(tokens[0]) ) {
126 case 0: // ignore blanks
128 case '#': // and comment lines
131 case 'n': // newrt|{start|end}
132 if( strcmp( tokens[1], "end" ) == 0 ) { // wrap up the table we were building
133 if( ctx->new_rtable ) {
134 uta_rt_drop( ctx->old_rtable ); // time to drop one that was previously replaced
135 ctx->old_rtable = ctx->rtable; // currently active becomes old and allowed to 'drain'
136 ctx->rtable = ctx->new_rtable; // one we've been adding to becomes active
137 ctx->new_rtable = NULL;
138 if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] end of route table noticed\n" );
140 if( DEBUG > 1 ) fprintf( stderr, "[DBUG] end of route table noticed, but one was not started!\n" );
141 ctx->new_rtable = NULL;
143 } else { // start a new table.
144 if( ctx->new_rtable != NULL ) { // one in progress? this forces it out
145 if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] new table; dropping incomplete table\n" );
146 uta_rt_drop( ctx->new_rtable );
150 ctx->new_rtable = uta_rt_clone( ctx->rtable ); // create by cloning endpoint entries from active table
152 ctx->new_rtable = uta_rt_init( ); // don't have one yet, just crate empty
154 if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] start of route table noticed\n" );
158 case 'r': // assume rt entry
159 if( ! ctx->new_rtable ) { // bad sequence, or malloc issue earlier; ignore siliently
163 if( ((tok = strchr( tokens[1], ',' )) == NULL ) || // no sender names
164 (uta_has_str( tokens[1], ctx->my_name, ',', 127) >= 0) || // our name isn't in the list
165 has_myip( tokens[1], ctx->ip_list, ',', 127 ) ) { // the list has one of our IP addresses
167 if( DEBUG > 1 || (vlevel > 1) ) fprintf( stderr, "[DBUG] create rte for mtype=%s\n", tokens[1] );
169 if( (ngtoks = uta_tokenise( tokens[2], gtokens, 64, ';' )) > 0 ) { // split last field by groups first
170 rte = uta_add_rte( ctx->new_rtable, atoi( tokens[1] ), ngtoks ); // get/create entry for message type
171 for( grp = 0; grp < ngtoks; grp++ ) {
172 if( (ntoks = uta_tokenise( gtokens[grp], tokens, 64, ',' )) > 0 ) {
173 for( i = 0; i < ntoks; i++ ) {
174 if( DEBUG > 1 || (vlevel > 1)) fprintf( stderr, "[DBUG] add endpoint %s\n", tokens[i] );
175 uta_add_ep( ctx->new_rtable, rte, tokens[i], grp );
181 if( DEBUG || (vlevel > 2) )
182 fprintf( stderr, "entry not included, sender not matched: %s\n", tokens[1] );
188 if( DEBUG ) fprintf( stderr, "[WRN] rmr_rtc: unrecognised request: %s\n", tokens[0] );
195 This function attempts to open a static route table in order to create a 'seed'
196 table during initialisation. The environment variable RMR_SEED_RT is expected
197 to contain the necessary path to the file. If missing, or if the file is empty,
198 no route table will be available until one is received from the generator.
200 This function is probably most useful for testing situations, or extreme
201 cases where the routes are static.
203 static void read_static_rt( uta_ctx_t* ctx, int vlevel ) {
206 char* fbuf; // buffer with file contents
207 char* rec; // start of the record
208 char* eor; // end of the record
209 int rcount = 0; // record count for debug
211 if( (fname = getenv( ENV_SEED_RT )) == NULL ) {
215 if( (fbuf = uta_fib( fname ) ) == NULL ) { // read file into a single buffer
216 fprintf( stderr, "[WRN] seed route table could not be opened: %s: %s\n", fname, strerror( errno ) );
220 if( DEBUG ) fprintf( stderr, "[INFO] seed route table successfully opened: %s\n", fname );
221 for( rec = fbuf; rec && *rec; rec = eor+1 ) {
223 if( (eor = strchr( rec, '\n' )) != NULL ) {
227 parse_rt_rec( ctx, rec, vlevel );
230 if( DEBUG ) fprintf( stderr, "[INFO] seed route table successfully parsed: %d records\n", rcount );
235 Callback driven for each named thing in a symtab. We collect the pointers to those
236 things for later use (cloning).
238 static void collect_things( void* st, void* entry, char const* name, void* thing, void* vthing_list ) {
241 if( (tl = (thing_list_t *) vthing_list) == NULL ) {
245 if( thing == NULL ) {
249 tl->things[tl->nused++] = thing; // save a reference to the thing
253 Called to delete a route table entry struct. We delete the array of endpoint
254 pointers, but NOT the endpoints referenced as those are referenced from
257 static void del_rte( void* st, void* entry, char const* name, void* thing, void* data ) {
261 if( (rte = (rtable_ent_t *) thing) == NULL ) {
265 if( rte->rrgroups ) { // clean up the round robin groups
266 for( i = 0; i < rte->nrrgroups; i++ ) {
267 if( rte->rrgroups[i] ) {
268 free( rte->rrgroups[i]->epts ); // ditch list of endpoint pointers (end points are reused; don't trash them)
272 free( rte->rrgroups );
275 free( rte ); // finally, drop the potato
279 Read an entire file into a buffer. We assume for route table files
280 they will be smallish and so this won't be a problem.
281 Returns a pointer to the buffer, or nil. Caller must free.
282 Terminates the buffer with a nil character for string processing.
284 If we cannot stat the file, we assume it's empty or missing and return
285 an empty buffer, as opposed to a nil, so the caller can generate defaults
286 or error if an empty/missing file isn't tolerated.
288 static char* uta_fib( char* fname ) {
290 off_t fsize = 8192; // size of the file
291 off_t nread; // number of bytes read
293 char* buf; // input buffer
295 if( (fd = open( fname, O_RDONLY )) >= 0 ) {
296 if( fstat( fd, &stats ) >= 0 ) {
297 if( stats.st_size <= 0 ) { // empty file
301 fsize = stats.st_size; // stat ok, save the file size
304 fsize = 8192; // stat failed, we'll leave the file open and try to read a default max of 8k
308 if( fd < 0 ) { // didn't open or empty
309 if( (buf = (char *) malloc( sizeof( char ) * 1 )) == NULL ) {
317 // add a size limit check here
319 if( (buf = (char *) malloc( sizeof( char ) * fsize + 2 )) == NULL ) { // enough to add nil char to make string
325 nread = read( fd, buf, fsize );
326 if( nread < 0 || nread > fsize ) { // failure of some kind
328 errno = EFBIG; // likely too much to handle
340 Create and initialise a route table; Returns a pointer to the table struct.
342 static route_table_t* uta_rt_init( ) {
345 if( (rt = (route_table_t *) malloc( sizeof( route_table_t ) )) == NULL ) {
349 if( (rt->hash = rmr_sym_alloc( 509 )) == NULL ) { // modest size, prime
358 Clone (sort of) an existing route table. This is done to preserve the endpoint
359 names referenced in a table (and thus existing sessions) when a new set
360 of message type to endpoint name mappings is received. A new route table
361 with only endpoint name references is returned based on the active table in
364 static route_table_t* uta_rt_clone( route_table_t* srt ) {
365 endpoint_t* ep; // an endpoint
366 route_table_t* nrt; // new route table
367 route_table_t* art; // active route table
368 void* sst; // source symtab
369 void* nst; // new symtab
377 if( (nrt = (route_table_t *) malloc( sizeof( *nrt ) )) == NULL ) {
381 if( (nrt->hash = rmr_sym_alloc( 509 )) == NULL ) { // modest size, prime
386 things.nalloc = 2048;
388 things.things = (void **) malloc( sizeof( void * ) * things.nalloc );
389 if( things.things == NULL ) {
395 sst = srt->hash; // convenience pointers (src symtab)
398 rmr_sym_foreach_class( sst, 1, collect_things, &things ); // collect the named endpoints in the active table
400 for( i = 0; i < things.nused; i++ ) {
401 ep = (endpoint_t *) things.things[i];
402 rmr_sym_put( nst, ep->name, 1, ep ); // slam this one into the new table
405 free( things.things );
410 Given a name, find the endpoint struct in the provided route table.
412 static endpoint_t* uta_get_ep( route_table_t* rt, char const* ep_name ) {
414 if( rt == NULL || rt->hash == NULL || ep_name == NULL || *ep_name == 0 ) {
418 return rmr_sym_get( rt->hash, ep_name, 1 );
422 Drop the given route table. Purge all type 0 entries, then drop the symtab itself.
424 static void uta_rt_drop( route_table_t* rt ) {
429 rmr_sym_foreach_class( rt->hash, 0, del_rte, NULL ); // free each rte referenced by the hash, but NOT the endpoints
430 rmr_sym_free( rt->hash ); // free all of the hash related data
435 Look up and return the pointer to the endpoint stuct matching the given name.
436 If not in the hash, a new endpoint is created, added to the hash. Should always
439 static endpoint_t* rt_ensure_ep( route_table_t* rt, char const* ep_name ) {
442 if( !rt || !ep_name || ! *ep_name ) {
443 fprintf( stderr, "[WARN] rmr: rt_ensure: internal mishap, something undefined rt=%p ep_name=%p\n", rt, ep_name );
448 if( (ep = uta_get_ep( rt, ep_name )) == NULL ) { // not there yet, make
449 if( (ep = (endpoint_t *) malloc( sizeof( *ep ) )) == NULL ) {
450 fprintf( stderr, "[WARN] rmr: rt_ensure: malloc failed for endpoint creation: %s\n", ep_name );
455 ep->open = 0; // not connected
456 ep->addr = uta_h2ip( ep_name );
457 ep->name = strdup( ep_name );
459 rmr_sym_put( rt->hash, ep_name, 1, ep );