# API and build change and fix summaries. Doc corrections
# and/or changes are not mentioned here; see the commit messages.
+2023 May 28; version 4.9.1
+ Fixes IPv6 support for binding to IPv6 interfaces (RIC-985)
+
2023 Feb 14; version 4.9.0
Added new message type for RIC subscription delete required
set( major_version "4" ) # should be automatically populated from git tag later, but until CI process sets a tag we use this
set( minor_version "9" )
-set( patch_level "0" )
+set( patch_level "1" )
set( install_root "${CMAKE_INSTALL_PREFIX}" )
set( install_inc "include/rmr" )
version 4.0.0, the RMR versions should no longer skip.
+2023 May 28; version 4.9.1
+------------------------------
+
+Fixes IPv6 binding in RMR (RIC-985)
+
+
2023 Feb 14; version 4.9.0
------------------------------
Added new message type for subscription delete required
+
2022 Nov 10; version 4.8.5
------------------------------
Fixes for errors detected by source code scanning (RIC-939)
+
2022 May 11; version 4.8.3
------------------------------
Added header definitions for config transfer messages (RIC-638)
+
2022 January 17; version 4.8.2
------------------------------
}
// ---- utility -----------------------------------------------------------------------------------
+
+int isspace_with_fence(int c) {
+ _mm_lfence();
+ return isspace( c );
+}
+
/*
Little diddy to trim whitespace and trailing comments. Like shell, trailing comments
must be at the start of a word (i.e. must be immediatly preceeded by whitespace).
pthread_mutex_unlock( ctx->rtgate );
}
-int isspace_with_fence(int c) {
- _mm_lfence();
- return isspace( c );
-}
-
#endif
static int announced = 0;
uta_ctx_t* ctx = NULL;
char bind_info[256]; // bind info
+ char* my_ip = NULL;
char* proto = "tcp"; // pointer into the proto/port string user supplied
char* port; // pointer into the proto_port buffer at the port value
char* interface = NULL; // interface to bind to (from RMR_BIND_IF, 0.0.0.0 if not defined)
}
}
- ctx->ip_list = mk_ip_list( port ); // suss out all IP addresses we can find on the box, and bang on our port for RT comparisons
+ ctx->ip_list = mk_ip_list( port ); // suss out all IP addresses we can find on the box, and bang on our port for RT comparisons
+ my_ip = get_default_ip( ctx->ip_list ); // and (guess) at what should be the default to put into messages as src
+ if( my_ip == NULL ) {
+ rmr_vlog( RMR_VL_WARN, "rmr_init: default ip address could not be sussed out, using name\n" );
+ my_ip = strdup( ctx->my_name ); // if we cannot suss it out, use the name rather than a nil pointer
+ }
+
if( flags & RMRFL_NAME_ONLY ) {
ctx->my_ip = strdup( ctx->my_name ); // user application or env var has specified that IP address is NOT sent out, use name
} else {
- ctx->my_ip = get_default_ip( ctx->ip_list ); // and (guess) at what should be the default to put into messages as src
- if( ctx->my_ip == NULL ) {
- rmr_vlog( RMR_VL_WARN, "rmr_init: default ip address could not be sussed out, using name\n" );
- ctx->my_ip = strdup( ctx->my_name ); // if we cannot suss it out, use the name rather than a nil pointer
- }
+ ctx->my_ip = strdup( my_ip );
}
- if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, " default ip address: %s\n", ctx->my_ip );
+ if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "default ip address: %s\n", ctx->my_ip );
if( (tok = getenv( ENV_WARNINGS )) != NULL ) {
if( *tok == '1' ) {
}
}
+ if( (interface = getenv( ENV_BIND_IF )) == NULL ) { // if specific interface not defined, listen on all (IPv4, IPv6, or interface name)
+ /*
+ compares the first ip sussed out by mk_ip_list (returned by get_default_ip)
+ NOTE: this might be not work very predictable in dual-stack where an interface can have IPv4 and IPv6 addresses assigned,
+ meaning that it can select either IPv4 or IPv6 on applications restarts (depends on the order of IP addresses assigned on the interface)
+ */
+ if( my_ip[0] == '[' ) { // IPv6
+ interface = "[::]";
+ } else { // IPv4
+ interface = "0.0.0.0";
+ if( flags & RMRFL_NAME_ONLY ) { // if hostname is given instead of IP in RMR source address
+ rmr_vlog( RMR_VL_WARN, "rmr_init: hostname:ip is provided as source information for rts() calls, falling back to any IPv4\n" );
+ }
+ }
+ }
- if( (interface = getenv( ENV_BIND_IF )) == NULL ) { // if specific interface not defined, listen on all
- interface = "0.0.0.0";
+ if( my_ip != NULL ) {
+ free( my_ip );
}
snprintf( bind_info, sizeof( bind_info ), "%s:%s", interface, port );
# run all tests; generates .gcov and .dcov files.
all:
- unit_test.ksh
+ bash unit_test.ksh
# a generic rule driven from the unit_test script to run all stand alone
# vetters after the build
# remove intermediates
clean:
- rm -f *.gcov *.gcda *.dcov *.gcno
+ rm -f *.gcov *.gcda *.dcov *.gcno *.stash*
# remove anything that can be built
nuke: clean
- rm -f ring_test symtab_test
+ rm -f ring_test symtab_test logging_test mbuf_api_test rmr_debug_si_test rmr_si_rcv_test rmr_si_test si95_test tools_test
#
# NOTE: this makefile assumes that RMR has been built using the directory .build
-# at the top most repo directory (e.g. ../../.build). It can be changed
+# at the top most repo directory (e.g. ../../.build). It can be changed
# if you need to by adding "build_path=<path>" to the make command line.
# To use this makefile to build on a system where RMR is already installed
# try: make build_path=/usr/local/lib
LIBRARY_PATH = $(LD_LIBRARY_PATH)
# These programmes are designed to test some basic application level functions
-# from the perspective of two, or more, communicating processes.
+# from the perspective of two, or more, communicating processes.
-.PHONY: all all_si
-all: sender receiver caller mt_receiver v_sender ex_rts_receiver
-
-all_si: sender_si receiver_si
+.PHONY: all
+all: sender receiver caller mt_receiver v_sender ex_rts_receiver lreceiver lsender
# ------ all builds are si95 now ---------------------------------------------------
# clean removes intermediates; nuke removes everything that can be built
.PHONY: clean nuke
clean:
- rm -f *.o
+ rm -f *.o *stash.inc
nuke: clean
- rm -f sender receiver caller mt_receiver
+ rm -f sender receiver caller mt_receiver v_sender ex_rts_receiver lreceiver lsender
#include <rmr/rmr.h>
#define TRACE_SIZE 40 // bytes in header to provide for trace junk
-#define WBUF_SIZE 1024
+#define WBUF_SIZE 2048
/*
Thread data
sbuf = rmr_alloc_msg( control->mrc, 512 ); // alloc first send buffer; subsequent buffers allcoated on send
- memset( trace, 0, sizeof( trace ) );
+ memset( trace, 0, sizeof( trace ) );
while( count < control->n2send ) { // we send n messages after the first message is successful
snprintf( trace, 100, "%lld", (long long) time( NULL ) );
rmr_set_trace( sbuf, trace, TRACE_SIZE ); // fully populate so we dont cause a buffer realloc
- snprintf( wbuf, 200, "count=%d tr=%s %d stand up and cheer! @ %d", count, trace, rand(), control->id );
+ snprintf( wbuf, WBUF_SIZE, "count=%d tr=%s %d stand up and cheer! @ %d", count, trace, rand(), control->id );
snprintf( sbuf->payload, 300, "%d %d|%s", sum( wbuf ), sum( trace ), wbuf );
snprintf( xbuf, 200, "%31d", xaction_id );
rmr_bytes2xact( sbuf, xbuf, 32 );
}
control->state = -state; // signal inactive to main thread; -1 == pass, 0 == fail
- fprintf( stderr, "<THRD> [%s] tid=%-2d sent=%d ok-acks=%d bad-acks=%d drops=%d failures=%d retries=%d\n",
+ fprintf( stderr, "<THRD> [%s] tid=%-2d sent=%d ok-acks=%d bad-acks=%d drops=%d failures=%d retries=%d\n",
state ? "PASS" : "FAIL", control->id, count, ok_msg, bad_msg, drops, fail_count, rt_count );
pt_info = malloc( sizeof( pthread_t ) * nthreads );
if( cvs == NULL ) {
fprintf( stderr, "<CALL> unable to allocate control vector\n" );
- exit( 1 );
+ exit( 1 );
}
}
fprintf( stderr, "<RCVR> mtype histogram: %s\n", wbuf );
- fprintf( stderr, "<RCVR> [%s] %ld messages; good=%ld acked=%ld bad=%ld bad-trace=%ld bad-sub_id=%ld\n",
+ fprintf( stderr, "<RCVR> [%s] %ld messages; good=%ld acked=%ld bad=%ld bad-trace=%ld bad-sub_id=%ld\n",
!!(errors + bad + bad_tr) ? "FAIL" : "PASS", count, good, ack_count, bad, bad_tr, bad_sid );
sleep( 2 ); // let any outbound acks flow before closing
# If the var is not set, and nether of these directories exists,
# the tests will not be executed.
#
-# At the moment, it assumes a deb based system for tests.
+# At the moment, it assumes a deb based system for tests.
#
# Author: E. Scott Daniels
# Date: 2019
if (( purge ))
then
- rm -f sender receiver
+ rm -f sender receiver
fi
echo "----- app --------------------"
run_test run_multi_test.sh $build
echo "----- round robin -----------"
-run_test run_rr_test.sh
+run_test run_rr_test.sh
echo "----- rts -------------------"
run_test run_rts_test.sh -s 5 -d 100
-echo "----- extended payload nocopy no clone------"
+echo "----- extended payload nocopy no clone ------"
run_test run_exrts_test.sh -d 10 -n 1000
-echo "----- extended payload copy clone------"
+echo "----- extended payload copy clone ------"
run_test run_exrts_test.sh -d 10 -n 1000 -c 11
+echo "----- ipv6 support ------"
+run_test run_ipv6_test.sh -n 10 -d 100000
+
if (( errors == 0 ))
then
echo "[PASS] all test pass"
--- /dev/null
+#!/usr/bin/env ksh
+# vim: ts=4 sw=4 noet :
+#==================================================================================
+# Copyright (c) 2019-2021 Nokia
+# Copyright (c) 2018-2021 AT&T Intellectual Property
+# Copyright (c) 2023 Alexandre Huff
+#
+# 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: run_ipv6_test.sh
+# Abstract: This is a simple script to set up and run the basic send/receive
+# processes using IPv6 for some library validation on top of si95.
+# This particular test starts a sender and a receiver application.
+# All messages go to the receiver and an ack is sent back to the
+# sending process.
+#
+# The number of message, and the delay between each message may be given
+# on the command line.
+#
+# Example command line:
+# bash ./run_ipv6_test.sh # default 10 messages at 1 msg/sec
+# bash ./run_ipv6_test.sh -n 10 -d 100000 # 10 messages, delay 100 ms
+#
+# Date: 28 May 2023
+# Author: Alexandre Huff
+# ---------------------------------------------------------------------------------
+
+
+# The sender and receiver are run asynch. Their exit statuses are captured in a
+# file in order for the 'main' to pick them up easily.
+#
+function run_sender {
+ ./sender${si} $nmsg $delay
+ echo $? >/tmp/PID$$.src # must communicate state back via file b/c asynch
+}
+
+# start receiver listening for nmsgs from each thread
+function run_rcvr {
+ ./receiver${si} $nmsg
+ echo $? >/tmp/PID$$.rrc
+}
+
+# Generate a route table that is tailored to our needs.
+#
+function mk_rt {
+
+cat <<endKat >ipv6.rt
+# This is a route table to test IPv6 support with sender and receiver
+
+newrt | start
+mse | 0 | 0 | $localhost:4560
+mse | 1 | 10 | $localhost:4560
+mse | 2 | 20 | $localhost:4560
+rte | 3 | $localhost:4560
+rte | 4 | $localhost:4560
+rte | 5 | $localhost:4560
+rte | 6 | $localhost:4560
+rte | 7 | $localhost:4560
+rte | 8 | $localhost:4560
+rte | 9 | $localhost:4560
+newrt | end
+endKat
+}
+
+function ensure_ipv6 {
+ v6=$(ip -6 address show lo)
+ if [[ ! -z $v6 ]]
+ then
+ v4=$(ip -4 address show lo)
+ if [[ ! -z $v4 ]]
+ then
+ export RMR_BIND_IF=$localhost # force RMR to bind to an IPv6 address (defaults to IPv4 on dual stack)
+ echo "[INFO] forcing RMR binding to $localhost"
+ else
+ echo "[INFO] using IPv6-only stack" # RMR binds to any interface address, no need to specify RMR_BIND_IF
+ fi
+ else
+ echo "[WARN] skipping IPv6 test, unable to detect IPv6 stack"
+ exit 0 # exit success in favor to allow overall tests to pass if all other tests have passed
+ fi
+}
+
+# ---------------------------------------------------------
+
+nmsg=10 # total number of messages to be exchanged (-n value changes)
+delay=1000000 # microsec sleep between msg 1,000,000 == 1s
+wait=1
+rebuild=0
+verbose=0
+dev_base=1 # -D turns off to allow this to run on installed libs
+force_make=0
+si=""
+localhost="[::1]"
+
+
+
+while [[ $1 == -* ]]
+do
+ case $1 in
+ -B) rebuild=1;;
+ -d) delay=$2; shift;;
+ -D) dev_base=0;;
+ -n) nmsg=$2; shift;;
+ -M) force_make=1;;
+ -v) verbose=1;;
+
+ *) echo "unrecognised option: $1"
+ echo "usage: $0 [-B] [-M] [-d micro-sec-delay] [-n num-msgs]"
+ echo " -B forces an RMR rebuild"
+ echo " -M force test applications to rebuild"
+ exit 1
+ ;;
+ esac
+
+ shift
+done
+
+ensure_ipv6
+
+if (( verbose ))
+then
+ echo "2" >.verbose
+ export RMR_VCTL_FILE=".verbose"
+fi
+
+src_root="../.."
+if [[ -z $BUILD_PATH ]] # if not explicitly set, assume one of our standard spots
+then
+ if [[ -d $src_root/.build ]] # look for build directory in expected places
+ then # run scripts will honour this
+ export BUILD_PATH=$src_root/.build
+ else
+ if [[ -d $src_root/build ]]
+ then
+ export BUILD_PATH=$src_root/build
+ else
+ echo "[ERR] BUILD_PATH not set and no logical build directory exists to use"
+ echo "[INFO] tried: $src_root/build and $src_root/.build"
+ exit 1
+ fi
+ fi
+ echo "[INFO] using discovered build directory: $BUILD_PATH"
+else
+ echo "[INFO] using externally supplied build directory: $BUILD_PATH"
+fi
+
+if (( rebuild ))
+then
+ set -e
+ $SHELL ./rebuild.sh
+ set +e
+fi
+
+if [[ -z $LD_LIBRARY_PATH ]] # cmake test will set and it must be honoured
+then
+ if (( dev_base )) # assume we are testing against what we've built, not what is installed
+ then
+ if [[ -d $BUILD_PATH/lib64 ]]
+ then
+ export LD_LIBRARY_PATH=$BUILD_PATH:$BUILD_PATH/lib64:$LD_LIBRARY_PATH
+ else
+ export LD_LIBRARY_PATH=$BUILD_PATH:$BUILD_PATH/lib:$LD_LIBRARY_PATH
+ fi
+ export LIBRARY_PATH=$LD_LIBRARY_PATH
+ else # -D option gets us here to test an installed library
+ export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
+ export LIBRARY_PATH=$LD_LIBRARY_PATH
+ fi
+fi
+
+export RMR_SEED_RT=${RMR_SEED_RT:-./ipv6.rt} # allow easy testing with different rt
+
+if [[ ! -f $RMR_SEED_RT ]] # create special rt
+then
+ mk_rt
+fi
+
+if (( rebuild || force_make )) || [[ ! -f ./sender${si} || ! -f ./receiver${si} ]]
+then
+ if ! make -B sender${si} receiver${si} >/tmp/PID$$.log 2>&1
+ then
+ echo "[FAIL] cannot find sender{$si} and/or receiver${si} binary, and cannot make them.... humm?"
+ cat /tmp/PID$$.log
+ rm -f /tmp/PID$$.*
+ exit 1
+ fi
+fi
+
+run_rcvr &
+sleep 2 # if caller starts faster than rcvr we can drop, so pause a bit
+run_sender &
+
+wait
+rrc=$(head -1 /tmp/PID$$.rrc) # get pass/fail state from each
+src=$(head -1 /tmp/PID$$.src)
+
+if (( !! (src + rrc) ))
+then
+ echo "[FAIL] sender rc=$src receiver rc=$rrc"
+else
+ echo "[PASS] sender rc=$src receiver rc=$rrc"
+fi
+
+rm /tmp/PID$$.*
+rm -f .verbose
+
+exit $(( !! (src + rrc) ))
+
sbuf->len = strlen( sbuf->payload ) + 1; // our receiver likely wants a nice acsii-z string
sbuf->state = 0;
+
+ fprintf( stderr, "<SNDR> sending msg type %d\n", sbuf->mtype );
+
sbuf = rmr_send_msg( mrc, sbuf ); // send it (send returns an empty payload on success, or the original payload on fail/retry)
switch( sbuf->state ) {
}
fprintf( stderr, "<SNDR> draining begins\n" );
- timeout = time( NULL ) + 20; // allow 20 seconds for the pipe to drain from the receiver
+ timeout = time( NULL ) + 10; // allow 10 seconds for the pipe to drain from the receiver
while( time( NULL ) < timeout ) {
if( rcv_fd >= 0 ) {
while( (nready = epoll_wait( ep_fd, events, 1, 100 )) > 0 ) { // if something ready to receive (non-blocking check)
pass = 0;
}
- fprintf( stderr, "<SNDR> [%s] sent=%d rcvd=%d rts-ok=%d failures=%d retries=%d\n",
+ fprintf( stderr, "<SNDR> [%s] sent=%d rcvd=%d rts-ok=%d failures=%d retries=%d\n",
pass ? "PASS" : "FAIL", count, rcvd_count, rts_ok, fail_count, rt_count );
rmr_close( mrc );
Abstract: This version of the sender will perform verification on response
messages received back from the receiver.
- It is expected that the response messages are created with the
+ It is expected that the response messages are created with the
functions in the test_support module so that they can easily be
vetted here.
int stats_freq = 100;
int successful = 0; // set to true after we have a successful send
char wbuf[DATA_SIZE];
- char me[128]; // who I am to vet rts was actually from me
+ char me[256]; // who I am to vet rts was actually from me
char trace[1024];
long timeout = 0;
long rep_timeout = 0; // report/stats timeout
exit( 1 );
}
} else {
- fprintf( stderr, "<VSNDR> abort: epoll not supported, can't listen for messages\n" );
+ fprintf( stderr, "<VSNDR> abort: epoll not supported, can't listen for messages\n" );
}
sbuf = rmr_alloc_msg( mrc, MSG_SIZE ); // alloc first send buffer; subsequent buffers allcoated on send
if( rbuf && rbuf->state == RMR_OK ) {
if( rmr_payload_size( rbuf ) >= HDR_SIZE+DATA_SIZE ) { // vet message
rts_ok += validate_msg( rbuf->payload, rbuf->len );
- } else {
+ } else {
fprintf( stderr, "<VSNDR> received short response: >%d expected, got %d\n", HDR_SIZE+DATA_SIZE, rmr_payload_size( rbuf ) );
short_count++;
}
state = rt_link2_ep( NULL );
errors += fail_if_equal( state, -1, "call to link2_ep with nil ep returned true when false expected" );
+ unlink( ".ut_rmr_verbose" );
return errors;
}
// ------ specific edge case tests -------------------------------------------------------------------------------
errors += lg_clone_test( );
+ unlink( ".ut_rmr_verbose" );
+
return errors; // 1 or 0 regardless of count
}
/*
Generate a simple route table (for all but direct route table testing).
This table contains multiple tables inasmuch as a second update set of
- records follows the initial set.
+ records follows the initial set.
*/
static void gen_rt( uta_ctx_t* ctx ) {
int fd;
ctx->rtable = NULL;
gen_mlnl_rt( ctx ); // ensure that a file with missing last new line does not trip us up
errors += fail_if_nil( ctx->rtable, "read route table file with missing last newline did not produce a table" );
-
+
ctx->rtable = NULL;
gen_rt( ctx ); // forces a static load with some known info since we don't start the rtc()
errors += fail_if_nil( ctx->rtable, "read multi test route table file did not produce a table" );
rtc( NULL ); // coverage test with nil pointer
rtc( ctx );
+ unlink( ".ut_rmr_verbose" );
return errors;
}
errors += fail_not_equal( rmr_payload_size( mbuf ), 1024, "realloc payload (clone+nocopy) validation of unchanged alloc length fails" );
errors += fail_not_equal( strncmp( payload_str, mbuf->payload, strlen( payload_str )), 0, "realloc payload (clone+nocopy) validation of unchanged payload fails" );
+ unlink( ".ut_rmr_verbose" );
return errors;
}
dump_n( payload_str, "A dump", strlen( payload_str ) );
dump_40( payload_str, "another dump" );
+ unlink( ".ut_rmr_verbose" );
+
return errors;
}
/*
Generate a simple route table (for all but direct route table testing).
- This gets tricky inasmuch as we generate two in one; first a whole table
+ This gets tricky inasmuch as we generate two in one; first a whole table
and then two update tables. The first is a table with a bad counter in the
last record to test that we don't load that table and error. The second
is a good update. The same applies to the meid map; first has a bad counter
"mme_ar | e2t-1 | one two three four\r" // also test bloody apple way with \r
"mme_del | one two\n"
"mme_del \n" // short entries drive various checks for coverage
- "mme_ar \n"
- "mme_ar | e2t-0 \n"
+ "mme_ar \n"
+ "mme_ar | e2t-0 \n"
"meid_map | end | 5\n"; // this will fail as the short recs don't "count"
write( fd, rt_stuff, strlen( rt_stuff ) );
"mme_del | meid11 meid12 meid13\n" // includes a non-existant meid
"meid_map | end | 1\n";
write( fd, rt_stuff, strlen( rt_stuff ) );
-
+
close( fd );
read_static_rt( ctx, 1 ); // force in verbose mode to see stats on tty if failure
unlink( "utesting.rt" );
close( fd );
read_static_rt( ctx, 1 ); // force in verbose mode to see stats on tty if failure
- unlink( "utesting.rt" );
+ unlink( "Xutesting.rt" );
}