From 74da0c1a26f516b5bafc248375515c2b61d32cc4 Mon Sep 17 00:00:00 2001 From: Laurent MAZET Date: Wed, 19 Mar 2025 18:27:12 +0100 Subject: [PATCH] initial commit --- debug.c | 5 + debug.h | 106 +++++++++++ makefile | 231 ++++++++++++++++++++++++ more.c | 462 +++++++++++++++++++++++++++++++++++++++++++++++ parse.h | 143 +++++++++++++++ raw_ethernet.cpp | 145 +++++++++++++++ raw_ethernet.h | 56 ++++++ sap.h | 237 ++++++++++++++++++++++++ sap_valid.c | 300 ++++++++++++++++++++++++++++++ 9 files changed, 1685 insertions(+) create mode 100644 debug.c create mode 100644 debug.h create mode 100644 makefile create mode 100644 more.c create mode 100644 parse.h create mode 100644 raw_ethernet.cpp create mode 100644 raw_ethernet.h create mode 100644 sap.h create mode 100644 sap_valid.c diff --git a/debug.c b/debug.c new file mode 100644 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 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 + + Description : This file contains verbose macros + + History : + - initial version +*/ + +#ifndef __VERBOSE_H__ +#define __VERBOSE_H__ + +#include +#include + +__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 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/"..\/\(.*\)"//;/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 index 0000000..f3dc053 --- /dev/null +++ b/more.c @@ -0,0 +1,462 @@ +/* + Messaging Over Raw Ethernet communication library + + Copyrigth Thales 20250319 +*/ + +#include +#include +#include +#if WIN32 +#include +#include +#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 +#include +#include +#include +#define OPT_TYPE int +#define SETBLOCK(fd) fcntl (fd, F_SETFL, 0) +#define SETNOBLK(fd) fcntl (fd, F_SETFL, 0_NONBLOCK) +#endif +#include +#include +#include +#include +#include + +#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 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 + + Description : This file contains parse macros + + History : + - initial version +*/ + +#ifndef __PARSE_H__ +#define __PARSE_H__ + +#include +#include +#include + +__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 index 0000000..b062dea --- /dev/null +++ b/raw_ethernet.cpp @@ -0,0 +1,145 @@ +/* + * File: raw_ethernet.cpp + * Author: fredo-local + * + * Created on 28 janvier 2009, 16:46 + */ + +#include +#include +#include +#include + +#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 index 0000000..a841ca7 --- /dev/null +++ b/raw_ethernet.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 + +__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 index 0000000..ed52c07 --- /dev/null +++ b/sap_valid.c @@ -0,0 +1,300 @@ +/* + File: sap_valid.c + Author: Laurent Mazet + + Validation test suite for SAP library + + Copyrigth Eichos 20100322 +*/ + +#include +#include +#include +#include +#include +#include +#include + +#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*/ -- 2.30.2