initial commit
authorLaurent MAZET <laurent.mazet@thalesgroup.com>
Wed, 19 Mar 2025 17:27:12 +0000 (18:27 +0100)
committerLaurent MAZET <laurent.mazet@thalesgroup.com>
Wed, 19 Mar 2025 17:27:12 +0000 (18:27 +0100)
debug.c [new file with mode: 0644]
debug.h [new file with mode: 0644]
makefile [new file with mode: 0644]
more.c [new file with mode: 0644]
parse.h [new file with mode: 0644]
raw_ethernet.cpp [new file with mode: 0644]
raw_ethernet.h [new file with mode: 0644]
sap.h [new file with mode: 0644]
sap_valid.c [new file with mode: 0644]

diff --git a/debug.c b/debug.c
new file mode 100644 (file)
index 0000000..b9eba44
--- /dev/null
+++ b/debug.c
@@ -0,0 +1,5 @@
+#include "debug.h"
+
+int verbose = 0;
+
+/* vim: set ts=4 sw=4 et: */
diff --git a/debug.h b/debug.h
new file mode 100644 (file)
index 0000000..03215c1
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,106 @@
+/*
+  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__ */
diff --git a/makefile b/makefile
new file mode 100644 (file)
index 0000000..921318b
--- /dev/null
+++ b/makefile
@@ -0,0 +1,231 @@
+# 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
diff --git a/more.c b/more.c
new file mode 100644 (file)
index 0000000..f3dc053
--- /dev/null
+++ b/more.c
@@ -0,0 +1,462 @@
+/*
+  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*/
diff --git a/parse.h b/parse.h
new file mode 100644 (file)
index 0000000..d247d26
--- /dev/null
+++ b/parse.h
@@ -0,0 +1,143 @@
+/*
+  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__ */
diff --git a/raw_ethernet.cpp b/raw_ethernet.cpp
new file mode 100644 (file)
index 0000000..b062dea
--- /dev/null
@@ -0,0 +1,145 @@
+/* 
+ * 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);
+}
diff --git a/raw_ethernet.h b/raw_ethernet.h
new file mode 100644 (file)
index 0000000..a841ca7
--- /dev/null
@@ -0,0 +1,56 @@
+/* -*- 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 */
+
diff --git a/sap.h b/sap.h
new file mode 100644 (file)
index 0000000..cf1c5d0
--- /dev/null
+++ b/sap.h
@@ -0,0 +1,237 @@
+/*
+  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 */
diff --git a/sap_valid.c b/sap_valid.c
new file mode 100644 (file)
index 0000000..ed52c07
--- /dev/null
@@ -0,0 +1,300 @@
+/* 
+   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*/