--- /dev/null
+#include "debug.h"
+
+int verbose = 0;
+
+/* vim: set ts=4 sw=4 et: */
--- /dev/null
+/*
+ File name : verbose.h
+ Projet : MERLIN
+ Date of creation : 2025/03/19
+ Version : 1.0
+ Copyright : Thales SIX
+ Author : Laurent Mazet <laurent.mazet@thalesgroup.com>
+
+ Description : This file contains verbose macros
+
+ History :
+ - initial version
+*/
+
+#ifndef __VERBOSE_H__
+#define __VERBOSE_H__
+
+#include <stdio.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ Declare a verbose level. Should be used only one time per module
+ outside the main function.
+
+ @param module module name
+ @param init initial value
+*/
+#define DECLARE_VERBOSE_LEVEL(module, init) \
+ int verbose_##module = init;
+
+/**
+ Change a verbose level.
+
+ @param module module name
+ @param level verbose level
+*/
+#define CHANGE_VERBOSE_LEVEL(module, level) \
+ do { \
+ extern int verbose_##module; \
+ verbose_##module = level; \
+ } while(0)
+
+/**
+ Execute a statement (mostly a message display) depending of a
+ level. Level is defined by global variable verbose_`module`.
+
+ @param module module name
+ @param level verbose level
+ @param statement controled statement
+*/
+#define VERBOSE(module, level, statement...) \
+ do { \
+ extern int verbose_##module; \
+ if (level > verbose_##module) \
+ break; \
+ statement; \
+ } while(0)
+
+/**
+ Printf macros
+
+ @param format format string
+ @param ... optionnal arguments
+*/
+#define PRINTF(format...) \
+ do { \
+ if (__LOG_PRINT__) { \
+ printf (__LOG_PRINT__ ": " format); \
+ fflush (stdout); \
+ } else { \
+ printf (format); \
+ fflush (stdout); \
+ } \
+ } while (0)
+
+
+/**
+ Fprintf macros
+
+ @param stream file stream (stdout, stderr, FILE *...)
+ @param format format string
+ @param ... optionnal arguments
+*/
+#define FPRINTF(stream, format...) \
+ do { \
+ if (__LOG_PRINT__) { \
+ fprintf (stream, __LOG_PRINT__ ": " format); \
+ fflush (stream); \
+ } else { \
+ fprintf (stream, format); \
+ fflush (stream); \
+ } \
+ } while (0)
+
+/**
+ Prefix for log printing (default is __FILE__)
+*/
+#ifndef __LOG_PRINT__
+#define __LOG_PRINT__ __FILE__
+#endif
+
+__END_DECLS
+
+#endif /* __DEBUG_H__ */
--- /dev/null
+# Messaging over raw Ethernet
+
+MDIR = $(shell pwd)
+
+PACKAGE = more
+
+IFLAGS += -I. -I../src/debug -I../src/gpio
+OFLAGS = -O4 -minline-all-stringops -fsingle-precision-constant
+#OFLAGS += -msse -msse2
+CFLAGS += -Wall -W -Wextra -g $(OFLAGS) $(IFLAGS) -fpic
+LFLAGS += -g -L$(MDIR) -Wl,-rpath=$(MDIR)
+
+LIBDEP = $(wildcard *.ld)
+NAMES = $(LIBDEP:.ld=)
+DEBIAN = $(addprefix debian-,$(NAMES))
+PACKS = $(addprefix pack-,$(NAMES))
+LIBPRE = lib
+LIBEXT = so
+LIBFUL = $(shell cat $(wildcard *.ld) | sed 's/:.*//')
+LIBNIC = $(shell cat $(wildcard *.ld) | sed 's/\(\.$(LIBEXT)\.[0-9]*\)\..*:.*/\1/')
+LIBNAM = $(shell cat $(wildcard *.ld) | sed 's/\(\.$(LIBEXT)\)\..*:.*/\1/')
+LIBSTA = $(shell cat $(wildcard *.ld) | sed 's/\(\.$(LIBEXT)\)\..*:.*/.a/')
+
+TESTS += $(shell [ "$(wildcard test/*.c)" ] && grep -l '/\* *linker:' test/*.c | sed 's/\.c//g')
+
+TARBALL = $(PACKAGE)_$(shell date +%F).tgz
+
+ALLDOC = $(wildcard *.3) $(wildcard $(addprefix README.,$(NAMES)))
+ALLHEA = $(wildcard *.h) $(wildcard test/*.h)
+ALLSRC = $(wildcard *.c) $(wildcard test/*.c)
+ALLDEP = $(ALLSRC:.c=.d) $(addprefix $(LIBPRE),$(LIBDEP:.ld=.d))
+ALLTST = $(subst /,_,$(TESTS))
+
+SHELL = bash
+
+MAKEFLAGS += -s
+
+DEBPKG = ../debian-pkg
+
+# Install
+
+DOCS = $(wildcard $(addprefix README.,$(NAMES))) COPYING makefile
+INCS = $(wildcard *.h)
+LIBS = $(LIBFUL) $(LIBNIC) $(LIBNAM) $(LIBSTA)
+MANS = $(wildcard *.3)
+TESS = $(wildcard test/*.c) $(wildcard test/*.h) test/makefile
+
+ifeq ($(DESTDIR),)
+DESTDIR = /usr/local
+endif
+
+BINDIR = $(DESTDIR)/bin
+LIBDIR = $(DESTDIR)/lib
+INCDIR = $(DESTDIR)/include/thales
+DOCDIR = $(DESTDIR)/share/doc/$(PACKAGE)
+MANDIR = $(DESTDIR)/share/man/man3
+TESDIR = $(DESTDIR)/share/doc/$(PACKAGE)/test
+
+INSTBIN = $(addprefix $(BINDIR)/, $(EXECS))
+INSTDOC = $(addprefix $(DOCDIR)/, $(DOCS))
+INSTINC = $(addprefix $(INCDIR)/, $(INCS))
+INSTLIB = $(addprefix $(LIBDIR)/, $(LIBS))
+INSTMAN = $(addprefix $(MANDIR)/, $(MANS))
+INSTTES = $(addprefix $(DOCDIR)/, $(TESS))
+
+FILES = $(INSTBIN) $(INSTDOC) $(INSTINC) $(INSTLIB) $(INSTMAN) $(INSTTES)
+
+# Functions
+
+TITLE = { tput colors 2>/dev/null | grep -qE '8|256' && echo -en "\033[0;1m$(strip $(1))\033[0;0m:\t" || echo -en "$(strip $(1)):\t"; }
+INFO = { tput colors 2>/dev/null | grep -qE '8|256' && echo -e "\033[1;34m$(strip $(1))\033[0;0m" || echo -e "$(strip $(1))"; }
+PASS = { tput colors 2>/dev/null | grep -qE '8|256' && echo -e "\033[1;32m$(strip $(1))\033[0;0m" || echo -e "$(strip $(1))"; }
+WARN = { tput colors 2>/dev/null | grep -qE '8|256' && echo -e "\033[1;33m$(strip $(1))\033[0;0m" || echo -e "$(strip $(1))"; }
+FAIL = { tput colors 2>/dev/null | grep -qE '8|256' && echo -e "\033[1;31m$(strip $(1))\033[0;0m" || echo -e "$(strip $(1))"; }
+
+_GET = awk '{sub(/\/\/.*/,"")} /\/\* *$(1):.*\*\// { sub(/^.*\/\* *$(1): */, ""); sub(/ *\*\/.*$$/, ""); print }' $(2)
+GET = $(shell $(call _GET,$(1),$(2)))
+# $(shell ./getcomments.pl -p='$(1):\s' -f='%' $(2))
+
+_VER = sed 's/.*\.$(LIBEXT)\.\([0-9.]*\):.*/\1/' $(1)
+VER = $(shell $(call _VER,$(1)))
+PVER = $(shell $(call _VER,$(subst pack-,,$(1).ld)))
+
+MKDIR = mkdir -p $(1) && chmod a+rx,go-w $(1)
+
+INSTALL = test -d `dirname $(2)` || $(call MKDIR, `dirname $(2)`) && cp -pa $(1) $(2) && { chmod a+rX,go-w -f $(2); true; }
+
+VALID = $(call TITLE, $(1)) && { $(2); } && $(call PASS, SUCCESS) || { $(call FAIL, FAILED); test; }
+
+## Generic rules
+
+all: depends
+ make $(LIBNAM) $(LIBNIC) $(LIBSTA) $(TESTS)
+
+archive:
+ $(call VALID, "Archiving $(TARBALL)", hg archive $(PACKAGE) && tar cfz ../$(TARBALL) $(PACKAGE); rm -rf $(PACKAGE))
+
+depends: $(ALLDEP)
+
+count:
+ $(call TITLE, "Counting")
+ echo
+ wc $(ALLHEA) $(ALLSRC) makefile $(ALLDOC)
+
+clean:
+ $(call TITLE, "Cleaning")
+ touch clean
+ rm -f $(wildcard *.[do] *.ho *~ test/*.[do] test/*.ho test/*~ web/*~ $(TESTS:=.log) $(TESTS:=.test)) clean
+ $(call PASS, SUCCESS)
+
+documentation: doxygen.conf $(wildcard *.[ch])
+ $(call TITLE, "Publishing $@")
+ doxygen $<; true
+ $(call PASS, SUCCESS)
+
+gcov:
+ make purge
+ CFLAGS="-O0 -fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs" make
+
+headers: $(ALLHEA:.h=.ho)
+
+install: $(FILES)
+
+$(INSTBIN):$(BINDIR)/%:%
+ $(call VALID, "File [$@]", $(call INSTALL, $<, $@))
+
+$(INSTDOC):$(DOCDIR)/%:%
+ $(call VALID, "Documentation [$@]", $(call INSTALL, $<, $@))
+
+$(INSTINC):$(INCDIR)/%:%
+ $(call VALID, "Include [$@]", $(call INSTALL, $<, $@))
+
+$(INSTLIB):$(LIBDIR)/%:%
+ $(call VALID, "Library [$@]", $(call INSTALL, $<, $@))
+
+$(INSTMAN):$(MANDIR)/%:%
+ $(call VALID, "Manpage [$@]", $(call INSTALL, $<, $@))
+
+$(INSTTES):$(TESDIR)/%:test/%
+ $(call VALID, "Test file [$@]", $(call INSTALL, $<, $@))
+ $(call VALID, "Adapt test [$@]", sed -i 's/"..\/\(.*\)"/<snd\/\1>/;/depend:/ s/: .* /: /' $@)
+
+purge: clean
+ $(call TITLE, "Purging")
+ touch purge
+ rm -rf $(TESTS) $(LIBFUL) $(LIBNIC) $(LIBNAM) $(LIBSTA) $(wildcard *.gcda *.gcno *.gcov test/*.gcda test/*.gcno test/*.gcov) documentation purge
+ $(call PASS, SUCCESS)
+
+%.test: %.c makefile
+ $(call VALID, "Extracting $@", $(call _GET,test, $<) | sed 's;{};${<:.c=};g' > $@)
+
+test_%: test/%.test test/%
+ $(call WARN, "Testing ${@:test_=}");
+ IFS=$$'\n'; \
+ LOGFILE=${<:.test=.log}; \
+ rm -f $$LOGFILE; \
+ for test in `cat $<`; do \
+ $(call INFO, "=== $$test"); \
+ eval $(VALGRIND) $$test 2>>$$LOGFILE; \
+ done; \
+ [ "`cat $<`" ] || { $(call INFO, "=== ${<:.test=}"); eval $(VALGRIND) ${<:.test=} 2>>$$LOGFILE; }; \
+ true
+
+tests: $(ALLTST)
+
+valgrind_%:
+ $(call WARN, "Activate valgrind")
+ VALGRIND="valgrind -v --leak-check=full --show-reachable=yes --log-fd=2"; \
+ export VALGRIND; \
+ make ${@:valgrind_%=test_%}
+
+%.d: %.c
+ $(call VALID, "Creating $@", { \
+ $(CC) $(IFLAGS) -MM $< -o - | sed "s;^`basename $(<:.c=.o)`;$(<:.c=.o);"; \
+ echo $(call GET,depend, $<) | sed "s;^;$(<:.c=): $(<:.c=.o) ;"; \
+ } > $@)
+
+lib%.d: %.ld
+ $(call VALID, "Creating $@", sed 's/-l[^ ]*//g;s/\(.*\)\.$(LIBEXT)\.\([0-9]*\)\.\(.*\): \(.*\)/.\1.$(LIBEXT): \4\n\1.a: \4\n\1.$(LIBEXT).\2.\3: .\1.$(LIBEXT)\n\t$$(call VALID, "Assembling $$@", cp -f $$< $$@ \&\& rm $$<)\n\1.$(LIBEXT) \1.$(LIBEXT).\2: \1.$(LIBEXT).\2.\3\n\t$$(call VALID, "Assembling $$@", ln -sf $$< $$@)/' $< > $@)
+
+%.o: %.c
+ $(call VALID, "Building $@", $(CC) $(CFLAGS) $(call GET,cflags, $<) -c $< -o $@)
+
+%.ho: %.h
+ $(call VALID, "Building $@", $(CC) $(CFLAGS) $(call GET,cflags, $<) -x c -c $< -o $@)
+
+%.a:
+ $(call VALID, "Archive $@", $(AR) -rc $@ $^)
+
+.$(LIBPRE)%:
+ $(call TITLE, "Linking `echo $@ | sed 's/^\.//'`")
+ $(CC) $(LDFLAGS) $$(cat `echo $@ | sed 's/\.$(LIBPRE)\(.*\)\.$(LIBEXT)/\1.ld/'` | sed 's/.*://') -shared -Wl,-soname=$$(cat `echo $@ | sed 's/\.$(LIBPRE)\(.*\)\.$(LIBEXT)/\1.ld/'` | sed 's/^\(.*\.$(LIBEXT)\.[0-9]*\)\..*/\1/') -o $@ || { $(call FAIL, FAILED); test; }
+ $(call PASS, SUCCESS)
+
+$(PACKS):pack-%: %.ld depends
+ $(call TITLE, "Packing $@-$(call VER,$<).tgz")
+ [ ! -e $@ ] || rm -rf $@
+ mkdir $@
+ awk 'function alength(A, n, val) { n = 0; for (val in A) n++; return n } function a_sort(A, hold, i, j, n) { n = alength(A); for (i = 2; i <= n ; i++) { hold = A[j = i]; while (A[j-1] > hold) { j--; A[j+1] = A[j] } A[j] = hold } delete A[0 ]; return n } /\.o:/ { sub(/.*: */, ""); gsub (/\.o/, ".d"); t=t" "$$0 } END { split(t, a); a_sort(a); for (k in a) { if (prev != a[k]) print a[k]; prev=a[k] } }' `sed 's/.*://;s/\.o/.d/g;s/-l[^ ]*//' $<` | xargs -i% cp % $@
+ cp $< makefile COPYING $(wildcard ${<:.ld=.3} README.${<:.ld=}) $@
+ mkdir $@/test
+ awk 'function alength(A, n, val) { n = 0; for (val in A) n++; return n } function a_sort(A, hold, i, j, n) { n = alength(A); for (i = 2; i <= n ; i++) { hold = A[j = i]; while (A[j-1] > hold) { j--; A[j+1] = A[j] } A[j] = hold } delete A[0 ]; return n } /$(LIBPRE)$(<:.ld=.$(LIBEXT))/ && ! /^$(LIBPRE)$(<:.ld=.$(LIBEXT))/ { sub(/.*: */, ""); gsub (/\.o/, ".d"); t=t" "$$0 } END { split(t, a); a_sort(a); for (k in a) { if ((prev != a[k]) && (a[k] != "$(LIBPRE)$(<:.ld=.$(LIBEXT))")) print a[k]; prev=a[k] } }' test/*.d | xargs awk 'function alength(A, n, val) { n = 0; for (val in A) n++; return n } function a_sort(A, hold, i, j, n) { n = alength(A); for (i = 2; i <= n ; i++) { hold = A[j = i]; while (A[j-1] > hold) { j--; A[j+1] = A[j] } A[j] = hold } delete A[0 ]; return n } /\.o:/ { sub(/.*: */, ""); gsub (/\.o/, ".d"); t=t" "$$0 } END { split(t, a); a_sort(a); for (k in a) { if ((prev != a[k]) && (a[k] != "\\") && !(a[k] ~ /\.\./)) print a[k]; prev=a[k] } }' | xargs -i% cp % $@/test
+ cp test/makefile $@/test
+ tar czf $@-$(call VER,$<).tgz $@
+ rm -rf $@
+ $(call PASS, SUCCESS)
+
+packages: $(PACKS)
+
+$(DEBIAN):debian-%: pack-%
+ $(call TITLE, "Debian package: $(<:pack-%=%) "$(call PVER,$<))
+ [ -d $(DEBPKG) ] || mkdir $(DEBPKG)
+ [ ! -d $(DEBPKG)/$<-$(call PVER,$<) ] || rm -rf $(DEBPKG)/$<-$(call PVER,$<)
+ [ ! -d $(DEBPKG)/$(<:pack-%=%)-$(call PVER,$<) ] || rm -rf $(DEBPKG)/$(<:pack-%=%)-$(call PVER,$<)
+ tar xfz $<-$(call PVER,$<).tgz -C $(DEBPKG)
+ mv $(DEBPKG)/$< $(DEBPKG)/$(<:pack-%=%)-$(call PVER,$<)
+ cd $(DEBPKG); tar czf $(<:pack-%=%)_$(call PVER,$<).orig.tar.gz $(<:pack-%=%)-$(call PVER,$<)
+ cp -par debian $(DEBPKG)/$(<:pack-%=%)-$(call PVER,$<)
+ for f in $(DEBPKG)/$(<:pack-%=%)-$(call PVER,$<)/debian/*.$(<:pack-%=%); do mv $$f $${f/.$(<:pack-%=%)}; done
+ cd $(DEBPKG)/$(<:pack-%=%)-$(call PVER,$<) && { echo; debuild; }
+
+debian: $(DEBIAN)
+
+test/%: test/%.o test/%.d
+ $(call VALID, "Linking $@", $(CC) $(LDFLAGS) $(LFLAGS) $< $(call GET,linker, $(<:.o=.c)) -o $@)
+
+include $(wildcard *.d) $(wildcard test/*.d)
+
+## Phony
+
+.PHONY: all archive clean count depends headers install packages purge
--- /dev/null
+/*
+ Messaging Over Raw Ethernet communication library
+
+ Copyrigth Thales 20250319
+*/
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#if WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#define OPT_TYPE char
+#define SETBLOCK(fd) do { u_long _m = 0; ioctlsocket (fd, FIONBIO, &_m); } while (0)
+#define SETNOBLK(fd) do { u_long _m = 1; ioctlsocket (fd, FIONBIO, &_m); } while (0)
+#else /* Linux */
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#define OPT_TYPE int
+#define SETBLOCK(fd) fcntl (fd, F_SETFL, 0)
+#define SETNOBLK(fd) fcntl (fd, F_SETFL, 0_NONBLOCK)
+#endif
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+
+#include "more.h"
+
+/**
+ MORE address structure
+*/
+typedef struct {
+ enum { MORE_error, MORE_tx, MORE_rx } mode;
+ char mac_address[17];
+} MORE_addr_t;
+
+#define MAX_SAP_NUMBER 256
+
+/**
+ Type of private descriptor associated to each connected SAP.
+*/
+typedef struct {
+ uint8_t mac[6];
+ int sock;
+ struct sockaddr_ll sock_addr;
+ uint16_t short tx_seqnum;
+ uint16_t short rx_seqnum;
+ unsigned char tx_buffer[9014]; // jumbo frame (1514 standard frame)
+ unsigned char rx_buffer[9014]; // jumbo frame (1514 standard frame)
+ int is_blocking;
+} MORE_descriptor_t ;
+
+/**
+ List of private descriptor associated to each connected MORE.
+*/
+MORE_descriptor_t *MORE_list[MAX_MORE_NUMBER] = { 0 };
+
+/**
+ Init flag
+*/
+static int __init = 1;
+
+void free_all_mores (void)
+{
+
+ VERBOSE (more, 2, FPRINTF (stdout, "free_all_mores\n"));
+
+ int i;
+ for (i = 0; i < MAX_SAP_NUMBER; i++) {
+ free (MORE_list[i]);
+ MORE_list[i] = NULL;
+ }
+}
+
+static inline int parse_url (const char *ascii_url, SAP_addr_t *addr)
+{
+ struct in_addr **addr_list;
+ char url_to_parse[256] = { 0 };
+
+ strncpy (url_to_parse, ascii_url, sizeof (url_to_parse) - 1);
+ if (strncmp (url_to_parse, "udp://", 6) == 0)
+ addr->type = SAP_udp;
+ else if (strncmp (url_to_parse, "tcp://", 6) == 0)
+ addr->type = SAP_tcp;
+ else
+ return -1;
+
+ char *hostname = strtok (url_to_parse + 6, ":");
+ char *port = strtok (NULL, ":");
+ if ((hostname == NULL) || (port == NULL)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ addr->sockaddr.sin_family = AF_INET;
+ addr->sockaddr.sin_port = htons (atoi (port));
+
+ VERBOSE (sap, 1, PRINTF ("parse URL %s %s\n", hostname, port));
+
+ if (hostname[0] != '*') {
+ struct hostent *he = gethostbyname (hostname);
+ if (he == NULL) {
+ errno = EHOSTUNREACH;
+ return -1;
+ }
+
+ addr_list = (struct in_addr **) he->h_addr_list;
+ addr->sockaddr.sin_addr.s_addr = addr_list[0]->s_addr;
+ } else {
+ addr->sockaddr.sin_addr.s_addr = INADDR_ANY;
+ }
+
+ return 0;
+}
+
+static inline int sap_create_and_listen (char *local_address, int doListen,
+ int port_offset)
+{
+ SAP_addr_t addr;
+ int sap;
+
+ VERBOSE (sap, 1, PRINTF ("SAP local addr %s\n", local_address));
+ int rc = parse_url (local_address, &addr);
+
+ addr.sockaddr.sin_port =
+ htons (ntohs (addr.sockaddr.sin_port) + port_offset);
+ VERBOSE (sap, 1, PRINTF ("local port: %d\n", ntohs (addr.sockaddr.sin_port)));
+
+ if (rc < 0) {
+ errno = EINVAL;
+ return rc;
+ }
+
+ switch (addr.type) {
+ case SAP_tcp:
+ sap = socket (PF_INET, SOCK_STREAM, 0);
+ if (sap >= 0) {
+ int val = 1;
+ setsockopt (sap, SOL_SOCKET, SO_REUSEADDR, (OPT_TYPE *)&val, sizeof (val));
+ rc = bind (sap, (struct sockaddr *)&addr.sockaddr, sizeof (addr.sockaddr));
+ if (rc < 0)
+ return rc;
+ if (doListen) {
+ rc = listen (sap, 5);
+ if (rc < 0) return rc;
+ }
+ }
+ break;
+ case SAP_udp:
+ sap = socket (PF_INET, SOCK_DGRAM, 0);
+ if (sap >= 0) {
+ int val = 1;
+ setsockopt (sap, SOL_SOCKET, SO_REUSEADDR, (OPT_TYPE *)&val, sizeof (val));
+ rc = bind (sap, (struct sockaddr *)&addr.sockaddr, sizeof (addr.sockaddr));
+ if (rc < 0)
+ return rc;
+ }
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ break;
+ }
+
+ SETNOBLK (sap);
+
+ return sap;
+}
+
+int SAP_Create (char *local_address)
+{
+ return sap_create_and_listen (local_address, 1, 0);
+}
+
+int SAP_Connect (char *local_address, char * remote_address)
+{
+ return SAP_Connect_Offset (local_address, remote_address, 0);
+}
+
+int SAP_Connect_Offset (char *local_address, char *remote_address, int port_offset)
+{
+ SAP_addr_t addr;
+ int sap;
+ int rc = parse_url (remote_address, &addr);
+
+ addr.sockaddr.sin_port =
+ htons (ntohs (addr.sockaddr.sin_port) + port_offset);
+ VERBOSE (sap, 1, PRINTF ("remote port: %d\n", ntohs (addr.sockaddr.sin_port)));
+
+ if (rc < 0) {
+ errno = EINVAL;
+ return rc;
+ }
+
+ if (local_address) {
+ sap = sap_create_and_listen (local_address, 0, port_offset);
+ } else {
+ switch (addr.type) {
+ case SAP_tcp:
+ sap = socket (PF_INET, SOCK_STREAM, 0);
+ break;
+ case SAP_udp:
+ sap = socket (PF_INET, SOCK_DGRAM, 0);
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ if (sap < 0)
+ return sap;
+ if (sap >= MAX_SAP_NUMBER) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ SETNOBLK (sap);
+ rc = connect (sap, (struct sockaddr *)&addr.sockaddr, sizeof (addr.sockaddr));
+
+ if (addr.type == SAP_tcp) {
+ int val = 1;
+ rc = setsockopt (sap, IPPROTO_TCP, TCP_NODELAY, (OPT_TYPE *)&val, sizeof (val));
+ if (rc < 0)
+ perror ("setsockopt/TCP_NODELAY");
+ }
+
+ assert (SAP_list[sap] == NULL);
+ SAP_list[sap] = (SAP_descriptor_t *) calloc (1, sizeof (SAP_descriptor_t));
+ assert (SAP_list[sap]);
+ if (__sync_bool_compare_and_swap (&__init, 1, 0))
+ atexit (free_saps);
+
+ return sap;
+}
+
+int SAP_IsConnected (int sap)
+{
+ int sockerr;
+ socklen_t rlen = sizeof (sockerr);
+
+ int rc = getsockopt (sap, SOL_SOCKET, SO_ERROR, (OPT_TYPE *)&sockerr, &rlen);
+ if (rc < 0)
+ return -1;
+
+ switch (sockerr) {
+ case 0:
+ return 1;
+ break;
+ case EINPROGRESS:
+ return 0;
+ break;
+ }
+ return -1;
+}
+
+int SAP_Accept (int sap)
+{
+
+ int newsap = accept (sap, NULL, 0);
+ if (newsap < 0)
+ return newsap;
+ if (newsap >= MAX_SAP_NUMBER) {
+ errno = ENOMEM;
+ return -1;
+ }
+ assert (SAP_list[newsap] == NULL);
+
+ SAP_list[newsap] = (SAP_descriptor_t *)calloc (1, sizeof (SAP_descriptor_t));
+ assert (SAP_list[newsap]);
+ if (__sync_bool_compare_and_swap (&__init, 1, 0))
+ atexit (free_saps);
+
+ SETNOBLK (newsap);
+
+ int val = 1;
+ int rc = setsockopt (newsap, IPPROTO_TCP, TCP_NODELAY, (OPT_TYPE *)&val, sizeof (val));
+ if (rc < 0)
+ perror ("setsockopt/TCP_NODELAY");
+
+ return newsap;
+}
+
+void SAP_Reject (int sap)
+{
+ int newsap = accept (sap, NULL, 0);
+ close (newsap);
+}
+
+void SAP_Close (int sap)
+{
+ if ((sap < 0) || (sap >= MAX_SAP_NUMBER))
+ return;
+ close (sap);
+ if (SAP_list[sap]) {
+ free (SAP_list[sap]);
+ SAP_list[sap] = NULL;
+ }
+}
+
+void SAP_Set_Blocking (int sap)
+{
+ SETBLOCK (sap);
+ if (SAP_list[sap])
+ SAP_list[sap]->is_blocking = 0;
+}
+
+int SAP_Send (int sap, unsigned long msgtype, unsigned char *data, int len)
+{
+
+ /* Message format is
+ * bytes 0-1 : len
+ * bytes 2-3 : seqnumber
+ * bytes 4-7 : msgtype
+ * bytes 8- : data
+ */
+
+ assert (((data != NULL) && (len > 0)) || ((data == NULL) && (len == 0)));
+
+ int msglen = len + 8;
+
+ if ((sap < 0) || (sap >= MAX_SAP_NUMBER) || (SAP_list[sap] == NULL)) {
+ errno = EBADF;
+ return -1;
+ }
+
+ unsigned char *buffer = calloc (1, msglen);
+ if (buffer == NULL) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ *(unsigned short *)buffer = htons (len);
+ *(unsigned short *)(buffer + 2) = SAP_list[sap]->tx_seqnum++;
+ *(unsigned long *)(buffer + 4) = htonl (msgtype);
+ if (len > 0)
+ memcpy (buffer + 8, data, len);
+
+ int rc = write (sap, buffer, msglen);
+ free (buffer);
+
+ return (rc >= 0) ? rc - 8 : rc;
+}
+
+int SAP_Send_Raw (int sap, unsigned long msgtype, unsigned char *data, int len)
+{
+
+ /* Message format is
+ * bytes 0-1 : len
+ * bytes 2-3 : seqnumber
+ * bytes 4-7 : msgtype
+ * bytes 8- : data
+ */
+
+ int msglen = len + 8;
+
+ if ((sap < 0) || (sap >= MAX_SAP_NUMBER) || (SAP_list[sap] == NULL)) {
+ errno = EBADF;
+ return -1;
+ }
+
+ unsigned char *buffer = calloc (1, msglen);
+ if (buffer == NULL) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ *(unsigned short *)buffer = htons (len);
+ *(unsigned short *)(buffer + 2) = SAP_list[sap]->tx_seqnum++;
+ *(unsigned long *)(buffer + 4) = htonl (msgtype);
+ memcpy (buffer + 8, data, len);
+
+ int rc = write (sap, buffer, msglen);
+ free (buffer);
+
+ return (rc >= 0) ? rc - 8 : rc;
+}
+
+int SAP_Receive_timeout (int sap, unsigned long *msgtype, unsigned char *buffer,
+ int buflen, int microseconds)
+{
+
+ fd_set rfds;
+ struct timeval tv = { 0, microseconds };
+ FD_ZERO (&rfds);
+ FD_SET (sap, &rfds);
+
+ int retval = select (sap + 1, &rfds, NULL, NULL, &tv);
+ if (retval != 1)
+ return retval;
+
+ return SAP_Receive (sap, msgtype, buffer, buflen);
+}
+
+int SAP_Receive (int sap, unsigned long *msgtype, unsigned char *buffer, int buflen)
+{
+ int len;
+ int closed = 0;
+
+ if ((sap < 0) || (sap >= MAX_SAP_NUMBER) || (SAP_list[sap] == NULL)) {
+ errno = EBADF;
+ return -1;
+ }
+
+ /* Read has much has possible in our internal buffer */
+ /* put received data after remaining data from previous read */
+
+ uint8_t *value = SAP_list[sap]->buffer;
+ if ((SAP_list[sap]->remain <= 8) ||
+ (ntohs (*(uint16_t *)value) > SAP_list[sap]->remain + 8)) {
+
+ int rc = read (sap, SAP_list[sap]->buffer + SAP_list[sap]->remain,
+ sizeof (SAP_list[sap]->buffer) - SAP_list[sap]->remain);
+
+ if (rc == 0)
+ closed = -1;
+
+ if ((rc < 0) && (errno != EAGAIN))
+ return -1;
+
+ if (rc > 0)
+ SAP_list[sap]->remain += rc;
+ }
+
+ /* Message header not fully received */
+ if (SAP_list[sap]->remain <= 8)
+ return closed;
+
+ /* Payload of message not fully received */
+ len = ntohs (*(unsigned short *)value);
+ if (len > SAP_list[sap]->remain + 8)
+ return closed;
+
+ if (buflen < len) {
+ VERBOSE (sap, 0, FPRINTF (stderr, "buflen %d < len %d (0x%X)\n", buflen, len, len));
+ return -ENOBUFS;
+ }
+
+ unsigned short seqnumber =
+ ntohs (*(unsigned short *)(SAP_list[sap]->buffer + 2));
+ if ((SAP_list[sap]->is_blocking) &&
+ (seqnumber != ((SAP_list[sap]->rx_seqnum + 1) & 0xffff)))
+ VERBOSE (sap, 0, FPRINTF (stderr, "missing sequence number\n"));
+ SAP_list[sap]->rx_seqnum = seqnumber;
+
+ *msgtype = ntohl (*(unsigned long *)(SAP_list[sap]->buffer + 4));
+ memcpy (buffer, SAP_list[sap]->buffer + 8, len);
+
+ /* Move remaining data to begin of private buffer */
+ SAP_list[sap]->remain -= len + 8;
+ memmove (SAP_list[sap]->buffer, SAP_list[sap]->buffer + 8 + len, SAP_list[sap]->remain);
+
+ return len;
+}
+
+/* vi:set tabstop=4 expandtab shiftwidth=4: this line set vi mode*/
--- /dev/null
+/*
+ File name : parse.h
+ Projet : MERLIN
+ Date of creation : 2025/03/19
+ Version : 1.0
+ Copyright : Thales SIX
+ Author : Laurent Mazet <laurent.mazet@thalesgroup.com>
+
+ Description : This file contains parse macros
+
+ History :
+ - initial version
+*/
+
+#ifndef __PARSE_H__
+#define __PARSE_H__
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ Begin of switch/case on a string
+
+ @param str string
+*/
+#define STR_SWITCH(str) \
+ { char *str_var = str; if (0) {}
+
+/**
+ Case of a switch/case on a string
+
+ @param value string value
+*/
+#define CASE(value) \
+ else if (strcasecmp (str_var, value) == 0)
+/**
+ Case of a switch/case on a string
+
+ @param value string value
+ @param assign statement
+*/
+#define CASE_MAP(value, assign) \
+ else if (strcasecmp (str_var, value) == 0) { assign; }
+
+/**
+ Default of a switch/case on a string
+*/
+#define DEFAULT else
+
+/**
+ End of a switch/case on a string
+*/
+#define END_SWITCH(str) }
+
+/**
+ Init parsing macro
+
+ @param delim delimitator characters (usualy "=\t\n\r")
+*/
+#define INIT_BIG_SWITCH(_delim) \
+ const char *delim = _delim; \
+ char *save_ptr; \
+ char *str = strdup (c_buf); \
+ char *ascii_type = strtok_r (str, delim, &save_ptr); \
+ STR_SWITCH(ascii_type)
+
+/**
+ Begin of parsing macro
+
+ @param T message type
+*/
+#define PARSE(T) \
+ T *e = (T *) msg; \
+ memset (msg, 0, sizeof(*e)); \
+ while (1) { \
+ char *var = strtok_r (NULL, delim, &save_ptr); \
+ if (!var) break; \
+ char *val = strtok_r (NULL, delim, &save_ptr); \
+ if (!val) break; \
+ STR_SWITCH(var)
+
+/**
+ End of parsing macro
+*/
+#define END_PARSE() \
+ DEFAULT { \
+ fprintf(stderr, "ERROR : %s message : %s field unknown\n", \
+ ascii_type, var); \
+ rc = -1; \
+ break; \
+ } \
+ END_SWITCH(var) \
+}
+
+/**
+ End parsing macro
+*/
+#define END_BIG_SWITCH() \
+ END_SWITCH(ascii_type) \
+ free (str);
+
+/**
+ Trim a string
+
+ @param buf string
+ @return trimed string
+*/
+static inline char *trim (char *buf)
+{
+ int l = 0;
+ while (buf[l] !='\0')
+ l++;
+ while (l-- > 0) {
+ switch (buf[l]) {
+ case '\n':
+ case '\r':
+ case '\t':
+ case ' ':
+ buf[l] = '\0';
+ continue;
+ }
+ l++;
+ break;
+ }
+
+ int i = 0;
+ while ((i < l) && ((buf[i] == ' ') || (buf[i] == '\t')))
+ i++;
+ if (i > 0) {
+ int k;
+ for (k = 0; k < l - i + 1; k++)
+ buf[k] = buf[i + k];
+ }
+
+ return buf;
+}
+
+__END_DECLS
+
+#endif /* __PARSE_H__ */
--- /dev/null
+/*
+ * File: raw_ethernet.cpp
+ * Author: fredo-local
+ *
+ * Created on 28 janvier 2009, 16:46
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include "tools.h"
+
+#include "raw_ethernet.h"
+
+raw_ethernet::raw_ethernet() {
+}
+
+int raw_ethernet::Open(const char *ifname, const char *mac_address, unsigned short EthType) {
+ this->mac_address = strdup(mac_address);
+
+ parse_mac(mac_address, this->remote_mac);
+
+ sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (sock < 0) {
+ VERBOSE (VERBOSE_ERROR, printf ("socket(): %s\n", strerror (errno)));
+ VERBOSE (VERBOSE_ERROR, printf("Warning: you must be root to have raw access to sockets\n"));
+ return -1;
+ }
+
+ memset(&sock_addr, 0, sizeof (struct sockaddr_ll));
+
+ sock_addr.sll_family = PF_PACKET;
+ sock_addr.sll_protocol = htons(EthType);
+ sock_addr.sll_ifindex = Get_IfaceIndex(ifname);
+ if (sock_addr.sll_ifindex < 0) return -1;
+ sock_addr.sll_hatype = ARPHRD_ETHER;
+ sock_addr.sll_pkttype = PACKET_OTHERHOST;
+ sock_addr.sll_halen = ETH_ALEN;
+ if (Get_MacAddress(ifname, sock_addr.sll_addr) != 0) {
+ printf("Cannot get local mac address for %s\n", ifname);
+ return -1;
+ }
+
+ int res = bind(sock, (struct sockaddr *) & sock_addr, sizeof (sock_addr));
+ if (res != 0) {
+ VERBOSE (VERBOSE_ERROR, printf ("bind(): %s\n", strerror (errno)));
+ return -1;
+ }
+
+ printf("I/O on %s local=[", ifname);
+ for (int i = 0; i < 6; i++) {
+ printf("%02x", sock_addr.sll_addr[i]);
+ if (i < 5) printf(":");
+ }
+ printf("]\n");
+
+ eth_type = htons(EthType);
+ memset(TxBuf, 0, sizeof (TxBuf));
+ memcpy(TxBuf, (void *) remote_mac, 6);
+ memcpy(TxBuf + 6, sock_addr.sll_addr, 6);
+ memcpy(TxBuf + 12, (void *) & eth_type, 2);
+
+ return 0;
+}
+
+raw_ethernet::~raw_ethernet() {
+}
+
+int raw_ethernet::parse_mac(const char *hexa, unsigned char *result) {
+ if (strlen(hexa) != 17) return -1;
+ for (int i = 0; i < 6; i++) {
+ char one_byte[] = "xx";
+ memcpy(one_byte, hexa + 3 * i, 2);
+ result[i] = strtoul(one_byte, NULL, 16);
+ }
+ return 0;
+}
+
+int raw_ethernet::Get_IfaceIndex(const char *ifname) {
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof (ifr));
+ strcpy(ifr.ifr_name, ifname);
+ int res = ioctl(sock, SIOCGIFINDEX, &ifr);
+ if (res != 0) {
+ VERBOSE (VERBOSE_ERROR, printf ("Get ethernet interface index: %s\n", strerror (errno)));
+ return -1;
+ }
+ return ifr.ifr_ifindex;
+}
+
+int raw_ethernet::Get_MacAddress(const char *ifname, unsigned char *mac) {
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof (ifr));
+ strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ int res = ioctl(sock, SIOCGIFHWADDR, &ifr);
+ if (res != 0) {
+ VERBOSE (VERBOSE_ERROR, printf ("Get ethernet mac address: %s\n", strerror (errno)));
+ return -1;
+ }
+
+ unsigned char *p = (unsigned char *)ifr.ifr_hwaddr.sa_data;
+ for (int i = 0; i < 6; i++) {
+ //while (*p == 0xFF) p++;
+ mac[i] = *p++;
+ }
+ return 0;
+}
+
+unsigned char *raw_ethernet::RxData(int nb) {
+ while (1) {
+ struct sockaddr_ll addr;
+
+ unsigned int sock_addr_len = sizeof (addr);
+
+ int len = recvfrom(sock, RxBuf, sizeof (RxBuf), 0, (struct sockaddr *) & addr, &sock_addr_len);
+ if (len != 14 + nb) continue; // Not for us, length is bad
+ unsigned short rx_ethtype;
+ memcpy(&rx_ethtype, RxBuf+12, 2);
+ if (rx_ethtype != eth_type) continue; // Not for us, bad ethtype
+#if 1
+ bool is_correct_mac_addr_source = true;
+ for (int i = 0; i < 6; i++)
+ if (remote_mac[i] != RxBuf[i + 6])
+ is_correct_mac_addr_source = false;
+ if (!is_correct_mac_addr_source) continue; // Not from our source
+#endif
+ return RxBuf+14;
+ }
+}
+
+int raw_ethernet::TxData(int nb) {
+ int rc = sendto(sock, TxBuf, nb + 14, 0, (struct sockaddr *) &sock_addr,
+ sizeof (sock_addr));
+ if (rc < 14) return -1;
+ return rc-14;
+}
+
+int raw_ethernet::SetNonBlocking ()
+{
+ int flags = fcntl (sock, F_GETFL, 0);
+ return fcntl (sock, F_SETFL, flags | O_NONBLOCK);
+}
--- /dev/null
+/* -*- C++ -*- */
+
+/*
+ * File: raw_ethernet.h
+ * Author: fredo-local
+ *
+ * Created on 28 janvier 2009, 16:46
+ */
+
+#ifndef _RAW_ETHERNET_H
+#define _RAW_ETHERNET_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/if_arp.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <sys/ioctl.h>
+
+class raw_ethernet {
+public:
+ raw_ethernet();
+ virtual ~raw_ethernet();
+ int Open(const char *ifname, const char *mac_address, unsigned short EthType);
+ unsigned char *GetTxBuf() { return TxBuf +14; };
+ unsigned char *RxData(int nb);
+ int TxData(int nb);
+ int SetNonBlocking ();
+
+private:
+ char *mac_address;
+ int sock;
+ unsigned char TxBuf[9014]; // extended for jumbo frame (standard was 1514)
+ unsigned char RxBuf[9014]; // extended for jumbo frame (standard was 1514)
+ unsigned short eth_type;
+ unsigned char remote_mac[6];
+ struct sockaddr_ll sock_addr;
+
+ int parse_mac(const char *hexa, unsigned char *result);
+ int Get_MacAddress(const char *ifname, unsigned char *mac);
+ int Get_IfaceIndex(const char *ifname);
+};
+
+#endif /* _RAW_ETHERNET_H */
+
--- /dev/null
+/*
+ Service Access Point communication library
+
+ Copyrigth Eichos 20100319
+*/
+
+#ifndef __SAP_H__
+#define __SAP_H__
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ @defgroup SAP Public API of SAP
+
+ The common message passing library offers both reliable and
+ unreliable connection oriented data blocks exchange service between
+ software modules running on separate CPU. The current
+ implementation is based on TCP (reliable) and UDP (unreliable),
+ however, the abstraction level provided by this library will permit
+ future implementations on other communication networks (shared
+ memory for example).
+
+ sap values returned by SAP_Create(), SAP_Connect and
+ SAP_Accept() are standard Linux file descriptors that can be used
+ in select()/poll() system calls.
+*/
+
+/**
+ @ingroup SAP
+
+ Create a new SAP (Service Access Point) that will be used later for
+ outgoing or incoming connection
+
+ @param local_address local address of the SAP with the following
+ syntax: 'udp://hostname:port' or 'tcp://hostname:port', hostname
+ can be '*' to listen on any address handled by the host
+
+ @return a new file descriptor associated with this SAP
+
+ @see SAP_Connect()
+ @see SAP_Accept()
+ @see SAP_Reject()
+ @see SAP_Close()
+*/
+int SAP_Create (char *local_address);
+
+/**
+ @ingroup SAP
+
+ Establish a connection between a local SAP and a remote SAP. Block
+ until connection is established or refused.
+
+ @param local_address local address of the SAP with the following
+ syntax: 'udp://hostname:port' or 'tcp://hostname:port'. Can be
+ NULL if local address dot not need to be specified
+ @param remote_address same format as for local_address
+
+ @return a new file descriptor associated with this connection
+
+ @see SAP_Create()
+ @see SAP_Accept()
+ @see SAP_Reject()
+ @see SAP_Close()
+ @see SAP_Send()
+ @see SAP_Receive()
+*/
+int SAP_Connect (char *local_address, char *remote_address);
+
+/**
+ @ingroup SAP
+
+ Establish a connection between a local SAP and a remote SAP. Block
+ until connection is established or refused. Apply the specified
+ offset to local and remote port number
+
+ @param local_address local address of the SAP with the following
+ syntax: 'udp://hostname:port' or 'tcp://hostname:port'. Can be
+ NULL if local address dot not need to be specified
+ @param remote_address same format as for local_address
+ @param port_offset offset to apply to port numbers
+
+ @return a new file descriptor associated with this connection
+
+ @see SAP_Create()
+ @see SAP_Accept()
+ @see SAP_Reject()
+ @see SAP_Close()
+ @see SAP_Send()
+ @see SAP_Receive()
+*/
+int SAP_Connect_Offset (char *local_address, char *remote_address,
+ int port_offset);
+
+/**
+ @ingroup SAP
+
+ Check if connection is established, typ. called after
+ SAP_Connect()
+
+ @param sap sap retuned by SAP_Connect
+
+ @return -1=error, 0=not yet connected, 1=connected
+
+ @see SAP_Connect()
+*/
+int SAP_Is_Connected (int sap);
+
+/**
+ @ingroup SAP
+
+ Accept an incoming connection. Returns immediately, even there is
+ no pending incoming connection.
+
+ @param sap the file descriptor retuned by SAP_Create()
+
+ @return a new file descriptor associated with this connection or -1,
+ errno=EWOULDBLOCK if there is no pending connection.
+
+ @see SAP_Create()
+ @see SAP_Reject()
+ @see SAP_Close()
+ @see SAP_Send()
+ @see SAP_Receive()
+*/
+int SAP_Accept (int sap);
+
+/**
+ @ingroup SAP
+
+ Reject an incoming connection. Returns immediately.
+
+ @param sap the file descriptor retuned by SAP_Create()
+
+ @see SAP_Create()
+ @see SAP_Accept()
+ @see SAP_Close()
+ @see SAP_Send()
+ @see SAP_Receive()
+*/
+void SAP_Reject (int sap);
+
+/**
+ @ingroup SAP
+
+ Destroy a SAP.
+
+ @param sap the file descriptor retuned by SAP_Create() or
+ SAP_Connect() or SAP_Accept()
+
+ @see SAP_Create()
+ @see SAP_Accept()
+ @see SAP_Connect()
+*/
+void SAP_Close (int sap);
+
+/**
+ @ingroup SAP
+
+ Turn a SAP in I/O blocking mode.
+
+ @param sap the file descriptor retuned by SAP_Create() or
+ SAP_Connect() or SAP_Accept()
+
+ @see SAP_Create()
+ @see SAP_Accept()
+ @see SAP_Connect()
+*/
+void SAP_Set_Blocking (int sap);
+
+/**
+ @ingroup SAP
+
+ Send a message over an existing SAP connection. Will block until
+ resolution in case of congestion.
+
+ @param sap the file descriptor retuned by SAP_Connect() or
+ SAP_Accept()
+ @param msgtype a user defined message type identifier, transported
+ transparently
+ @param data a pointer to the buffer containing the message to be
+ transmitted
+ @param len length of the message
+
+ @see SAP_Accept()
+ @see SAP_Connect()
+*/
+int SAP_Send (int sap, unsigned long msgtype, unsigned char *data, int len);
+
+/**
+ @ingroup SAP
+
+ Received a message over an existing SAP connection. Returns
+ immediately if SAP not set to blocking mode
+
+ @param sap the file descriptor retuned by SAP_Connect() or
+ SAP_Accept()
+ @param msgtype a pointer to an unsigned long to receive the user
+ defined message type identifier, transported transparently
+ @param buffer a pointer to the reception buffer
+ @param buflen length of the reception buffer
+
+ @return length of the message or 0 if no message was pending
+
+ @see SAP_Accept()
+ @see SAP_Connect()
+*/
+int SAP_Receive (int sap, unsigned long *msgtype, unsigned char *buffer,
+ int buflen);
+
+/**
+ @ingroup SAP
+
+ Received a message over an existing SAP connection with a timeout
+
+ @param sap the file descriptor retuned by SAP_Connect() or
+ SAP_Accept()
+ @param msgtype a pointer to an unsigned long to receive the user
+ defined message type identifier, transported transparently
+ @param buffer a pointer to the reception buffer
+ @param buflen length of the reception buffer
+ @param ms Exit after microseconds if now data is received
+
+ @return length of the message or 0 if no message was pending
+
+ @see SAP_Accept()
+ @see SAP_Connect()
+*/
+int SAP_Receive_timeout (int sap, unsigned long *msgtype,
+ unsigned char *buffer, int buflen, int ms);
+
+__END_DECLS
+
+#endif /* __SAP_H__ */
+
+/* vi:set tabstop=4 expandtab shiftwidth=4: this line set vi mode */
--- /dev/null
+/*
+ File: sap_valid.c
+ Author: Laurent Mazet <mazet@eichos.com>
+
+ Validation test suite for SAP library
+
+ Copyrigth Eichos 20100322
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tools.h"
+
+#include "sap.h"
+
+int SAP_Accept_NONBLOCK_OK = 0;
+int SAP_Receive_TCP_NONBLOCK_OK = 0;
+int SAP_Receive_UDP_NONBLOCK_OK = 0;
+int SAP_Send_TCP_OK = 0;
+int SAP_Echo_TCP_OK = 0;
+int SAP_Send_UDP_OK = 0;
+int SAP_Echo_UDP_OK = 0;
+
+void *tcp_server (UNUSED void *dummy)
+{
+
+ VERBOSE (sap, 1, PRINTF ("tcp_server started\n"));
+
+ int sap = SAP_Create ("tcp://localhost:12500");
+ if (sap < 0) {
+ perror ("SAP_Create");
+ exit (0);
+ }
+
+ VERBOSE (sap, 1, PRINTF ("tcp_server waiting for connection\n"));
+
+ while (1) {
+ int newsap = SAP_Accept (sap);
+ if (newsap < 0 && errno == EAGAIN)
+ SAP_Accept_NONBLOCK_OK = 1;
+ if (newsap < 0) {
+ sleep (1);
+ continue;
+ }
+
+ VERBOSE (sap, 1, PRINTF ("tcp_server connected, waiting for data\n"));
+
+ while (1) {
+ unsigned char data[8192];
+ unsigned long msgtype;
+
+ int rx_len = SAP_Receive (newsap, &msgtype, data, sizeof (data));
+ if (rx_len == 0) {
+ SAP_Receive_TCP_NONBLOCK_OK = 1;
+ sleep (1);
+ continue;
+ }
+ if (rx_len < 0) {
+ VERBOSE (sap, 1, FPRINTF (stderr, "tcp_server connection closed by peer\n"));
+ SAP_Close (newsap);
+ break;
+ }
+
+ VERBOSE (sap, 1, PRINTF ("tcp_server receive data len=%d, sending echo\n", rx_len));
+
+ int tx_len = SAP_Send (newsap, msgtype, data, rx_len);
+ if (tx_len == rx_len)
+ SAP_Send_TCP_OK = 1;
+ if (tx_len < 0) {
+ VERBOSE (sap, 1, FPRINTF (stderr, "tcp_server connection closed by peer\n"));
+ SAP_Close (newsap);
+ break;
+ }
+ /* sleep so that we go through remaining data case in
+ receive buffer */
+ usleep (1e5);
+ }
+ }
+}
+
+int test_client (char *local_address, char *remote_address)
+{
+ int count;
+ unsigned char tx_data[10][8192];
+ unsigned long tx_msgtype[10];
+ int tx_len[10];
+
+ VERBOSE (sap, 0, PRINTF ("%s started\n", remote_address));
+
+ int sap = SAP_Connect (local_address, remote_address);
+ if (sap < 0) {
+ VERBOSE (sap, 0, FPRINTF (stderr, "%s SAP_Connect error\n", remote_address));
+ return -1;
+ }
+
+ VERBOSE (sap, 0, PRINTF ("%s connected to server, trying to exchange 10 messages\n", remote_address));
+
+ for (count = 0; count < 10; count++) {
+ int i, rc;
+
+ /* to valid non block */
+ if (count == 5)
+ sleep (1);
+
+ tx_len[count] = rand () % sizeof (tx_data[count]);
+ tx_msgtype[count] = rand ();
+ for (i = 0; i < tx_len[count]; i++)
+ tx_data[count][i] = rand () % 256;
+
+ rc = SAP_Send (sap, tx_msgtype[count], tx_data[count], tx_len[count]);
+ if (rc < 0) {
+ VERBOSE (sap, 0, FPRINTF (stderr, "%s SAP_Send tx error\n", remote_address));
+ return -1;
+
+ }
+ if (tx_len[count] != rc) {
+ VERBOSE (sap, 0, FPRINTF (stderr, "%s SAP_Send tx len error tx=%d rc=%d\n", remote_address, tx_len[count], rc));
+ return -1;
+ }
+ }
+
+ for (count = 0; count < 10; count++) {
+ int rx_len;
+ unsigned char rx_data[32768];
+ unsigned long rx_msgtype;
+
+
+ do {
+ rx_len = SAP_Receive (sap, &rx_msgtype, rx_data, sizeof (rx_data));
+ if (rx_len == 0)
+ sleep (1);
+ } while (rx_len == 0);
+
+ VERBOSE (sap, 1, PRINTF ("%s echo received count=%d len=%d, sap=%d\n", remote_address, count, rx_len, sap));
+
+ if (tx_len[count] != rx_len) {
+ VERBOSE (sap, 0, FPRINTF (stderr, "%s SAP_Receive rx len error : tx=%d / rx=%d\n", remote_address, tx_len[count], rx_len));
+ return -1;
+ }
+
+ if (tx_msgtype[count] != rx_msgtype) {
+ VERBOSE (sap, 0, FPRINTF (stderr, "%s SAP_Receive rx msgtype error : tx=%08lX / rx=%08lX\n", remote_address, tx_msgtype[count], rx_msgtype));
+ return -1;
+ }
+
+ if (memcmp (rx_data, tx_data[count], tx_len[count]) != 0 ) {
+ VERBOSE (sap, 0, FPRINTF (stderr, "%s SAP_Receive rx corrupted data\n",remote_address));
+ return -1;
+ }
+ }
+
+ SAP_Close (sap);
+ VERBOSE (sap, 0, PRINTF ("%s end of test, %d echo processed\n", remote_address, count));
+ return 0;
+}
+
+void *udp_server (UNUSED void *dummy)
+{
+ VERBOSE (sap, 0, PRINTF ("udp_server started\n"));
+
+ int sap = SAP_Connect ("udp://localhost:12501", "udp://localhost:13501");
+ if (sap < 0) {
+ perror ("SAP_Connect (%d)");
+ exit (1);
+ }
+
+ VERBOSE (sap, 0, PRINTF ("udp_server waiting for data\n"));
+
+ while (1) {
+ unsigned char data[8192];
+ unsigned long msgtype;
+
+ int rx_len = SAP_Receive (sap, &msgtype, data, sizeof (data));
+ if (rx_len == 0) {
+ SAP_Receive_UDP_NONBLOCK_OK = 1;
+ sleep (1);
+ continue;
+ }
+ if (rx_len < 0) {
+ SAP_Close (sap);
+ break;
+ }
+
+ VERBOSE (sap, 1, PRINTF ("udp_server receive data len=%d, sending echo\n", rx_len));
+
+ int tx_len = SAP_Send (sap, msgtype, data, rx_len);
+ if (tx_len == rx_len)
+ SAP_Send_UDP_OK = 1;
+ }
+
+ return NULL;
+}
+
+void *tcp_client (UNUSED void *dummy)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ int rc = test_client (NULL, "tcp://localhost:12500");
+ if (rc)
+ pthread_exit (&SAP_Echo_TCP_OK);
+ }
+ for (i = 0; i < 5; i++) {
+ int rc = test_client ("tcp://*:0", "tcp://localhost:12500");
+ if (rc)
+ pthread_exit (&SAP_Echo_TCP_OK);
+ }
+
+
+ SAP_Echo_TCP_OK = 1;
+ pthread_exit (&SAP_Echo_TCP_OK);
+ return NULL;
+}
+
+void *udp_client (UNUSED void *dummy)
+{
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ int rc = test_client ("udp://localhost:13501", "udp://localhost:12501");
+ if (rc)
+ pthread_exit (&SAP_Echo_UDP_OK);
+ }
+
+ SAP_Echo_UDP_OK = 1;
+ pthread_exit (&SAP_Echo_UDP_OK);
+ return NULL;
+}
+
+/**
+ Dump status macro
+*/
+#define DUMP_STATUS(x) do { \
+ printf (#x " %s\n", (x) ? "OK" : "BAD"); \
+ if (!(x)) \
+ failed = 1; \
+ } while (0) \
+
+/**
+ Verbose level
+*/
+DECLARE_VERBOSE_LEVEL(sap, 0);
+
+int main (int argc, char **argv)
+{
+
+ /* process arguments */
+ if (argc > 1) {
+ CHANGE_VERBOSE_LEVEL(sap, atoi (argv[1]));
+ argc--;
+ }
+ if (argc > 1) {
+ printf ("usage: %s [verbose level]\n", argv[0]);
+ exit (1);
+ }
+
+ printf ("*** STARTING TEST SEQUENCE ***\n");
+
+ pthread_t tcp_thread, udp_thread;
+
+ pthread_create (&tcp_thread, NULL, tcp_server, NULL);
+ pthread_create (&udp_thread, NULL, udp_server, NULL);
+
+ /* Give some delay to server to setup there SAP */
+ sleep (1);
+
+ pthread_t tcp_client_thread, udp_client_thread;
+
+ pthread_create (&tcp_client_thread, NULL, tcp_client, NULL);
+ pthread_create (&udp_client_thread, NULL, udp_client, NULL);
+
+ pthread_join (tcp_client_thread, NULL);
+ pthread_join (udp_client_thread, NULL);
+
+ printf ("*** END OF TEST SEQUENCE ***\n");
+
+ int failed = 0;
+ DUMP_STATUS (SAP_Accept_NONBLOCK_OK);
+ DUMP_STATUS (SAP_Receive_TCP_NONBLOCK_OK);
+ DUMP_STATUS (SAP_Receive_UDP_NONBLOCK_OK);
+ DUMP_STATUS (SAP_Send_TCP_OK);
+ DUMP_STATUS (SAP_Echo_TCP_OK);
+ DUMP_STATUS (SAP_Send_UDP_OK);
+ DUMP_STATUS (SAP_Echo_UDP_OK);
+
+ return failed ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+/* test-depend: sap_valid */
+
+/* test: ./sap_valid */
+/* test: ./sap_valid 1 */
+
+/* vi:set tabstop=4 expandtab shiftwidth=4: this line set vi mode*/